ab249642b3c6ddcf702854f8bf032a6af2f6f8eb
[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 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 NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
940 {
941         CLIENT *mclient;
942         char *hostname;
943         char *pathname;
944         char *mounthost;
945         /* prior to 2.6.23, kernel took NFS options in a form of this struct
946          * only. 2.6.23+ looks at data->version, and if it's not 1..6,
947          * then data pointer is interpreted as a string. */
948         struct nfs_mount_data data;
949         char *opt;
950         struct hostent *hp;
951         struct sockaddr_in server_addr;
952         struct sockaddr_in mount_server_addr;
953         int msock, fsock;
954         union {
955                 struct fhstatus nfsv2;
956                 struct mountres3 nfsv3;
957         } status;
958         int daemonized;
959         char *s;
960         int port;
961         int mountport;
962         int proto;
963 #if BB_MMU
964         smallint bg = 0;
965 #else
966         enum { bg = 0 };
967 #endif
968         int retry;
969         int mountprog;
970         int mountvers;
971         int nfsprog;
972         int nfsvers;
973         int retval;
974         /* these all are one-bit really. gcc 4.3.1 likes this combination: */
975         smallint tcp;
976         smallint soft;
977         int intr;
978         int posix;
979         int nocto;
980         int noac;
981         int nordirplus;
982         int nolock;
983
984         find_kernel_nfs_mount_version();
985
986         daemonized = 0;
987         mounthost = NULL;
988         retval = ETIMEDOUT;
989         msock = fsock = -1;
990         mclient = NULL;
991
992         /* NB: hostname, mounthost, filteropts must be free()d prior to return */
993
994         filteropts = xstrdup(filteropts); /* going to trash it later... */
995
996         hostname = xstrdup(mp->mnt_fsname);
997         /* mount_main() guarantees that ':' is there */
998         s = strchr(hostname, ':');
999         pathname = s + 1;
1000         *s = '\0';
1001         /* Ignore all but first hostname in replicated mounts
1002            until they can be fully supported. (mack@sgi.com) */
1003         s = strchr(hostname, ',');
1004         if (s) {
1005                 *s = '\0';
1006                 bb_error_msg("warning: multiple hostnames not supported");
1007         }
1008
1009         server_addr.sin_family = AF_INET;
1010         if (!inet_aton(hostname, &server_addr.sin_addr)) {
1011                 hp = gethostbyname(hostname);
1012                 if (hp == NULL) {
1013                         bb_herror_msg("%s", hostname);
1014                         goto fail;
1015                 }
1016                 if ((size_t)hp->h_length > sizeof(struct in_addr)) {
1017                         bb_error_msg("got bad hp->h_length");
1018                         hp->h_length = sizeof(struct in_addr);
1019                 }
1020                 memcpy(&server_addr.sin_addr,
1021                                 hp->h_addr, hp->h_length);
1022         }
1023
1024         memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1025
1026         /* add IP address to mtab options for use when unmounting */
1027
1028         if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1029                 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1030         } else {
1031                 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1032                                         mp->mnt_opts[0] ? "," : "",
1033                                         inet_ntoa(server_addr.sin_addr));
1034                 free(mp->mnt_opts);
1035                 mp->mnt_opts = tmp;
1036         }
1037
1038         /* Set default options.
1039          * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1040          * let the kernel decide.
1041          * timeo is filled in after we know whether it'll be TCP or UDP. */
1042         memset(&data, 0, sizeof(data));
1043         data.retrans  = 3;
1044         data.acregmin = 3;
1045         data.acregmax = 60;
1046         data.acdirmin = 30;
1047         data.acdirmax = 60;
1048         data.namlen   = NAME_MAX;
1049
1050         soft = 0;
1051         intr = 0;
1052         posix = 0;
1053         nocto = 0;
1054         nolock = 0;
1055         noac = 0;
1056         nordirplus = 0;
1057         retry = 10000;          /* 10000 minutes ~ 1 week */
1058         tcp = 0;
1059
1060         mountprog = MOUNTPROG;
1061         mountvers = 0;
1062         port = 0;
1063         mountport = 0;
1064         nfsprog = 100003;
1065         nfsvers = 0;
1066
1067         /* parse options */
1068         if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
1069                 char *opteq = strchr(opt, '=');
1070                 if (opteq) {
1071                         int val, idx;
1072                         static const char options[] ALIGN1 =
1073                                 /* 0 */ "rsize\0"
1074                                 /* 1 */ "wsize\0"
1075                                 /* 2 */ "timeo\0"
1076                                 /* 3 */ "retrans\0"
1077                                 /* 4 */ "acregmin\0"
1078                                 /* 5 */ "acregmax\0"
1079                                 /* 6 */ "acdirmin\0"
1080                                 /* 7 */ "acdirmax\0"
1081                                 /* 8 */ "actimeo\0"
1082                                 /* 9 */ "retry\0"
1083                                 /* 10 */ "port\0"
1084                                 /* 11 */ "mountport\0"
1085                                 /* 12 */ "mounthost\0"
1086                                 /* 13 */ "mountprog\0"
1087                                 /* 14 */ "mountvers\0"
1088                                 /* 15 */ "nfsprog\0"
1089                                 /* 16 */ "nfsvers\0"
1090                                 /* 17 */ "vers\0"
1091                                 /* 18 */ "proto\0"
1092                                 /* 19 */ "namlen\0"
1093                                 /* 20 */ "addr\0";
1094
1095                         *opteq++ = '\0';
1096                         idx = index_in_strings(options, opt);
1097                         switch (idx) {
1098                         case 12: // "mounthost"
1099                                 mounthost = xstrndup(opteq,
1100                                                 strcspn(opteq, " \t\n\r,"));
1101                                 continue;
1102                         case 18: // "proto"
1103                                 if (!strncmp(opteq, "tcp", 3))
1104                                         tcp = 1;
1105                                 else if (!strncmp(opteq, "udp", 3))
1106                                         tcp = 0;
1107                                 else
1108                                         bb_error_msg("warning: unrecognized proto= option");
1109                                 continue;
1110                         case 20: // "addr" - ignore
1111                                 continue;
1112                         }
1113
1114                         val = xatoi_u(opteq);
1115                         switch (idx) {
1116                         case 0: // "rsize"
1117                                 data.rsize = val;
1118                                 continue;
1119                         case 1: // "wsize"
1120                                 data.wsize = val;
1121                                 continue;
1122                         case 2: // "timeo"
1123                                 data.timeo = val;
1124                                 continue;
1125                         case 3: // "retrans"
1126                                 data.retrans = val;
1127                                 continue;
1128                         case 4: // "acregmin"
1129                                 data.acregmin = val;
1130                                 continue;
1131                         case 5: // "acregmax"
1132                                 data.acregmax = val;
1133                                 continue;
1134                         case 6: // "acdirmin"
1135                                 data.acdirmin = val;
1136                                 continue;
1137                         case 7: // "acdirmax"
1138                                 data.acdirmax = val;
1139                                 continue;
1140                         case 8: // "actimeo"
1141                                 data.acregmin = val;
1142                                 data.acregmax = val;
1143                                 data.acdirmin = val;
1144                                 data.acdirmax = val;
1145                                 continue;
1146                         case 9: // "retry"
1147                                 retry = val;
1148                                 continue;
1149                         case 10: // "port"
1150                                 port = val;
1151                                 continue;
1152                         case 11: // "mountport"
1153                                 mountport = val;
1154                                 continue;
1155                         case 13: // "mountprog"
1156                                 mountprog = val;
1157                                 continue;
1158                         case 14: // "mountvers"
1159                                 mountvers = val;
1160                                 continue;
1161                         case 15: // "nfsprog"
1162                                 nfsprog = val;
1163                                 continue;
1164                         case 16: // "nfsvers"
1165                         case 17: // "vers"
1166                                 nfsvers = val;
1167                                 continue;
1168                         case 19: // "namlen"
1169                                 //if (nfs_mount_version >= 2)
1170                                         data.namlen = val;
1171                                 //else
1172                                 //      bb_error_msg("warning: option namlen is not supported\n");
1173                                 continue;
1174                         default:
1175                                 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1176                                 goto fail;
1177                         }
1178                 }
1179                 else { /* not of the form opt=val */
1180                         static const char options[] ALIGN1 =
1181                                 "bg\0"
1182                                 "fg\0"
1183                                 "soft\0"
1184                                 "hard\0"
1185                                 "intr\0"
1186                                 "posix\0"
1187                                 "cto\0"
1188                                 "ac\0"
1189                                 "tcp\0"
1190                                 "udp\0"
1191                                 "lock\0"
1192                                 "rdirplus\0";
1193                         int val = 1;
1194                         if (!strncmp(opt, "no", 2)) {
1195                                 val = 0;
1196                                 opt += 2;
1197                         }
1198                         switch (index_in_strings(options, opt)) {
1199                         case 0: // "bg"
1200 #if BB_MMU
1201                                 bg = val;
1202 #endif
1203                                 break;
1204                         case 1: // "fg"
1205 #if BB_MMU
1206                                 bg = !val;
1207 #endif
1208                                 break;
1209                         case 2: // "soft"
1210                                 soft = val;
1211                                 break;
1212                         case 3: // "hard"
1213                                 soft = !val;
1214                                 break;
1215                         case 4: // "intr"
1216                                 intr = val;
1217                                 break;
1218                         case 5: // "posix"
1219                                 posix = val;
1220                                 break;
1221                         case 6: // "cto"
1222                                 nocto = !val;
1223                                 break;
1224                         case 7: // "ac"
1225                                 noac = !val;
1226                                 break;
1227                         case 8: // "tcp"
1228                                 tcp = val;
1229                                 break;
1230                         case 9: // "udp"
1231                                 tcp = !val;
1232                                 break;
1233                         case 10: // "lock"
1234                                 if (nfs_mount_version >= 3)
1235                                         nolock = !val;
1236                                 else
1237                                         bb_error_msg("warning: option nolock is not supported");
1238                                 break;
1239                         case 11: //rdirplus
1240                                 nordirplus = !val;
1241                                 break;
1242                         default:
1243                                 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1244                                 goto fail;
1245                         }
1246                 }
1247         }
1248         proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1249
1250         data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1251                 | (intr ? NFS_MOUNT_INTR : 0)
1252                 | (posix ? NFS_MOUNT_POSIX : 0)
1253                 | (nocto ? NFS_MOUNT_NOCTO : 0)
1254                 | (noac ? NFS_MOUNT_NOAC : 0)
1255                 | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0);
1256         if (nfs_mount_version >= 2)
1257                 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1258         if (nfs_mount_version >= 3)
1259                 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1260         if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1261                 bb_error_msg("NFSv%d not supported", nfsvers);
1262                 goto fail;
1263         }
1264         if (nfsvers && !mountvers)
1265                 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1266         if (nfsvers && nfsvers < mountvers) {
1267                 mountvers = nfsvers;
1268         }
1269
1270         /* Adjust options if none specified */
1271         if (!data.timeo)
1272                 data.timeo = tcp ? 70 : 7;
1273
1274         data.version = nfs_mount_version;
1275
1276         if (vfsflags & MS_REMOUNT)
1277                 goto do_mount;
1278
1279         /*
1280          * If the previous mount operation on the same host was
1281          * backgrounded, and the "bg" for this mount is also set,
1282          * give up immediately, to avoid the initial timeout.
1283          */
1284         if (bg && we_saw_this_host_before(hostname)) {
1285                 daemonized = daemonize();
1286                 if (daemonized <= 0) { /* parent or error */
1287                         retval = -daemonized;
1288                         goto ret;
1289                 }
1290         }
1291
1292         /* Create mount daemon client */
1293         /* See if the nfs host = mount host. */
1294         if (mounthost) {
1295                 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1296                         mount_server_addr.sin_family = AF_INET;
1297                         mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1298                 } else {
1299                         hp = gethostbyname(mounthost);
1300                         if (hp == NULL) {
1301                                 bb_herror_msg("%s", mounthost);
1302                                 goto fail;
1303                         }
1304                         if ((size_t)hp->h_length > sizeof(struct in_addr)) {
1305                                 bb_error_msg("got bad hp->h_length");
1306                                 hp->h_length = sizeof(struct in_addr);
1307                         }
1308                         mount_server_addr.sin_family = AF_INET;
1309                         memcpy(&mount_server_addr.sin_addr,
1310                                                 hp->h_addr, hp->h_length);
1311                 }
1312         }
1313
1314         /*
1315          * The following loop implements the mount retries. When the mount
1316          * times out, and the "bg" option is set, we background ourself
1317          * and continue trying.
1318          *
1319          * The case where the mount point is not present and the "bg"
1320          * option is set, is treated as a timeout. This is done to
1321          * support nested mounts.
1322          *
1323          * The "retry" count specified by the user is the number of
1324          * minutes to retry before giving up.
1325          */
1326         {
1327                 struct timeval total_timeout;
1328                 struct timeval retry_timeout;
1329                 struct pmap pm_mnt;
1330                 time_t t;
1331                 time_t prevt;
1332                 time_t timeout;
1333
1334                 retry_timeout.tv_sec = 3;
1335                 retry_timeout.tv_usec = 0;
1336                 total_timeout.tv_sec = 20;
1337                 total_timeout.tv_usec = 0;
1338 /* FIXME: use monotonic()? */
1339                 timeout = time(NULL) + 60 * retry;
1340                 prevt = 0;
1341                 t = 30;
1342  retry:
1343                 /* Be careful not to use too many CPU cycles */
1344                 if (t - prevt < 30)
1345                         sleep(30);
1346
1347                 get_mountport(&pm_mnt, &mount_server_addr,
1348                                 mountprog,
1349                                 mountvers,
1350                                 proto,
1351                                 mountport);
1352                 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
1353
1354                 /* contact the mount daemon via TCP */
1355                 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1356                 msock = RPC_ANYSOCK;
1357
1358                 switch (pm_mnt.pm_prot) {
1359                 case IPPROTO_UDP:
1360                         mclient = clntudp_create(&mount_server_addr,
1361                                                  pm_mnt.pm_prog,
1362                                                  pm_mnt.pm_vers,
1363                                                  retry_timeout,
1364                                                  &msock);
1365                         if (mclient)
1366                                 break;
1367                         mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1368                         msock = RPC_ANYSOCK;
1369                 case IPPROTO_TCP:
1370                         mclient = clnttcp_create(&mount_server_addr,
1371                                                  pm_mnt.pm_prog,
1372                                                  pm_mnt.pm_vers,
1373                                                  &msock, 0, 0);
1374                         break;
1375                 default:
1376                         mclient = NULL;
1377                 }
1378                 if (!mclient) {
1379                         if (!daemonized && prevt == 0)
1380                                 error_msg_rpc(clnt_spcreateerror(" "));
1381                 } else {
1382                         enum clnt_stat clnt_stat;
1383
1384                         /* Try to mount hostname:pathname */
1385                         mclient->cl_auth = authunix_create_default();
1386
1387                         /* Make pointers in xdr_mountres3 NULL so
1388                          * that xdr_array allocates memory for us
1389                          */
1390                         memset(&status, 0, sizeof(status));
1391
1392                         if (pm_mnt.pm_vers == 3)
1393                                 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1394                                               (xdrproc_t) xdr_dirpath,
1395                                               (caddr_t) &pathname,
1396                                               (xdrproc_t) xdr_mountres3,
1397                                               (caddr_t) &status,
1398                                               total_timeout);
1399                         else
1400                                 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1401                                               (xdrproc_t) xdr_dirpath,
1402                                               (caddr_t) &pathname,
1403                                               (xdrproc_t) xdr_fhstatus,
1404                                               (caddr_t) &status,
1405                                               total_timeout);
1406
1407                         if (clnt_stat == RPC_SUCCESS)
1408                                 goto prepare_kernel_data; /* we're done */
1409                         if (errno != ECONNREFUSED) {
1410                                 error_msg_rpc(clnt_sperror(mclient, " "));
1411                                 goto fail;      /* don't retry */
1412                         }
1413                         /* Connection refused */
1414                         if (!daemonized && prevt == 0) /* print just once */
1415                                 error_msg_rpc(clnt_sperror(mclient, " "));
1416                         auth_destroy(mclient->cl_auth);
1417                         clnt_destroy(mclient);
1418                         mclient = NULL;
1419                         close(msock);
1420                         msock = -1;
1421                 }
1422
1423                 /* Timeout. We are going to retry... maybe */
1424                 if (!bg)
1425                         goto fail;
1426                 if (!daemonized) {
1427                         daemonized = daemonize();
1428                         if (daemonized <= 0) { /* parent or error */
1429                                 retval = -daemonized;
1430                                 goto ret;
1431                         }
1432                 }
1433                 prevt = t;
1434                 t = time(NULL);
1435                 if (t >= timeout)
1436                         /* TODO error message */
1437                         goto fail;
1438
1439                 goto retry;
1440         }
1441
1442  prepare_kernel_data:
1443
1444         if (nfsvers == 2) {
1445                 if (status.nfsv2.fhs_status != 0) {
1446                         bb_error_msg("%s:%s failed, reason given by server: %s",
1447                                 hostname, pathname,
1448                                 nfs_strerror(status.nfsv2.fhs_status));
1449                         goto fail;
1450                 }
1451                 memcpy(data.root.data,
1452                                 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1453                                 NFS_FHSIZE);
1454                 data.root.size = NFS_FHSIZE;
1455                 memcpy(data.old_root.data,
1456                                 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1457                                 NFS_FHSIZE);
1458         } else {
1459                 fhandle3 *my_fhandle;
1460                 if (status.nfsv3.fhs_status != 0) {
1461                         bb_error_msg("%s:%s failed, reason given by server: %s",
1462                                 hostname, pathname,
1463                                 nfs_strerror(status.nfsv3.fhs_status));
1464                         goto fail;
1465                 }
1466                 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1467                 memset(data.old_root.data, 0, NFS_FHSIZE);
1468                 memset(&data.root, 0, sizeof(data.root));
1469                 data.root.size = my_fhandle->fhandle3_len;
1470                 memcpy(data.root.data,
1471                                 (char *) my_fhandle->fhandle3_val,
1472                                 my_fhandle->fhandle3_len);
1473
1474                 data.flags |= NFS_MOUNT_VER3;
1475         }
1476
1477         /* Create nfs socket for kernel */
1478         if (tcp) {
1479                 if (nfs_mount_version < 3) {
1480                         bb_error_msg("NFS over TCP is not supported");
1481                         goto fail;
1482                 }
1483                 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1484         } else
1485                 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1486         if (fsock < 0) {
1487                 bb_perror_msg("nfs socket");
1488                 goto fail;
1489         }
1490         if (bindresvport(fsock, 0) < 0) {
1491                 bb_perror_msg("nfs bindresvport");
1492                 goto fail;
1493         }
1494         if (port == 0) {
1495                 server_addr.sin_port = PMAPPORT;
1496                 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1497                                         tcp ? IPPROTO_TCP : IPPROTO_UDP);
1498                 if (port == 0)
1499                         port = NFS_PORT;
1500         }
1501         server_addr.sin_port = htons(port);
1502
1503         /* Prepare data structure for kernel */
1504         data.fd = fsock;
1505         memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1506         strncpy(data.hostname, hostname, sizeof(data.hostname));
1507
1508         /* Clean up */
1509         auth_destroy(mclient->cl_auth);
1510         clnt_destroy(mclient);
1511         close(msock);
1512         msock = -1;
1513
1514         if (bg) {
1515                 /* We must wait until mount directory is available */
1516                 struct stat statbuf;
1517                 int delay = 1;
1518                 while (stat(mp->mnt_dir, &statbuf) == -1) {
1519                         if (!daemonized) {
1520                                 daemonized = daemonize();
1521                                 if (daemonized <= 0) { /* parent or error */
1522 /* FIXME: parent doesn't close fsock - ??! */
1523                                         retval = -daemonized;
1524                                         goto ret;
1525                                 }
1526                         }
1527                         sleep(delay);   /* 1, 2, 4, 8, 16, 30, ... */
1528                         delay *= 2;
1529                         if (delay > 30)
1530                                 delay = 30;
1531                 }
1532         }
1533
1534         /* Perform actual mount */
1535  do_mount:
1536         mp->mnt_type = (char*)"nfs";
1537         retval = mount_it_now(mp, vfsflags, (char*)&data);
1538         goto ret;
1539
1540         /* Abort */
1541  fail:
1542         if (msock >= 0) {
1543                 if (mclient) {
1544                         auth_destroy(mclient->cl_auth);
1545                         clnt_destroy(mclient);
1546                 }
1547                 close(msock);
1548         }
1549         if (fsock >= 0)
1550                 close(fsock);
1551
1552  ret:
1553         free(hostname);
1554         free(mounthost);
1555         free(filteropts);
1556         return retval;
1557 }
1558
1559 #else // !ENABLE_FEATURE_MOUNT_NFS
1560
1561 // Never called. Call should be optimized out.
1562 int nfsmount(struct mntent *mp, long vfsflags, char *filteropts);
1563
1564 #endif // !ENABLE_FEATURE_MOUNT_NFS
1565
1566 // Mount one directory.  Handles CIFS, NFS, loopback, autobind, and filesystem
1567 // type detection.  Returns 0 for success, nonzero for failure.
1568 // NB: mp->xxx fields may be trashed on exit
1569 static int singlemount(struct mntent *mp, int ignore_busy)
1570 {
1571         int rc = -1;
1572         long vfsflags;
1573         char *loopFile = 0, *filteropts = 0;
1574         llist_t *fl = 0;
1575         struct stat st;
1576
1577         vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1578
1579         // Treat fstype "auto" as unspecified
1580         if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1581                 mp->mnt_type = NULL;
1582
1583         // Might this be a virtual filesystem?
1584         if (ENABLE_FEATURE_MOUNT_HELPERS
1585          && (strchr(mp->mnt_fsname, '#'))
1586         ) {
1587                 char *s, *p, *args[35];
1588                 int n = 0;
1589 // FIXME: does it allow execution of arbitrary commands?!
1590 // What args[0] can end up with?
1591                 for (s = p = mp->mnt_fsname; *s && n < 35-3; ++s) {
1592                         if (s[0] == '#' && s[1] != '#') {
1593                                 *s = '\0';
1594                                 args[n++] = p;
1595                                 p = s + 1;
1596                         }
1597                 }
1598                 args[n++] = p;
1599                 args[n++] = mp->mnt_dir;
1600                 args[n] = NULL;
1601                 rc = wait4pid(xspawn(args));
1602                 goto report_error;
1603         }
1604
1605         // Might this be an CIFS filesystem?
1606         if (ENABLE_FEATURE_MOUNT_CIFS
1607          && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
1608          && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
1609          && mp->mnt_fsname[0] == mp->mnt_fsname[1]
1610         ) {
1611 #if 0 /* reported to break things */
1612                 len_and_sockaddr *lsa;
1613                 char *ip, *dotted;
1614                 char *s;
1615
1616                 // Replace '/' with '\' and verify that unc points to "//server/share".
1617                 for (s = mp->mnt_fsname; *s; ++s)
1618                         if (*s == '/') *s = '\\';
1619
1620                 // Get server IP
1621                 s = strrchr(mp->mnt_fsname, '\\');
1622                 if (s <= mp->mnt_fsname+1)
1623                         goto report_error;
1624                 *s = '\0';
1625                 lsa = host2sockaddr(mp->mnt_fsname+2, 0);
1626                 *s = '\\';
1627                 if (!lsa)
1628                         goto report_error;
1629
1630                 // Insert ip=... option into string flags.
1631                 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1632                 ip = xasprintf("ip=%s", dotted);
1633                 parse_mount_options(ip, &filteropts);
1634
1635                 // Compose new unc '\\server-ip\share'
1636                 // (s => slash after hostname)
1637                 mp->mnt_fsname = xasprintf("\\\\%s%s", dotted, s);
1638 #endif
1639                 // Lock is required [why?]
1640                 vfsflags |= MS_MANDLOCK;
1641                 mp->mnt_type = (char*)"cifs";
1642                 rc = mount_it_now(mp, vfsflags, filteropts);
1643 #if 0
1644                 if (ENABLE_FEATURE_CLEAN_UP) {
1645                         free(mp->mnt_fsname);
1646                         free(ip);
1647                         free(dotted);
1648                         free(lsa);
1649                 }
1650 #endif
1651                 goto report_error;
1652         }
1653
1654         // Might this be an NFS filesystem?
1655         if (ENABLE_FEATURE_MOUNT_NFS
1656          && (!mp->mnt_type || !strcmp(mp->mnt_type, "nfs"))
1657          && strchr(mp->mnt_fsname, ':') != NULL
1658         ) {
1659                 rc = nfsmount(mp, vfsflags, filteropts);
1660                 goto report_error;
1661         }
1662
1663         // Look at the file.  (Not found isn't a failure for remount, or for
1664         // a synthetic filesystem like proc or sysfs.)
1665         // (We use stat, not lstat, in order to allow
1666         // mount symlink_to_file_or_blkdev dir)
1667         if (!stat(mp->mnt_fsname, &st)
1668          && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1669         ) {
1670                 // Do we need to allocate a loopback device for it?
1671                 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1672                         loopFile = bb_simplify_path(mp->mnt_fsname);
1673                         mp->mnt_fsname = NULL; // will receive malloced loop dev name
1674                         if (set_loop(&(mp->mnt_fsname), loopFile, 0) < 0) {
1675                                 if (errno == EPERM || errno == EACCES)
1676                                         bb_error_msg(bb_msg_perm_denied_are_you_root);
1677                                 else
1678                                         bb_perror_msg("cannot setup loop device");
1679                                 return errno;
1680                         }
1681
1682                 // Autodetect bind mounts
1683                 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1684                         vfsflags |= MS_BIND;
1685         }
1686
1687         // If we know the fstype (or don't need to), jump straight
1688         // to the actual mount.
1689         if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1690                 rc = mount_it_now(mp, vfsflags, filteropts);
1691         else {
1692                 // Loop through filesystem types until mount succeeds
1693                 // or we run out
1694
1695                 // Initialize list of block backed filesystems.  This has to be
1696                 // done here so that during "mount -a", mounts after /proc shows up
1697                 // can autodetect.
1698                 if (!fslist) {
1699                         fslist = get_block_backed_filesystems();
1700                         if (ENABLE_FEATURE_CLEAN_UP && fslist)
1701                                 atexit(delete_block_backed_filesystems);
1702                 }
1703
1704                 for (fl = fslist; fl; fl = fl->link) {
1705                         mp->mnt_type = fl->data;
1706                         rc = mount_it_now(mp, vfsflags, filteropts);
1707                         if (!rc) break;
1708                 }
1709         }
1710
1711         // If mount failed, clean up loop file (if any).
1712         if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1713                 del_loop(mp->mnt_fsname);
1714                 if (ENABLE_FEATURE_CLEAN_UP) {
1715                         free(loopFile);
1716                         free(mp->mnt_fsname);
1717                 }
1718         }
1719
1720  report_error:
1721         if (ENABLE_FEATURE_CLEAN_UP)
1722                 free(filteropts);
1723
1724         if (errno == EBUSY && ignore_busy)
1725                 return 0;
1726         if (rc < 0)
1727                 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
1728         return rc;
1729 }
1730
1731 /* -O support
1732  * Unlike -t, -O should interpret "no" prefix differently:
1733  * -t noa,b,c = -t no(a,b,c) = mount all except fs'es with types a,b, and c
1734  * -O noa,b,c = -O noa,b,c = mount all with without option a,
1735  * or with option b or c.
1736  * But for now we do not support -O a,b,c at all (only -O a).
1737  *
1738  * Another difference from -t support (match_fstype) is that
1739  * we need to examine the _list_ of options in fsopt, not just a string.
1740  */
1741 static int match_opt(const char *fs_opt, const char *O_opt)
1742 {
1743         int match = 1;
1744         int len;
1745
1746         if (!O_opt)
1747                 return match;
1748
1749         if (O_opt[0] == 'n' && O_opt[1] == 'o') {
1750                 match--;
1751                 O_opt += 2;
1752         }
1753
1754         len = strlen(O_opt);
1755         while (1) {
1756                 if (strncmp(fs_opt, O_opt, len) == 0
1757                  && (fs_opt[len] == '\0' || fs_opt[len] == ',')
1758                 ) {
1759                         return match;
1760                 }
1761                 fs_opt = strchr(fs_opt, ',');
1762                 if (!fs_opt)
1763                         break;
1764                 fs_opt++;
1765         }
1766
1767         return !match;
1768 }
1769
1770 // Parse options, if necessary parse fstab/mtab, and call singlemount for
1771 // each directory to be mounted.
1772 static const char must_be_root[] ALIGN1 = "you must be root";
1773
1774 int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1775 int mount_main(int argc UNUSED_PARAM, char **argv)
1776 {
1777         char *cmdopts = xzalloc(1);
1778         char *fstype = NULL;
1779         char *O_optmatch = NULL;
1780         char *storage_path;
1781         llist_t *lst_o = NULL;
1782         const char *fstabname;
1783         FILE *fstab;
1784         int i, j, rc = 0;
1785         unsigned opt;
1786         struct mntent mtpair[2], *mtcur = mtpair;
1787         IF_NOT_DESKTOP(const int nonroot = 0;)
1788
1789         IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
1790
1791         // Parse long options, like --bind and --move.  Note that -o option
1792         // and --option are synonymous.  Yes, this means --remount,rw works.
1793         for (i = j = 1; argv[i]; i++) {
1794                 if (argv[i][0] == '-' && argv[i][1] == '-')
1795                         append_mount_options(&cmdopts, argv[i] + 2);
1796                 else
1797                         argv[j++] = argv[i];
1798         }
1799         argv[j] = NULL;
1800
1801         // Parse remaining options
1802         // Max 2 params; -o is a list, -v is a counter
1803         opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv");
1804         opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch
1805                         IF_FEATURE_MOUNT_VERBOSE(, &verbose));
1806         while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
1807         if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
1808         if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
1809         argv += optind;
1810
1811         // If we have no arguments, show currently mounted filesystems
1812         if (!argv[0]) {
1813                 if (!(opt & OPT_a)) {
1814                         FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1815
1816                         if (!mountTable)
1817                                 bb_error_msg_and_die("no %s", bb_path_mtab_file);
1818
1819                         while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
1820                                                                 GETMNTENT_BUFSIZE))
1821                         {
1822                                 // Don't show rootfs. FIXME: why??
1823                                 // util-linux 2.12a happily shows rootfs...
1824                                 //if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
1825
1826                                 if (!fstype || !strcmp(mtpair->mnt_type, fstype))
1827                                         printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1828                                                         mtpair->mnt_dir, mtpair->mnt_type,
1829                                                         mtpair->mnt_opts);
1830                         }
1831                         if (ENABLE_FEATURE_CLEAN_UP)
1832                                 endmntent(mountTable);
1833                         return EXIT_SUCCESS;
1834                 }
1835                 storage_path = NULL;
1836         } else {
1837                 // When we have two arguments, the second is the directory and we can
1838                 // skip looking at fstab entirely.  We can always abspath() the directory
1839                 // argument when we get it.
1840                 if (argv[1]) {
1841                         if (nonroot)
1842                                 bb_error_msg_and_die(must_be_root);
1843                         mtpair->mnt_fsname = argv[0];
1844                         mtpair->mnt_dir = argv[1];
1845                         mtpair->mnt_type = fstype;
1846                         mtpair->mnt_opts = cmdopts;
1847                         resolve_mount_spec(&mtpair->mnt_fsname);
1848                         rc = singlemount(mtpair, 0);
1849                         return rc;
1850                 }
1851                 storage_path = bb_simplify_path(argv[0]); // malloced
1852         }
1853
1854         // Past this point, we are handling either "mount -a [opts]"
1855         // or "mount [opts] single_param"
1856
1857         i = parse_mount_options(cmdopts, NULL); // FIXME: should be "long", not "int"
1858         if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags
1859                 bb_error_msg_and_die(must_be_root);
1860
1861         // If we have a shared subtree flag, don't worry about fstab or mtab.
1862         if (ENABLE_FEATURE_MOUNT_FLAGS
1863          && (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
1864         ) {
1865                 // verbose_mount(source, target, type, flags, data)
1866                 rc = verbose_mount("", argv[0], "", i, "");
1867                 if (rc)
1868                         bb_simple_perror_msg_and_die(argv[0]);
1869                 return rc;
1870         }
1871
1872         // Open either fstab or mtab
1873         fstabname = "/etc/fstab";
1874         if (i & MS_REMOUNT) {
1875                 // WARNING. I am not sure this matches util-linux's
1876                 // behavior. It's possible util-linux does not
1877                 // take -o opts from mtab (takes only mount source).
1878                 fstabname = bb_path_mtab_file;
1879         }
1880         fstab = setmntent(fstabname, "r");
1881         if (!fstab)
1882                 bb_perror_msg_and_die("cannot read %s", fstabname);
1883
1884         // Loop through entries until we find what we're looking for
1885         memset(mtpair, 0, sizeof(mtpair));
1886         for (;;) {
1887                 struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
1888
1889                 // Get next fstab entry
1890                 if (!getmntent_r(fstab, mtcur, getmntent_buf
1891                                         + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
1892                                 GETMNTENT_BUFSIZE/2)
1893                 ) { // End of fstab/mtab is reached
1894                         mtcur = mtother; // the thing we found last time
1895                         break;
1896                 }
1897
1898                 // If we're trying to mount something specific and this isn't it,
1899                 // skip it.  Note we must match the exact text in fstab (ala
1900                 // "proc") or a full path from root
1901                 if (argv[0]) {
1902
1903                         // Is this what we're looking for?
1904                         if (strcmp(argv[0], mtcur->mnt_fsname) &&
1905                            strcmp(storage_path, mtcur->mnt_fsname) &&
1906                            strcmp(argv[0], mtcur->mnt_dir) &&
1907                            strcmp(storage_path, mtcur->mnt_dir)) continue;
1908
1909                         // Remember this entry.  Something later may have
1910                         // overmounted it, and we want the _last_ match.
1911                         mtcur = mtother;
1912
1913                 // If we're mounting all
1914                 } else {
1915                         // No, mount -a won't mount anything,
1916                         // even user mounts, for mere humans
1917                         if (nonroot)
1918                                 bb_error_msg_and_die(must_be_root);
1919
1920                         // Does type match? (NULL matches always)
1921                         if (!match_fstype(mtcur, fstype))
1922                                 continue;
1923
1924                         // Skip noauto and swap anyway.
1925                         if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
1926                         // swap is bogus "fstype", parse_mount_options can't check fstypes
1927                          || strcasecmp(mtcur->mnt_type, "swap") == 0
1928                         ) {
1929                                 continue;
1930                         }
1931
1932                         // Does (at least one) option match?
1933                         // (NULL matches always)
1934                         if (!match_opt(mtcur->mnt_opts, O_optmatch))
1935                                 continue;
1936
1937                         resolve_mount_spec(&mtcur->mnt_fsname);
1938
1939                         // NFS mounts want this to be xrealloc-able
1940                         mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
1941
1942                         // Mount this thing
1943                         if (singlemount(mtcur, 1)) {
1944                                 // Count number of failed mounts
1945                                 rc++;
1946                         }
1947                         free(mtcur->mnt_opts);
1948                 }
1949         }
1950
1951         // End of fstab/mtab is reached.
1952         // Were we looking for something specific?
1953         if (argv[0]) {
1954                 long l;
1955
1956                 // If we didn't find anything, complain
1957                 if (!mtcur->mnt_fsname)
1958                         bb_error_msg_and_die("can't find %s in %s",
1959                                 argv[0], fstabname);
1960
1961                 // What happens when we try to "mount swap_partition"?
1962                 // (fstab containts "swap_partition swap swap defaults 0 0")
1963                 // util-linux-ng 2.13.1 does this:
1964                 // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
1965                 // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
1966                 // lstat("swap", 0x7fff62a3a640)           = -1 ENOENT (No such file or directory)
1967                 // write(2, "mount: mount point swap does not exist\n", 39) = 39
1968                 // exit_group(32)                          = ?
1969 #if 0
1970                 // In case we want to simply skip swap partitions:
1971                 l = parse_mount_options(mtcur->mnt_opts, NULL);
1972                 if ((l & MOUNT_SWAP)
1973                 // swap is bogus "fstype", parse_mount_options can't check fstypes
1974                  || strcasecmp(mtcur->mnt_type, "swap") == 0
1975                 ) {
1976                         goto ret;
1977                 }
1978 #endif
1979                 if (nonroot) {
1980                         // fstab must have "users" or "user"
1981                         l = parse_mount_options(mtcur->mnt_opts, NULL);
1982                         if (!(l & MOUNT_USERS))
1983                                 bb_error_msg_and_die(must_be_root);
1984                 }
1985
1986                 // Mount the last thing we found
1987                 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
1988                 append_mount_options(&(mtcur->mnt_opts), cmdopts);
1989                 resolve_mount_spec(&mtpair->mnt_fsname);
1990                 rc = singlemount(mtcur, 0);
1991                 if (ENABLE_FEATURE_CLEAN_UP)
1992                         free(mtcur->mnt_opts);
1993         }
1994
1995  //ret:
1996         if (ENABLE_FEATURE_CLEAN_UP)
1997                 endmntent(fstab);
1998         if (ENABLE_FEATURE_CLEAN_UP) {
1999                 free(storage_path);
2000                 free(cmdopts);
2001         }
2002         return rc;
2003 }