mount: code shrink (-81 bytes)
[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 source tree.
10  */
11 // Design notes: There is no spec for mount.  Remind me to write one.
12 //
13 // mount_main() calls singlemount() which calls mount_it_now().
14 //
15 // mount_main() can loop through /etc/fstab for mount -a
16 // singlemount() can loop through /etc/filesystems for fstype detection.
17 // mount_it_now() does the actual mount.
18 //
19 #include <mntent.h>
20 #include <syslog.h>
21 #include <sys/mount.h>
22 // Grab more as needed from util-linux's mount/mount_constants.h
23 #ifndef MS_DIRSYNC
24 # define MS_DIRSYNC     (1 << 7) // Directory modifications are synchronous
25 #endif
26 #ifndef MS_UNION
27 # define MS_UNION       (1 << 8)
28 #endif
29 #ifndef MS_BIND
30 # define MS_BIND        (1 << 12)
31 #endif
32 #ifndef MS_MOVE
33 # define MS_MOVE        (1 << 13)
34 #endif
35 #ifndef MS_RECURSIVE
36 # define MS_RECURSIVE   (1 << 14)
37 #endif
38 #ifndef MS_SILENT
39 # define MS_SILENT      (1 << 15)
40 #endif
41 // The shared subtree stuff, which went in around 2.6.15
42 #ifndef MS_UNBINDABLE
43 # define MS_UNBINDABLE  (1 << 17)
44 #endif
45 #ifndef MS_PRIVATE
46 # define MS_PRIVATE     (1 << 18)
47 #endif
48 #ifndef MS_SLAVE
49 # define MS_SLAVE       (1 << 19)
50 #endif
51 #ifndef MS_SHARED
52 # define MS_SHARED      (1 << 20)
53 #endif
54 #ifndef MS_RELATIME
55 # define MS_RELATIME    (1 << 21)
56 #endif
57
58 #include "libbb.h"
59 #if ENABLE_FEATURE_MOUNT_LABEL
60 # include "volume_id.h"
61 #else
62 # define resolve_mount_spec(fsname) ((void)0)
63 #endif
64
65 // Needed for nfs support only
66 #include <sys/utsname.h>
67 #undef TRUE
68 #undef FALSE
69 #if ENABLE_FEATURE_MOUNT_NFS
70 /* This is just a warning of a common mistake.  Possibly this should be a
71  * uclibc faq entry rather than in busybox... */
72 # if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
73 #  error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support"
74 # endif
75 # include <rpc/rpc.h>
76 # include <rpc/pmap_prot.h>
77 # include <rpc/pmap_clnt.h>
78 #endif
79
80
81 #if defined(__dietlibc__)
82 // 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
83 // dietlibc-0.30 does not have implementation of getmntent_r()
84 static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
85                 char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM)
86 {
87         struct mntent* ment = getmntent(stream);
88         return memcpy(result, ment, sizeof(*ment));
89 }
90 #endif
91
92
93 // Not real flags, but we want to be able to check for this.
94 enum {
95         MOUNT_USERS  = (1 << 28) * ENABLE_DESKTOP,
96         MOUNT_NOAUTO = (1 << 29),
97         MOUNT_SWAP   = (1 << 30),
98 };
99
100
101 #define OPTION_STR "o:t:rwanfvsiO:"
102 enum {
103         OPT_o = (1 << 0),
104         OPT_t = (1 << 1),
105         OPT_r = (1 << 2),
106         OPT_w = (1 << 3),
107         OPT_a = (1 << 4),
108         OPT_n = (1 << 5),
109         OPT_f = (1 << 6),
110         OPT_v = (1 << 7),
111         OPT_s = (1 << 8),
112         OPT_i = (1 << 9),
113         OPT_O = (1 << 10),
114 };
115
116 #if ENABLE_FEATURE_MTAB_SUPPORT
117 #define USE_MTAB (!(option_mask32 & OPT_n))
118 #else
119 #define USE_MTAB 0
120 #endif
121
122 #if ENABLE_FEATURE_MOUNT_FAKE
123 #define FAKE_IT (option_mask32 & OPT_f)
124 #else
125 #define FAKE_IT 0
126 #endif
127
128 #if ENABLE_FEATURE_MOUNT_HELPERS
129 #define HELPERS_ALLOWED (!(option_mask32 & OPT_i))
130 #else
131 #define HELPERS_ALLOWED 0
132 #endif
133
134
135 // TODO: more "user" flag compatibility.
136 // "user" option (from mount manpage):
137 // Only the user that mounted a filesystem can unmount it again.
138 // If any user should be able to unmount, then use users instead of user
139 // in the fstab line.  The owner option is similar to the user option,
140 // with the restriction that the user must be the owner of the special file.
141 // This may be useful e.g. for /dev/fd if a login script makes
142 // the console user owner of this device.
143
144 // Standard mount options (from -o options or --options),
145 // with corresponding flags
146 static const int32_t mount_options[] = {
147         // MS_FLAGS set a bit.  ~MS_FLAGS disable that bit.  0 flags are NOPs.
148
149         IF_FEATURE_MOUNT_LOOP(
150                 /* "loop" */ 0,
151         )
152
153         IF_FEATURE_MOUNT_FSTAB(
154                 /* "defaults" */ 0,
155                 /* "quiet" 0 - do not filter out, vfat wants to see it */
156                 /* "noauto" */ MOUNT_NOAUTO,
157                 /* "sw"     */ MOUNT_SWAP,
158                 /* "swap"   */ MOUNT_SWAP,
159                 IF_DESKTOP(/* "user"  */ MOUNT_USERS,)
160                 IF_DESKTOP(/* "users" */ MOUNT_USERS,)
161                 /* "_netdev" */ 0,
162         )
163
164         IF_FEATURE_MOUNT_FLAGS(
165                 // vfs flags
166                 /* "nosuid"      */ MS_NOSUID,
167                 /* "suid"        */ ~MS_NOSUID,
168                 /* "dev"         */ ~MS_NODEV,
169                 /* "nodev"       */ MS_NODEV,
170                 /* "exec"        */ ~MS_NOEXEC,
171                 /* "noexec"      */ MS_NOEXEC,
172                 /* "sync"        */ MS_SYNCHRONOUS,
173                 /* "dirsync"     */ MS_DIRSYNC,
174                 /* "async"       */ ~MS_SYNCHRONOUS,
175                 /* "atime"       */ ~MS_NOATIME,
176                 /* "noatime"     */ MS_NOATIME,
177                 /* "diratime"    */ ~MS_NODIRATIME,
178                 /* "nodiratime"  */ MS_NODIRATIME,
179                 /* "mand"        */ MS_MANDLOCK,
180                 /* "nomand"      */ ~MS_MANDLOCK,
181                 /* "relatime"    */ MS_RELATIME,
182                 /* "norelatime"  */ ~MS_RELATIME,
183                 /* "loud"        */ ~MS_SILENT,
184
185                 // action flags
186                 /* "union"       */ MS_UNION,
187                 /* "bind"        */ MS_BIND,
188                 /* "move"        */ MS_MOVE,
189                 /* "shared"      */ MS_SHARED,
190                 /* "slave"       */ MS_SLAVE,
191                 /* "private"     */ MS_PRIVATE,
192                 /* "unbindable"  */ MS_UNBINDABLE,
193                 /* "rshared"     */ MS_SHARED|MS_RECURSIVE,
194                 /* "rslave"      */ MS_SLAVE|MS_RECURSIVE,
195                 /* "rprivate"    */ MS_SLAVE|MS_RECURSIVE,
196                 /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
197         )
198
199         // Always understood.
200         /* "ro"      */ MS_RDONLY,  // vfs flag
201         /* "rw"      */ ~MS_RDONLY, // vfs flag
202         /* "remount" */ MS_REMOUNT  // action flag
203 };
204
205 static const char mount_option_str[] =
206         IF_FEATURE_MOUNT_LOOP(
207                 "loop\0"
208         )
209         IF_FEATURE_MOUNT_FSTAB(
210                 "defaults\0"
211                 // "quiet\0" - do not filter out, vfat wants to see it
212                 "noauto\0"
213                 "sw\0"
214                 "swap\0"
215                 IF_DESKTOP("user\0")
216                 IF_DESKTOP("users\0")
217                 "_netdev\0"
218         )
219         IF_FEATURE_MOUNT_FLAGS(
220                 // vfs flags
221                 "nosuid\0"
222                 "suid\0"
223                 "dev\0"
224                 "nodev\0"
225                 "exec\0"
226                 "noexec\0"
227                 "sync\0"
228                 "dirsync\0"
229                 "async\0"
230                 "atime\0"
231                 "noatime\0"
232                 "diratime\0"
233                 "nodiratime\0"
234                 "mand\0"
235                 "nomand\0"
236                 "relatime\0"
237                 "norelatime\0"
238                 "loud\0"
239
240                 // action flags
241                 "union\0"
242                 "bind\0"
243                 "move\0"
244                 "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 = spawn_and_wait(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 source tree.
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 #if ( \
753         EPERM | ENOENT      | EIO      | ENXIO | EACCES| EEXIST | \
754         ENODEV| ENOTDIR     | EISDIR   | EINVAL| EFBIG | ENOSPC | \
755         EROFS | ENAMETOOLONG| ENOTEMPTY| EDQUOT| ESTALE| EREMOTE) < 256
756 typedef uint8_t nfs_err_type;
757 #else
758 typedef uint16_t nfs_err_type;
759 #endif
760 static const nfs_err_type nfs_err_errnum[] = {
761         EPERM , ENOENT      , EIO      , ENXIO , EACCES, EEXIST,
762         ENODEV, ENOTDIR     , EISDIR   , EINVAL, EFBIG , ENOSPC,
763         EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE
764 };
765 static char *nfs_strerror(int status)
766 {
767         int i;
768
769         for (i = 0; i < ARRAY_SIZE(nfs_err_stat); i++) {
770                 if (nfs_err_stat[i] == status)
771                         return strerror(nfs_err_errnum[i]);
772         }
773         return xasprintf("unknown nfs status return value: %d", status);
774 }
775
776 static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
777 {
778         return xdr_opaque(xdrs, objp, FHSIZE);
779 }
780
781 static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
782 {
783         if (!xdr_u_int(xdrs, &objp->fhs_status))
784                  return FALSE;
785         if (objp->fhs_status == 0)
786                 return xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle);
787         return TRUE;
788 }
789
790 static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
791 {
792         return xdr_string(xdrs, objp, MNTPATHLEN);
793 }
794
795 static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
796 {
797         return xdr_bytes(xdrs, (char **)&objp->fhandle3_val,
798                            (unsigned int *) &objp->fhandle3_len,
799                            FHSIZE3);
800 }
801
802 static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
803 {
804         if (!xdr_fhandle3(xdrs, &objp->fhandle))
805                 return FALSE;
806         return xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val),
807                            &(objp->auth_flavours.auth_flavours_len),
808                            ~0,
809                            sizeof(int),
810                            (xdrproc_t) xdr_int);
811 }
812
813 static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
814 {
815         return xdr_enum(xdrs, (enum_t *) objp);
816 }
817
818 static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
819 {
820         if (!xdr_mountstat3(xdrs, &objp->fhs_status))
821                 return FALSE;
822         if (objp->fhs_status == MNT_OK)
823                 return xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo);
824         return TRUE;
825 }
826
827 #define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
828
829 /*
830  * Unfortunately, the kernel prints annoying console messages
831  * in case of an unexpected nfs mount version (instead of
832  * just returning some error).  Therefore we'll have to try
833  * and figure out what version the kernel expects.
834  *
835  * Variables:
836  *      KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
837  *      NFS_MOUNT_VERSION: these nfsmount sources at compile time
838  *      nfs_mount_version: version this source and running kernel can handle
839  */
840 static void
841 find_kernel_nfs_mount_version(void)
842 {
843         int kernel_version;
844
845         if (nfs_mount_version)
846                 return;
847
848         nfs_mount_version = 4; /* default */
849
850         kernel_version = get_linux_version_code();
851         if (kernel_version) {
852                 if (kernel_version < KERNEL_VERSION(2,2,18))
853                         nfs_mount_version = 3;
854                 /* else v4 since 2.3.99pre4 */
855         }
856 }
857
858 static void
859 get_mountport(struct pmap *pm_mnt,
860         struct sockaddr_in *server_addr,
861         long unsigned prog,
862         long unsigned version,
863         long unsigned proto,
864         long unsigned port)
865 {
866         struct pmaplist *pmap;
867
868         server_addr->sin_port = PMAPPORT;
869 /* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
870  * I understand it like "IPv6 for this is not 100% ready" */
871         pmap = pmap_getmaps(server_addr);
872
873         if (version > MAX_NFSPROT)
874                 version = MAX_NFSPROT;
875         if (!prog)
876                 prog = MOUNTPROG;
877         pm_mnt->pm_prog = prog;
878         pm_mnt->pm_vers = version;
879         pm_mnt->pm_prot = proto;
880         pm_mnt->pm_port = port;
881
882         while (pmap) {
883                 if (pmap->pml_map.pm_prog != prog)
884                         goto next;
885                 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
886                         goto next;
887                 if (version > 2 && pmap->pml_map.pm_vers != version)
888                         goto next;
889                 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
890                         goto next;
891                 if (pmap->pml_map.pm_vers > MAX_NFSPROT
892                  || (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto)
893                  || (port && pmap->pml_map.pm_port != port)
894                 ) {
895                         goto next;
896                 }
897                 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
898  next:
899                 pmap = pmap->pml_next;
900         }
901         if (!pm_mnt->pm_vers)
902                 pm_mnt->pm_vers = MOUNTVERS;
903         if (!pm_mnt->pm_port)
904                 pm_mnt->pm_port = MOUNTPORT;
905         if (!pm_mnt->pm_prot)
906                 pm_mnt->pm_prot = IPPROTO_TCP;
907 }
908
909 #if BB_MMU
910 static int daemonize(void)
911 {
912         int pid = fork();
913         if (pid < 0) /* error */
914                 return -errno;
915         if (pid > 0) /* parent */
916                 return 0;
917         /* child */
918         close(0);
919         xopen(bb_dev_null, O_RDWR);
920         xdup2(0, 1);
921         xdup2(0, 2);
922         setsid();
923         openlog(applet_name, LOG_PID, LOG_DAEMON);
924         logmode = LOGMODE_SYSLOG;
925         return 1;
926 }
927 #else
928 static inline int daemonize(void) { return -ENOSYS; }
929 #endif
930
931 /* TODO */
932 static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
933 {
934         return 0;
935 }
936
937 /* RPC strerror analogs are terminally idiotic:
938  * *mandatory* prefix and \n at end.
939  * This hopefully helps. Usage:
940  * error_msg_rpc(clnt_*error*(" ")) */
941 static void error_msg_rpc(const char *msg)
942 {
943         int len;
944         while (msg[0] == ' ' || msg[0] == ':') msg++;
945         len = strlen(msg);
946         while (len && msg[len-1] == '\n') len--;
947         bb_error_msg("%.*s", len, msg);
948 }
949
950 /* NB: mp->xxx fields may be trashed on exit */
951 static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
952 {
953         CLIENT *mclient;
954         char *hostname;
955         char *pathname;
956         char *mounthost;
957         /* prior to 2.6.23, kernel took NFS options in a form of this struct
958          * only. 2.6.23+ looks at data->version, and if it's not 1..6,
959          * then data pointer is interpreted as a string. */
960         struct nfs_mount_data data;
961         char *opt;
962         struct hostent *hp;
963         struct sockaddr_in server_addr;
964         struct sockaddr_in mount_server_addr;
965         int msock, fsock;
966         union {
967                 struct fhstatus nfsv2;
968                 struct mountres3 nfsv3;
969         } status;
970         int daemonized;
971         char *s;
972         int port;
973         int mountport;
974         int proto;
975 #if BB_MMU
976         smallint bg = 0;
977 #else
978         enum { bg = 0 };
979 #endif
980         int retry;
981         int mountprog;
982         int mountvers;
983         int nfsprog;
984         int nfsvers;
985         int retval;
986         /* these all are one-bit really. gcc 4.3.1 likes this combination: */
987         smallint tcp;
988         smallint soft;
989         int intr;
990         int posix;
991         int nocto;
992         int noac;
993         int nordirplus;
994         int nolock;
995
996         find_kernel_nfs_mount_version();
997
998         daemonized = 0;
999         mounthost = NULL;
1000         retval = ETIMEDOUT;
1001         msock = fsock = -1;
1002         mclient = NULL;
1003
1004         /* NB: hostname, mounthost, filteropts must be free()d prior to return */
1005
1006         filteropts = xstrdup(filteropts); /* going to trash it later... */
1007
1008         hostname = xstrdup(mp->mnt_fsname);
1009         /* mount_main() guarantees that ':' is there */
1010         s = strchr(hostname, ':');
1011         pathname = s + 1;
1012         *s = '\0';
1013         /* Ignore all but first hostname in replicated mounts
1014            until they can be fully supported. (mack@sgi.com) */
1015         s = strchr(hostname, ',');
1016         if (s) {
1017                 *s = '\0';
1018                 bb_error_msg("warning: multiple hostnames not supported");
1019         }
1020
1021         server_addr.sin_family = AF_INET;
1022         if (!inet_aton(hostname, &server_addr.sin_addr)) {
1023                 hp = gethostbyname(hostname);
1024                 if (hp == NULL) {
1025                         bb_herror_msg("%s", hostname);
1026                         goto fail;
1027                 }
1028                 if (hp->h_length != (int)sizeof(struct in_addr)) {
1029                         bb_error_msg_and_die("only IPv4 is supported");
1030                 }
1031                 memcpy(&server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
1032         }
1033
1034         memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1035
1036         /* add IP address to mtab options for use when unmounting */
1037
1038         if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1039                 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1040         } else {
1041                 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1042                                         mp->mnt_opts[0] ? "," : "",
1043                                         inet_ntoa(server_addr.sin_addr));
1044                 free(mp->mnt_opts);
1045                 mp->mnt_opts = tmp;
1046         }
1047
1048         /* Set default options.
1049          * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1050          * let the kernel decide.
1051          * timeo is filled in after we know whether it'll be TCP or UDP. */
1052         memset(&data, 0, sizeof(data));
1053         data.retrans  = 3;
1054         data.acregmin = 3;
1055         data.acregmax = 60;
1056         data.acdirmin = 30;
1057         data.acdirmax = 60;
1058         data.namlen   = NAME_MAX;
1059
1060         soft = 0;
1061         intr = 0;
1062         posix = 0;
1063         nocto = 0;
1064         nolock = 0;
1065         noac = 0;
1066         nordirplus = 0;
1067         retry = 10000;          /* 10000 minutes ~ 1 week */
1068         tcp = 0;
1069
1070         mountprog = MOUNTPROG;
1071         mountvers = 0;
1072         port = 0;
1073         mountport = 0;
1074         nfsprog = 100003;
1075         nfsvers = 0;
1076
1077         /* parse options */
1078         if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
1079                 char *opteq = strchr(opt, '=');
1080                 if (opteq) {
1081                         int val, idx;
1082                         static const char options[] ALIGN1 =
1083                                 /* 0 */ "rsize\0"
1084                                 /* 1 */ "wsize\0"
1085                                 /* 2 */ "timeo\0"
1086                                 /* 3 */ "retrans\0"
1087                                 /* 4 */ "acregmin\0"
1088                                 /* 5 */ "acregmax\0"
1089                                 /* 6 */ "acdirmin\0"
1090                                 /* 7 */ "acdirmax\0"
1091                                 /* 8 */ "actimeo\0"
1092                                 /* 9 */ "retry\0"
1093                                 /* 10 */ "port\0"
1094                                 /* 11 */ "mountport\0"
1095                                 /* 12 */ "mounthost\0"
1096                                 /* 13 */ "mountprog\0"
1097                                 /* 14 */ "mountvers\0"
1098                                 /* 15 */ "nfsprog\0"
1099                                 /* 16 */ "nfsvers\0"
1100                                 /* 17 */ "vers\0"
1101                                 /* 18 */ "proto\0"
1102                                 /* 19 */ "namlen\0"
1103                                 /* 20 */ "addr\0";
1104
1105                         *opteq++ = '\0';
1106                         idx = index_in_strings(options, opt);
1107                         switch (idx) {
1108                         case 12: // "mounthost"
1109                                 mounthost = xstrndup(opteq,
1110                                                 strcspn(opteq, " \t\n\r,"));
1111                                 continue;
1112                         case 18: // "proto"
1113                                 if (!strncmp(opteq, "tcp", 3))
1114                                         tcp = 1;
1115                                 else if (!strncmp(opteq, "udp", 3))
1116                                         tcp = 0;
1117                                 else
1118                                         bb_error_msg("warning: unrecognized proto= option");
1119                                 continue;
1120                         case 20: // "addr" - ignore
1121                                 continue;
1122                         }
1123
1124                         val = xatoi_positive(opteq);
1125                         switch (idx) {
1126                         case 0: // "rsize"
1127                                 data.rsize = val;
1128                                 continue;
1129                         case 1: // "wsize"
1130                                 data.wsize = val;
1131                                 continue;
1132                         case 2: // "timeo"
1133                                 data.timeo = val;
1134                                 continue;
1135                         case 3: // "retrans"
1136                                 data.retrans = val;
1137                                 continue;
1138                         case 4: // "acregmin"
1139                                 data.acregmin = val;
1140                                 continue;
1141                         case 5: // "acregmax"
1142                                 data.acregmax = val;
1143                                 continue;
1144                         case 6: // "acdirmin"
1145                                 data.acdirmin = val;
1146                                 continue;
1147                         case 7: // "acdirmax"
1148                                 data.acdirmax = val;
1149                                 continue;
1150                         case 8: // "actimeo"
1151                                 data.acregmin = val;
1152                                 data.acregmax = val;
1153                                 data.acdirmin = val;
1154                                 data.acdirmax = val;
1155                                 continue;
1156                         case 9: // "retry"
1157                                 retry = val;
1158                                 continue;
1159                         case 10: // "port"
1160                                 port = val;
1161                                 continue;
1162                         case 11: // "mountport"
1163                                 mountport = val;
1164                                 continue;
1165                         case 13: // "mountprog"
1166                                 mountprog = val;
1167                                 continue;
1168                         case 14: // "mountvers"
1169                                 mountvers = val;
1170                                 continue;
1171                         case 15: // "nfsprog"
1172                                 nfsprog = val;
1173                                 continue;
1174                         case 16: // "nfsvers"
1175                         case 17: // "vers"
1176                                 nfsvers = val;
1177                                 continue;
1178                         case 19: // "namlen"
1179                                 //if (nfs_mount_version >= 2)
1180                                         data.namlen = val;
1181                                 //else
1182                                 //      bb_error_msg("warning: option namlen is not supported\n");
1183                                 continue;
1184                         default:
1185                                 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1186                                 goto fail;
1187                         }
1188                 }
1189                 else { /* not of the form opt=val */
1190                         static const char options[] ALIGN1 =
1191                                 "bg\0"
1192                                 "fg\0"
1193                                 "soft\0"
1194                                 "hard\0"
1195                                 "intr\0"
1196                                 "posix\0"
1197                                 "cto\0"
1198                                 "ac\0"
1199                                 "tcp\0"
1200                                 "udp\0"
1201                                 "lock\0"
1202                                 "rdirplus\0";
1203                         int val = 1;
1204                         if (!strncmp(opt, "no", 2)) {
1205                                 val = 0;
1206                                 opt += 2;
1207                         }
1208                         switch (index_in_strings(options, opt)) {
1209                         case 0: // "bg"
1210 #if BB_MMU
1211                                 bg = val;
1212 #endif
1213                                 break;
1214                         case 1: // "fg"
1215 #if BB_MMU
1216                                 bg = !val;
1217 #endif
1218                                 break;
1219                         case 2: // "soft"
1220                                 soft = val;
1221                                 break;
1222                         case 3: // "hard"
1223                                 soft = !val;
1224                                 break;
1225                         case 4: // "intr"
1226                                 intr = val;
1227                                 break;
1228                         case 5: // "posix"
1229                                 posix = val;
1230                                 break;
1231                         case 6: // "cto"
1232                                 nocto = !val;
1233                                 break;
1234                         case 7: // "ac"
1235                                 noac = !val;
1236                                 break;
1237                         case 8: // "tcp"
1238                                 tcp = val;
1239                                 break;
1240                         case 9: // "udp"
1241                                 tcp = !val;
1242                                 break;
1243                         case 10: // "lock"
1244                                 if (nfs_mount_version >= 3)
1245                                         nolock = !val;
1246                                 else
1247                                         bb_error_msg("warning: option nolock is not supported");
1248                                 break;
1249                         case 11: //rdirplus
1250                                 nordirplus = !val;
1251                                 break;
1252                         default:
1253                                 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1254                                 goto fail;
1255                         }
1256                 }
1257         }
1258         proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1259
1260         data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1261                 | (intr ? NFS_MOUNT_INTR : 0)
1262                 | (posix ? NFS_MOUNT_POSIX : 0)
1263                 | (nocto ? NFS_MOUNT_NOCTO : 0)
1264                 | (noac ? NFS_MOUNT_NOAC : 0)
1265                 | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0);
1266         if (nfs_mount_version >= 2)
1267                 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1268         if (nfs_mount_version >= 3)
1269                 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1270         if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1271                 bb_error_msg("NFSv%d not supported", nfsvers);
1272                 goto fail;
1273         }
1274         if (nfsvers && !mountvers)
1275                 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1276         if (nfsvers && nfsvers < mountvers) {
1277                 mountvers = nfsvers;
1278         }
1279
1280         /* Adjust options if none specified */
1281         if (!data.timeo)
1282                 data.timeo = tcp ? 70 : 7;
1283
1284         data.version = nfs_mount_version;
1285
1286         if (vfsflags & MS_REMOUNT)
1287                 goto do_mount;
1288
1289         /*
1290          * If the previous mount operation on the same host was
1291          * backgrounded, and the "bg" for this mount is also set,
1292          * give up immediately, to avoid the initial timeout.
1293          */
1294         if (bg && we_saw_this_host_before(hostname)) {
1295                 daemonized = daemonize();
1296                 if (daemonized <= 0) { /* parent or error */
1297                         retval = -daemonized;
1298                         goto ret;
1299                 }
1300         }
1301
1302         /* Create mount daemon client */
1303         /* See if the nfs host = mount host. */
1304         if (mounthost) {
1305                 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1306                         mount_server_addr.sin_family = AF_INET;
1307                         mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1308                 } else {
1309                         hp = gethostbyname(mounthost);
1310                         if (hp == NULL) {
1311                                 bb_herror_msg("%s", mounthost);
1312                                 goto fail;
1313                         }
1314                         if (hp->h_length != (int)sizeof(struct in_addr)) {
1315                                 bb_error_msg_and_die("only IPv4 is supported");
1316                         }
1317                         mount_server_addr.sin_family = AF_INET;
1318                         memcpy(&mount_server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
1319                 }
1320         }
1321
1322         /*
1323          * The following loop implements the mount retries. When the mount
1324          * times out, and the "bg" option is set, we background ourself
1325          * and continue trying.
1326          *
1327          * The case where the mount point is not present and the "bg"
1328          * option is set, is treated as a timeout. This is done to
1329          * support nested mounts.
1330          *
1331          * The "retry" count specified by the user is the number of
1332          * minutes to retry before giving up.
1333          */
1334         {
1335                 struct timeval total_timeout;
1336                 struct timeval retry_timeout;
1337                 struct pmap pm_mnt;
1338                 time_t t;
1339                 time_t prevt;
1340                 time_t timeout;
1341
1342                 retry_timeout.tv_sec = 3;
1343                 retry_timeout.tv_usec = 0;
1344                 total_timeout.tv_sec = 20;
1345                 total_timeout.tv_usec = 0;
1346 /* FIXME: use monotonic()? */
1347                 timeout = time(NULL) + 60 * retry;
1348                 prevt = 0;
1349                 t = 30;
1350  retry:
1351                 /* Be careful not to use too many CPU cycles */
1352                 if (t - prevt < 30)
1353                         sleep(30);
1354
1355                 get_mountport(&pm_mnt, &mount_server_addr,
1356                                 mountprog,
1357                                 mountvers,
1358                                 proto,
1359                                 mountport);
1360                 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
1361
1362                 /* contact the mount daemon via TCP */
1363                 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1364                 msock = RPC_ANYSOCK;
1365
1366                 switch (pm_mnt.pm_prot) {
1367                 case IPPROTO_UDP:
1368                         mclient = clntudp_create(&mount_server_addr,
1369                                                  pm_mnt.pm_prog,
1370                                                  pm_mnt.pm_vers,
1371                                                  retry_timeout,
1372                                                  &msock);
1373                         if (mclient)
1374                                 break;
1375                         mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1376                         msock = RPC_ANYSOCK;
1377                 case IPPROTO_TCP:
1378                         mclient = clnttcp_create(&mount_server_addr,
1379                                                  pm_mnt.pm_prog,
1380                                                  pm_mnt.pm_vers,
1381                                                  &msock, 0, 0);
1382                         break;
1383                 default:
1384                         mclient = NULL;
1385                 }
1386                 if (!mclient) {
1387                         if (!daemonized && prevt == 0)
1388                                 error_msg_rpc(clnt_spcreateerror(" "));
1389                 } else {
1390                         enum clnt_stat clnt_stat;
1391
1392                         /* Try to mount hostname:pathname */
1393                         mclient->cl_auth = authunix_create_default();
1394
1395                         /* Make pointers in xdr_mountres3 NULL so
1396                          * that xdr_array allocates memory for us
1397                          */
1398                         memset(&status, 0, sizeof(status));
1399
1400                         if (pm_mnt.pm_vers == 3)
1401                                 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1402                                               (xdrproc_t) xdr_dirpath,
1403                                               (caddr_t) &pathname,
1404                                               (xdrproc_t) xdr_mountres3,
1405                                               (caddr_t) &status,
1406                                               total_timeout);
1407                         else
1408                                 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1409                                               (xdrproc_t) xdr_dirpath,
1410                                               (caddr_t) &pathname,
1411                                               (xdrproc_t) xdr_fhstatus,
1412                                               (caddr_t) &status,
1413                                               total_timeout);
1414
1415                         if (clnt_stat == RPC_SUCCESS)
1416                                 goto prepare_kernel_data; /* we're done */
1417                         if (errno != ECONNREFUSED) {
1418                                 error_msg_rpc(clnt_sperror(mclient, " "));
1419                                 goto fail;      /* don't retry */
1420                         }
1421                         /* Connection refused */
1422                         if (!daemonized && prevt == 0) /* print just once */
1423                                 error_msg_rpc(clnt_sperror(mclient, " "));
1424                         auth_destroy(mclient->cl_auth);
1425                         clnt_destroy(mclient);
1426                         mclient = NULL;
1427                         close(msock);
1428                         msock = -1;
1429                 }
1430
1431                 /* Timeout. We are going to retry... maybe */
1432                 if (!bg)
1433                         goto fail;
1434                 if (!daemonized) {
1435                         daemonized = daemonize();
1436                         if (daemonized <= 0) { /* parent or error */
1437                                 retval = -daemonized;
1438                                 goto ret;
1439                         }
1440                 }
1441                 prevt = t;
1442                 t = time(NULL);
1443                 if (t >= timeout)
1444                         /* TODO error message */
1445                         goto fail;
1446
1447                 goto retry;
1448         }
1449
1450  prepare_kernel_data:
1451
1452         if (nfsvers == 2) {
1453                 if (status.nfsv2.fhs_status != 0) {
1454                         bb_error_msg("%s:%s failed, reason given by server: %s",
1455                                 hostname, pathname,
1456                                 nfs_strerror(status.nfsv2.fhs_status));
1457                         goto fail;
1458                 }
1459                 memcpy(data.root.data,
1460                                 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1461                                 NFS_FHSIZE);
1462                 data.root.size = NFS_FHSIZE;
1463                 memcpy(data.old_root.data,
1464                                 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1465                                 NFS_FHSIZE);
1466         } else {
1467                 fhandle3 *my_fhandle;
1468                 if (status.nfsv3.fhs_status != 0) {
1469                         bb_error_msg("%s:%s failed, reason given by server: %s",
1470                                 hostname, pathname,
1471                                 nfs_strerror(status.nfsv3.fhs_status));
1472                         goto fail;
1473                 }
1474                 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1475                 memset(data.old_root.data, 0, NFS_FHSIZE);
1476                 memset(&data.root, 0, sizeof(data.root));
1477                 data.root.size = my_fhandle->fhandle3_len;
1478                 memcpy(data.root.data,
1479                                 (char *) my_fhandle->fhandle3_val,
1480                                 my_fhandle->fhandle3_len);
1481
1482                 data.flags |= NFS_MOUNT_VER3;
1483         }
1484
1485         /* Create nfs socket for kernel */
1486         if (tcp) {
1487                 if (nfs_mount_version < 3) {
1488                         bb_error_msg("NFS over TCP is not supported");
1489                         goto fail;
1490                 }
1491                 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1492         } else
1493                 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1494         if (fsock < 0) {
1495                 bb_perror_msg("nfs socket");
1496                 goto fail;
1497         }
1498         if (bindresvport(fsock, 0) < 0) {
1499                 bb_perror_msg("nfs bindresvport");
1500                 goto fail;
1501         }
1502         if (port == 0) {
1503                 server_addr.sin_port = PMAPPORT;
1504                 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1505                                         tcp ? IPPROTO_TCP : IPPROTO_UDP);
1506                 if (port == 0)
1507                         port = NFS_PORT;
1508         }
1509         server_addr.sin_port = htons(port);
1510
1511         /* Prepare data structure for kernel */
1512         data.fd = fsock;
1513         memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1514         strncpy(data.hostname, hostname, sizeof(data.hostname));
1515
1516         /* Clean up */
1517         auth_destroy(mclient->cl_auth);
1518         clnt_destroy(mclient);
1519         close(msock);
1520         msock = -1;
1521
1522         if (bg) {
1523                 /* We must wait until mount directory is available */
1524                 struct stat statbuf;
1525                 int delay = 1;
1526                 while (stat(mp->mnt_dir, &statbuf) == -1) {
1527                         if (!daemonized) {
1528                                 daemonized = daemonize();
1529                                 if (daemonized <= 0) { /* parent or error */
1530 /* FIXME: parent doesn't close fsock - ??! */
1531                                         retval = -daemonized;
1532                                         goto ret;
1533                                 }
1534                         }
1535                         sleep(delay);   /* 1, 2, 4, 8, 16, 30, ... */
1536                         delay *= 2;
1537                         if (delay > 30)
1538                                 delay = 30;
1539                 }
1540         }
1541
1542         /* Perform actual mount */
1543  do_mount:
1544         mp->mnt_type = (char*)"nfs";
1545         retval = mount_it_now(mp, vfsflags, (char*)&data);
1546         goto ret;
1547
1548         /* Abort */
1549  fail:
1550         if (msock >= 0) {
1551                 if (mclient) {
1552                         auth_destroy(mclient->cl_auth);
1553                         clnt_destroy(mclient);
1554                 }
1555                 close(msock);
1556         }
1557         if (fsock >= 0)
1558                 close(fsock);
1559
1560  ret:
1561         free(hostname);
1562         free(mounthost);
1563         free(filteropts);
1564         return retval;
1565 }
1566
1567 #else // !ENABLE_FEATURE_MOUNT_NFS
1568
1569 // Never called. Call should be optimized out.
1570 int nfsmount(struct mntent *mp, long vfsflags, char *filteropts);
1571
1572 #endif // !ENABLE_FEATURE_MOUNT_NFS
1573
1574 // Mount one directory.  Handles CIFS, NFS, loopback, autobind, and filesystem
1575 // type detection.  Returns 0 for success, nonzero for failure.
1576 // NB: mp->xxx fields may be trashed on exit
1577 static int singlemount(struct mntent *mp, int ignore_busy)
1578 {
1579         int rc = -1;
1580         long vfsflags;
1581         char *loopFile = NULL, *filteropts = NULL;
1582         llist_t *fl = NULL;
1583         struct stat st;
1584
1585         errno = 0;
1586
1587         vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1588
1589         // Treat fstype "auto" as unspecified
1590         if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1591                 mp->mnt_type = NULL;
1592
1593         // Might this be a virtual filesystem?
1594         if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
1595                 char *args[35];
1596                 char *s;
1597                 int n;
1598                 // fsname: "cmd#arg1#arg2..."
1599                 // WARNING: allows execution of arbitrary commands!
1600                 // Try "mount 'sh#-c#sh' bogus_dir".
1601                 // It is safe ONLY because non-root
1602                 // cannot use two-argument mount command
1603                 // and using one-argument "mount 'sh#-c#sh'" doesn't work:
1604                 // "mount: can't find sh#-c#sh in /etc/fstab"
1605                 // (if /etc/fstab has it, it's ok: root sets up /etc/fstab).
1606
1607                 s = mp->mnt_fsname;
1608                 n = 0;
1609                 args[n++] = s;
1610                 while (*s && n < 35 - 2) {
1611                         if (*s++ == '#' && *s != '#') {
1612                                 s[-1] = '\0';
1613                                 args[n++] = s;
1614                         }
1615                 }
1616                 args[n++] = mp->mnt_dir;
1617                 args[n] = NULL;
1618                 rc = spawn_and_wait(args);
1619                 goto report_error;
1620         }
1621
1622         // Might this be an CIFS filesystem?
1623         if (ENABLE_FEATURE_MOUNT_CIFS
1624          && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
1625          && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
1626          && mp->mnt_fsname[0] == mp->mnt_fsname[1]
1627         ) {
1628                 int len;
1629                 char c;
1630                 len_and_sockaddr *lsa;
1631                 char *hostname, *dotted, *ip;
1632
1633                 hostname = mp->mnt_fsname + 2;
1634                 len = strcspn(hostname, "/\\");
1635                 if (len == 0 || hostname[len] == '\0')
1636                         goto report_error;
1637                 c = hostname[len];
1638                 hostname[len] = '\0';
1639                 lsa = host2sockaddr(hostname, 0);
1640                 hostname[len] = c;
1641                 if (!lsa)
1642                         goto report_error;
1643
1644                 // Insert "ip=..." option into options
1645                 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1646                 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
1647                 ip = xasprintf("ip=%s", dotted);
1648                 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
1649                 parse_mount_options(ip, &filteropts);
1650                 if (ENABLE_FEATURE_CLEAN_UP) free(ip);
1651
1652                 // "-o mand" is required [why?]
1653                 vfsflags |= MS_MANDLOCK;
1654                 mp->mnt_type = (char*)"cifs";
1655                 rc = mount_it_now(mp, vfsflags, filteropts);
1656
1657                 goto report_error;
1658         }
1659
1660         // Might this be an NFS filesystem?
1661         if (ENABLE_FEATURE_MOUNT_NFS
1662          && (!mp->mnt_type || strcmp(mp->mnt_type, "nfs") == 0)
1663          && strchr(mp->mnt_fsname, ':') != NULL
1664         ) {
1665                 rc = nfsmount(mp, vfsflags, filteropts);
1666                 goto report_error;
1667         }
1668
1669         // Look at the file.  (Not found isn't a failure for remount, or for
1670         // a synthetic filesystem like proc or sysfs.)
1671         // (We use stat, not lstat, in order to allow
1672         // mount symlink_to_file_or_blkdev dir)
1673         if (!stat(mp->mnt_fsname, &st)
1674          && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1675         ) {
1676                 // Do we need to allocate a loopback device for it?
1677                 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1678                         loopFile = bb_simplify_path(mp->mnt_fsname);
1679                         mp->mnt_fsname = NULL; // will receive malloced loop dev name
1680                         if (set_loop(&mp->mnt_fsname, loopFile, 0) < 0) {
1681                                 if (errno == EPERM || errno == EACCES)
1682                                         bb_error_msg(bb_msg_perm_denied_are_you_root);
1683                                 else
1684                                         bb_perror_msg("can't setup loop device");
1685                                 return errno;
1686                         }
1687
1688                 // Autodetect bind mounts
1689                 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1690                         vfsflags |= MS_BIND;
1691         }
1692
1693         // If we know the fstype (or don't need to), jump straight
1694         // to the actual mount.
1695         if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
1696                 rc = mount_it_now(mp, vfsflags, filteropts);
1697         } else {
1698                 // Loop through filesystem types until mount succeeds
1699                 // or we run out
1700
1701                 // Initialize list of block backed filesystems.
1702                 // This has to be done here so that during "mount -a",
1703                 // mounts after /proc shows up can autodetect.
1704                 if (!fslist) {
1705                         fslist = get_block_backed_filesystems();
1706                         if (ENABLE_FEATURE_CLEAN_UP && fslist)
1707                                 atexit(delete_block_backed_filesystems);
1708                 }
1709
1710                 for (fl = fslist; fl; fl = fl->link) {
1711                         mp->mnt_type = fl->data;
1712                         rc = mount_it_now(mp, vfsflags, filteropts);
1713                         if (!rc)
1714                                 break;
1715                 }
1716         }
1717
1718         // If mount failed, clean up loop file (if any).
1719         if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1720                 del_loop(mp->mnt_fsname);
1721                 if (ENABLE_FEATURE_CLEAN_UP) {
1722                         free(loopFile);
1723                         free(mp->mnt_fsname);
1724                 }
1725         }
1726
1727  report_error:
1728         if (ENABLE_FEATURE_CLEAN_UP)
1729                 free(filteropts);
1730
1731         if (errno == EBUSY && ignore_busy)
1732                 return 0;
1733         if (rc != 0)
1734                 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
1735         return rc;
1736 }
1737
1738 // -O support
1739 //    -O interprets a list of filter options which select whether a mount
1740 // point will be mounted: only mounts with options matching *all* filtering
1741 // options will be selected.
1742 //    By default each -O filter option must be present in the list of mount
1743 // options, but if it is prefixed by "no" then it must be absent.
1744 // For example,
1745 //  -O a,nob,c  matches  -o a,c  but fails to match  -o a,b,c
1746 //              (and also fails to match  -o a  because  -o c  is absent).
1747 //
1748 // It is different from -t in that each option is matched exactly; a leading
1749 // "no" at the beginning of one option does not negate the rest.
1750 static int match_opt(const char *fs_opt_in, const char *O_opt)
1751 {
1752         if (!O_opt)
1753                 return 1;
1754
1755         while (*O_opt) {
1756                 const char *fs_opt = fs_opt_in;
1757                 int O_len;
1758                 int match;
1759
1760                 // If option begins with "no" then treat as an inverted match:
1761                 // matching is a failure
1762                 match = 0;
1763                 if (O_opt[0] == 'n' && O_opt[1] == 'o') {
1764                         match = 1;
1765                         O_opt += 2;
1766                 }
1767                 // Isolate the current O option
1768                 O_len = strchrnul(O_opt, ',') - O_opt;
1769                 // Check for a match against existing options
1770                 while (1) {
1771                         if (strncmp(fs_opt, O_opt, O_len) == 0
1772                          && (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',')
1773                         ) {
1774                                 if (match)
1775                                         return 0;  // "no" prefix, but option found
1776                                 match = 1;  // current O option found, go check next one
1777                                 break;
1778                         }
1779                         fs_opt = strchr(fs_opt, ',');
1780                         if (!fs_opt)
1781                                 break;
1782                         fs_opt++;
1783                 }
1784                 if (match == 0)
1785                         return 0;     // match wanted but not found
1786                 if (O_opt[O_len] == '\0') // end?
1787                         break;
1788                 // Step to the next O option
1789                 O_opt += O_len + 1;
1790         }
1791         // If we get here then everything matched
1792         return 1;
1793 }
1794
1795 // Parse options, if necessary parse fstab/mtab, and call singlemount for
1796 // each directory to be mounted.
1797 int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1798 int mount_main(int argc UNUSED_PARAM, char **argv)
1799 {
1800         char *cmdopts = xzalloc(1);
1801         char *fstype = NULL;
1802         char *O_optmatch = NULL;
1803         char *storage_path;
1804         llist_t *lst_o = NULL;
1805         const char *fstabname;
1806         FILE *fstab;
1807         int i, j;
1808         int rc = EXIT_SUCCESS;
1809         unsigned opt;
1810         struct mntent mtpair[2], *mtcur = mtpair;
1811         IF_NOT_DESKTOP(const int nonroot = 0;)
1812
1813         IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
1814
1815         // Parse long options, like --bind and --move.  Note that -o option
1816         // and --option are synonymous.  Yes, this means --remount,rw works.
1817         for (i = j = 1; argv[i]; i++) {
1818                 if (argv[i][0] == '-' && argv[i][1] == '-')
1819                         append_mount_options(&cmdopts, argv[i] + 2);
1820                 else
1821                         argv[j++] = argv[i];
1822         }
1823         argv[j] = NULL;
1824
1825         // Parse remaining options
1826         // Max 2 params; -o is a list, -v is a counter
1827         opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv");
1828         opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch
1829                         IF_FEATURE_MOUNT_VERBOSE(, &verbose));
1830         while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
1831         if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
1832         if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
1833         argv += optind;
1834
1835         // If we have no arguments, show currently mounted filesystems
1836         if (!argv[0]) {
1837                 if (!(opt & OPT_a)) {
1838                         FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1839
1840                         if (!mountTable)
1841                                 bb_error_msg_and_die("no %s", bb_path_mtab_file);
1842
1843                         while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
1844                                                                 GETMNTENT_BUFSIZE))
1845                         {
1846                                 // Don't show rootfs. FIXME: why??
1847                                 // util-linux 2.12a happily shows rootfs...
1848                                 //if (strcmp(mtpair->mnt_fsname, "rootfs") == 0) continue;
1849
1850                                 if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0)
1851                                         printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1852                                                         mtpair->mnt_dir, mtpair->mnt_type,
1853                                                         mtpair->mnt_opts);
1854                         }
1855                         if (ENABLE_FEATURE_CLEAN_UP)
1856                                 endmntent(mountTable);
1857                         return EXIT_SUCCESS;
1858                 }
1859                 storage_path = NULL;
1860         } else {
1861                 // When we have two arguments, the second is the directory and we can
1862                 // skip looking at fstab entirely.  We can always abspath() the directory
1863                 // argument when we get it.
1864                 if (argv[1]) {
1865                         if (nonroot)
1866                                 bb_error_msg_and_die(bb_msg_you_must_be_root);
1867                         mtpair->mnt_fsname = argv[0];
1868                         mtpair->mnt_dir = argv[1];
1869                         mtpair->mnt_type = fstype;
1870                         mtpair->mnt_opts = cmdopts;
1871                         resolve_mount_spec(&mtpair->mnt_fsname);
1872                         rc = singlemount(mtpair, /*ignore_busy:*/ 0);
1873                         return rc;
1874                 }
1875                 storage_path = bb_simplify_path(argv[0]); // malloced
1876         }
1877
1878         // Past this point, we are handling either "mount -a [opts]"
1879         // or "mount [opts] single_param"
1880
1881         i = parse_mount_options(cmdopts, NULL); // FIXME: should be "long", not "int"
1882         if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags
1883                 bb_error_msg_and_die(bb_msg_you_must_be_root);
1884
1885         // If we have a shared subtree flag, don't worry about fstab or mtab.
1886         if (ENABLE_FEATURE_MOUNT_FLAGS
1887          && (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
1888         ) {
1889                 // verbose_mount(source, target, type, flags, data)
1890                 rc = verbose_mount("", argv[0], "", i, "");
1891                 if (rc)
1892                         bb_simple_perror_msg_and_die(argv[0]);
1893                 return rc;
1894         }
1895
1896         // Open either fstab or mtab
1897         fstabname = "/etc/fstab";
1898         if (i & MS_REMOUNT) {
1899                 // WARNING. I am not sure this matches util-linux's
1900                 // behavior. It's possible util-linux does not
1901                 // take -o opts from mtab (takes only mount source).
1902                 fstabname = bb_path_mtab_file;
1903         }
1904         fstab = setmntent(fstabname, "r");
1905         if (!fstab)
1906                 bb_perror_msg_and_die("can't read '%s'", fstabname);
1907
1908         // Loop through entries until we find what we're looking for
1909         memset(mtpair, 0, sizeof(mtpair));
1910         for (;;) {
1911                 struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
1912
1913                 // Get next fstab entry
1914                 if (!getmntent_r(fstab, mtcur, getmntent_buf
1915                                         + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
1916                                 GETMNTENT_BUFSIZE/2)
1917                 ) { // End of fstab/mtab is reached
1918                         mtcur = mtother; // the thing we found last time
1919                         break;
1920                 }
1921
1922                 // If we're trying to mount something specific and this isn't it,
1923                 // skip it.  Note we must match the exact text in fstab (ala
1924                 // "proc") or a full path from root
1925                 if (argv[0]) {
1926
1927                         // Is this what we're looking for?
1928                         if (strcmp(argv[0], mtcur->mnt_fsname) != 0
1929                          && strcmp(storage_path, mtcur->mnt_fsname) != 0
1930                          && strcmp(argv[0], mtcur->mnt_dir) != 0
1931                          && strcmp(storage_path, mtcur->mnt_dir) != 0
1932                         ) {
1933                                 continue; // no
1934                         }
1935
1936                         // Remember this entry.  Something later may have
1937                         // overmounted it, and we want the _last_ match.
1938                         mtcur = mtother;
1939
1940                 // If we're mounting all
1941                 } else {
1942                         struct mntent *mp;
1943                         // No, mount -a won't mount anything,
1944                         // even user mounts, for mere humans
1945                         if (nonroot)
1946                                 bb_error_msg_and_die(bb_msg_you_must_be_root);
1947
1948                         // Does type match? (NULL matches always)
1949                         if (!match_fstype(mtcur, fstype))
1950                                 continue;
1951
1952                         // Skip noauto and swap anyway
1953                         if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
1954                         // swap is bogus "fstype", parse_mount_options can't check fstypes
1955                          || strcasecmp(mtcur->mnt_type, "swap") == 0
1956                         ) {
1957                                 continue;
1958                         }
1959
1960                         // Does (at least one) option match?
1961                         // (NULL matches always)
1962                         if (!match_opt(mtcur->mnt_opts, O_optmatch))
1963                                 continue;
1964
1965                         resolve_mount_spec(&mtcur->mnt_fsname);
1966
1967                         // NFS mounts want this to be xrealloc-able
1968                         mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
1969
1970                         // If nothing is mounted on this directory...
1971                         // (otherwise repeated "mount -a" mounts everything again)
1972                         mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
1973                         // We do not check fsname match of found mount point -
1974                         // "/" may have fsname of "/dev/root" while fstab
1975                         // says "/dev/something_else".
1976                         if (mp) {
1977                                 if (verbose) {
1978                                         bb_error_msg("according to %s, "
1979                                                 "%s is already mounted on %s",
1980                                                 bb_path_mtab_file,
1981                                                 mp->mnt_fsname, mp->mnt_dir);
1982                                 }
1983                         } else {
1984                                 // ...mount this thing
1985                                 if (singlemount(mtcur, /*ignore_busy:*/ 1)) {
1986                                         // Count number of failed mounts
1987                                         rc++;
1988                                 }
1989                         }
1990                         free(mtcur->mnt_opts);
1991                 }
1992         }
1993
1994         // End of fstab/mtab is reached.
1995         // Were we looking for something specific?
1996         if (argv[0]) { // yes
1997                 long l;
1998
1999                 // If we didn't find anything, complain
2000                 if (!mtcur->mnt_fsname)
2001                         bb_error_msg_and_die("can't find %s in %s",
2002                                 argv[0], fstabname);
2003
2004                 // What happens when we try to "mount swap_partition"?
2005                 // (fstab containts "swap_partition swap swap defaults 0 0")
2006                 // util-linux-ng 2.13.1 does this:
2007                 // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
2008                 // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
2009                 // lstat("swap", 0x7fff62a3a640)           = -1 ENOENT (No such file or directory)
2010                 // write(2, "mount: mount point swap does not exist\n", 39) = 39
2011                 // exit_group(32)                          = ?
2012 #if 0
2013                 // In case we want to simply skip swap partitions:
2014                 l = parse_mount_options(mtcur->mnt_opts, NULL);
2015                 if ((l & MOUNT_SWAP)
2016                 // swap is bogus "fstype", parse_mount_options can't check fstypes
2017                  || strcasecmp(mtcur->mnt_type, "swap") == 0
2018                 ) {
2019                         goto ret;
2020                 }
2021 #endif
2022                 if (nonroot) {
2023                         // fstab must have "users" or "user"
2024                         l = parse_mount_options(mtcur->mnt_opts, NULL);
2025                         if (!(l & MOUNT_USERS))
2026                                 bb_error_msg_and_die(bb_msg_you_must_be_root);
2027                 }
2028
2029                 //util-linux-2.12 does not do this check.
2030                 //// If nothing is mounted on this directory...
2031                 //// (otherwise repeated "mount FOO" mounts FOO again)
2032                 //mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2033                 //if (mp) {
2034                 //      bb_error_msg("according to %s, "
2035                 //              "%s is already mounted on %s",
2036                 //              bb_path_mtab_file,
2037                 //              mp->mnt_fsname, mp->mnt_dir);
2038                 //} else {
2039                         // ...mount the last thing we found
2040                         mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2041                         append_mount_options(&(mtcur->mnt_opts), cmdopts);
2042                         resolve_mount_spec(&mtpair->mnt_fsname);
2043                         rc = singlemount(mtcur, /*ignore_busy:*/ 0);
2044                         if (ENABLE_FEATURE_CLEAN_UP)
2045                                 free(mtcur->mnt_opts);
2046                 //}
2047         }
2048
2049  //ret:
2050         if (ENABLE_FEATURE_CLEAN_UP)
2051                 endmntent(fstab);
2052         if (ENABLE_FEATURE_CLEAN_UP) {
2053                 free(storage_path);
2054                 free(cmdopts);
2055         }
2056
2057 //TODO: exitcode should be ORed mask of (from "man mount"):
2058 // 0 success
2059 // 1 incorrect invocation or permissions
2060 // 2 system error (out of memory, cannot fork, no more loop devices)
2061 // 4 internal mount bug or missing nfs support in mount
2062 // 8 user interrupt
2063 //16 problems writing or locking /etc/mtab
2064 //32 mount failure
2065 //64 some mount succeeded
2066         return rc;
2067 }