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