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