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