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