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