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