mount: make -f work even without mtab support
[oweals/busybox.git] / util-linux / mount.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini mount implementation for busybox
4  *
5  * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
6  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
7  * Copyright (C) 2005-2006 by Rob Landley <rob@landley.net>
8  *
9  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
10  */
11
12 /* Design notes: There is no spec for mount.  Remind me to write one.
13
14    mount_main() calls singlemount() which calls mount_it_now().
15
16    mount_main() can loop through /etc/fstab for mount -a
17    singlemount() can loop through /etc/filesystems for fstype detection.
18    mount_it_now() does the actual mount.
19 */
20
21 #include <mntent.h>
22 #include "libbb.h"
23 #include <syslog.h>
24
25 /* Needed for nfs support only... */
26 #include <sys/utsname.h>
27 #undef TRUE
28 #undef FALSE
29 #include <rpc/rpc.h>
30 #include <rpc/pmap_prot.h>
31 #include <rpc/pmap_clnt.h>
32
33 #ifndef MS_SILENT
34 #define MS_SILENT       (1 << 15)
35 #endif
36
37 #if defined(__dietlibc__)
38 /* 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
39  * dietlibc-0.30 does not have implementation of getmntent_r() */
40 static struct mntent *getmntent_r(FILE* stream, struct mntent* result, char* buffer, int bufsize)
41 {
42         struct mntent* ment = getmntent(stream);
43         memcpy(result, ment, sizeof(struct mntent));
44         return result;
45 }
46 #endif
47
48 #define getmntent_buf bb_common_bufsiz1
49
50
51 // Not real flags, but we want to be able to check for this.
52 enum {
53         MOUNT_USERS  = (1<<28)*ENABLE_DESKTOP,
54         MOUNT_NOAUTO = (1<<29),
55         MOUNT_SWAP   = (1<<30),
56 };
57
58
59 #define OPTION_STR "o:t:rwanfvsi"
60 enum {
61         OPT_o = (1 << 0),
62         OPT_t = (1 << 1),
63         OPT_r = (1 << 2),
64         OPT_w = (1 << 3),
65         OPT_a = (1 << 4),
66         OPT_n = (1 << 5),
67         OPT_f = (1 << 6),
68         OPT_v = (1 << 7),
69         OPT_s = (1 << 8),
70         OPT_i = (1 << 9),
71 };
72
73 #if ENABLE_FEATURE_MTAB_SUPPORT
74 #define useMtab (!(option_mask32 & OPT_n))
75 #else
76 #define useMtab 0
77 #endif
78
79 #if ENABLE_FEATURE_MOUNT_FAKE
80 #define fakeIt (option_mask32 & OPT_f)
81 #else
82 #define fakeIt 0
83 #endif
84
85
86 // TODO: more "user" flag compatibility.
87 // "user" option (from mount manpage):
88 // Only the user that mounted a filesystem can unmount it again.
89 // If any user should be able to unmount, then use users instead of user
90 // in the fstab line.  The owner option is similar to the user option,
91 // with the restriction that the user must be the owner of the special file.
92 // This may be useful e.g. for /dev/fd if a login script makes
93 // the console user owner of this device.
94
95 /* Standard mount options (from -o options or --options), with corresponding
96  * flags */
97
98 static const int32_t mount_options[] = {
99         // MS_FLAGS set a bit.  ~MS_FLAGS disable that bit.  0 flags are NOPs.
100
101         USE_FEATURE_MOUNT_LOOP(
102                 /* "loop" */ 0,
103         )
104
105         USE_FEATURE_MOUNT_FSTAB(
106                 /* "defaults" */ 0,
107                 /* "quiet" 0 - do not filter out, vfat wants to see it */
108                 /* "noauto" */ MOUNT_NOAUTO,
109                 /* "sw"     */ MOUNT_SWAP,
110                 /* "swap"   */ MOUNT_SWAP,
111                 USE_DESKTOP(/* "user"  */ MOUNT_USERS,)
112                 USE_DESKTOP(/* "users" */ MOUNT_USERS,)
113                 /* "_netdev" */ 0,
114         )
115
116         USE_FEATURE_MOUNT_FLAGS(
117                 // vfs flags
118                 /* "nosuid"      */ MS_NOSUID,
119                 /* "suid"        */ ~MS_NOSUID,
120                 /* "dev"         */ ~MS_NODEV,
121                 /* "nodev"       */ MS_NODEV,
122                 /* "exec"        */ ~MS_NOEXEC,
123                 /* "noexec"      */ MS_NOEXEC,
124                 /* "sync"        */ MS_SYNCHRONOUS,
125                 /* "async"       */ ~MS_SYNCHRONOUS,
126                 /* "atime"       */ ~MS_NOATIME,
127                 /* "noatime"     */ MS_NOATIME,
128                 /* "diratime"    */ ~MS_NODIRATIME,
129                 /* "nodiratime"  */ MS_NODIRATIME,
130                 /* "loud"        */ ~MS_SILENT,
131
132                 // action flags
133                 /* "bind"        */ MS_BIND,
134                 /* "move"        */ MS_MOVE,
135                 /* "shared"      */ MS_SHARED,
136                 /* "slave"       */ MS_SLAVE,
137                 /* "private"     */ MS_PRIVATE,
138                 /* "unbindable"  */ MS_UNBINDABLE,
139                 /* "rshared"     */ MS_SHARED|MS_RECURSIVE,
140                 /* "rslave"      */ MS_SLAVE|MS_RECURSIVE,
141                 /* "rprivate"    */ MS_SLAVE|MS_RECURSIVE,
142                 /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
143         )
144
145         // Always understood.
146         /* "ro"      */ MS_RDONLY,  // vfs flag
147         /* "rw"      */ ~MS_RDONLY, // vfs flag
148         /* "remount" */ MS_REMOUNT  // action flag
149 };
150
151 static const char mount_option_str[] =
152         USE_FEATURE_MOUNT_LOOP(
153                 "loop" "\0"
154         )
155         USE_FEATURE_MOUNT_FSTAB(
156                 "defaults" "\0"
157                 /* "quiet" "\0" - do not filter out, vfat wants to see it */
158                 "noauto" "\0"
159                 "sw" "\0"
160                 "swap" "\0"
161                 USE_DESKTOP("user" "\0")
162                 USE_DESKTOP("users" "\0")
163                 "_netdev" "\0"
164         )
165         USE_FEATURE_MOUNT_FLAGS(
166                 // vfs flags
167                 "nosuid" "\0"
168                 "suid" "\0"
169                 "dev" "\0"
170                 "nodev" "\0"
171                 "exec" "\0"
172                 "noexec" "\0"
173                 "sync" "\0"
174                 "async" "\0"
175                 "atime" "\0"
176                 "noatime" "\0"
177                 "diratime" "\0"
178                 "nodiratime" "\0"
179                 "loud" "\0"
180
181                 // action flags
182                 "bind" "\0"
183                 "move" "\0"
184                 "shared" "\0"
185                 "slave" "\0"
186                 "private" "\0"
187                 "unbindable" "\0"
188                 "rshared" "\0"
189                 "rslave" "\0"
190                 "rprivate" "\0"
191                 "runbindable" "\0"
192         )
193
194         // Always understood.
195         "ro" "\0"        // vfs flag
196         "rw" "\0"        // vfs flag
197         "remount" "\0"   // action flag
198 ;
199
200 /* Append mount options to string */
201 static void append_mount_options(char **oldopts, const char *newopts)
202 {
203         if (*oldopts && **oldopts) {
204                 /* do not insert options which are already there */
205                 while (newopts[0]) {
206                         char *p;
207                         int len = strlen(newopts);
208                         p = strchr(newopts, ',');
209                         if (p) len = p - newopts;
210                         p = *oldopts;
211                         while (1) {
212                                 if (!strncmp(p, newopts, len)
213                                  && (p[len] == ',' || p[len] == '\0'))
214                                         goto skip;
215                                 p = strchr(p,',');
216                                 if (!p) break;
217                                 p++;
218                         }
219                         p = xasprintf("%s,%.*s", *oldopts, len, newopts);
220                         free(*oldopts);
221                         *oldopts = p;
222  skip:
223                         newopts += len;
224                         while (newopts[0] == ',') newopts++;
225                 }
226         } else {
227                 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
228                 *oldopts = xstrdup(newopts);
229         }
230 }
231
232 /* Use the mount_options list to parse options into flags.
233  * Also return list of unrecognized options if unrecognized!=NULL */
234 static int parse_mount_options(char *options, char **unrecognized)
235 {
236         int flags = MS_SILENT;
237
238         // Loop through options
239         for (;;) {
240                 int i;
241                 char *comma = strchr(options, ',');
242                 const char *option_str = mount_option_str;
243
244                 if (comma) *comma = '\0';
245
246                 // Find this option in mount_options
247                 for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
248                         if (!strcasecmp(option_str, options)) {
249                                 long fl = mount_options[i];
250                                 if (fl < 0) flags &= fl;
251                                 else flags |= fl;
252                                 break;
253                         }
254                         option_str += strlen(option_str) + 1;
255                 }
256                 // If unrecognized not NULL, append unrecognized mount options */
257                 if (unrecognized && i == ARRAY_SIZE(mount_options)) {
258                         // Add it to strflags, to pass on to kernel
259                         i = *unrecognized ? strlen(*unrecognized) : 0;
260                         *unrecognized = xrealloc(*unrecognized, i+strlen(options)+2);
261
262                         // Comma separated if it's not the first one
263                         if (i) (*unrecognized)[i++] = ',';
264                         strcpy((*unrecognized)+i, options);
265                 }
266
267                 if (!comma)
268                         break;
269                 // Advance to next option
270                 *comma = ',';
271                 options = ++comma;
272         }
273
274         return flags;
275 }
276
277 // Return a list of all block device backed filesystems
278
279 static llist_t *get_block_backed_filesystems(void)
280 {
281         static const char filesystems[2][sizeof("/proc/filesystems")] = {
282                 "/etc/filesystems",
283                 "/proc/filesystems",
284         };
285         char *fs, *buf;
286         llist_t *list = 0;
287         int i;
288         FILE *f;
289
290         for (i = 0; i < 2; i++) {
291                 f = fopen(filesystems[i], "r");
292                 if (!f) continue;
293
294                 while ((buf = xmalloc_getline(f)) != 0) {
295                         if (!strncmp(buf, "nodev", 5) && isspace(buf[5]))
296                                 continue;
297                         fs = skip_whitespace(buf);
298                         if (*fs=='#' || *fs=='*' || !*fs) continue;
299
300                         llist_add_to_end(&list, xstrdup(fs));
301                         free(buf);
302                 }
303                 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
304         }
305
306         return list;
307 }
308
309 static llist_t *fslist;
310
311 #if ENABLE_FEATURE_CLEAN_UP
312 static void delete_block_backed_filesystems(void)
313 {
314         llist_free(fslist, free);
315 }
316 #else
317 void delete_block_backed_filesystems(void);
318 #endif
319
320 // Perform actual mount of specific filesystem at specific location.
321 // NB: mp->xxx fields may be trashed on exit
322 static int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts)
323 {
324         int rc = 0;
325
326         if (fakeIt) goto mtab;
327
328         // Mount, with fallback to read-only if necessary.
329
330         for (;;) {
331                 rc = mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
332                                 vfsflags, filteropts);
333
334                 // If mount failed, try
335                 // helper program <mnt_type>
336                 if (ENABLE_FEATURE_MOUNT_HELPERS && rc) {
337                         char *args[6];
338                         int errno_save = errno;
339                         args[0] = mp->mnt_type;
340                         rc = 1;
341                         if (filteropts) {
342                                 args[rc++] = (char *)"-o";
343                                 args[rc++] = filteropts;
344                         }
345                         args[rc++] = mp->mnt_fsname;
346                         args[rc++] = mp->mnt_dir;
347                         args[rc] = NULL;
348                         rc = wait4pid(spawn(args));
349                         if (!rc)
350                                 break;
351                         errno = errno_save;
352                 }
353
354                 if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
355                         break;
356                 if (!(vfsflags & MS_SILENT))
357                         bb_error_msg("%s is write-protected, mounting read-only",
358                                                 mp->mnt_fsname);
359                 vfsflags |= MS_RDONLY;
360         }
361
362         // Abort entirely if permission denied.
363
364         if (rc && errno == EPERM)
365                 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
366
367         /* If the mount was successful, and we're maintaining an old-style
368          * mtab file by hand, add the new entry to it now. */
369  mtab:
370         if (useMtab && !rc && !(vfsflags & MS_REMOUNT)) {
371                 char *fsname;
372                 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
373                 const char *option_str = mount_option_str;
374                 int i;
375
376                 if (!mountTable) {
377                         bb_error_msg("no %s", bb_path_mtab_file);
378                         goto ret;
379                 }
380
381                 // Add vfs string flags
382
383                 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
384                         if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
385                                 append_mount_options(&(mp->mnt_opts), option_str);
386                         option_str += strlen(option_str) + 1;
387                 }
388
389                 // Remove trailing / (if any) from directory we mounted on
390
391                 i = strlen(mp->mnt_dir) - 1;
392                 if (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = '\0';
393
394                 // Convert to canonical pathnames as needed
395
396                 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
397                 fsname = 0;
398                 if (!mp->mnt_type || !*mp->mnt_type) { /* bind mount */
399                         mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
400                         mp->mnt_type = (char*)"bind";
401                 }
402                 mp->mnt_freq = mp->mnt_passno = 0;
403
404                 // Write and close.
405
406                 addmntent(mountTable, mp);
407                 endmntent(mountTable);
408                 if (ENABLE_FEATURE_CLEAN_UP) {
409                         free(mp->mnt_dir);
410                         free(fsname);
411                 }
412         }
413  ret:
414         return rc;
415 }
416
417 #if ENABLE_FEATURE_MOUNT_NFS
418
419 /*
420  * Linux NFS mount
421  * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
422  *
423  * Licensed under GPLv2, see file LICENSE in this tarball for details.
424  *
425  * Wed Feb  8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
426  * numbers to be specified on the command line.
427  *
428  * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
429  * Omit the call to connect() for Linux version 1.3.11 or later.
430  *
431  * Wed Oct  1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
432  * Implemented the "bg", "fg" and "retry" mount options for NFS.
433  *
434  * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
435  * - added Native Language Support
436  *
437  * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
438  * plus NFSv3 stuff.
439  */
440
441 /* This is just a warning of a common mistake.  Possibly this should be a
442  * uclibc faq entry rather than in busybox... */
443 #if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
444 #error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support."
445 #endif
446
447 #define MOUNTPORT 635
448 #define MNTPATHLEN 1024
449 #define MNTNAMLEN 255
450 #define FHSIZE 32
451 #define FHSIZE3 64
452
453 typedef char fhandle[FHSIZE];
454
455 typedef struct {
456         unsigned int fhandle3_len;
457         char *fhandle3_val;
458 } fhandle3;
459
460 enum mountstat3 {
461         MNT_OK = 0,
462         MNT3ERR_PERM = 1,
463         MNT3ERR_NOENT = 2,
464         MNT3ERR_IO = 5,
465         MNT3ERR_ACCES = 13,
466         MNT3ERR_NOTDIR = 20,
467         MNT3ERR_INVAL = 22,
468         MNT3ERR_NAMETOOLONG = 63,
469         MNT3ERR_NOTSUPP = 10004,
470         MNT3ERR_SERVERFAULT = 10006,
471 };
472 typedef enum mountstat3 mountstat3;
473
474 struct fhstatus {
475         unsigned int fhs_status;
476         union {
477                 fhandle fhs_fhandle;
478         } fhstatus_u;
479 };
480 typedef struct fhstatus fhstatus;
481
482 struct mountres3_ok {
483         fhandle3 fhandle;
484         struct {
485                 unsigned int auth_flavours_len;
486                 char *auth_flavours_val;
487         } auth_flavours;
488 };
489 typedef struct mountres3_ok mountres3_ok;
490
491 struct mountres3 {
492         mountstat3 fhs_status;
493         union {
494                 mountres3_ok mountinfo;
495         } mountres3_u;
496 };
497 typedef struct mountres3 mountres3;
498
499 typedef char *dirpath;
500
501 typedef char *name;
502
503 typedef struct mountbody *mountlist;
504
505 struct mountbody {
506         name ml_hostname;
507         dirpath ml_directory;
508         mountlist ml_next;
509 };
510 typedef struct mountbody mountbody;
511
512 typedef struct groupnode *groups;
513
514 struct groupnode {
515         name gr_name;
516         groups gr_next;
517 };
518 typedef struct groupnode groupnode;
519
520 typedef struct exportnode *exports;
521
522 struct exportnode {
523         dirpath ex_dir;
524         groups ex_groups;
525         exports ex_next;
526 };
527 typedef struct exportnode exportnode;
528
529 struct ppathcnf {
530         int pc_link_max;
531         short pc_max_canon;
532         short pc_max_input;
533         short pc_name_max;
534         short pc_path_max;
535         short pc_pipe_buf;
536         uint8_t pc_vdisable;
537         char pc_xxx;
538         short pc_mask[2];
539 };
540 typedef struct ppathcnf ppathcnf;
541
542 #define MOUNTPROG 100005
543 #define MOUNTVERS 1
544
545 #define MOUNTPROC_NULL 0
546 #define MOUNTPROC_MNT 1
547 #define MOUNTPROC_DUMP 2
548 #define MOUNTPROC_UMNT 3
549 #define MOUNTPROC_UMNTALL 4
550 #define MOUNTPROC_EXPORT 5
551 #define MOUNTPROC_EXPORTALL 6
552
553 #define MOUNTVERS_POSIX 2
554
555 #define MOUNTPROC_PATHCONF 7
556
557 #define MOUNT_V3 3
558
559 #define MOUNTPROC3_NULL 0
560 #define MOUNTPROC3_MNT 1
561 #define MOUNTPROC3_DUMP 2
562 #define MOUNTPROC3_UMNT 3
563 #define MOUNTPROC3_UMNTALL 4
564 #define MOUNTPROC3_EXPORT 5
565
566 enum {
567 #ifndef NFS_FHSIZE
568         NFS_FHSIZE = 32,
569 #endif
570 #ifndef NFS_PORT
571         NFS_PORT = 2049
572 #endif
573 };
574
575 /*
576  * We want to be able to compile mount on old kernels in such a way
577  * that the binary will work well on more recent kernels.
578  * Thus, if necessary we teach nfsmount.c the structure of new fields
579  * that will come later.
580  *
581  * Moreover, the new kernel includes conflict with glibc includes
582  * so it is easiest to ignore the kernel altogether (at compile time).
583  */
584
585 struct nfs2_fh {
586         char                    data[32];
587 };
588 struct nfs3_fh {
589         unsigned short          size;
590         unsigned char           data[64];
591 };
592
593 struct nfs_mount_data {
594         int             version;                /* 1 */
595         int             fd;                     /* 1 */
596         struct nfs2_fh  old_root;               /* 1 */
597         int             flags;                  /* 1 */
598         int             rsize;                  /* 1 */
599         int             wsize;                  /* 1 */
600         int             timeo;                  /* 1 */
601         int             retrans;                /* 1 */
602         int             acregmin;               /* 1 */
603         int             acregmax;               /* 1 */
604         int             acdirmin;               /* 1 */
605         int             acdirmax;               /* 1 */
606         struct sockaddr_in addr;                /* 1 */
607         char            hostname[256];          /* 1 */
608         int             namlen;                 /* 2 */
609         unsigned int    bsize;                  /* 3 */
610         struct nfs3_fh  root;                   /* 4 */
611 };
612
613 /* bits in the flags field */
614 enum {
615         NFS_MOUNT_SOFT = 0x0001,        /* 1 */
616         NFS_MOUNT_INTR = 0x0002,        /* 1 */
617         NFS_MOUNT_SECURE = 0x0004,      /* 1 */
618         NFS_MOUNT_POSIX = 0x0008,       /* 1 */
619         NFS_MOUNT_NOCTO = 0x0010,       /* 1 */
620         NFS_MOUNT_NOAC = 0x0020,        /* 1 */
621         NFS_MOUNT_TCP = 0x0040,         /* 2 */
622         NFS_MOUNT_VER3 = 0x0080,        /* 3 */
623         NFS_MOUNT_KERBEROS = 0x0100,    /* 3 */
624         NFS_MOUNT_NONLM = 0x0200        /* 3 */
625 };
626
627
628 /*
629  * We need to translate between nfs status return values and
630  * the local errno values which may not be the same.
631  *
632  * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
633  * "after #include <errno.h> the symbol errno is reserved for any use,
634  *  it cannot even be used as a struct tag or field name".
635  */
636
637 #ifndef EDQUOT
638 #define EDQUOT  ENOSPC
639 #endif
640
641 // Convert each NFSERR_BLAH into EBLAH
642
643 static const struct {
644         short stat;
645         short errnum;
646 } nfs_errtbl[] = {
647         {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST},
648         {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG},
649         {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT},
650         {70,ESTALE}, {71,EREMOTE}, {-1,EIO}
651 };
652
653 static char *nfs_strerror(int status)
654 {
655         int i;
656
657         for (i = 0; nfs_errtbl[i].stat != -1; i++) {
658                 if (nfs_errtbl[i].stat == status)
659                         return strerror(nfs_errtbl[i].errnum);
660         }
661         return xasprintf("unknown nfs status return value: %d", status);
662 }
663
664 static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
665 {
666         if (!xdr_opaque(xdrs, objp, FHSIZE))
667                  return FALSE;
668         return TRUE;
669 }
670
671 static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
672 {
673         if (!xdr_u_int(xdrs, &objp->fhs_status))
674                  return FALSE;
675         switch (objp->fhs_status) {
676         case 0:
677                 if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle))
678                          return FALSE;
679                 break;
680         default:
681                 break;
682         }
683         return TRUE;
684 }
685
686 static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
687 {
688         if (!xdr_string(xdrs, objp, MNTPATHLEN))
689                  return FALSE;
690         return TRUE;
691 }
692
693 static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
694 {
695         if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
696                  return FALSE;
697         return TRUE;
698 }
699
700 static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
701 {
702         if (!xdr_fhandle3(xdrs, &objp->fhandle))
703                 return FALSE;
704         if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0,
705                                 sizeof (int), (xdrproc_t) xdr_int))
706                 return FALSE;
707         return TRUE;
708 }
709
710 static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
711 {
712         if (!xdr_enum(xdrs, (enum_t *) objp))
713                  return FALSE;
714         return TRUE;
715 }
716
717 static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
718 {
719         if (!xdr_mountstat3(xdrs, &objp->fhs_status))
720                 return FALSE;
721         switch (objp->fhs_status) {
722         case MNT_OK:
723                 if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo))
724                          return FALSE;
725                 break;
726         default:
727                 break;
728         }
729         return TRUE;
730 }
731
732 #define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
733
734 static smalluint nfs_mount_version;
735
736 /*
737  * Unfortunately, the kernel prints annoying console messages
738  * in case of an unexpected nfs mount version (instead of
739  * just returning some error).  Therefore we'll have to try
740  * and figure out what version the kernel expects.
741  *
742  * Variables:
743  *      KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
744  *      NFS_MOUNT_VERSION: these nfsmount sources at compile time
745  *      nfs_mount_version: version this source and running kernel can handle
746  */
747 static void
748 find_kernel_nfs_mount_version(void)
749 {
750         int kernel_version;
751
752         if (nfs_mount_version)
753                 return;
754
755         nfs_mount_version = 4; /* default */
756
757         kernel_version = get_linux_version_code();
758         if (kernel_version) {
759                 if (kernel_version < KERNEL_VERSION(2,1,32))
760                         nfs_mount_version = 1;
761                 else if (kernel_version < KERNEL_VERSION(2,2,18) ||
762                                 (kernel_version >= KERNEL_VERSION(2,3,0) &&
763                                  kernel_version < KERNEL_VERSION(2,3,99)))
764                         nfs_mount_version = 3;
765                 /* else v4 since 2.3.99pre4 */
766         }
767 }
768
769 static void
770 get_mountport(struct pmap *pm_mnt,
771         struct sockaddr_in *server_addr,
772         long unsigned prog,
773         long unsigned version,
774         long unsigned proto,
775         long unsigned port)
776 {
777         struct pmaplist *pmap;
778
779         server_addr->sin_port = PMAPPORT;
780 /* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
781  * I understand it like "IPv6 for this is not 100% ready" */
782         pmap = pmap_getmaps(server_addr);
783
784         if (version > MAX_NFSPROT)
785                 version = MAX_NFSPROT;
786         if (!prog)
787                 prog = MOUNTPROG;
788         pm_mnt->pm_prog = prog;
789         pm_mnt->pm_vers = version;
790         pm_mnt->pm_prot = proto;
791         pm_mnt->pm_port = port;
792
793         while (pmap) {
794                 if (pmap->pml_map.pm_prog != prog)
795                         goto next;
796                 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
797                         goto next;
798                 if (version > 2 && pmap->pml_map.pm_vers != version)
799                         goto next;
800                 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
801                         goto next;
802                 if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
803                     (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto) ||
804                     (port && pmap->pml_map.pm_port != port))
805                         goto next;
806                 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
807  next:
808                 pmap = pmap->pml_next;
809         }
810         if (!pm_mnt->pm_vers)
811                 pm_mnt->pm_vers = MOUNTVERS;
812         if (!pm_mnt->pm_port)
813                 pm_mnt->pm_port = MOUNTPORT;
814         if (!pm_mnt->pm_prot)
815                 pm_mnt->pm_prot = IPPROTO_TCP;
816 }
817
818 #if BB_MMU
819 static int daemonize(void)
820 {
821         int fd;
822         int pid = fork();
823         if (pid < 0) /* error */
824                 return -errno;
825         if (pid > 0) /* parent */
826                 return 0;
827         /* child */
828         fd = xopen(bb_dev_null, O_RDWR);
829         dup2(fd, 0);
830         dup2(fd, 1);
831         dup2(fd, 2);
832         while (fd > 2) close(fd--);
833         setsid();
834         openlog(applet_name, LOG_PID, LOG_DAEMON);
835         logmode = LOGMODE_SYSLOG;
836         return 1;
837 }
838 #else
839 static inline int daemonize(void) { return -ENOSYS; }
840 #endif
841
842 // TODO
843 static inline int we_saw_this_host_before(const char *hostname)
844 {
845         return 0;
846 }
847
848 /* RPC strerror analogs are terminally idiotic:
849  * *mandatory* prefix and \n at end.
850  * This hopefully helps. Usage:
851  * error_msg_rpc(clnt_*error*(" ")) */
852 static void error_msg_rpc(const char *msg)
853 {
854         int len;
855         while (msg[0] == ' ' || msg[0] == ':') msg++;
856         len = strlen(msg);
857         while (len && msg[len-1] == '\n') len--;
858         bb_error_msg("%.*s", len, msg);
859 }
860
861 // NB: mp->xxx fields may be trashed on exit
862 static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts)
863 {
864         CLIENT *mclient;
865         char *hostname;
866         char *pathname;
867         char *mounthost;
868         struct nfs_mount_data data;
869         char *opt;
870         struct hostent *hp;
871         struct sockaddr_in server_addr;
872         struct sockaddr_in mount_server_addr;
873         int msock, fsock;
874         union {
875                 struct fhstatus nfsv2;
876                 struct mountres3 nfsv3;
877         } status;
878         int daemonized;
879         char *s;
880         int port;
881         int mountport;
882         int proto;
883 #if BB_MMU
884         int bg = 0;
885 #else
886         enum { bg = 0 };
887 #endif
888         int soft;
889         int intr;
890         int posix;
891         int nocto;
892         int noac;
893         int nolock;
894         int retry;
895         int tcp;
896         int mountprog;
897         int mountvers;
898         int nfsprog;
899         int nfsvers;
900         int retval;
901
902         find_kernel_nfs_mount_version();
903
904         daemonized = 0;
905         mounthost = NULL;
906         retval = ETIMEDOUT;
907         msock = fsock = -1;
908         mclient = NULL;
909
910         /* NB: hostname, mounthost, filteropts must be free()d prior to return */
911
912         filteropts = xstrdup(filteropts); /* going to trash it later... */
913
914         hostname = xstrdup(mp->mnt_fsname);
915         /* mount_main() guarantees that ':' is there */
916         s = strchr(hostname, ':');
917         pathname = s + 1;
918         *s = '\0';
919         /* Ignore all but first hostname in replicated mounts
920            until they can be fully supported. (mack@sgi.com) */
921         s = strchr(hostname, ',');
922         if (s) {
923                 *s = '\0';
924                 bb_error_msg("warning: multiple hostnames not supported");
925         }
926
927         server_addr.sin_family = AF_INET;
928         if (!inet_aton(hostname, &server_addr.sin_addr)) {
929                 hp = gethostbyname(hostname);
930                 if (hp == NULL) {
931                         bb_herror_msg("%s", hostname);
932                         goto fail;
933                 }
934                 if (hp->h_length > sizeof(struct in_addr)) {
935                         bb_error_msg("got bad hp->h_length");
936                         hp->h_length = sizeof(struct in_addr);
937                 }
938                 memcpy(&server_addr.sin_addr,
939                                 hp->h_addr, hp->h_length);
940         }
941
942         memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
943
944         /* add IP address to mtab options for use when unmounting */
945
946         if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
947                 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
948         } else {
949                 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
950                                         mp->mnt_opts[0] ? "," : "",
951                                         inet_ntoa(server_addr.sin_addr));
952                 free(mp->mnt_opts);
953                 mp->mnt_opts = tmp;
954         }
955
956         /* Set default options.
957          * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
958          * let the kernel decide.
959          * timeo is filled in after we know whether it'll be TCP or UDP. */
960         memset(&data, 0, sizeof(data));
961         data.retrans    = 3;
962         data.acregmin   = 3;
963         data.acregmax   = 60;
964         data.acdirmin   = 30;
965         data.acdirmax   = 60;
966         data.namlen     = NAME_MAX;
967
968         soft = 0;
969         intr = 0;
970         posix = 0;
971         nocto = 0;
972         nolock = 0;
973         noac = 0;
974         retry = 10000;          /* 10000 minutes ~ 1 week */
975         tcp = 0;
976
977         mountprog = MOUNTPROG;
978         mountvers = 0;
979         port = 0;
980         mountport = 0;
981         nfsprog = 100003;
982         nfsvers = 0;
983
984         /* parse options */
985         if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
986                 char *opteq = strchr(opt, '=');
987                 if (opteq) {
988                         static const char options[] ALIGN1 =
989                                 /* 0 */ "rsize\0"
990                                 /* 1 */ "wsize\0"
991                                 /* 2 */ "timeo\0"
992                                 /* 3 */ "retrans\0"
993                                 /* 4 */ "acregmin\0"
994                                 /* 5 */ "acregmax\0"
995                                 /* 6 */ "acdirmin\0"
996                                 /* 7 */ "acdirmax\0"
997                                 /* 8 */ "actimeo\0"
998                                 /* 9 */ "retry\0"
999                                 /* 10 */ "port\0"
1000                                 /* 11 */ "mountport\0"
1001                                 /* 12 */ "mounthost\0"
1002                                 /* 13 */ "mountprog\0"
1003                                 /* 14 */ "mountvers\0"
1004                                 /* 15 */ "nfsprog\0"
1005                                 /* 16 */ "nfsvers\0"
1006                                 /* 17 */ "vers\0"
1007                                 /* 18 */ "proto\0"
1008                                 /* 19 */ "namlen\0"
1009                                 /* 20 */ "addr\0";
1010                         int val = xatoi_u(opteq + 1);
1011                         *opteq = '\0';
1012                         switch (index_in_strings(options, opt)) {
1013                         case 0: // "rsize"
1014                                 data.rsize = val;
1015                                 break;
1016                         case 1: // "wsize"
1017                                 data.wsize = val;
1018                                 break;
1019                         case 2: // "timeo"
1020                                 data.timeo = val;
1021                                 break;
1022                         case 3: // "retrans"
1023                                 data.retrans = val;
1024                                 break;
1025                         case 4: // "acregmin"
1026                                 data.acregmin = val;
1027                                 break;
1028                         case 5: // "acregmax"
1029                                 data.acregmax = val;
1030                                 break;
1031                         case 6: // "acdirmin"
1032                                 data.acdirmin = val;
1033                                 break;
1034                         case 7: // "acdirmax"
1035                                 data.acdirmax = val;
1036                                 break;
1037                         case 8: // "actimeo"
1038                                 data.acregmin = val;
1039                                 data.acregmax = val;
1040                                 data.acdirmin = val;
1041                                 data.acdirmax = val;
1042                                 break;
1043                         case 9: // "retry"
1044                                 retry = val;
1045                                 break;
1046                         case 10: // "port"
1047                                 port = val;
1048                                 break;
1049                         case 11: // "mountport"
1050                                 mountport = val;
1051                                 break;
1052                         case 12: // "mounthost"
1053                                 mounthost = xstrndup(opteq+1,
1054                                                 strcspn(opteq+1," \t\n\r,"));
1055                                 break;
1056                         case 13: // "mountprog"
1057                                 mountprog = val;
1058                                 break;
1059                         case 14: // "mountvers"
1060                                 mountvers = val;
1061                                 break;
1062                         case 15: // "nfsprog"
1063                                 nfsprog = val;
1064                                 break;
1065                         case 16: // "nfsvers"
1066                         case 17: // "vers"
1067                                 nfsvers = val;
1068                                 break;
1069                         case 18: // "proto"
1070                                 if (!strncmp(opteq+1, "tcp", 3))
1071                                         tcp = 1;
1072                                 else if (!strncmp(opteq+1, "udp", 3))
1073                                         tcp = 0;
1074                                 else
1075                                         bb_error_msg("warning: unrecognized proto= option");
1076                                 break;
1077                         case 19: // "namlen"
1078                                 if (nfs_mount_version >= 2)
1079                                         data.namlen = val;
1080                                 else
1081                                         bb_error_msg("warning: option namlen is not supported\n");
1082                                 break;
1083                         case 20: // "addr" - ignore
1084                                 break;
1085                         default:
1086                                 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1087                                 goto fail;
1088                         }
1089                 }
1090                 else {
1091                         static const char options[] ALIGN1 =
1092                                 "bg\0"
1093                                 "fg\0"
1094                                 "soft\0"
1095                                 "hard\0"
1096                                 "intr\0"
1097                                 "posix\0"
1098                                 "cto\0"
1099                                 "ac\0"
1100                                 "tcp\0"
1101                                 "udp\0"
1102                                 "lock\0";
1103                         int val = 1;
1104                         if (!strncmp(opt, "no", 2)) {
1105                                 val = 0;
1106                                 opt += 2;
1107                         }
1108                         switch (index_in_strings(options, opt)) {
1109                         case 0: // "bg"
1110 #if BB_MMU
1111                                 bg = val;
1112 #endif
1113                                 break;
1114                         case 1: // "fg"
1115 #if BB_MMU
1116                                 bg = !val;
1117 #endif
1118                                 break;
1119                         case 2: // "soft"
1120                                 soft = val;
1121                                 break;
1122                         case 3: // "hard"
1123                                 soft = !val;
1124                                 break;
1125                         case 4: // "intr"
1126                                 intr = val;
1127                                 break;
1128                         case 5: // "posix"
1129                                 posix = val;
1130                                 break;
1131                         case 6: // "cto"
1132                                 nocto = !val;
1133                                 break;
1134                         case 7: // "ac"
1135                                 noac = !val;
1136                                 break;
1137                         case 8: // "tcp"
1138                                 tcp = val;
1139                                 break;
1140                         case 9: // "udp"
1141                                 tcp = !val;
1142                                 break;
1143                         case 10: // "lock"
1144                                 if (nfs_mount_version >= 3)
1145                                         nolock = !val;
1146                                 else
1147                                         bb_error_msg("warning: option nolock is not supported");
1148                                 break;
1149                         default:
1150                                 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1151                                 goto fail;
1152                         }
1153                 }
1154         }
1155         proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1156
1157         data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1158                 | (intr ? NFS_MOUNT_INTR : 0)
1159                 | (posix ? NFS_MOUNT_POSIX : 0)
1160                 | (nocto ? NFS_MOUNT_NOCTO : 0)
1161                 | (noac ? NFS_MOUNT_NOAC : 0);
1162         if (nfs_mount_version >= 2)
1163                 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1164         if (nfs_mount_version >= 3)
1165                 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1166         if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1167                 bb_error_msg("NFSv%d not supported", nfsvers);
1168                 goto fail;
1169         }
1170         if (nfsvers && !mountvers)
1171                 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1172         if (nfsvers && nfsvers < mountvers) {
1173                 mountvers = nfsvers;
1174         }
1175
1176         /* Adjust options if none specified */
1177         if (!data.timeo)
1178                 data.timeo = tcp ? 70 : 7;
1179
1180         data.version = nfs_mount_version;
1181
1182         if (vfsflags & MS_REMOUNT)
1183                 goto do_mount;
1184
1185         /*
1186          * If the previous mount operation on the same host was
1187          * backgrounded, and the "bg" for this mount is also set,
1188          * give up immediately, to avoid the initial timeout.
1189          */
1190         if (bg && we_saw_this_host_before(hostname)) {
1191                 daemonized = daemonize();
1192                 if (daemonized <= 0) { /* parent or error */
1193                         retval = -daemonized;
1194                         goto ret;
1195                 }
1196         }
1197
1198         /* create mount daemon client */
1199         /* See if the nfs host = mount host. */
1200         if (mounthost) {
1201                 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1202                         mount_server_addr.sin_family = AF_INET;
1203                         mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1204                 } else {
1205                         hp = gethostbyname(mounthost);
1206                         if (hp == NULL) {
1207                                 bb_herror_msg("%s", mounthost);
1208                                 goto fail;
1209                         } else {
1210                                 if (hp->h_length > sizeof(struct in_addr)) {
1211                                         bb_error_msg("got bad hp->h_length?");
1212                                         hp->h_length = sizeof(struct in_addr);
1213                                 }
1214                                 mount_server_addr.sin_family = AF_INET;
1215                                 memcpy(&mount_server_addr.sin_addr,
1216                                                 hp->h_addr, hp->h_length);
1217                         }
1218                 }
1219         }
1220
1221         /*
1222          * The following loop implements the mount retries. When the mount
1223          * times out, and the "bg" option is set, we background ourself
1224          * and continue trying.
1225          *
1226          * The case where the mount point is not present and the "bg"
1227          * option is set, is treated as a timeout. This is done to
1228          * support nested mounts.
1229          *
1230          * The "retry" count specified by the user is the number of
1231          * minutes to retry before giving up.
1232          */
1233         {
1234                 struct timeval total_timeout;
1235                 struct timeval retry_timeout;
1236                 struct pmap pm_mnt;
1237                 time_t t;
1238                 time_t prevt;
1239                 time_t timeout;
1240
1241                 retry_timeout.tv_sec = 3;
1242                 retry_timeout.tv_usec = 0;
1243                 total_timeout.tv_sec = 20;
1244                 total_timeout.tv_usec = 0;
1245                 timeout = time(NULL) + 60 * retry;
1246                 prevt = 0;
1247                 t = 30;
1248  retry:
1249                 /* be careful not to use too many CPU cycles */
1250                 if (t - prevt < 30)
1251                         sleep(30);
1252
1253                 get_mountport(&pm_mnt, &mount_server_addr,
1254                                 mountprog,
1255                                 mountvers,
1256                                 proto,
1257                                 mountport);
1258                 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
1259
1260                 /* contact the mount daemon via TCP */
1261                 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1262                 msock = RPC_ANYSOCK;
1263
1264                 switch (pm_mnt.pm_prot) {
1265                 case IPPROTO_UDP:
1266                         mclient = clntudp_create(&mount_server_addr,
1267                                                  pm_mnt.pm_prog,
1268                                                  pm_mnt.pm_vers,
1269                                                  retry_timeout,
1270                                                  &msock);
1271                         if (mclient)
1272                                 break;
1273                         mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1274                         msock = RPC_ANYSOCK;
1275                 case IPPROTO_TCP:
1276                         mclient = clnttcp_create(&mount_server_addr,
1277                                                  pm_mnt.pm_prog,
1278                                                  pm_mnt.pm_vers,
1279                                                  &msock, 0, 0);
1280                         break;
1281                 default:
1282                         mclient = NULL;
1283                 }
1284                 if (!mclient) {
1285                         if (!daemonized && prevt == 0)
1286                                 error_msg_rpc(clnt_spcreateerror(" "));
1287                 } else {
1288                         enum clnt_stat clnt_stat;
1289                         /* try to mount hostname:pathname */
1290                         mclient->cl_auth = authunix_create_default();
1291
1292                         /* make pointers in xdr_mountres3 NULL so
1293                          * that xdr_array allocates memory for us
1294                          */
1295                         memset(&status, 0, sizeof(status));
1296
1297                         if (pm_mnt.pm_vers == 3)
1298                                 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1299                                               (xdrproc_t) xdr_dirpath,
1300                                               (caddr_t) &pathname,
1301                                               (xdrproc_t) xdr_mountres3,
1302                                               (caddr_t) &status,
1303                                               total_timeout);
1304                         else
1305                                 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1306                                               (xdrproc_t) xdr_dirpath,
1307                                               (caddr_t) &pathname,
1308                                               (xdrproc_t) xdr_fhstatus,
1309                                               (caddr_t) &status,
1310                                               total_timeout);
1311
1312                         if (clnt_stat == RPC_SUCCESS)
1313                                 goto prepare_kernel_data; /* we're done */
1314                         if (errno != ECONNREFUSED) {
1315                                 error_msg_rpc(clnt_sperror(mclient, " "));
1316                                 goto fail;      /* don't retry */
1317                         }
1318                         /* Connection refused */
1319                         if (!daemonized && prevt == 0) /* print just once */
1320                                 error_msg_rpc(clnt_sperror(mclient, " "));
1321                         auth_destroy(mclient->cl_auth);
1322                         clnt_destroy(mclient);
1323                         mclient = NULL;
1324                         close(msock);
1325                         msock = -1;
1326                 }
1327
1328                 /* Timeout. We are going to retry... maybe */
1329
1330                 if (!bg)
1331                         goto fail;
1332                 if (!daemonized) {
1333                         daemonized = daemonize();
1334                         if (daemonized <= 0) { /* parent or error */
1335                                 retval = -daemonized;
1336                                 goto ret;
1337                         }
1338                 }
1339                 prevt = t;
1340                 t = time(NULL);
1341                 if (t >= timeout)
1342                         /* TODO error message */
1343                         goto fail;
1344
1345                 goto retry;
1346         }
1347
1348  prepare_kernel_data:
1349
1350         if (nfsvers == 2) {
1351                 if (status.nfsv2.fhs_status != 0) {
1352                         bb_error_msg("%s:%s failed, reason given by server: %s",
1353                                 hostname, pathname,
1354                                 nfs_strerror(status.nfsv2.fhs_status));
1355                         goto fail;
1356                 }
1357                 memcpy(data.root.data,
1358                                 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1359                                 NFS_FHSIZE);
1360                 data.root.size = NFS_FHSIZE;
1361                 memcpy(data.old_root.data,
1362                                 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1363                                 NFS_FHSIZE);
1364         } else {
1365                 fhandle3 *my_fhandle;
1366                 if (status.nfsv3.fhs_status != 0) {
1367                         bb_error_msg("%s:%s failed, reason given by server: %s",
1368                                 hostname, pathname,
1369                                 nfs_strerror(status.nfsv3.fhs_status));
1370                         goto fail;
1371                 }
1372                 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1373                 memset(data.old_root.data, 0, NFS_FHSIZE);
1374                 memset(&data.root, 0, sizeof(data.root));
1375                 data.root.size = my_fhandle->fhandle3_len;
1376                 memcpy(data.root.data,
1377                                 (char *) my_fhandle->fhandle3_val,
1378                                 my_fhandle->fhandle3_len);
1379
1380                 data.flags |= NFS_MOUNT_VER3;
1381         }
1382
1383         /* create nfs socket for kernel */
1384
1385         if (tcp) {
1386                 if (nfs_mount_version < 3) {
1387                         bb_error_msg("NFS over TCP is not supported");
1388                         goto fail;
1389                 }
1390                 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1391         } else
1392                 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1393         if (fsock < 0) {
1394                 bb_perror_msg("nfs socket");
1395                 goto fail;
1396         }
1397         if (bindresvport(fsock, 0) < 0) {
1398                 bb_perror_msg("nfs bindresvport");
1399                 goto fail;
1400         }
1401         if (port == 0) {
1402                 server_addr.sin_port = PMAPPORT;
1403                 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1404                                         tcp ? IPPROTO_TCP : IPPROTO_UDP);
1405                 if (port == 0)
1406                         port = NFS_PORT;
1407         }
1408         server_addr.sin_port = htons(port);
1409
1410         /* prepare data structure for kernel */
1411
1412         data.fd = fsock;
1413         memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1414         strncpy(data.hostname, hostname, sizeof(data.hostname));
1415
1416         /* clean up */
1417
1418         auth_destroy(mclient->cl_auth);
1419         clnt_destroy(mclient);
1420         close(msock);
1421         msock = -1;
1422
1423         if (bg) {
1424                 /* We must wait until mount directory is available */
1425                 struct stat statbuf;
1426                 int delay = 1;
1427                 while (stat(mp->mnt_dir, &statbuf) == -1) {
1428                         if (!daemonized) {
1429                                 daemonized = daemonize();
1430                                 if (daemonized <= 0) { /* parent or error */
1431         // FIXME: parent doesn't close fsock - ??!
1432                                         retval = -daemonized;
1433                                         goto ret;
1434                                 }
1435                         }
1436                         sleep(delay);   /* 1, 2, 4, 8, 16, 30, ... */
1437                         delay *= 2;
1438                         if (delay > 30)
1439                                 delay = 30;
1440                 }
1441         }
1442
1443  do_mount: /* perform actual mount */
1444
1445         mp->mnt_type = (char*)"nfs";
1446         retval = mount_it_now(mp, vfsflags, (char*)&data);
1447         goto ret;
1448
1449  fail:  /* abort */
1450
1451         if (msock >= 0) {
1452                 if (mclient) {
1453                         auth_destroy(mclient->cl_auth);
1454                         clnt_destroy(mclient);
1455                 }
1456                 close(msock);
1457         }
1458         if (fsock >= 0)
1459                 close(fsock);
1460
1461  ret:
1462         free(hostname);
1463         free(mounthost);
1464         free(filteropts);
1465         return retval;
1466 }
1467
1468 #else /* !ENABLE_FEATURE_MOUNT_NFS */
1469
1470 /* Never called. Call should be optimized out. */
1471 int nfsmount(struct mntent *mp, int vfsflags, char *filteropts);
1472
1473 #endif /* !ENABLE_FEATURE_MOUNT_NFS */
1474
1475 // Mount one directory.  Handles CIFS, NFS, loopback, autobind, and filesystem
1476 // type detection.  Returns 0 for success, nonzero for failure.
1477 // NB: mp->xxx fields may be trashed on exit
1478 static int singlemount(struct mntent *mp, int ignore_busy)
1479 {
1480         int rc = -1, vfsflags;
1481         char *loopFile = 0, *filteropts = 0;
1482         llist_t *fl = 0;
1483         struct stat st;
1484
1485         vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1486
1487         // Treat fstype "auto" as unspecified.
1488
1489         if (mp->mnt_type && strcmp(mp->mnt_type,"auto") == 0)
1490                 mp->mnt_type = 0;
1491
1492         // Might this be a virtual filesystem?
1493
1494         if (ENABLE_FEATURE_MOUNT_HELPERS
1495          && (strchr(mp->mnt_fsname, '#'))
1496         ) {
1497                 char *s, *p, *args[35];
1498                 int n = 0;
1499                 for (s = p = mp->mnt_fsname; *s && n < 35-3; ++s) {
1500                         if (s[0] == '#' && s[1] != '#') {
1501                                 *s = '\0';
1502                                 args[n++] = p;
1503                                 p = s + 1;
1504                         }
1505                 }
1506                 args[n++] = p;
1507                 args[n++] = mp->mnt_dir;
1508                 args[n] = NULL;
1509                 rc = wait4pid(xspawn(args));
1510                 goto report_error;
1511         }
1512
1513         // Might this be an CIFS filesystem?
1514
1515         if (ENABLE_FEATURE_MOUNT_CIFS
1516          && (!mp->mnt_type || strcmp(mp->mnt_type,"cifs") == 0)
1517          && (mp->mnt_fsname[0]=='/' || mp->mnt_fsname[0]=='\\')
1518          && mp->mnt_fsname[0]==mp->mnt_fsname[1]
1519         ) {
1520                 len_and_sockaddr *lsa;
1521                 char *ip, *dotted;
1522                 char *s;
1523
1524                 rc = 1;
1525                 // Replace '/' with '\' and verify that unc points to "//server/share".
1526
1527                 for (s = mp->mnt_fsname; *s; ++s)
1528                         if (*s == '/') *s = '\\';
1529
1530                 // get server IP
1531
1532                 s = strrchr(mp->mnt_fsname, '\\');
1533                 if (s <= mp->mnt_fsname+1) goto report_error;
1534                 *s = '\0';
1535                 lsa = host2sockaddr(mp->mnt_fsname+2, 0);
1536                 *s = '\\';
1537                 if (!lsa) goto report_error;
1538
1539                 // insert ip=... option into string flags.
1540
1541                 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1542                 ip = xasprintf("ip=%s", dotted);
1543                 parse_mount_options(ip, &filteropts);
1544
1545                 // compose new unc '\\server-ip\share'
1546                 // (s => slash after hostname)
1547
1548                 mp->mnt_fsname = xasprintf("\\\\%s%s", dotted, s);
1549
1550                 // lock is required
1551                 vfsflags |= MS_MANDLOCK;
1552
1553                 mp->mnt_type = (char*)"cifs";
1554                 rc = mount_it_now(mp, vfsflags, filteropts);
1555                 if (ENABLE_FEATURE_CLEAN_UP) {
1556                         free(mp->mnt_fsname);
1557                         free(ip);
1558                         free(dotted);
1559                         free(lsa);
1560                 }
1561                 goto report_error;
1562         }
1563
1564         // Might this be an NFS filesystem?
1565
1566         if (ENABLE_FEATURE_MOUNT_NFS
1567          && (!mp->mnt_type || !strcmp(mp->mnt_type, "nfs"))
1568          && strchr(mp->mnt_fsname, ':') != NULL
1569         ) {
1570                 rc = nfsmount(mp, vfsflags, filteropts);
1571                 goto report_error;
1572         }
1573
1574         // Look at the file.  (Not found isn't a failure for remount, or for
1575         // a synthetic filesystem like proc or sysfs.)
1576         // (We use stat, not lstat, in order to allow
1577         // mount symlink_to_file_or_blkdev dir)
1578
1579         if (!stat(mp->mnt_fsname, &st)
1580          && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1581         ) {
1582                 // Do we need to allocate a loopback device for it?
1583
1584                 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1585                         loopFile = bb_simplify_path(mp->mnt_fsname);
1586                         mp->mnt_fsname = NULL; /* will receive malloced loop dev name */
1587                         if (set_loop(&(mp->mnt_fsname), loopFile, 0) < 0) {
1588                                 if (errno == EPERM || errno == EACCES)
1589                                         bb_error_msg(bb_msg_perm_denied_are_you_root);
1590                                 else
1591                                         bb_perror_msg("cannot setup loop device");
1592                                 return errno;
1593                         }
1594
1595                 // Autodetect bind mounts
1596
1597                 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1598                         vfsflags |= MS_BIND;
1599         }
1600
1601         /* If we know the fstype (or don't need to), jump straight
1602          * to the actual mount. */
1603
1604         if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1605                 rc = mount_it_now(mp, vfsflags, filteropts);
1606         else {
1607                 // Loop through filesystem types until mount succeeds
1608                 // or we run out
1609
1610                 /* Initialize list of block backed filesystems.  This has to be
1611                  * done here so that during "mount -a", mounts after /proc shows up
1612                  * can autodetect. */
1613
1614                 if (!fslist) {
1615                         fslist = get_block_backed_filesystems();
1616                         if (ENABLE_FEATURE_CLEAN_UP && fslist)
1617                                 atexit(delete_block_backed_filesystems);
1618                 }
1619
1620                 for (fl = fslist; fl; fl = fl->link) {
1621                         mp->mnt_type = fl->data;
1622                         rc = mount_it_now(mp, vfsflags, filteropts);
1623                         if (!rc) break;
1624                 }
1625         }
1626
1627         // If mount failed, clean up loop file (if any).
1628
1629         if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1630                 del_loop(mp->mnt_fsname);
1631                 if (ENABLE_FEATURE_CLEAN_UP) {
1632                         free(loopFile);
1633                         free(mp->mnt_fsname);
1634                 }
1635         }
1636
1637  report_error:
1638         if (ENABLE_FEATURE_CLEAN_UP)
1639                 free(filteropts);
1640
1641         if (errno == EBUSY && ignore_busy)
1642                 return 0;
1643         if (rc < 0)
1644                 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
1645         return rc;
1646 }
1647
1648 // Parse options, if necessary parse fstab/mtab, and call singlemount for
1649 // each directory to be mounted.
1650
1651 static const char must_be_root[] ALIGN1 = "you must be root";
1652
1653 int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1654 int mount_main(int argc, char **argv)
1655 {
1656         enum { OPT_ALL = 0x10 };
1657
1658         char *cmdopts = xstrdup(""), *fstype=0, *storage_path=0;
1659         char *opt_o;
1660         const char *fstabname;
1661         FILE *fstab;
1662         int i, j, rc = 0;
1663         unsigned opt;
1664         struct mntent mtpair[2], *mtcur = mtpair;
1665         SKIP_DESKTOP(const int nonroot = 0;)
1666         USE_DESKTOP( int nonroot = (getuid() != 0);)
1667
1668         /* parse long options, like --bind and --move.  Note that -o option
1669          * and --option are synonymous.  Yes, this means --remount,rw works. */
1670
1671         for (i = j = 0; i < argc; i++) {
1672                 if (argv[i][0] == '-' && argv[i][1] == '-') {
1673                         append_mount_options(&cmdopts, argv[i]+2);
1674                 } else argv[j++] = argv[i];
1675         }
1676         argv[j] = 0;
1677         argc = j;
1678
1679         // Parse remaining options
1680
1681         opt = getopt32(argv, OPTION_STR, &opt_o, &fstype);
1682         if (opt & OPT_o) append_mount_options(&cmdopts, opt_o); // -o
1683         if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
1684         if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
1685         argv += optind;
1686         argc -= optind;
1687
1688         // Three or more non-option arguments?  Die with a usage message.
1689
1690         if (argc > 2) bb_show_usage();
1691
1692         // If we have no arguments, show currently mounted filesystems
1693
1694         if (!argc) {
1695                 if (!(opt & OPT_ALL)) {
1696                         FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1697
1698                         if (!mountTable) bb_error_msg_and_die("no %s", bb_path_mtab_file);
1699
1700                         while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
1701                                                                 sizeof(getmntent_buf)))
1702                         {
1703                                 // Don't show rootfs. FIXME: why??
1704                                 // util-linux 2.12a happily shows rootfs...
1705                                 //if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
1706
1707                                 if (!fstype || !strcmp(mtpair->mnt_type, fstype))
1708                                         printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1709                                                         mtpair->mnt_dir, mtpair->mnt_type,
1710                                                         mtpair->mnt_opts);
1711                         }
1712                         if (ENABLE_FEATURE_CLEAN_UP) endmntent(mountTable);
1713                         return EXIT_SUCCESS;
1714                 }
1715         } else storage_path = bb_simplify_path(argv[0]);
1716
1717         // When we have two arguments, the second is the directory and we can
1718         // skip looking at fstab entirely.  We can always abspath() the directory
1719         // argument when we get it.
1720
1721         if (argc == 2) {
1722                 if (nonroot)
1723                         bb_error_msg_and_die(must_be_root);
1724                 mtpair->mnt_fsname = argv[0];
1725                 mtpair->mnt_dir = argv[1];
1726                 mtpair->mnt_type = fstype;
1727                 mtpair->mnt_opts = cmdopts;
1728                 rc = singlemount(mtpair, 0);
1729                 goto clean_up;
1730         }
1731
1732         i = parse_mount_options(cmdopts, 0);
1733         if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags
1734                 bb_error_msg_and_die(must_be_root);
1735
1736         // If we have a shared subtree flag, don't worry about fstab or mtab.
1737
1738         if (ENABLE_FEATURE_MOUNT_FLAGS
1739          && (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
1740         ) {
1741                 rc = mount("", argv[0], "", i, "");
1742                 if (rc) bb_simple_perror_msg_and_die(argv[0]);
1743                 goto clean_up;
1744         }
1745
1746         // Open either fstab or mtab
1747
1748         fstabname = "/etc/fstab";
1749         if (i & MS_REMOUNT) {
1750                 fstabname = bb_path_mtab_file;
1751         }
1752         fstab = setmntent(fstabname, "r");
1753         if (!fstab)
1754                 bb_perror_msg_and_die("cannot read %s", fstabname);
1755
1756         // Loop through entries until we find what we're looking for.
1757
1758         memset(mtpair, 0, sizeof(mtpair));
1759         for (;;) {
1760                 struct mntent *mtnext = (mtcur==mtpair ? mtpair+1 : mtpair);
1761
1762                 // Get next fstab entry
1763
1764                 if (!getmntent_r(fstab, mtcur, getmntent_buf
1765                                         + (mtcur==mtpair ? sizeof(getmntent_buf)/2 : 0),
1766                                 sizeof(getmntent_buf)/2))
1767                 {
1768                         // Were we looking for something specific?
1769
1770                         if (argc) {
1771
1772                                 // If we didn't find anything, complain.
1773
1774                                 if (!mtnext->mnt_fsname)
1775                                         bb_error_msg_and_die("can't find %s in %s",
1776                                                 argv[0], fstabname);
1777
1778                                 mtcur = mtnext;
1779                                 if (nonroot) {
1780                                         // fstab must have "users" or "user"
1781                                         if (!(parse_mount_options(mtcur->mnt_opts, 0) & MOUNT_USERS))
1782                                                 bb_error_msg_and_die(must_be_root);
1783                                 }
1784
1785                                 // Mount the last thing we found.
1786
1787                                 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
1788                                 append_mount_options(&(mtcur->mnt_opts), cmdopts);
1789                                 rc = singlemount(mtcur, 0);
1790                                 free(mtcur->mnt_opts);
1791                         }
1792                         goto clean_up;
1793                 }
1794
1795                 /* If we're trying to mount something specific and this isn't it,
1796                  * skip it.  Note we must match both the exact text in fstab (ala
1797                  * "proc") or a full path from root */
1798
1799                 if (argc) {
1800
1801                         // Is this what we're looking for?
1802
1803                         if (strcmp(argv[0], mtcur->mnt_fsname) &&
1804                            strcmp(storage_path, mtcur->mnt_fsname) &&
1805                            strcmp(argv[0], mtcur->mnt_dir) &&
1806                            strcmp(storage_path, mtcur->mnt_dir)) continue;
1807
1808                         // Remember this entry.  Something later may have overmounted
1809                         // it, and we want the _last_ match.
1810
1811                         mtcur = mtnext;
1812
1813                 // If we're mounting all.
1814
1815                 } else {
1816                         // Do we need to match a filesystem type?
1817                         if (fstype && match_fstype(mtcur, fstype)) continue;
1818
1819                         // Skip noauto and swap anyway.
1820
1821                         if (parse_mount_options(mtcur->mnt_opts, 0)
1822                                 & (MOUNT_NOAUTO | MOUNT_SWAP)) continue;
1823
1824                         // No, mount -a won't mount anything,
1825                         // even user mounts, for mere humans.
1826
1827                         if (nonroot)
1828                                 bb_error_msg_and_die(must_be_root);
1829
1830                         // Mount this thing.
1831
1832                         // NFS mounts want this to be xrealloc-able
1833                         mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
1834                         if (singlemount(mtcur, 1)) {
1835                                 /* Count number of failed mounts */
1836                                 rc++;
1837                         }
1838                         free(mtcur->mnt_opts);
1839                 }
1840         }
1841         if (ENABLE_FEATURE_CLEAN_UP) endmntent(fstab);
1842
1843  clean_up:
1844
1845         if (ENABLE_FEATURE_CLEAN_UP) {
1846                 free(storage_path);
1847                 free(cmdopts);
1848         }
1849
1850         return rc;
1851 }