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