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