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