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