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