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