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