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