mount: make FEATURE_MOUNT_NFS not needed for Linux 2.6.23+
[platform/upstream/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 source tree.
10  */
11 // Design notes: There is no spec for mount.  Remind me to write one.
12 //
13 // mount_main() calls singlemount() which calls mount_it_now().
14 //
15 // mount_main() can loop through /etc/fstab for mount -a
16 // singlemount() can loop through /etc/filesystems for fstype detection.
17 // mount_it_now() does the actual mount.
18 //
19
20 //usage:#define mount_trivial_usage
21 //usage:       "[OPTIONS] [-o OPTS] DEVICE NODE"
22 //usage:#define mount_full_usage "\n\n"
23 //usage:       "Mount a filesystem. Filesystem autodetection requires /proc.\n"
24 //usage:     "\n        -a              Mount all filesystems in fstab"
25 //usage:        IF_FEATURE_MOUNT_FAKE(
26 //usage:        IF_FEATURE_MTAB_SUPPORT(
27 //usage:     "\n        -f              Update /etc/mtab, but don't mount"
28 //usage:        )
29 //usage:        IF_NOT_FEATURE_MTAB_SUPPORT(
30 //usage:     "\n        -f              Dry run"
31 //usage:        )
32 //usage:        )
33 //usage:        IF_FEATURE_MOUNT_HELPERS(
34 //usage:     "\n        -i              Don't run mount helper"
35 //usage:        )
36 //usage:        IF_FEATURE_MTAB_SUPPORT(
37 //usage:     "\n        -n              Don't update /etc/mtab"
38 //usage:        )
39 //usage:     "\n        -r              Read-only mount"
40 //usage:     "\n        -w              Read-write mount (default)"
41 //usage:     "\n        -t FSTYPE[,...] Filesystem type(s)"
42 //usage:     "\n        -O OPT          Mount only filesystems with option OPT (-a only)"
43 //usage:     "\n-o OPT:"
44 //usage:        IF_FEATURE_MOUNT_LOOP(
45 //usage:     "\n        loop            Ignored (loop devices are autodetected)"
46 //usage:        )
47 //usage:        IF_FEATURE_MOUNT_FLAGS(
48 //usage:     "\n        [a]sync         Writes are [a]synchronous"
49 //usage:     "\n        [no]atime       Disable/enable updates to inode access times"
50 //usage:     "\n        [no]diratime    Disable/enable atime updates to directories"
51 //usage:     "\n        [no]relatime    Disable/enable atime updates relative to modification time"
52 //usage:     "\n        [no]dev         (Dis)allow use of special device files"
53 //usage:     "\n        [no]exec        (Dis)allow use of executable files"
54 //usage:     "\n        [no]suid        (Dis)allow set-user-id-root programs"
55 //usage:     "\n        [r]shared       Convert [recursively] to a shared subtree"
56 //usage:     "\n        [r]slave        Convert [recursively] to a slave subtree"
57 //usage:     "\n        [r]private      Convert [recursively] to a private subtree"
58 //usage:     "\n        [un]bindable    Make mount point [un]able to be bind mounted"
59 //usage:     "\n        [r]bind         Bind a file or directory [recursively] to another location"
60 //usage:     "\n        move            Relocate an existing mount point"
61 //usage:        )
62 //usage:     "\n        remount         Remount a mounted filesystem, changing flags"
63 //usage:     "\n        ro/rw           Same as -r/-w"
64 //usage:     "\n"
65 //usage:     "\nThere are filesystem-specific -o flags."
66 //usage:
67 //usage:#define mount_example_usage
68 //usage:       "$ mount\n"
69 //usage:       "/dev/hda3 on / type minix (rw)\n"
70 //usage:       "proc on /proc type proc (rw)\n"
71 //usage:       "devpts on /dev/pts type devpts (rw)\n"
72 //usage:       "$ mount /dev/fd0 /mnt -t msdos -o ro\n"
73 //usage:       "$ mount /tmp/diskimage /opt -t ext2 -o loop\n"
74 //usage:       "$ mount cd_image.iso mydir\n"
75 //usage:#define mount_notes_usage
76 //usage:       "Returns 0 for success, number of failed mounts for -a, or errno for one mount."
77
78 #include <mntent.h>
79 #include <syslog.h>
80 #include <sys/mount.h>
81 // Grab more as needed from util-linux's mount/mount_constants.h
82 #ifndef MS_DIRSYNC
83 # define MS_DIRSYNC     (1 << 7) // Directory modifications are synchronous
84 #endif
85 #ifndef MS_UNION
86 # define MS_UNION       (1 << 8)
87 #endif
88 #ifndef MS_BIND
89 # define MS_BIND        (1 << 12)
90 #endif
91 #ifndef MS_MOVE
92 # define MS_MOVE        (1 << 13)
93 #endif
94 #ifndef MS_RECURSIVE
95 # define MS_RECURSIVE   (1 << 14)
96 #endif
97 #ifndef MS_SILENT
98 # define MS_SILENT      (1 << 15)
99 #endif
100 // The shared subtree stuff, which went in around 2.6.15
101 #ifndef MS_UNBINDABLE
102 # define MS_UNBINDABLE  (1 << 17)
103 #endif
104 #ifndef MS_PRIVATE
105 # define MS_PRIVATE     (1 << 18)
106 #endif
107 #ifndef MS_SLAVE
108 # define MS_SLAVE       (1 << 19)
109 #endif
110 #ifndef MS_SHARED
111 # define MS_SHARED      (1 << 20)
112 #endif
113 #ifndef MS_RELATIME
114 # define MS_RELATIME    (1 << 21)
115 #endif
116
117 #include "libbb.h"
118 #if ENABLE_FEATURE_MOUNT_LABEL
119 # include "volume_id.h"
120 #else
121 # define resolve_mount_spec(fsname) ((void)0)
122 #endif
123
124 // Needed for nfs support only
125 #include <sys/utsname.h>
126 #undef TRUE
127 #undef FALSE
128 #if ENABLE_FEATURE_MOUNT_NFS
129 /* This is just a warning of a common mistake.  Possibly this should be a
130  * uclibc faq entry rather than in busybox... */
131 # if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
132 #  error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support"
133 # endif
134 # include <rpc/rpc.h>
135 # include <rpc/pmap_prot.h>
136 # include <rpc/pmap_clnt.h>
137 #endif
138
139
140 #if defined(__dietlibc__)
141 // 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
142 // dietlibc-0.30 does not have implementation of getmntent_r()
143 static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
144                 char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM)
145 {
146         struct mntent* ment = getmntent(stream);
147         return memcpy(result, ment, sizeof(*ment));
148 }
149 #endif
150
151
152 // Not real flags, but we want to be able to check for this.
153 enum {
154         MOUNT_USERS  = (1 << 28) * ENABLE_DESKTOP,
155         MOUNT_NOAUTO = (1 << 29),
156         MOUNT_SWAP   = (1 << 30),
157 };
158
159
160 #define OPTION_STR "o:t:rwanfvsiO:"
161 enum {
162         OPT_o = (1 << 0),
163         OPT_t = (1 << 1),
164         OPT_r = (1 << 2),
165         OPT_w = (1 << 3),
166         OPT_a = (1 << 4),
167         OPT_n = (1 << 5),
168         OPT_f = (1 << 6),
169         OPT_v = (1 << 7),
170         OPT_s = (1 << 8),
171         OPT_i = (1 << 9),
172         OPT_O = (1 << 10),
173 };
174
175 #if ENABLE_FEATURE_MTAB_SUPPORT
176 #define USE_MTAB (!(option_mask32 & OPT_n))
177 #else
178 #define USE_MTAB 0
179 #endif
180
181 #if ENABLE_FEATURE_MOUNT_FAKE
182 #define FAKE_IT (option_mask32 & OPT_f)
183 #else
184 #define FAKE_IT 0
185 #endif
186
187 #if ENABLE_FEATURE_MOUNT_HELPERS
188 #define HELPERS_ALLOWED (!(option_mask32 & OPT_i))
189 #else
190 #define HELPERS_ALLOWED 0
191 #endif
192
193
194 // TODO: more "user" flag compatibility.
195 // "user" option (from mount manpage):
196 // Only the user that mounted a filesystem can unmount it again.
197 // If any user should be able to unmount, then use users instead of user
198 // in the fstab line.  The owner option is similar to the user option,
199 // with the restriction that the user must be the owner of the special file.
200 // This may be useful e.g. for /dev/fd if a login script makes
201 // the console user owner of this device.
202
203 // Standard mount options (from -o options or --options),
204 // with corresponding flags
205 static const int32_t mount_options[] = {
206         // MS_FLAGS set a bit.  ~MS_FLAGS disable that bit.  0 flags are NOPs.
207
208         IF_FEATURE_MOUNT_LOOP(
209                 /* "loop" */ 0,
210         )
211
212         IF_FEATURE_MOUNT_FSTAB(
213                 /* "defaults" */ 0,
214                 /* "quiet" 0 - do not filter out, vfat wants to see it */
215                 /* "noauto" */ MOUNT_NOAUTO,
216                 /* "sw"     */ MOUNT_SWAP,
217                 /* "swap"   */ MOUNT_SWAP,
218                 IF_DESKTOP(/* "user"  */ MOUNT_USERS,)
219                 IF_DESKTOP(/* "users" */ MOUNT_USERS,)
220                 /* "_netdev" */ 0,
221         )
222
223         IF_FEATURE_MOUNT_FLAGS(
224                 // vfs flags
225                 /* "nosuid"      */ MS_NOSUID,
226                 /* "suid"        */ ~MS_NOSUID,
227                 /* "dev"         */ ~MS_NODEV,
228                 /* "nodev"       */ MS_NODEV,
229                 /* "exec"        */ ~MS_NOEXEC,
230                 /* "noexec"      */ MS_NOEXEC,
231                 /* "sync"        */ MS_SYNCHRONOUS,
232                 /* "dirsync"     */ MS_DIRSYNC,
233                 /* "async"       */ ~MS_SYNCHRONOUS,
234                 /* "atime"       */ ~MS_NOATIME,
235                 /* "noatime"     */ MS_NOATIME,
236                 /* "diratime"    */ ~MS_NODIRATIME,
237                 /* "nodiratime"  */ MS_NODIRATIME,
238                 /* "mand"        */ MS_MANDLOCK,
239                 /* "nomand"      */ ~MS_MANDLOCK,
240                 /* "relatime"    */ MS_RELATIME,
241                 /* "norelatime"  */ ~MS_RELATIME,
242                 /* "loud"        */ ~MS_SILENT,
243                 /* "rbind"       */ MS_BIND|MS_RECURSIVE,
244
245                 // action flags
246                 /* "union"       */ MS_UNION,
247                 /* "bind"        */ MS_BIND,
248                 /* "move"        */ MS_MOVE,
249                 /* "shared"      */ MS_SHARED,
250                 /* "slave"       */ MS_SLAVE,
251                 /* "private"     */ MS_PRIVATE,
252                 /* "unbindable"  */ MS_UNBINDABLE,
253                 /* "rshared"     */ MS_SHARED|MS_RECURSIVE,
254                 /* "rslave"      */ MS_SLAVE|MS_RECURSIVE,
255                 /* "rprivate"    */ MS_PRIVATE|MS_RECURSIVE,
256                 /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
257         )
258
259         // Always understood.
260         /* "ro"      */ MS_RDONLY,  // vfs flag
261         /* "rw"      */ ~MS_RDONLY, // vfs flag
262         /* "remount" */ MS_REMOUNT  // action flag
263 };
264
265 static const char mount_option_str[] =
266         IF_FEATURE_MOUNT_LOOP(
267                 "loop\0"
268         )
269         IF_FEATURE_MOUNT_FSTAB(
270                 "defaults\0"
271                 // "quiet\0" - do not filter out, vfat wants to see it
272                 "noauto\0"
273                 "sw\0"
274                 "swap\0"
275                 IF_DESKTOP("user\0")
276                 IF_DESKTOP("users\0")
277                 "_netdev\0"
278         )
279         IF_FEATURE_MOUNT_FLAGS(
280                 // vfs flags
281                 "nosuid\0"
282                 "suid\0"
283                 "dev\0"
284                 "nodev\0"
285                 "exec\0"
286                 "noexec\0"
287                 "sync\0"
288                 "dirsync\0"
289                 "async\0"
290                 "atime\0"
291                 "noatime\0"
292                 "diratime\0"
293                 "nodiratime\0"
294                 "mand\0"
295                 "nomand\0"
296                 "relatime\0"
297                 "norelatime\0"
298                 "loud\0"
299                 "rbind\0"
300
301                 // action flags
302                 "union\0"
303                 "bind\0"
304                 "move\0"
305                 "make-shared\0"
306                 "make-slave\0"
307                 "make-private\0"
308                 "make-unbindable\0"
309                 "make-rshared\0"
310                 "make-rslave\0"
311                 "make-rprivate\0"
312                 "make-runbindable\0"
313         )
314
315         // Always understood.
316         "ro\0"        // vfs flag
317         "rw\0"        // vfs flag
318         "remount\0"   // action flag
319 ;
320
321
322 struct globals {
323 #if ENABLE_FEATURE_MOUNT_NFS
324         smalluint nfs_mount_version;
325 #endif
326 #if ENABLE_FEATURE_MOUNT_VERBOSE
327         unsigned verbose;
328 #endif
329         llist_t *fslist;
330         char getmntent_buf[1];
331 } FIX_ALIASING;
332 enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
333 #define G (*(struct globals*)&bb_common_bufsiz1)
334 #define nfs_mount_version (G.nfs_mount_version)
335 #if ENABLE_FEATURE_MOUNT_VERBOSE
336 #define verbose           (G.verbose          )
337 #else
338 #define verbose           0
339 #endif
340 #define fslist            (G.fslist           )
341 #define getmntent_buf     (G.getmntent_buf    )
342 #define INIT_G() do { } while (0)
343
344 #if ENABLE_FEATURE_MTAB_SUPPORT
345 /*
346  * update_mtab_entry_on_move() is used to update entry in case of mount --move.
347  * we are looking for existing entries mnt_dir which is equal to mnt_fsname of
348  * input mntent and replace it by new one.
349  */
350 static void FAST_FUNC update_mtab_entry_on_move(const struct mntent *mp)
351 {
352         struct mntent *entries, *m;
353         int i, count;
354         FILE *mountTable;
355
356         mountTable = setmntent(bb_path_mtab_file, "r");
357         if (!mountTable) {
358                 bb_perror_msg(bb_path_mtab_file);
359                 return;
360         }
361
362         entries = NULL;
363         count = 0;
364         while ((m = getmntent(mountTable)) != NULL) {
365                 entries = xrealloc_vector(entries, 3, count);
366                 entries[count].mnt_fsname = xstrdup(m->mnt_fsname);
367                 entries[count].mnt_dir = xstrdup(m->mnt_dir);
368                 entries[count].mnt_type = xstrdup(m->mnt_type);
369                 entries[count].mnt_opts = xstrdup(m->mnt_opts);
370                 entries[count].mnt_freq = m->mnt_freq;
371                 entries[count].mnt_passno = m->mnt_passno;
372                 count++;
373         }
374         endmntent(mountTable);
375
376         mountTable = setmntent(bb_path_mtab_file, "w");
377         if (mountTable) {
378                 for (i = 0; i < count; i++) {
379                         if (strcmp(entries[i].mnt_dir, mp->mnt_fsname) != 0)
380                                 addmntent(mountTable, &entries[i]);
381                         else
382                                 addmntent(mountTable, mp);
383                 }
384                 endmntent(mountTable);
385         } else if (errno != EROFS)
386                 bb_perror_msg(bb_path_mtab_file);
387
388         if (ENABLE_FEATURE_CLEAN_UP) {
389                 for (i = 0; i < count; i++) {
390                         free(entries[i].mnt_fsname);
391                         free(entries[i].mnt_dir);
392                         free(entries[i].mnt_type);
393                         free(entries[i].mnt_opts);
394                 }
395                 free(entries);
396         }
397 }
398 #endif
399
400 #if ENABLE_FEATURE_MOUNT_VERBOSE
401 static int verbose_mount(const char *source, const char *target,
402                 const char *filesystemtype,
403                 unsigned long mountflags, const void *data)
404 {
405         int rc;
406
407         errno = 0;
408         rc = mount(source, target, filesystemtype, mountflags, data);
409         if (verbose >= 2)
410                 bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
411                         source, target, filesystemtype,
412                         mountflags, (char*)data, rc);
413         return rc;
414 }
415 #else
416 #define verbose_mount(...) mount(__VA_ARGS__)
417 #endif
418
419 // Append mount options to string
420 static void append_mount_options(char **oldopts, const char *newopts)
421 {
422         if (*oldopts && **oldopts) {
423                 // Do not insert options which are already there
424                 while (newopts[0]) {
425                         char *p;
426                         int len = strlen(newopts);
427                         p = strchr(newopts, ',');
428                         if (p) len = p - newopts;
429                         p = *oldopts;
430                         while (1) {
431                                 if (!strncmp(p, newopts, len)
432                                  && (p[len] == ',' || p[len] == '\0'))
433                                         goto skip;
434                                 p = strchr(p,',');
435                                 if (!p) break;
436                                 p++;
437                         }
438                         p = xasprintf("%s,%.*s", *oldopts, len, newopts);
439                         free(*oldopts);
440                         *oldopts = p;
441  skip:
442                         newopts += len;
443                         while (newopts[0] == ',') newopts++;
444                 }
445         } else {
446                 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
447                 *oldopts = xstrdup(newopts);
448         }
449 }
450
451 // Use the mount_options list to parse options into flags.
452 // Also update list of unrecognized options if unrecognized != NULL
453 static long parse_mount_options(char *options, char **unrecognized)
454 {
455         long flags = MS_SILENT;
456
457         // Loop through options
458         for (;;) {
459                 unsigned i;
460                 char *comma = strchr(options, ',');
461                 const char *option_str = mount_option_str;
462
463                 if (comma) *comma = '\0';
464
465 // FIXME: use hasmntopt()
466                 // Find this option in mount_options
467                 for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
468                         if (strcasecmp(option_str, options) == 0) {
469                                 long fl = mount_options[i];
470                                 if (fl < 0)
471                                         flags &= fl;
472                                 else
473                                         flags |= fl;
474                                 goto found;
475                         }
476                         option_str += strlen(option_str) + 1;
477                 }
478                 // We did not recognize this option.
479                 // If "unrecognized" is not NULL, append option there.
480                 // Note that we should not append *empty* option -
481                 // in this case we want to pass NULL, not "", to "data"
482                 // parameter of mount(2) syscall.
483                 // This is crucial for filesystems that don't accept
484                 // any arbitrary mount options, like cgroup fs:
485                 // "mount -t cgroup none /mnt"
486                 if (options[0] && unrecognized) {
487                         // Add it to strflags, to pass on to kernel
488                         char *p = *unrecognized;
489                         unsigned len = p ? strlen(p) : 0;
490                         *unrecognized = p = xrealloc(p, len + strlen(options) + 2);
491
492                         // Comma separated if it's not the first one
493                         if (len) p[len++] = ',';
494                         strcpy(p + len, options);
495                 }
496  found:
497                 if (!comma)
498                         break;
499                 // Advance to next option
500                 *comma = ',';
501                 options = ++comma;
502         }
503
504         return flags;
505 }
506
507 // Return a list of all block device backed filesystems
508 static llist_t *get_block_backed_filesystems(void)
509 {
510         static const char filesystems[2][sizeof("/proc/filesystems")] = {
511                 "/etc/filesystems",
512                 "/proc/filesystems",
513         };
514         char *fs, *buf;
515         llist_t *list = NULL;
516         int i;
517         FILE *f;
518
519         for (i = 0; i < 2; i++) {
520                 f = fopen_for_read(filesystems[i]);
521                 if (!f) continue;
522
523                 while ((buf = xmalloc_fgetline(f)) != NULL) {
524                         if (strncmp(buf, "nodev", 5) == 0 && isspace(buf[5]))
525                                 goto next;
526                         fs = skip_whitespace(buf);
527                         if (*fs == '#' || *fs == '*' || !*fs)
528                                 goto next;
529
530                         llist_add_to_end(&list, xstrdup(fs));
531  next:
532                         free(buf);
533                 }
534                 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
535         }
536
537         return list;
538 }
539
540 #if ENABLE_FEATURE_CLEAN_UP
541 static void delete_block_backed_filesystems(void)
542 {
543         llist_free(fslist, free);
544 }
545 #else
546 void delete_block_backed_filesystems(void);
547 #endif
548
549 // Perform actual mount of specific filesystem at specific location.
550 // NB: mp->xxx fields may be trashed on exit
551 static int mount_it_now(struct mntent *mp, long vfsflags, char *filteropts)
552 {
553         int rc = 0;
554
555         if (FAKE_IT) {
556                 if (verbose >= 2)
557                         bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
558                                 mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
559                                 vfsflags, filteropts);
560                 goto mtab;
561         }
562
563         // Mount, with fallback to read-only if necessary.
564         for (;;) {
565                 errno = 0;
566                 rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
567                                 vfsflags, filteropts);
568
569                 // If mount failed, try
570                 // helper program mount.<mnt_type>
571                 if (HELPERS_ALLOWED && rc && mp->mnt_type) {
572                         char *args[8];
573                         int errno_save = errno;
574                         args[0] = xasprintf("mount.%s", mp->mnt_type);
575                         rc = 1;
576                         if (FAKE_IT)
577                                 args[rc++] = (char *)"-f";
578                         if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB)
579                                 args[rc++] = (char *)"-n";
580                         args[rc++] = mp->mnt_fsname;
581                         args[rc++] = mp->mnt_dir;
582                         if (filteropts) {
583                                 args[rc++] = (char *)"-o";
584                                 args[rc++] = filteropts;
585                         }
586                         args[rc] = NULL;
587                         rc = spawn_and_wait(args);
588                         free(args[0]);
589                         if (!rc)
590                                 break;
591                         errno = errno_save;
592                 }
593
594                 if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
595                         break;
596                 if (!(vfsflags & MS_SILENT))
597                         bb_error_msg("%s is write-protected, mounting read-only",
598                                                 mp->mnt_fsname);
599                 vfsflags |= MS_RDONLY;
600         }
601
602         // Abort entirely if permission denied.
603
604         if (rc && errno == EPERM)
605                 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
606
607         // If the mount was successful, and we're maintaining an old-style
608         // mtab file by hand, add the new entry to it now.
609  mtab:
610         if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) {
611                 char *fsname;
612                 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
613                 const char *option_str = mount_option_str;
614                 int i;
615
616                 if (!mountTable) {
617                         bb_perror_msg(bb_path_mtab_file);
618                         goto ret;
619                 }
620
621                 // Add vfs string flags
622                 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
623                         if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
624                                 append_mount_options(&(mp->mnt_opts), option_str);
625                         option_str += strlen(option_str) + 1;
626                 }
627
628                 // Remove trailing / (if any) from directory we mounted on
629                 i = strlen(mp->mnt_dir) - 1;
630                 while (i > 0 && mp->mnt_dir[i] == '/')
631                         mp->mnt_dir[i--] = '\0';
632
633                 // Convert to canonical pathnames as needed
634                 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
635                 fsname = NULL;
636                 if (!mp->mnt_type || !*mp->mnt_type) { // bind mount
637                         mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
638                         mp->mnt_type = (char*)"bind";
639                 }
640                 mp->mnt_freq = mp->mnt_passno = 0;
641
642                 // Write and close
643 #if ENABLE_FEATURE_MTAB_SUPPORT
644                 if (vfsflags & MS_MOVE)
645                         update_mtab_entry_on_move(mp);
646                 else
647 #endif
648                         addmntent(mountTable, mp);
649                 endmntent(mountTable);
650
651                 if (ENABLE_FEATURE_CLEAN_UP) {
652                         free(mp->mnt_dir);
653                         free(fsname);
654                 }
655         }
656  ret:
657         return rc;
658 }
659
660 #if ENABLE_FEATURE_MOUNT_NFS
661
662 /*
663  * Linux NFS mount
664  * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
665  *
666  * Licensed under GPLv2, see file LICENSE in this source tree.
667  *
668  * Wed Feb  8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
669  * numbers to be specified on the command line.
670  *
671  * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
672  * Omit the call to connect() for Linux version 1.3.11 or later.
673  *
674  * Wed Oct  1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
675  * Implemented the "bg", "fg" and "retry" mount options for NFS.
676  *
677  * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
678  * - added Native Language Support
679  *
680  * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
681  * plus NFSv3 stuff.
682  */
683
684 #define MOUNTPORT 635
685 #define MNTPATHLEN 1024
686 #define MNTNAMLEN 255
687 #define FHSIZE 32
688 #define FHSIZE3 64
689
690 typedef char fhandle[FHSIZE];
691
692 typedef struct {
693         unsigned int fhandle3_len;
694         char *fhandle3_val;
695 } fhandle3;
696
697 enum mountstat3 {
698         MNT_OK = 0,
699         MNT3ERR_PERM = 1,
700         MNT3ERR_NOENT = 2,
701         MNT3ERR_IO = 5,
702         MNT3ERR_ACCES = 13,
703         MNT3ERR_NOTDIR = 20,
704         MNT3ERR_INVAL = 22,
705         MNT3ERR_NAMETOOLONG = 63,
706         MNT3ERR_NOTSUPP = 10004,
707         MNT3ERR_SERVERFAULT = 10006,
708 };
709 typedef enum mountstat3 mountstat3;
710
711 struct fhstatus {
712         unsigned int fhs_status;
713         union {
714                 fhandle fhs_fhandle;
715         } fhstatus_u;
716 };
717 typedef struct fhstatus fhstatus;
718
719 struct mountres3_ok {
720         fhandle3 fhandle;
721         struct {
722                 unsigned int auth_flavours_len;
723                 char *auth_flavours_val;
724         } auth_flavours;
725 };
726 typedef struct mountres3_ok mountres3_ok;
727
728 struct mountres3 {
729         mountstat3 fhs_status;
730         union {
731                 mountres3_ok mountinfo;
732         } mountres3_u;
733 };
734 typedef struct mountres3 mountres3;
735
736 typedef char *dirpath;
737
738 typedef char *name;
739
740 typedef struct mountbody *mountlist;
741
742 struct mountbody {
743         name ml_hostname;
744         dirpath ml_directory;
745         mountlist ml_next;
746 };
747 typedef struct mountbody mountbody;
748
749 typedef struct groupnode *groups;
750
751 struct groupnode {
752         name gr_name;
753         groups gr_next;
754 };
755 typedef struct groupnode groupnode;
756
757 typedef struct exportnode *exports;
758
759 struct exportnode {
760         dirpath ex_dir;
761         groups ex_groups;
762         exports ex_next;
763 };
764 typedef struct exportnode exportnode;
765
766 struct ppathcnf {
767         int pc_link_max;
768         short pc_max_canon;
769         short pc_max_input;
770         short pc_name_max;
771         short pc_path_max;
772         short pc_pipe_buf;
773         uint8_t pc_vdisable;
774         char pc_xxx;
775         short pc_mask[2];
776 };
777 typedef struct ppathcnf ppathcnf;
778
779 #define MOUNTPROG 100005
780 #define MOUNTVERS 1
781
782 #define MOUNTPROC_NULL 0
783 #define MOUNTPROC_MNT 1
784 #define MOUNTPROC_DUMP 2
785 #define MOUNTPROC_UMNT 3
786 #define MOUNTPROC_UMNTALL 4
787 #define MOUNTPROC_EXPORT 5
788 #define MOUNTPROC_EXPORTALL 6
789
790 #define MOUNTVERS_POSIX 2
791
792 #define MOUNTPROC_PATHCONF 7
793
794 #define MOUNT_V3 3
795
796 #define MOUNTPROC3_NULL 0
797 #define MOUNTPROC3_MNT 1
798 #define MOUNTPROC3_DUMP 2
799 #define MOUNTPROC3_UMNT 3
800 #define MOUNTPROC3_UMNTALL 4
801 #define MOUNTPROC3_EXPORT 5
802
803 enum {
804 #ifndef NFS_FHSIZE
805         NFS_FHSIZE = 32,
806 #endif
807 #ifndef NFS_PORT
808         NFS_PORT = 2049
809 #endif
810 };
811
812 /*
813  * We want to be able to compile mount on old kernels in such a way
814  * that the binary will work well on more recent kernels.
815  * Thus, if necessary we teach nfsmount.c the structure of new fields
816  * that will come later.
817  *
818  * Moreover, the new kernel includes conflict with glibc includes
819  * so it is easiest to ignore the kernel altogether (at compile time).
820  */
821
822 struct nfs2_fh {
823         char            data[32];
824 };
825 struct nfs3_fh {
826         unsigned short  size;
827         unsigned char   data[64];
828 };
829
830 struct nfs_mount_data {
831         int             version;        /* 1 */
832         int             fd;             /* 1 */
833         struct nfs2_fh  old_root;       /* 1 */
834         int             flags;          /* 1 */
835         int             rsize;          /* 1 */
836         int             wsize;          /* 1 */
837         int             timeo;          /* 1 */
838         int             retrans;        /* 1 */
839         int             acregmin;       /* 1 */
840         int             acregmax;       /* 1 */
841         int             acdirmin;       /* 1 */
842         int             acdirmax;       /* 1 */
843         struct sockaddr_in addr;        /* 1 */
844         char            hostname[256];  /* 1 */
845         int             namlen;         /* 2 */
846         unsigned int    bsize;          /* 3 */
847         struct nfs3_fh  root;           /* 4 */
848 };
849
850 /* bits in the flags field */
851 enum {
852         NFS_MOUNT_SOFT = 0x0001,        /* 1 */
853         NFS_MOUNT_INTR = 0x0002,        /* 1 */
854         NFS_MOUNT_SECURE = 0x0004,      /* 1 */
855         NFS_MOUNT_POSIX = 0x0008,       /* 1 */
856         NFS_MOUNT_NOCTO = 0x0010,       /* 1 */
857         NFS_MOUNT_NOAC = 0x0020,        /* 1 */
858         NFS_MOUNT_TCP = 0x0040,         /* 2 */
859         NFS_MOUNT_VER3 = 0x0080,        /* 3 */
860         NFS_MOUNT_KERBEROS = 0x0100,    /* 3 */
861         NFS_MOUNT_NONLM = 0x0200,       /* 3 */
862         NFS_MOUNT_NOACL = 0x0800,       /* 4 */
863         NFS_MOUNT_NORDIRPLUS = 0x4000
864 };
865
866
867 /*
868  * We need to translate between nfs status return values and
869  * the local errno values which may not be the same.
870  *
871  * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
872  * "after #include <errno.h> the symbol errno is reserved for any use,
873  *  it cannot even be used as a struct tag or field name".
874  */
875 #ifndef EDQUOT
876 # define EDQUOT ENOSPC
877 #endif
878 /* Convert each NFSERR_BLAH into EBLAH */
879 static const uint8_t nfs_err_stat[] = {
880          1,  2,  5,  6, 13, 17,
881         19, 20, 21, 22, 27, 28,
882         30, 63, 66, 69, 70, 71
883 };
884 #if ( \
885         EPERM | ENOENT      | EIO      | ENXIO | EACCES| EEXIST | \
886         ENODEV| ENOTDIR     | EISDIR   | EINVAL| EFBIG | ENOSPC | \
887         EROFS | ENAMETOOLONG| ENOTEMPTY| EDQUOT| ESTALE| EREMOTE) < 256
888 typedef uint8_t nfs_err_type;
889 #else
890 typedef uint16_t nfs_err_type;
891 #endif
892 static const nfs_err_type nfs_err_errnum[] = {
893         EPERM , ENOENT      , EIO      , ENXIO , EACCES, EEXIST,
894         ENODEV, ENOTDIR     , EISDIR   , EINVAL, EFBIG , ENOSPC,
895         EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE
896 };
897 static char *nfs_strerror(int status)
898 {
899         int i;
900
901         for (i = 0; i < ARRAY_SIZE(nfs_err_stat); i++) {
902                 if (nfs_err_stat[i] == status)
903                         return strerror(nfs_err_errnum[i]);
904         }
905         return xasprintf("unknown nfs status return value: %d", status);
906 }
907
908 static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
909 {
910         return xdr_opaque(xdrs, objp, FHSIZE);
911 }
912
913 static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
914 {
915         if (!xdr_u_int(xdrs, &objp->fhs_status))
916                  return FALSE;
917         if (objp->fhs_status == 0)
918                 return xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle);
919         return TRUE;
920 }
921
922 static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
923 {
924         return xdr_string(xdrs, objp, MNTPATHLEN);
925 }
926
927 static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
928 {
929         return xdr_bytes(xdrs, (char **)&objp->fhandle3_val,
930                            (unsigned int *) &objp->fhandle3_len,
931                            FHSIZE3);
932 }
933
934 static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
935 {
936         if (!xdr_fhandle3(xdrs, &objp->fhandle))
937                 return FALSE;
938         return xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val),
939                            &(objp->auth_flavours.auth_flavours_len),
940                            ~0,
941                            sizeof(int),
942                            (xdrproc_t) xdr_int);
943 }
944
945 static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
946 {
947         return xdr_enum(xdrs, (enum_t *) objp);
948 }
949
950 static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
951 {
952         if (!xdr_mountstat3(xdrs, &objp->fhs_status))
953                 return FALSE;
954         if (objp->fhs_status == MNT_OK)
955                 return xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo);
956         return TRUE;
957 }
958
959 #define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
960
961 /*
962  * Unfortunately, the kernel prints annoying console messages
963  * in case of an unexpected nfs mount version (instead of
964  * just returning some error).  Therefore we'll have to try
965  * and figure out what version the kernel expects.
966  *
967  * Variables:
968  *      KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
969  *      NFS_MOUNT_VERSION: these nfsmount sources at compile time
970  *      nfs_mount_version: version this source and running kernel can handle
971  */
972 static void
973 find_kernel_nfs_mount_version(void)
974 {
975         int kernel_version;
976
977         if (nfs_mount_version)
978                 return;
979
980         nfs_mount_version = 4; /* default */
981
982         kernel_version = get_linux_version_code();
983         if (kernel_version) {
984                 if (kernel_version < KERNEL_VERSION(2,2,18))
985                         nfs_mount_version = 3;
986                 /* else v4 since 2.3.99pre4 */
987         }
988 }
989
990 static void
991 get_mountport(struct pmap *pm_mnt,
992         struct sockaddr_in *server_addr,
993         long unsigned prog,
994         long unsigned version,
995         long unsigned proto,
996         long unsigned port)
997 {
998         struct pmaplist *pmap;
999
1000         server_addr->sin_port = PMAPPORT;
1001 /* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
1002  * I understand it like "IPv6 for this is not 100% ready" */
1003         pmap = pmap_getmaps(server_addr);
1004
1005         if (version > MAX_NFSPROT)
1006                 version = MAX_NFSPROT;
1007         if (!prog)
1008                 prog = MOUNTPROG;
1009         pm_mnt->pm_prog = prog;
1010         pm_mnt->pm_vers = version;
1011         pm_mnt->pm_prot = proto;
1012         pm_mnt->pm_port = port;
1013
1014         while (pmap) {
1015                 if (pmap->pml_map.pm_prog != prog)
1016                         goto next;
1017                 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
1018                         goto next;
1019                 if (version > 2 && pmap->pml_map.pm_vers != version)
1020                         goto next;
1021                 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
1022                         goto next;
1023                 if (pmap->pml_map.pm_vers > MAX_NFSPROT
1024                  || (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto)
1025                  || (port && pmap->pml_map.pm_port != port)
1026                 ) {
1027                         goto next;
1028                 }
1029                 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
1030  next:
1031                 pmap = pmap->pml_next;
1032         }
1033         if (!pm_mnt->pm_vers)
1034                 pm_mnt->pm_vers = MOUNTVERS;
1035         if (!pm_mnt->pm_port)
1036                 pm_mnt->pm_port = MOUNTPORT;
1037         if (!pm_mnt->pm_prot)
1038                 pm_mnt->pm_prot = IPPROTO_TCP;
1039 }
1040
1041 #if BB_MMU
1042 static int daemonize(void)
1043 {
1044         int pid = fork();
1045         if (pid < 0) /* error */
1046                 return -errno;
1047         if (pid > 0) /* parent */
1048                 return 0;
1049         /* child */
1050         close(0);
1051         xopen(bb_dev_null, O_RDWR);
1052         xdup2(0, 1);
1053         xdup2(0, 2);
1054         setsid();
1055         openlog(applet_name, LOG_PID, LOG_DAEMON);
1056         logmode = LOGMODE_SYSLOG;
1057         return 1;
1058 }
1059 #else
1060 static inline int daemonize(void) { return -ENOSYS; }
1061 #endif
1062
1063 /* TODO */
1064 static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
1065 {
1066         return 0;
1067 }
1068
1069 /* RPC strerror analogs are terminally idiotic:
1070  * *mandatory* prefix and \n at end.
1071  * This hopefully helps. Usage:
1072  * error_msg_rpc(clnt_*error*(" ")) */
1073 static void error_msg_rpc(const char *msg)
1074 {
1075         int len;
1076         while (msg[0] == ' ' || msg[0] == ':') msg++;
1077         len = strlen(msg);
1078         while (len && msg[len-1] == '\n') len--;
1079         bb_error_msg("%.*s", len, msg);
1080 }
1081
1082 /* NB: mp->xxx fields may be trashed on exit */
1083 static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
1084 {
1085         CLIENT *mclient;
1086         char *hostname;
1087         char *pathname;
1088         char *mounthost;
1089         /* prior to 2.6.23, kernel took NFS options in a form of this struct
1090          * only. 2.6.23+ looks at data->version, and if it's not 1..6,
1091          * then data pointer is interpreted as a string. */
1092         struct nfs_mount_data data;
1093         char *opt;
1094         struct hostent *hp;
1095         struct sockaddr_in server_addr;
1096         struct sockaddr_in mount_server_addr;
1097         int msock, fsock;
1098         union {
1099                 struct fhstatus nfsv2;
1100                 struct mountres3 nfsv3;
1101         } status;
1102         int daemonized;
1103         char *s;
1104         int port;
1105         int mountport;
1106         int proto;
1107 #if BB_MMU
1108         smallint bg = 0;
1109 #else
1110         enum { bg = 0 };
1111 #endif
1112         int retry;
1113         int mountprog;
1114         int mountvers;
1115         int nfsprog;
1116         int nfsvers;
1117         int retval;
1118         /* these all are one-bit really. gcc 4.3.1 likes this combination: */
1119         smallint tcp;
1120         smallint soft;
1121         int intr;
1122         int posix;
1123         int nocto;
1124         int noac;
1125         int nordirplus;
1126         int nolock;
1127         int noacl;
1128
1129         find_kernel_nfs_mount_version();
1130
1131         daemonized = 0;
1132         mounthost = NULL;
1133         retval = ETIMEDOUT;
1134         msock = fsock = -1;
1135         mclient = NULL;
1136
1137         /* NB: hostname, mounthost, filteropts must be free()d prior to return */
1138
1139         filteropts = xstrdup(filteropts); /* going to trash it later... */
1140
1141         hostname = xstrdup(mp->mnt_fsname);
1142         /* mount_main() guarantees that ':' is there */
1143         s = strchr(hostname, ':');
1144         pathname = s + 1;
1145         *s = '\0';
1146         /* Ignore all but first hostname in replicated mounts
1147          * until they can be fully supported. (mack@sgi.com) */
1148         s = strchr(hostname, ',');
1149         if (s) {
1150                 *s = '\0';
1151                 bb_error_msg("warning: multiple hostnames not supported");
1152         }
1153
1154         server_addr.sin_family = AF_INET;
1155         if (!inet_aton(hostname, &server_addr.sin_addr)) {
1156                 hp = gethostbyname(hostname);
1157                 if (hp == NULL) {
1158                         bb_herror_msg("%s", hostname);
1159                         goto fail;
1160                 }
1161                 if (hp->h_length != (int)sizeof(struct in_addr)) {
1162                         bb_error_msg_and_die("only IPv4 is supported");
1163                 }
1164                 memcpy(&server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
1165         }
1166
1167         memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1168
1169         /* add IP address to mtab options for use when unmounting */
1170
1171         if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1172                 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1173         } else {
1174                 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1175                                         mp->mnt_opts[0] ? "," : "",
1176                                         inet_ntoa(server_addr.sin_addr));
1177                 free(mp->mnt_opts);
1178                 mp->mnt_opts = tmp;
1179         }
1180
1181         /* Set default options.
1182          * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1183          * let the kernel decide.
1184          * timeo is filled in after we know whether it'll be TCP or UDP. */
1185         memset(&data, 0, sizeof(data));
1186         data.retrans  = 3;
1187         data.acregmin = 3;
1188         data.acregmax = 60;
1189         data.acdirmin = 30;
1190         data.acdirmax = 60;
1191         data.namlen   = NAME_MAX;
1192
1193         soft = 0;
1194         intr = 0;
1195         posix = 0;
1196         nocto = 0;
1197         nolock = 0;
1198         noac = 0;
1199         nordirplus = 0;
1200         noacl = 0;
1201         retry = 10000;          /* 10000 minutes ~ 1 week */
1202         tcp = 1;                        /* nfs-utils uses tcp per default */
1203
1204         mountprog = MOUNTPROG;
1205         mountvers = 0;
1206         port = 0;
1207         mountport = 0;
1208         nfsprog = 100003;
1209         nfsvers = 0;
1210
1211         /* parse options */
1212         if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
1213                 char *opteq = strchr(opt, '=');
1214                 if (opteq) {
1215                         int val, idx;
1216                         static const char options[] ALIGN1 =
1217                                 /* 0 */ "rsize\0"
1218                                 /* 1 */ "wsize\0"
1219                                 /* 2 */ "timeo\0"
1220                                 /* 3 */ "retrans\0"
1221                                 /* 4 */ "acregmin\0"
1222                                 /* 5 */ "acregmax\0"
1223                                 /* 6 */ "acdirmin\0"
1224                                 /* 7 */ "acdirmax\0"
1225                                 /* 8 */ "actimeo\0"
1226                                 /* 9 */ "retry\0"
1227                                 /* 10 */ "port\0"
1228                                 /* 11 */ "mountport\0"
1229                                 /* 12 */ "mounthost\0"
1230                                 /* 13 */ "mountprog\0"
1231                                 /* 14 */ "mountvers\0"
1232                                 /* 15 */ "nfsprog\0"
1233                                 /* 16 */ "nfsvers\0"
1234                                 /* 17 */ "vers\0"
1235                                 /* 18 */ "proto\0"
1236                                 /* 19 */ "namlen\0"
1237                                 /* 20 */ "addr\0";
1238
1239                         *opteq++ = '\0';
1240                         idx = index_in_strings(options, opt);
1241                         switch (idx) {
1242                         case 12: // "mounthost"
1243                                 mounthost = xstrndup(opteq,
1244                                                 strcspn(opteq, " \t\n\r,"));
1245                                 continue;
1246                         case 18: // "proto"
1247                                 if (!strncmp(opteq, "tcp", 3))
1248                                         tcp = 1;
1249                                 else if (!strncmp(opteq, "udp", 3))
1250                                         tcp = 0;
1251                                 else
1252                                         bb_error_msg("warning: unrecognized proto= option");
1253                                 continue;
1254                         case 20: // "addr" - ignore
1255                                 continue;
1256                         case -1: // unknown
1257                                 if (vfsflags & MS_REMOUNT)
1258                                         continue;
1259                         }
1260
1261                         val = xatoi_positive(opteq);
1262                         switch (idx) {
1263                         case 0: // "rsize"
1264                                 data.rsize = val;
1265                                 continue;
1266                         case 1: // "wsize"
1267                                 data.wsize = val;
1268                                 continue;
1269                         case 2: // "timeo"
1270                                 data.timeo = val;
1271                                 continue;
1272                         case 3: // "retrans"
1273                                 data.retrans = val;
1274                                 continue;
1275                         case 4: // "acregmin"
1276                                 data.acregmin = val;
1277                                 continue;
1278                         case 5: // "acregmax"
1279                                 data.acregmax = val;
1280                                 continue;
1281                         case 6: // "acdirmin"
1282                                 data.acdirmin = val;
1283                                 continue;
1284                         case 7: // "acdirmax"
1285                                 data.acdirmax = val;
1286                                 continue;
1287                         case 8: // "actimeo"
1288                                 data.acregmin = val;
1289                                 data.acregmax = val;
1290                                 data.acdirmin = val;
1291                                 data.acdirmax = val;
1292                                 continue;
1293                         case 9: // "retry"
1294                                 retry = val;
1295                                 continue;
1296                         case 10: // "port"
1297                                 port = val;
1298                                 continue;
1299                         case 11: // "mountport"
1300                                 mountport = val;
1301                                 continue;
1302                         case 13: // "mountprog"
1303                                 mountprog = val;
1304                                 continue;
1305                         case 14: // "mountvers"
1306                                 mountvers = val;
1307                                 continue;
1308                         case 15: // "nfsprog"
1309                                 nfsprog = val;
1310                                 continue;
1311                         case 16: // "nfsvers"
1312                         case 17: // "vers"
1313                                 nfsvers = val;
1314                                 continue;
1315                         case 19: // "namlen"
1316                                 //if (nfs_mount_version >= 2)
1317                                         data.namlen = val;
1318                                 //else
1319                                 //      bb_error_msg("warning: option namlen is not supported\n");
1320                                 continue;
1321                         default:
1322                                 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1323                                 goto fail;
1324                         }
1325                 }
1326                 else { /* not of the form opt=val */
1327                         static const char options[] ALIGN1 =
1328                                 "bg\0"
1329                                 "fg\0"
1330                                 "soft\0"
1331                                 "hard\0"
1332                                 "intr\0"
1333                                 "posix\0"
1334                                 "cto\0"
1335                                 "ac\0"
1336                                 "tcp\0"
1337                                 "udp\0"
1338                                 "lock\0"
1339                                 "rdirplus\0"
1340                                 "acl\0";
1341                         int val = 1;
1342                         if (!strncmp(opt, "no", 2)) {
1343                                 val = 0;
1344                                 opt += 2;
1345                         }
1346                         switch (index_in_strings(options, opt)) {
1347                         case 0: // "bg"
1348 #if BB_MMU
1349                                 bg = val;
1350 #endif
1351                                 break;
1352                         case 1: // "fg"
1353 #if BB_MMU
1354                                 bg = !val;
1355 #endif
1356                                 break;
1357                         case 2: // "soft"
1358                                 soft = val;
1359                                 break;
1360                         case 3: // "hard"
1361                                 soft = !val;
1362                                 break;
1363                         case 4: // "intr"
1364                                 intr = val;
1365                                 break;
1366                         case 5: // "posix"
1367                                 posix = val;
1368                                 break;
1369                         case 6: // "cto"
1370                                 nocto = !val;
1371                                 break;
1372                         case 7: // "ac"
1373                                 noac = !val;
1374                                 break;
1375                         case 8: // "tcp"
1376                                 tcp = val;
1377                                 break;
1378                         case 9: // "udp"
1379                                 tcp = !val;
1380                                 break;
1381                         case 10: // "lock"
1382                                 if (nfs_mount_version >= 3)
1383                                         nolock = !val;
1384                                 else
1385                                         bb_error_msg("warning: option nolock is not supported");
1386                                 break;
1387                         case 11: //rdirplus
1388                                 nordirplus = !val;
1389                                 break;
1390                         case 12: // acl
1391                                 noacl = !val;
1392                                 break;
1393                         default:
1394                                 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1395                                 goto fail;
1396                         }
1397                 }
1398         }
1399         proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1400
1401         data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1402                 | (intr ? NFS_MOUNT_INTR : 0)
1403                 | (posix ? NFS_MOUNT_POSIX : 0)
1404                 | (nocto ? NFS_MOUNT_NOCTO : 0)
1405                 | (noac ? NFS_MOUNT_NOAC : 0)
1406                 | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0)
1407                 | (noacl ? NFS_MOUNT_NOACL : 0);
1408         if (nfs_mount_version >= 2)
1409                 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1410         if (nfs_mount_version >= 3)
1411                 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1412         if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1413                 bb_error_msg("NFSv%d not supported", nfsvers);
1414                 goto fail;
1415         }
1416         if (nfsvers && !mountvers)
1417                 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1418         if (nfsvers && nfsvers < mountvers) {
1419                 mountvers = nfsvers;
1420         }
1421
1422         /* Adjust options if none specified */
1423         if (!data.timeo)
1424                 data.timeo = tcp ? 70 : 7;
1425
1426         data.version = nfs_mount_version;
1427
1428         if (vfsflags & MS_REMOUNT)
1429                 goto do_mount;
1430
1431         /*
1432          * If the previous mount operation on the same host was
1433          * backgrounded, and the "bg" for this mount is also set,
1434          * give up immediately, to avoid the initial timeout.
1435          */
1436         if (bg && we_saw_this_host_before(hostname)) {
1437                 daemonized = daemonize();
1438                 if (daemonized <= 0) { /* parent or error */
1439                         retval = -daemonized;
1440                         goto ret;
1441                 }
1442         }
1443
1444         /* Create mount daemon client */
1445         /* See if the nfs host = mount host. */
1446         if (mounthost) {
1447                 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1448                         mount_server_addr.sin_family = AF_INET;
1449                         mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1450                 } else {
1451                         hp = gethostbyname(mounthost);
1452                         if (hp == NULL) {
1453                                 bb_herror_msg("%s", mounthost);
1454                                 goto fail;
1455                         }
1456                         if (hp->h_length != (int)sizeof(struct in_addr)) {
1457                                 bb_error_msg_and_die("only IPv4 is supported");
1458                         }
1459                         mount_server_addr.sin_family = AF_INET;
1460                         memcpy(&mount_server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
1461                 }
1462         }
1463
1464         /*
1465          * The following loop implements the mount retries. When the mount
1466          * times out, and the "bg" option is set, we background ourself
1467          * and continue trying.
1468          *
1469          * The case where the mount point is not present and the "bg"
1470          * option is set, is treated as a timeout. This is done to
1471          * support nested mounts.
1472          *
1473          * The "retry" count specified by the user is the number of
1474          * minutes to retry before giving up.
1475          */
1476         {
1477                 struct timeval total_timeout;
1478                 struct timeval retry_timeout;
1479                 struct pmap pm_mnt;
1480                 time_t t;
1481                 time_t prevt;
1482                 time_t timeout;
1483
1484                 retry_timeout.tv_sec = 3;
1485                 retry_timeout.tv_usec = 0;
1486                 total_timeout.tv_sec = 20;
1487                 total_timeout.tv_usec = 0;
1488 /* FIXME: use monotonic()? */
1489                 timeout = time(NULL) + 60 * retry;
1490                 prevt = 0;
1491                 t = 30;
1492  retry:
1493                 /* Be careful not to use too many CPU cycles */
1494                 if (t - prevt < 30)
1495                         sleep(30);
1496
1497                 get_mountport(&pm_mnt, &mount_server_addr,
1498                                 mountprog,
1499                                 mountvers,
1500                                 proto,
1501                                 mountport);
1502                 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
1503
1504                 /* contact the mount daemon via TCP */
1505                 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1506                 msock = RPC_ANYSOCK;
1507
1508                 switch (pm_mnt.pm_prot) {
1509                 case IPPROTO_UDP:
1510                         mclient = clntudp_create(&mount_server_addr,
1511                                                  pm_mnt.pm_prog,
1512                                                  pm_mnt.pm_vers,
1513                                                  retry_timeout,
1514                                                  &msock);
1515                         if (mclient)
1516                                 break;
1517                         mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1518                         msock = RPC_ANYSOCK;
1519                 case IPPROTO_TCP:
1520                         mclient = clnttcp_create(&mount_server_addr,
1521                                                  pm_mnt.pm_prog,
1522                                                  pm_mnt.pm_vers,
1523                                                  &msock, 0, 0);
1524                         break;
1525                 default:
1526                         mclient = NULL;
1527                 }
1528                 if (!mclient) {
1529                         if (!daemonized && prevt == 0)
1530                                 error_msg_rpc(clnt_spcreateerror(" "));
1531                 } else {
1532                         enum clnt_stat clnt_stat;
1533
1534                         /* Try to mount hostname:pathname */
1535                         mclient->cl_auth = authunix_create_default();
1536
1537                         /* Make pointers in xdr_mountres3 NULL so
1538                          * that xdr_array allocates memory for us
1539                          */
1540                         memset(&status, 0, sizeof(status));
1541
1542                         if (pm_mnt.pm_vers == 3)
1543                                 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1544                                               (xdrproc_t) xdr_dirpath,
1545                                               (caddr_t) &pathname,
1546                                               (xdrproc_t) xdr_mountres3,
1547                                               (caddr_t) &status,
1548                                               total_timeout);
1549                         else
1550                                 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1551                                               (xdrproc_t) xdr_dirpath,
1552                                               (caddr_t) &pathname,
1553                                               (xdrproc_t) xdr_fhstatus,
1554                                               (caddr_t) &status,
1555                                               total_timeout);
1556
1557                         if (clnt_stat == RPC_SUCCESS)
1558                                 goto prepare_kernel_data; /* we're done */
1559                         if (errno != ECONNREFUSED) {
1560                                 error_msg_rpc(clnt_sperror(mclient, " "));
1561                                 goto fail;      /* don't retry */
1562                         }
1563                         /* Connection refused */
1564                         if (!daemonized && prevt == 0) /* print just once */
1565                                 error_msg_rpc(clnt_sperror(mclient, " "));
1566                         auth_destroy(mclient->cl_auth);
1567                         clnt_destroy(mclient);
1568                         mclient = NULL;
1569                         close(msock);
1570                         msock = -1;
1571                 }
1572
1573                 /* Timeout. We are going to retry... maybe */
1574                 if (!bg)
1575                         goto fail;
1576                 if (!daemonized) {
1577                         daemonized = daemonize();
1578                         if (daemonized <= 0) { /* parent or error */
1579                                 retval = -daemonized;
1580                                 goto ret;
1581                         }
1582                 }
1583                 prevt = t;
1584                 t = time(NULL);
1585                 if (t >= timeout)
1586                         /* TODO error message */
1587                         goto fail;
1588
1589                 goto retry;
1590         }
1591
1592  prepare_kernel_data:
1593
1594         if (nfsvers == 2) {
1595                 if (status.nfsv2.fhs_status != 0) {
1596                         bb_error_msg("%s:%s failed, reason given by server: %s",
1597                                 hostname, pathname,
1598                                 nfs_strerror(status.nfsv2.fhs_status));
1599                         goto fail;
1600                 }
1601                 memcpy(data.root.data,
1602                                 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1603                                 NFS_FHSIZE);
1604                 data.root.size = NFS_FHSIZE;
1605                 memcpy(data.old_root.data,
1606                                 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1607                                 NFS_FHSIZE);
1608         } else {
1609                 fhandle3 *my_fhandle;
1610                 if (status.nfsv3.fhs_status != 0) {
1611                         bb_error_msg("%s:%s failed, reason given by server: %s",
1612                                 hostname, pathname,
1613                                 nfs_strerror(status.nfsv3.fhs_status));
1614                         goto fail;
1615                 }
1616                 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1617                 memset(data.old_root.data, 0, NFS_FHSIZE);
1618                 memset(&data.root, 0, sizeof(data.root));
1619                 data.root.size = my_fhandle->fhandle3_len;
1620                 memcpy(data.root.data,
1621                                 (char *) my_fhandle->fhandle3_val,
1622                                 my_fhandle->fhandle3_len);
1623
1624                 data.flags |= NFS_MOUNT_VER3;
1625         }
1626
1627         /* Create nfs socket for kernel */
1628         if (tcp) {
1629                 if (nfs_mount_version < 3) {
1630                         bb_error_msg("NFS over TCP is not supported");
1631                         goto fail;
1632                 }
1633                 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1634         } else
1635                 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1636         if (fsock < 0) {
1637                 bb_perror_msg("nfs socket");
1638                 goto fail;
1639         }
1640         if (bindresvport(fsock, 0) < 0) {
1641                 bb_perror_msg("nfs bindresvport");
1642                 goto fail;
1643         }
1644         if (port == 0) {
1645                 server_addr.sin_port = PMAPPORT;
1646                 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1647                                         tcp ? IPPROTO_TCP : IPPROTO_UDP);
1648                 if (port == 0)
1649                         port = NFS_PORT;
1650         }
1651         server_addr.sin_port = htons(port);
1652
1653         /* Prepare data structure for kernel */
1654         data.fd = fsock;
1655         memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1656         strncpy(data.hostname, hostname, sizeof(data.hostname));
1657
1658         /* Clean up */
1659         auth_destroy(mclient->cl_auth);
1660         clnt_destroy(mclient);
1661         close(msock);
1662         msock = -1;
1663
1664         if (bg) {
1665                 /* We must wait until mount directory is available */
1666                 struct stat statbuf;
1667                 int delay = 1;
1668                 while (stat(mp->mnt_dir, &statbuf) == -1) {
1669                         if (!daemonized) {
1670                                 daemonized = daemonize();
1671                                 if (daemonized <= 0) { /* parent or error */
1672 /* FIXME: parent doesn't close fsock - ??! */
1673                                         retval = -daemonized;
1674                                         goto ret;
1675                                 }
1676                         }
1677                         sleep(delay);   /* 1, 2, 4, 8, 16, 30, ... */
1678                         delay *= 2;
1679                         if (delay > 30)
1680                                 delay = 30;
1681                 }
1682         }
1683
1684         /* Perform actual mount */
1685  do_mount:
1686         retval = mount_it_now(mp, vfsflags, (char*)&data);
1687         goto ret;
1688
1689         /* Abort */
1690  fail:
1691         if (msock >= 0) {
1692                 if (mclient) {
1693                         auth_destroy(mclient->cl_auth);
1694                         clnt_destroy(mclient);
1695                 }
1696                 close(msock);
1697         }
1698         if (fsock >= 0)
1699                 close(fsock);
1700
1701  ret:
1702         free(hostname);
1703         free(mounthost);
1704         free(filteropts);
1705         return retval;
1706 }
1707
1708 #else // !ENABLE_FEATURE_MOUNT_NFS
1709
1710 /* Linux 2.6.23+ supports nfs mounts with options passed as a string.
1711  * For older kernels, you must build busybox with ENABLE_FEATURE_MOUNT_NFS.
1712  * (However, note that then you lose any chances that NFS over IPv6 would work).
1713  */
1714 static int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
1715 {
1716         len_and_sockaddr *lsa;
1717         char *opts;
1718         char *end;
1719         char *dotted;
1720         int ret;
1721
1722 # if ENABLE_FEATURE_IPV6
1723         end = strchr(mp->mnt_fsname, ']');
1724         if (end && end[1] == ':')
1725                 end++;
1726         else
1727 # endif
1728                 /* mount_main() guarantees that ':' is there */
1729                 end = strchr(mp->mnt_fsname, ':');
1730
1731         *end = '\0';
1732         lsa = xdotted2sockaddr(mp->mnt_fsname, /*port:*/ 0);
1733         *end = ':';
1734         dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1735         if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
1736         opts = xasprintf("%s%saddr=%s",
1737                 filteropts ? filteropts : "",
1738                 filteropts ? "," : "",
1739                 dotted
1740         );
1741         if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
1742         ret = mount_it_now(mp, vfsflags, opts);
1743         if (ENABLE_FEATURE_CLEAN_UP) free(opts);
1744
1745         return ret;
1746 }
1747
1748 #endif // !ENABLE_FEATURE_MOUNT_NFS
1749
1750 // Mount one directory.  Handles CIFS, NFS, loopback, autobind, and filesystem
1751 // type detection.  Returns 0 for success, nonzero for failure.
1752 // NB: mp->xxx fields may be trashed on exit
1753 static int singlemount(struct mntent *mp, int ignore_busy)
1754 {
1755         int rc = -1;
1756         long vfsflags;
1757         char *loopFile = NULL, *filteropts = NULL;
1758         llist_t *fl = NULL;
1759         struct stat st;
1760
1761         errno = 0;
1762
1763         vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1764
1765         // Treat fstype "auto" as unspecified
1766         if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1767                 mp->mnt_type = NULL;
1768
1769         // Might this be a virtual filesystem?
1770         if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
1771                 char *args[35];
1772                 char *s;
1773                 int n;
1774                 // fsname: "cmd#arg1#arg2..."
1775                 // WARNING: allows execution of arbitrary commands!
1776                 // Try "mount 'sh#-c#sh' bogus_dir".
1777                 // It is safe ONLY because non-root
1778                 // cannot use two-argument mount command
1779                 // and using one-argument "mount 'sh#-c#sh'" doesn't work:
1780                 // "mount: can't find sh#-c#sh in /etc/fstab"
1781                 // (if /etc/fstab has it, it's ok: root sets up /etc/fstab).
1782
1783                 s = mp->mnt_fsname;
1784                 n = 0;
1785                 args[n++] = s;
1786                 while (*s && n < 35 - 2) {
1787                         if (*s++ == '#' && *s != '#') {
1788                                 s[-1] = '\0';
1789                                 args[n++] = s;
1790                         }
1791                 }
1792                 args[n++] = mp->mnt_dir;
1793                 args[n] = NULL;
1794                 rc = spawn_and_wait(args);
1795                 goto report_error;
1796         }
1797
1798         // Might this be an CIFS filesystem?
1799         if (ENABLE_FEATURE_MOUNT_CIFS
1800          && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
1801          && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
1802          && mp->mnt_fsname[0] == mp->mnt_fsname[1]
1803         ) {
1804                 int len;
1805                 char c;
1806                 len_and_sockaddr *lsa;
1807                 char *hostname, *dotted, *ip;
1808
1809                 hostname = mp->mnt_fsname + 2;
1810                 len = strcspn(hostname, "/\\");
1811                 if (len == 0 || hostname[len] == '\0')
1812                         goto report_error;
1813                 c = hostname[len];
1814                 hostname[len] = '\0';
1815                 lsa = host2sockaddr(hostname, 0);
1816                 hostname[len] = c;
1817                 if (!lsa)
1818                         goto report_error;
1819
1820                 // Insert "ip=..." option into options
1821                 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1822                 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
1823                 ip = xasprintf("ip=%s", dotted);
1824                 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
1825                 parse_mount_options(ip, &filteropts);
1826                 if (ENABLE_FEATURE_CLEAN_UP) free(ip);
1827
1828                 // "-o mand" is required [why?]
1829                 vfsflags |= MS_MANDLOCK;
1830                 mp->mnt_type = (char*)"cifs";
1831                 rc = mount_it_now(mp, vfsflags, filteropts);
1832
1833                 goto report_error;
1834         }
1835
1836         // Might this be an NFS filesystem?
1837         if ((!mp->mnt_type || strncmp(mp->mnt_type, "nfs", 3) == 0)
1838          && strchr(mp->mnt_fsname, ':') != NULL
1839         ) {
1840                 if (!mp->mnt_type)
1841                         mp->mnt_type = (char*)"nfs";
1842                 rc = nfsmount(mp, vfsflags, filteropts);
1843                 goto report_error;
1844         }
1845
1846         // Look at the file.  (Not found isn't a failure for remount, or for
1847         // a synthetic filesystem like proc or sysfs.)
1848         // (We use stat, not lstat, in order to allow
1849         // mount symlink_to_file_or_blkdev dir)
1850         if (!stat(mp->mnt_fsname, &st)
1851          && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1852         ) {
1853                 // Do we need to allocate a loopback device for it?
1854                 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1855                         loopFile = bb_simplify_path(mp->mnt_fsname);
1856                         mp->mnt_fsname = NULL; // will receive malloced loop dev name
1857                         if (set_loop(&mp->mnt_fsname, loopFile, 0, /*ro:*/ 0) < 0) {
1858                                 if (errno == EPERM || errno == EACCES)
1859                                         bb_error_msg(bb_msg_perm_denied_are_you_root);
1860                                 else
1861                                         bb_perror_msg("can't setup loop device");
1862                                 return errno;
1863                         }
1864
1865                 // Autodetect bind mounts
1866                 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1867                         vfsflags |= MS_BIND;
1868         }
1869
1870         // If we know the fstype (or don't need to), jump straight
1871         // to the actual mount.
1872         if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
1873                 char *next;
1874                 for (;;) {
1875                         next = mp->mnt_type ? strchr(mp->mnt_type, ',') : NULL;
1876                         if (next)
1877                                 *next = '\0';
1878                         rc = mount_it_now(mp, vfsflags, filteropts);
1879                         if (rc == 0 || !next)
1880                                 break;
1881                         mp->mnt_type = next + 1;
1882                 }
1883         } else {
1884                 // Loop through filesystem types until mount succeeds
1885                 // or we run out
1886
1887                 // Initialize list of block backed filesystems.
1888                 // This has to be done here so that during "mount -a",
1889                 // mounts after /proc shows up can autodetect.
1890                 if (!fslist) {
1891                         fslist = get_block_backed_filesystems();
1892                         if (ENABLE_FEATURE_CLEAN_UP && fslist)
1893                                 atexit(delete_block_backed_filesystems);
1894                 }
1895
1896                 for (fl = fslist; fl; fl = fl->link) {
1897                         mp->mnt_type = fl->data;
1898                         rc = mount_it_now(mp, vfsflags, filteropts);
1899                         if (rc == 0)
1900                                 break;
1901                 }
1902         }
1903
1904         // If mount failed, clean up loop file (if any).
1905         if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1906                 del_loop(mp->mnt_fsname);
1907                 if (ENABLE_FEATURE_CLEAN_UP) {
1908                         free(loopFile);
1909                         free(mp->mnt_fsname);
1910                 }
1911         }
1912
1913  report_error:
1914         if (ENABLE_FEATURE_CLEAN_UP)
1915                 free(filteropts);
1916
1917         if (errno == EBUSY && ignore_busy)
1918                 return 0;
1919         if (rc != 0)
1920                 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
1921         return rc;
1922 }
1923
1924 // -O support
1925 //    -O interprets a list of filter options which select whether a mount
1926 // point will be mounted: only mounts with options matching *all* filtering
1927 // options will be selected.
1928 //    By default each -O filter option must be present in the list of mount
1929 // options, but if it is prefixed by "no" then it must be absent.
1930 // For example,
1931 //  -O a,nob,c  matches  -o a,c  but fails to match  -o a,b,c
1932 //              (and also fails to match  -o a  because  -o c  is absent).
1933 //
1934 // It is different from -t in that each option is matched exactly; a leading
1935 // "no" at the beginning of one option does not negate the rest.
1936 static int match_opt(const char *fs_opt_in, const char *O_opt)
1937 {
1938         if (!O_opt)
1939                 return 1;
1940
1941         while (*O_opt) {
1942                 const char *fs_opt = fs_opt_in;
1943                 int O_len;
1944                 int match;
1945
1946                 // If option begins with "no" then treat as an inverted match:
1947                 // matching is a failure
1948                 match = 0;
1949                 if (O_opt[0] == 'n' && O_opt[1] == 'o') {
1950                         match = 1;
1951                         O_opt += 2;
1952                 }
1953                 // Isolate the current O option
1954                 O_len = strchrnul(O_opt, ',') - O_opt;
1955                 // Check for a match against existing options
1956                 while (1) {
1957                         if (strncmp(fs_opt, O_opt, O_len) == 0
1958                          && (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',')
1959                         ) {
1960                                 if (match)
1961                                         return 0;  // "no" prefix, but option found
1962                                 match = 1;  // current O option found, go check next one
1963                                 break;
1964                         }
1965                         fs_opt = strchr(fs_opt, ',');
1966                         if (!fs_opt)
1967                                 break;
1968                         fs_opt++;
1969                 }
1970                 if (match == 0)
1971                         return 0;     // match wanted but not found
1972                 if (O_opt[O_len] == '\0') // end?
1973                         break;
1974                 // Step to the next O option
1975                 O_opt += O_len + 1;
1976         }
1977         // If we get here then everything matched
1978         return 1;
1979 }
1980
1981 // Parse options, if necessary parse fstab/mtab, and call singlemount for
1982 // each directory to be mounted.
1983 int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1984 int mount_main(int argc UNUSED_PARAM, char **argv)
1985 {
1986         char *cmdopts = xzalloc(1);
1987         char *fstype = NULL;
1988         char *O_optmatch = NULL;
1989         char *storage_path;
1990         llist_t *lst_o = NULL;
1991         const char *fstabname;
1992         FILE *fstab;
1993         int i, j;
1994         int rc = EXIT_SUCCESS;
1995         unsigned opt;
1996         struct mntent mtpair[2], *mtcur = mtpair;
1997         IF_NOT_DESKTOP(const int nonroot = 0;)
1998
1999         IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
2000
2001         INIT_G();
2002
2003         // Parse long options, like --bind and --move.  Note that -o option
2004         // and --option are synonymous.  Yes, this means --remount,rw works.
2005         for (i = j = 1; argv[i]; i++) {
2006                 if (argv[i][0] == '-' && argv[i][1] == '-')
2007                         append_mount_options(&cmdopts, argv[i] + 2);
2008                 else
2009                         argv[j++] = argv[i];
2010         }
2011         argv[j] = NULL;
2012
2013         // Parse remaining options
2014         // Max 2 params; -o is a list, -v is a counter
2015         opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv");
2016         opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch
2017                         IF_FEATURE_MOUNT_VERBOSE(, &verbose));
2018         while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
2019         if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
2020         if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
2021         argv += optind;
2022
2023         // If we have no arguments, show currently mounted filesystems
2024         if (!argv[0]) {
2025                 if (!(opt & OPT_a)) {
2026                         FILE *mountTable = setmntent(bb_path_mtab_file, "r");
2027
2028                         if (!mountTable)
2029                                 bb_error_msg_and_die("no %s", bb_path_mtab_file);
2030
2031                         while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
2032                                                                 GETMNTENT_BUFSIZE))
2033                         {
2034                                 // Don't show rootfs. FIXME: why??
2035                                 // util-linux 2.12a happily shows rootfs...
2036                                 //if (strcmp(mtpair->mnt_fsname, "rootfs") == 0) continue;
2037
2038                                 if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0)
2039                                         printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
2040                                                         mtpair->mnt_dir, mtpair->mnt_type,
2041                                                         mtpair->mnt_opts);
2042                         }
2043                         if (ENABLE_FEATURE_CLEAN_UP)
2044                                 endmntent(mountTable);
2045                         return EXIT_SUCCESS;
2046                 }
2047                 storage_path = NULL;
2048         } else {
2049                 // When we have two arguments, the second is the directory and we can
2050                 // skip looking at fstab entirely.  We can always abspath() the directory
2051                 // argument when we get it.
2052                 if (argv[1]) {
2053                         if (nonroot)
2054                                 bb_error_msg_and_die(bb_msg_you_must_be_root);
2055                         mtpair->mnt_fsname = argv[0];
2056                         mtpair->mnt_dir = argv[1];
2057                         mtpair->mnt_type = fstype;
2058                         mtpair->mnt_opts = cmdopts;
2059                         resolve_mount_spec(&mtpair->mnt_fsname);
2060                         rc = singlemount(mtpair, /*ignore_busy:*/ 0);
2061                         return rc;
2062                 }
2063                 storage_path = bb_simplify_path(argv[0]); // malloced
2064         }
2065
2066         // Past this point, we are handling either "mount -a [opts]"
2067         // or "mount [opts] single_param"
2068
2069         i = parse_mount_options(cmdopts, NULL); // FIXME: should be "long", not "int"
2070         if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags
2071                 bb_error_msg_and_die(bb_msg_you_must_be_root);
2072
2073         // If we have a shared subtree flag, don't worry about fstab or mtab.
2074         if (ENABLE_FEATURE_MOUNT_FLAGS
2075          && (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
2076         ) {
2077                 // verbose_mount(source, target, type, flags, data)
2078                 rc = verbose_mount("", argv[0], "", i, "");
2079                 if (rc)
2080                         bb_simple_perror_msg_and_die(argv[0]);
2081                 return rc;
2082         }
2083
2084         // Open either fstab or mtab
2085         fstabname = "/etc/fstab";
2086         if (i & MS_REMOUNT) {
2087                 // WARNING. I am not sure this matches util-linux's
2088                 // behavior. It's possible util-linux does not
2089                 // take -o opts from mtab (takes only mount source).
2090                 fstabname = bb_path_mtab_file;
2091         }
2092         fstab = setmntent(fstabname, "r");
2093         if (!fstab)
2094                 bb_perror_msg_and_die("can't read '%s'", fstabname);
2095
2096         // Loop through entries until we find what we're looking for
2097         memset(mtpair, 0, sizeof(mtpair));
2098         for (;;) {
2099                 struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
2100
2101                 // Get next fstab entry
2102                 if (!getmntent_r(fstab, mtcur, getmntent_buf
2103                                         + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
2104                                 GETMNTENT_BUFSIZE/2)
2105                 ) { // End of fstab/mtab is reached
2106                         mtcur = mtother; // the thing we found last time
2107                         break;
2108                 }
2109
2110                 // If we're trying to mount something specific and this isn't it,
2111                 // skip it.  Note we must match the exact text in fstab (ala
2112                 // "proc") or a full path from root
2113                 if (argv[0]) {
2114
2115                         // Is this what we're looking for?
2116                         if (strcmp(argv[0], mtcur->mnt_fsname) != 0
2117                          && strcmp(storage_path, mtcur->mnt_fsname) != 0
2118                          && strcmp(argv[0], mtcur->mnt_dir) != 0
2119                          && strcmp(storage_path, mtcur->mnt_dir) != 0
2120                         ) {
2121                                 continue; // no
2122                         }
2123
2124                         // Remember this entry.  Something later may have
2125                         // overmounted it, and we want the _last_ match.
2126                         mtcur = mtother;
2127
2128                 // If we're mounting all
2129                 } else {
2130                         struct mntent *mp;
2131                         // No, mount -a won't mount anything,
2132                         // even user mounts, for mere humans
2133                         if (nonroot)
2134                                 bb_error_msg_and_die(bb_msg_you_must_be_root);
2135
2136                         // Does type match? (NULL matches always)
2137                         if (!match_fstype(mtcur, fstype))
2138                                 continue;
2139
2140                         // Skip noauto and swap anyway
2141                         if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
2142                         // swap is bogus "fstype", parse_mount_options can't check fstypes
2143                          || strcasecmp(mtcur->mnt_type, "swap") == 0
2144                         ) {
2145                                 continue;
2146                         }
2147
2148                         // Does (at least one) option match?
2149                         // (NULL matches always)
2150                         if (!match_opt(mtcur->mnt_opts, O_optmatch))
2151                                 continue;
2152
2153                         resolve_mount_spec(&mtcur->mnt_fsname);
2154
2155                         // NFS mounts want this to be xrealloc-able
2156                         mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2157
2158                         // If nothing is mounted on this directory...
2159                         // (otherwise repeated "mount -a" mounts everything again)
2160                         mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2161                         // We do not check fsname match of found mount point -
2162                         // "/" may have fsname of "/dev/root" while fstab
2163                         // says "/dev/something_else".
2164                         if (mp) {
2165                                 if (verbose) {
2166                                         bb_error_msg("according to %s, "
2167                                                 "%s is already mounted on %s",
2168                                                 bb_path_mtab_file,
2169                                                 mp->mnt_fsname, mp->mnt_dir);
2170                                 }
2171                         } else {
2172                                 // ...mount this thing
2173                                 if (singlemount(mtcur, /*ignore_busy:*/ 1)) {
2174                                         // Count number of failed mounts
2175                                         rc++;
2176                                 }
2177                         }
2178                         free(mtcur->mnt_opts);
2179                 }
2180         }
2181
2182         // End of fstab/mtab is reached.
2183         // Were we looking for something specific?
2184         if (argv[0]) { // yes
2185                 long l;
2186
2187                 // If we didn't find anything, complain
2188                 if (!mtcur->mnt_fsname)
2189                         bb_error_msg_and_die("can't find %s in %s",
2190                                 argv[0], fstabname);
2191
2192                 // What happens when we try to "mount swap_partition"?
2193                 // (fstab containts "swap_partition swap swap defaults 0 0")
2194                 // util-linux-ng 2.13.1 does this:
2195                 // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
2196                 // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
2197                 // lstat("swap", 0x7fff62a3a640)           = -1 ENOENT (No such file or directory)
2198                 // write(2, "mount: mount point swap does not exist\n", 39) = 39
2199                 // exit_group(32)                          = ?
2200 #if 0
2201                 // In case we want to simply skip swap partitions:
2202                 l = parse_mount_options(mtcur->mnt_opts, NULL);
2203                 if ((l & MOUNT_SWAP)
2204                 // swap is bogus "fstype", parse_mount_options can't check fstypes
2205                  || strcasecmp(mtcur->mnt_type, "swap") == 0
2206                 ) {
2207                         goto ret;
2208                 }
2209 #endif
2210                 if (nonroot) {
2211                         // fstab must have "users" or "user"
2212                         l = parse_mount_options(mtcur->mnt_opts, NULL);
2213                         if (!(l & MOUNT_USERS))
2214                                 bb_error_msg_and_die(bb_msg_you_must_be_root);
2215                 }
2216
2217                 //util-linux-2.12 does not do this check.
2218                 //// If nothing is mounted on this directory...
2219                 //// (otherwise repeated "mount FOO" mounts FOO again)
2220                 //mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2221                 //if (mp) {
2222                 //      bb_error_msg("according to %s, "
2223                 //              "%s is already mounted on %s",
2224                 //              bb_path_mtab_file,
2225                 //              mp->mnt_fsname, mp->mnt_dir);
2226                 //} else {
2227                         // ...mount the last thing we found
2228                         mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2229                         append_mount_options(&(mtcur->mnt_opts), cmdopts);
2230                         resolve_mount_spec(&mtpair->mnt_fsname);
2231                         rc = singlemount(mtcur, /*ignore_busy:*/ 0);
2232                         if (ENABLE_FEATURE_CLEAN_UP)
2233                                 free(mtcur->mnt_opts);
2234                 //}
2235         }
2236
2237  //ret:
2238         if (ENABLE_FEATURE_CLEAN_UP)
2239                 endmntent(fstab);
2240         if (ENABLE_FEATURE_CLEAN_UP) {
2241                 free(storage_path);
2242                 free(cmdopts);
2243         }
2244
2245 //TODO: exitcode should be ORed mask of (from "man mount"):
2246 // 0 success
2247 // 1 incorrect invocation or permissions
2248 // 2 system error (out of memory, cannot fork, no more loop devices)
2249 // 4 internal mount bug or missing nfs support in mount
2250 // 8 user interrupt
2251 //16 problems writing or locking /etc/mtab
2252 //32 mount failure
2253 //64 some mount succeeded
2254         return rc;
2255 }