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