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 source tree.
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 
20 //config:config MOUNT
21 //config:	bool "mount (23 kb)"
22 //config:	default y
23 //config:	help
24 //config:	All files and filesystems in Unix are arranged into one big directory
25 //config:	tree. The 'mount' utility is used to graft a filesystem onto a
26 //config:	particular part of the tree. A filesystem can either live on a block
27 //config:	device, or it can be accessible over the network, as is the case with
28 //config:	NFS filesystems.
29 //config:
30 //config:config FEATURE_MOUNT_FAKE
31 //config:	bool "Support -f (fake mount)"
32 //config:	default y
33 //config:	depends on MOUNT
34 //config:	help
35 //config:	Enable support for faking a file system mount.
36 //config:
37 //config:config FEATURE_MOUNT_VERBOSE
38 //config:	bool "Support -v (verbose)"
39 //config:	default y
40 //config:	depends on MOUNT
41 //config:	help
42 //config:	Enable multi-level -v[vv...] verbose messages. Useful if you
43 //config:	debug mount problems and want to see what is exactly passed
44 //config:	to the kernel.
45 //config:
46 //config:config FEATURE_MOUNT_HELPERS
47 //config:	bool "Support mount helpers"
48 //config:	default n
49 //config:	depends on MOUNT
50 //config:	help
51 //config:	Enable mounting of virtual file systems via external helpers.
52 //config:	E.g. "mount obexfs#-b00.11.22.33.44.55 /mnt" will in effect call
53 //config:	"obexfs -b00.11.22.33.44.55 /mnt"
54 //config:	Also "mount -t sometype [-o opts] fs /mnt" will try
55 //config:	"sometype [-o opts] fs /mnt" if simple mount syscall fails.
56 //config:	The idea is to use such virtual filesystems in /etc/fstab.
57 //config:
58 //config:config FEATURE_MOUNT_LABEL
59 //config:	bool "Support specifying devices by label or UUID"
60 //config:	default y
61 //config:	depends on MOUNT
62 //config:	select VOLUMEID
63 //config:	help
64 //config:	This allows for specifying a device by label or uuid, rather than by
65 //config:	name. This feature utilizes the same functionality as blkid/findfs.
66 //config:
67 //config:config FEATURE_MOUNT_NFS
68 //config:	bool "Support mounting NFS file systems on Linux < 2.6.23"
69 //config:	default n
70 //config:	depends on MOUNT
71 //config:	select FEATURE_SYSLOG
72 //config:	help
73 //config:	Enable mounting of NFS file systems on Linux kernels prior
74 //config:	to version 2.6.23. Note that in this case mounting of NFS
75 //config:	over IPv6 will not be possible.
76 //config:
77 //config:	Note that this option links in RPC support from libc,
78 //config:	which is rather large (~10 kbytes on uclibc).
79 //config:
80 //config:config FEATURE_MOUNT_CIFS
81 //config:	bool "Support mounting CIFS/SMB file systems"
82 //config:	default y
83 //config:	depends on MOUNT
84 //config:	help
85 //config:	Enable support for samba mounts.
86 //config:
87 //config:config FEATURE_MOUNT_FLAGS
88 //config:	depends on MOUNT
89 //config:	bool "Support lots of -o flags"
90 //config:	default y
91 //config:	help
92 //config:	Without this, mount only supports ro/rw/remount. With this, it
93 //config:	supports nosuid, suid, dev, nodev, exec, noexec, sync, async, atime,
94 //config:	noatime, diratime, nodiratime, loud, bind, move, shared, slave,
95 //config:	private, unbindable, rshared, rslave, rprivate, and runbindable.
96 //config:
97 //config:config FEATURE_MOUNT_FSTAB
98 //config:	depends on MOUNT
99 //config:	bool "Support /etc/fstab and -a (mount all)"
100 //config:	default y
101 //config:	help
102 //config:	Support mount all and looking for files in /etc/fstab.
103 //config:
104 //config:config FEATURE_MOUNT_OTHERTAB
105 //config:	depends on FEATURE_MOUNT_FSTAB
106 //config:	bool "Support -T <alt_fstab>"
107 //config:	default y
108 //config:	help
109 //config:	Support mount -T (specifying an alternate fstab)
110 
111 /* On full-blown systems, requires suid for user mounts.
112  * But it's not unthinkable to have it available in non-suid flavor on some systems,
113  * for viewing mount table.
114  * Therefore we use BB_SUID_MAYBE instead of BB_SUID_REQUIRE: */
115 //applet:IF_MOUNT(APPLET(mount, BB_DIR_BIN, IF_DESKTOP(BB_SUID_MAYBE) IF_NOT_DESKTOP(BB_SUID_DROP)))
116 
117 //kbuild:lib-$(CONFIG_MOUNT) += mount.o
118 
119 //usage:#define mount_trivial_usage
120 //usage:       "[OPTIONS] [-o OPT] DEVICE NODE"
121 //usage:#define mount_full_usage "\n\n"
122 //usage:       "Mount a filesystem. Filesystem autodetection requires /proc.\n"
123 //usage:     "\n	-a		Mount all filesystems in fstab"
124 //usage:	IF_FEATURE_MOUNT_FAKE(
125 //usage:	IF_FEATURE_MTAB_SUPPORT(
126 //usage:     "\n	-f		Update /etc/mtab, but don't mount"
127 //usage:	)
128 //usage:	IF_NOT_FEATURE_MTAB_SUPPORT(
129 //usage:     "\n	-f		Dry run"
130 //usage:	)
131 //usage:	)
132 //usage:	IF_FEATURE_MOUNT_HELPERS(
133 //usage:     "\n	-i		Don't run mount helper"
134 //usage:	)
135 //usage:	IF_FEATURE_MTAB_SUPPORT(
136 //usage:     "\n	-n		Don't update /etc/mtab"
137 //usage:	)
138 //usage:	IF_FEATURE_MOUNT_VERBOSE(
139 //usage:     "\n	-v		Verbose"
140 //usage:	)
141 ////usage:   "\n	-s		Sloppy (ignored)"
142 //usage:     "\n	-r		Read-only mount"
143 ////usage:     "\n	-w		Read-write mount (default)"
144 //usage:     "\n	-t FSTYPE[,...]	Filesystem type(s)"
145 //usage:	IF_FEATURE_MOUNT_OTHERTAB(
146 //usage:     "\n	-T FILE		Read FILE instead of /etc/fstab"
147 //usage:	)
148 //usage:     "\n	-O OPT		Mount only filesystems with option OPT (-a only)"
149 //usage:     "\n-o OPT:"
150 //usage:	IF_FEATURE_MOUNT_LOOP(
151 //usage:     "\n	loop		Ignored (loop devices are autodetected)"
152 //usage:	)
153 //usage:	IF_FEATURE_MOUNT_FLAGS(
154 //usage:     "\n	[a]sync		Writes are [a]synchronous"
155 //usage:     "\n	[no]atime	Disable/enable updates to inode access times"
156 //usage:     "\n	[no]diratime	Disable/enable atime updates to directories"
157 //usage:     "\n	[no]relatime	Disable/enable atime updates relative to modification time"
158 //usage:     "\n	[no]dev		(Dis)allow use of special device files"
159 //usage:     "\n	[no]exec	(Dis)allow use of executable files"
160 //usage:     "\n	[no]suid	(Dis)allow set-user-id-root programs"
161 //usage:     "\n	[r]shared	Convert [recursively] to a shared subtree"
162 //usage:     "\n	[r]slave	Convert [recursively] to a slave subtree"
163 //usage:     "\n	[r]private	Convert [recursively] to a private subtree"
164 //usage:     "\n	[un]bindable	Make mount point [un]able to be bind mounted"
165 //usage:     "\n	[r]bind		Bind a file or directory [recursively] to another location"
166 //usage:     "\n	move		Relocate an existing mount point"
167 //usage:	)
168 //usage:     "\n	remount		Remount a mounted filesystem, changing flags"
169 //usage:     "\n	ro		Same as -r"
170 //usage:     "\n"
171 //usage:     "\nThere are filesystem-specific -o flags."
172 //usage:
173 //usage:#define mount_example_usage
174 //usage:       "$ mount\n"
175 //usage:       "/dev/hda3 on / type minix (rw)\n"
176 //usage:       "proc on /proc type proc (rw)\n"
177 //usage:       "devpts on /dev/pts type devpts (rw)\n"
178 //usage:       "$ mount /dev/fd0 /mnt -t msdos -o ro\n"
179 //usage:       "$ mount /tmp/diskimage /opt -t ext2 -o loop\n"
180 //usage:       "$ mount cd_image.iso mydir\n"
181 //usage:#define mount_notes_usage
182 //usage:       "Returns 0 for success, number of failed mounts for -a, or errno for one mount."
183 
184 #include <mntent.h>
185 #if ENABLE_FEATURE_SYSLOG
186 #include <syslog.h>
187 #endif
188 #include <sys/mount.h>
189 // Grab more as needed from util-linux's mount/mount_constants.h
190 #ifndef MS_DIRSYNC
191 # define MS_DIRSYNC     (1 << 7) // Directory modifications are synchronous
192 #endif
193 #ifndef MS_NOSYMFOLLOW
194 # define MS_NOSYMFOLLOW (1 << 8)
195 #endif
196 #ifndef MS_BIND
197 # define MS_BIND        (1 << 12)
198 #endif
199 #ifndef MS_MOVE
200 # define MS_MOVE        (1 << 13)
201 #endif
202 #ifndef MS_RECURSIVE
203 # define MS_RECURSIVE   (1 << 14)
204 #endif
205 #ifndef MS_SILENT
206 # define MS_SILENT      (1 << 15)
207 #endif
208 // The shared subtree stuff, which went in around 2.6.15
209 #ifndef MS_UNBINDABLE
210 # define MS_UNBINDABLE  (1 << 17)
211 #endif
212 #ifndef MS_PRIVATE
213 # define MS_PRIVATE     (1 << 18)
214 #endif
215 #ifndef MS_SLAVE
216 # define MS_SLAVE       (1 << 19)
217 #endif
218 #ifndef MS_SHARED
219 # define MS_SHARED      (1 << 20)
220 #endif
221 
222 #ifndef MS_RELATIME
223 # define MS_RELATIME    (1 << 21)
224 #endif
225 #ifndef MS_STRICTATIME
226 # define MS_STRICTATIME (1 << 24)
227 #endif
228 #ifndef MS_LAZYTIME
229 # define MS_LAZYTIME    (1 << 25)
230 #endif
231 
232 /* Any ~MS_FOO value has this bit set: */
233 #define BB_MS_INVERTED_VALUE (1u << 31)
234 
235 #include "libbb.h"
236 #include "common_bufsiz.h"
237 #if ENABLE_FEATURE_MOUNT_LABEL
238 # include "volume_id.h"
239 #else
240 # define resolve_mount_spec(fsname) ((void)0)
241 #endif
242 
243 // Needed for nfs support only
244 #include <sys/utsname.h>
245 #undef TRUE
246 #undef FALSE
247 #if ENABLE_FEATURE_MOUNT_NFS
248 /* This is just a warning of a common mistake.  Possibly this should be a
249  * uclibc faq entry rather than in busybox... */
250 # if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
251 #  warning "You probably need to build uClibc with UCLIBC_HAS_RPC for NFS support"
252    /* not #error, since user may be using e.g. libtirpc instead.
253     * This might work:
254     * CONFIG_EXTRA_CFLAGS="-I/usr/include/tirpc"
255     * CONFIG_EXTRA_LDLIBS="tirpc"
256     */
257 # endif
258 # include <rpc/rpc.h>
259 # include <rpc/pmap_prot.h>
260 # include <rpc/pmap_clnt.h>
261 #endif
262 
263 
264 #if defined(__dietlibc__)
265 // 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
266 // dietlibc-0.30 does not have implementation of getmntent_r()
getmntent_r(FILE * stream,struct mntent * result,char * buffer UNUSED_PARAM,int bufsize UNUSED_PARAM)267 static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
268 		char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM)
269 {
270 	struct mntent* ment = getmntent(stream);
271 	return memcpy(result, ment, sizeof(*ment));
272 }
273 #endif
274 
275 
276 // Not real flags, but we want to be able to check for this.
277 enum {
278 	MOUNT_USERS  = (1 << 27) * ENABLE_DESKTOP,
279 	MOUNT_NOFAIL = (1 << 28) * ENABLE_DESKTOP,
280 	MOUNT_NOAUTO = (1 << 29),
281 	MOUNT_SWAP   = (1 << 30),
282 	MOUNT_FAKEFLAGS = MOUNT_USERS | MOUNT_NOFAIL | MOUNT_NOAUTO | MOUNT_SWAP
283 };
284 
285 
286 #define OPTION_STR "o:*t:rwanfvsiO:" IF_FEATURE_MOUNT_OTHERTAB("T:")
287 enum {
288 	OPT_o = (1 << 0),
289 	OPT_t = (1 << 1),
290 	OPT_r = (1 << 2),
291 	OPT_w = (1 << 3),
292 	OPT_a = (1 << 4),
293 	OPT_n = (1 << 5),
294 	OPT_f = (1 << 6),
295 	OPT_v = (1 << 7),
296 	OPT_s = (1 << 8),
297 	OPT_i = (1 << 9),
298 	OPT_O = (1 << 10),
299 	OPT_T = (1 << 11),
300 };
301 
302 #if ENABLE_FEATURE_MTAB_SUPPORT
303 #define USE_MTAB (!(option_mask32 & OPT_n))
304 #else
305 #define USE_MTAB 0
306 #endif
307 
308 #if ENABLE_FEATURE_MOUNT_FAKE
309 #define FAKE_IT (option_mask32 & OPT_f)
310 #else
311 #define FAKE_IT 0
312 #endif
313 
314 #if ENABLE_FEATURE_MOUNT_HELPERS
315 #define HELPERS_ALLOWED (!(option_mask32 & OPT_i))
316 #else
317 #define HELPERS_ALLOWED 0
318 #endif
319 
320 
321 // TODO: more "user" flag compatibility.
322 // "user" option (from mount manpage):
323 // Only the user that mounted a filesystem can unmount it again.
324 // If any user should be able to unmount, then use users instead of user
325 // in the fstab line.  The owner option is similar to the user option,
326 // with the restriction that the user must be the owner of the special file.
327 // This may be useful e.g. for /dev/fd if a login script makes
328 // the console user owner of this device.
329 
330 // Standard mount options (from -o options or --options),
331 // with corresponding flags
332 static const int32_t mount_options[] ALIGN4 = {
333 	// MS_FLAGS set a bit.  ~MS_FLAGS disable that bit.  0 flags are NOPs.
334 
335 	IF_FEATURE_MOUNT_LOOP(
336 		/* "loop" */ 0,
337 	)
338 
339 	IF_FEATURE_MOUNT_FSTAB(
340 		/* "defaults" */ 0,
341 		/* "quiet" 0 - do not filter out, vfat wants to see it */
342 		/* "noauto" */ MOUNT_NOAUTO,
343 		/* "sw"     */ MOUNT_SWAP,
344 		/* "swap"   */ MOUNT_SWAP,
345 		IF_DESKTOP(/* "user"  */ MOUNT_USERS,)
346 		IF_DESKTOP(/* "users" */ MOUNT_USERS,)
347 		IF_DESKTOP(/* "nofail" */ MOUNT_NOFAIL,)
348 		/* "_netdev" */ 0,
349 		IF_DESKTOP(/* "comment=" */ 0,) /* systemd uses this in fstab */
350 	)
351 
352 	IF_FEATURE_MOUNT_FLAGS(
353 		// vfs flags
354 		/* "nosuid"      */ MS_NOSUID,
355 		/* "suid"        */ ~MS_NOSUID,
356 		/* "dev"         */ ~MS_NODEV,
357 		/* "nodev"       */ MS_NODEV,
358 		/* "exec"        */ ~MS_NOEXEC,
359 		/* "noexec"      */ MS_NOEXEC,
360 		/* "sync"        */ MS_SYNCHRONOUS,
361 		/* "dirsync"     */ MS_DIRSYNC,
362 		/* "async"       */ ~MS_SYNCHRONOUS,
363 		/* "atime"       */ ~MS_NOATIME,
364 		/* "noatime"     */ MS_NOATIME,
365 		/* "diratime"    */ ~MS_NODIRATIME,
366 		/* "nodiratime"  */ MS_NODIRATIME,
367 		/* "relatime"    */ MS_RELATIME,
368 		/* "norelatime"  */ ~MS_RELATIME,
369 		/* "strictatime" */ MS_STRICTATIME,
370 		/* "nostrictatime"*/ ~MS_STRICTATIME,
371 		/* "lazytime"    */ MS_LAZYTIME,
372 		/* "nolazytime"  */ ~MS_LAZYTIME,
373 		/* "nosymfollow" */ MS_NOSYMFOLLOW,
374 		/* "mand"        */ MS_MANDLOCK,
375 		/* "nomand"      */ ~MS_MANDLOCK,
376 		/* "loud"      	 */ ~MS_SILENT,
377 
378 		// action flags
379 		/* "rbind"       */ MS_BIND|MS_RECURSIVE,
380 		/* "bind"        */ MS_BIND,
381 		/* "move"        */ MS_MOVE,
382 		/* "shared"      */ MS_SHARED,
383 		/* "slave"       */ MS_SLAVE,
384 		/* "private"     */ MS_PRIVATE,
385 		/* "unbindable"  */ MS_UNBINDABLE,
386 		/* "rshared"     */ MS_SHARED|MS_RECURSIVE,
387 		/* "rslave"      */ MS_SLAVE|MS_RECURSIVE,
388 		/* "rprivate"    */ MS_PRIVATE|MS_RECURSIVE,
389 		/* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
390 	)
391 
392 	// Always understood.
393 	/* "ro"      */ MS_RDONLY,  // vfs flag
394 	/* "rw"      */ ~MS_RDONLY, // vfs flag
395 	/* "remount" */ MS_REMOUNT  // action flag
396 };
397 
398 static const char mount_option_str[] ALIGN1 =
399 	IF_FEATURE_MOUNT_LOOP(
400 		"loop\0"
401 	)
402 	IF_FEATURE_MOUNT_FSTAB(
403 		"defaults\0"
404 		// "quiet\0" - do not filter out, vfat wants to see it
405 		"noauto\0"
406 		"sw\0"
407 		"swap\0"
408 		IF_DESKTOP("user\0")
409 		IF_DESKTOP("users\0")
410 		IF_DESKTOP("nofail\0")
411 		"_netdev\0"
412 		IF_DESKTOP("comment=\0") /* systemd uses this in fstab */
413 	)
414 	IF_FEATURE_MOUNT_FLAGS(
415 		// vfs flags
416 		"nosuid"       "\0"
417 		"suid"         "\0"
418 		"dev"          "\0"
419 		"nodev"        "\0"
420 		"exec"         "\0"
421 		"noexec"       "\0"
422 		"sync"         "\0"
423 		"dirsync"      "\0"
424 		"async"        "\0"
425 		"atime"        "\0"
426 		"noatime"      "\0"
427 		"diratime"     "\0"
428 		"nodiratime"   "\0"
429 		"relatime"     "\0"
430 		"norelatime"   "\0"
431 		"strictatime"  "\0"
432 		"nostrictatime""\0"
433 		"lazytime"     "\0"
434 		"nolazytime"   "\0"
435 		"nosymfollow"  "\0"
436 		"mand"         "\0"
437 		"nomand"       "\0"
438 		"loud"         "\0"
439 
440 		// action flags
441 		"rbind\0"
442 		"bind\0"
443 		"move\0"
444 		"make-shared\0"
445 		"make-slave\0"
446 		"make-private\0"
447 		"make-unbindable\0"
448 		"make-rshared\0"
449 		"make-rslave\0"
450 		"make-rprivate\0"
451 		"make-runbindable\0"
452 	)
453 
454 	// Always understood.
455 	"ro\0"        // vfs flag
456 	"rw\0"        // vfs flag
457 	"remount\0"   // action flag
458 ;
459 
460 
461 struct globals {
462 #if ENABLE_FEATURE_MOUNT_NFS
463 	smalluint nfs_mount_version;
464 #endif
465 #if ENABLE_FEATURE_MOUNT_VERBOSE
466 	unsigned verbose;
467 #endif
468 	llist_t *fslist;
469 	char getmntent_buf[1];
470 } FIX_ALIASING;
471 enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
472 #define G (*(struct globals*)bb_common_bufsiz1)
473 #define nfs_mount_version (G.nfs_mount_version)
474 #if ENABLE_FEATURE_MOUNT_VERBOSE
475 #define verbose           (G.verbose          )
476 #else
477 #define verbose           0
478 #endif
479 #define fslist            (G.fslist           )
480 #define getmntent_buf     (G.getmntent_buf    )
481 #define INIT_G() do { setup_common_bufsiz(); } while (0)
482 
483 #if ENABLE_FEATURE_MTAB_SUPPORT
484 /*
485  * update_mtab_entry_on_move() is used to update entry in case of mount --move.
486  * we are looking for existing entries mnt_dir which is equal to mnt_fsname of
487  * input mntent and replace it by new one.
488  */
update_mtab_entry_on_move(const struct mntent * mp)489 static void FAST_FUNC update_mtab_entry_on_move(const struct mntent *mp)
490 {
491 	struct mntent *entries, *m;
492 	int i, count;
493 	FILE *mountTable;
494 
495 	mountTable = setmntent(bb_path_mtab_file, "r");
496 	if (!mountTable) {
497 		bb_simple_perror_msg(bb_path_mtab_file);
498 		return;
499 	}
500 
501 	entries = NULL;
502 	count = 0;
503 	while ((m = getmntent(mountTable)) != NULL) {
504 		entries = xrealloc_vector(entries, 3, count);
505 		entries[count].mnt_fsname = xstrdup(m->mnt_fsname);
506 		entries[count].mnt_dir = xstrdup(m->mnt_dir);
507 		entries[count].mnt_type = xstrdup(m->mnt_type);
508 		entries[count].mnt_opts = xstrdup(m->mnt_opts);
509 		entries[count].mnt_freq = m->mnt_freq;
510 		entries[count].mnt_passno = m->mnt_passno;
511 		count++;
512 	}
513 	endmntent(mountTable);
514 
515 	mountTable = setmntent(bb_path_mtab_file, "w");
516 	if (mountTable) {
517 		for (i = 0; i < count; i++) {
518 			if (strcmp(entries[i].mnt_dir, mp->mnt_fsname) != 0)
519 				addmntent(mountTable, &entries[i]);
520 			else
521 				addmntent(mountTable, mp);
522 		}
523 		endmntent(mountTable);
524 	} else if (errno != EROFS)
525 		bb_simple_perror_msg(bb_path_mtab_file);
526 
527 	if (ENABLE_FEATURE_CLEAN_UP) {
528 		for (i = 0; i < count; i++) {
529 			free(entries[i].mnt_fsname);
530 			free(entries[i].mnt_dir);
531 			free(entries[i].mnt_type);
532 			free(entries[i].mnt_opts);
533 		}
534 		free(entries);
535 	}
536 }
537 #endif
538 
539 #if ENABLE_FEATURE_MOUNT_VERBOSE
verbose_mount(const char * source,const char * target,const char * filesystemtype,unsigned long mountflags,const void * data)540 static int verbose_mount(const char *source, const char *target,
541 		const char *filesystemtype,
542 		unsigned long mountflags, const void *data)
543 {
544 	int rc;
545 
546 	errno = 0;
547 	rc = mount(source, target, filesystemtype, mountflags, data);
548 	if (verbose >= 2)
549 		bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
550 			source, target, filesystemtype,
551 			mountflags, (char*)data, rc);
552 	return rc;
553 }
554 #else
555 #define verbose_mount(...) mount(__VA_ARGS__)
556 #endif
557 
558 // Append mount options to string
append_mount_options(char ** oldopts,const char * newopts)559 static void append_mount_options(char **oldopts, const char *newopts)
560 {
561 	if (*oldopts && **oldopts) {
562 		// Do not insert options which are already there
563 		while (newopts[0]) {
564 			char *p;
565 			int len;
566 
567 			len = strchrnul(newopts, ',') - newopts;
568 			p = *oldopts;
569 			while (1) {
570 				if (!strncmp(p, newopts, len)
571 				 && (p[len] == ',' || p[len] == '\0'))
572 					goto skip;
573 				p = strchr(p,',');
574 				if (!p) break;
575 				p++;
576 			}
577 			p = xasprintf("%s,%.*s", *oldopts, len, newopts);
578 			free(*oldopts);
579 			*oldopts = p;
580  skip:
581 			newopts += len;
582 			while (*newopts == ',') newopts++;
583 		}
584 	} else {
585 		if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
586 		*oldopts = xstrdup(newopts);
587 	}
588 }
589 
590 // Use the mount_options list to parse options into flags.
591 // Also update list of unrecognized options if unrecognized != NULL
parse_mount_options(char * options,char ** unrecognized,uint32_t * opt)592 static unsigned long parse_mount_options(char *options, char **unrecognized, uint32_t *opt)
593 {
594 	unsigned long flags = MS_SILENT;
595 
596 	// Loop through options
597 	for (;;) {
598 		unsigned i;
599 		char *comma = strchr(options, ',');
600 		const char *option_str = mount_option_str;
601 
602 		if (comma) *comma = '\0';
603 
604 // FIXME: use hasmntopt()
605 		// Find this option in mount_options
606 		for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
607 			unsigned opt_len = strlen(option_str);
608 
609 			if (strncasecmp(option_str, options, opt_len) == 0
610 			 && (options[opt_len] == '\0'
611 			    /* or is it "comment=" thingy in fstab? */
612 			    IF_FEATURE_MOUNT_FSTAB(IF_DESKTOP( || option_str[opt_len-1] == '=' ))
613 			    )
614 			) {
615 				unsigned long fl = mount_options[i];
616 				if (fl & BB_MS_INVERTED_VALUE)
617 					flags &= fl;
618 				else
619 					flags |= fl;
620 				/* If we see "-o rw" on command line, it's the same as -w:
621 				 * "do not try to fall back to RO mounts"
622 				 */
623 				if (fl == ~MS_RDONLY && opt)
624 					(*opt) |= OPT_w;
625 				goto found;
626 			}
627 			option_str += opt_len + 1;
628 		}
629 		// We did not recognize this option.
630 		// If "unrecognized" is not NULL, append option there.
631 		// Note that we should not append *empty* option -
632 		// in this case we want to pass NULL, not "", to "data"
633 		// parameter of mount(2) syscall.
634 		// This is crucial for filesystems that don't accept
635 		// any arbitrary mount options, like cgroup fs:
636 		// "mount -t cgroup none /mnt"
637 		if (options[0] && unrecognized) {
638 			// Add it to strflags, to pass on to kernel
639 			char *p = *unrecognized;
640 			unsigned len = p ? strlen(p) : 0;
641 			*unrecognized = p = xrealloc(p, len + strlen(options) + 2);
642 
643 			// Comma separated if it's not the first one
644 			if (len) p[len++] = ',';
645 			strcpy(p + len, options);
646 		}
647  found:
648 		if (!comma)
649 			break;
650 		// Advance to next option
651 		*comma = ',';
652 		options = ++comma;
653 	}
654 
655 	return flags;
656 }
657 
658 // Return a list of all block device backed filesystems
get_block_backed_filesystems(void)659 static llist_t *get_block_backed_filesystems(void)
660 {
661 	static const char filesystems[2][sizeof("/proc/filesystems")] ALIGN1 = {
662 		"/etc/filesystems",
663 		"/proc/filesystems",
664 	};
665 	char *fs, *buf;
666 	llist_t *list = NULL;
667 	int i;
668 	FILE *f;
669 
670 	for (i = 0; i < 2; i++) {
671 		f = fopen_for_read(filesystems[i]);
672 		if (!f) continue;
673 
674 		while ((buf = xmalloc_fgetline(f)) != NULL) {
675 			if (is_prefixed_with(buf, "nodev") && isspace(buf[5]))
676 				goto next;
677 			fs = skip_whitespace(buf);
678 			if (*fs == '#' || *fs == '*' || !*fs)
679 				goto next;
680 
681 			llist_add_to_end(&list, xstrdup(fs));
682  next:
683 			free(buf);
684 		}
685 		if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
686 	}
687 
688 	return list;
689 }
690 
691 #if ENABLE_FEATURE_CLEAN_UP
delete_block_backed_filesystems(void)692 static void delete_block_backed_filesystems(void)
693 {
694 	llist_free(fslist, free);
695 }
696 #else
697 void delete_block_backed_filesystems(void);
698 #endif
699 
700 // Perform actual mount of specific filesystem at specific location.
701 // NB: mp->xxx fields may be trashed on exit
mount_it_now(struct mntent * mp,unsigned long vfsflags,char * filteropts)702 static int mount_it_now(struct mntent *mp, unsigned long vfsflags, char *filteropts)
703 {
704 	int rc = 0;
705 
706 	vfsflags &= ~(unsigned long)MOUNT_FAKEFLAGS;
707 
708 	if (FAKE_IT) {
709 		if (verbose >= 2)
710 			bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
711 				mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
712 				vfsflags, filteropts);
713 		goto mtab;
714 	}
715 
716 	// Mount, with fallback to read-only if necessary.
717 	for (;;) {
718 		errno = 0;
719 		rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
720 				vfsflags, filteropts);
721 		if (rc == 0)
722 			goto mtab; // success
723 
724 		// mount failed, try helper program
725 		// mount.<mnt_type>
726 		if (HELPERS_ALLOWED && mp->mnt_type) {
727 			char *args[8];
728 			int errno_save = errno;
729 			args[0] = xasprintf("mount.%s", mp->mnt_type);
730 			rc = 1;
731 			if (FAKE_IT)
732 				args[rc++] = (char *)"-f";
733 			if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB)
734 				args[rc++] = (char *)"-n";
735 			args[rc++] = mp->mnt_fsname;
736 			args[rc++] = mp->mnt_dir;
737 			if (filteropts) {
738 				args[rc++] = (char *)"-o";
739 				args[rc++] = filteropts;
740 			}
741 			args[rc] = NULL;
742 			rc = spawn_and_wait(args);
743 			free(args[0]);
744 			if (rc == 0)
745 				goto mtab; // success
746 			errno = errno_save;
747 		}
748 
749 		// Should we retry read-only mount?
750 		if (vfsflags & MS_RDONLY)
751 			break;		// no, already was tried
752 		if (option_mask32 & OPT_w)
753 			break;		// no, "mount -w" never falls back to RO
754 		if (errno != EACCES && errno != EROFS)
755 			break;		// no, error isn't hinting that RO may work
756 
757 		if (!(vfsflags & MS_SILENT))
758 			bb_error_msg("%s is write-protected, mounting read-only",
759 						mp->mnt_fsname);
760 		vfsflags |= MS_RDONLY;
761 	}
762 
763 	// Abort entirely if permission denied.
764 
765 	if (rc && errno == EPERM)
766 		bb_simple_error_msg_and_die(bb_msg_perm_denied_are_you_root);
767 
768 	// If the mount was successful, and we're maintaining an old-style
769 	// mtab file by hand, add the new entry to it now.
770  mtab:
771 	if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) {
772 		char *fsname;
773 		FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
774 		const char *option_str = mount_option_str;
775 		int i;
776 
777 		if (!mountTable) {
778 			bb_simple_perror_msg(bb_path_mtab_file);
779 			goto ret;
780 		}
781 
782 		// Add vfs string flags
783 		for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
784 			if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
785 				append_mount_options(&(mp->mnt_opts), option_str);
786 			option_str += strlen(option_str) + 1;
787 		}
788 
789 		// Remove trailing / (if any) from directory we mounted on
790 		i = strlen(mp->mnt_dir) - 1;
791 		while (i > 0 && mp->mnt_dir[i] == '/')
792 			mp->mnt_dir[i--] = '\0';
793 
794 		// Convert to canonical pathnames as needed
795 		mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
796 		fsname = NULL;
797 		if (!mp->mnt_type || !*mp->mnt_type) { // bind mount
798 			mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
799 			mp->mnt_type = (char*)"bind";
800 		}
801 		mp->mnt_freq = mp->mnt_passno = 0;
802 
803 		// Write and close
804 #if ENABLE_FEATURE_MTAB_SUPPORT
805 		if (vfsflags & MS_MOVE)
806 			update_mtab_entry_on_move(mp);
807 		else
808 #endif
809 			addmntent(mountTable, mp);
810 		endmntent(mountTable);
811 
812 		if (ENABLE_FEATURE_CLEAN_UP) {
813 			free(mp->mnt_dir);
814 			free(fsname);
815 		}
816 	}
817  ret:
818 	return rc;
819 }
820 
821 #if ENABLE_FEATURE_MOUNT_NFS
822 
823 /*
824  * Linux NFS mount
825  * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
826  *
827  * Licensed under GPLv2, see file LICENSE in this source tree.
828  *
829  * Wed Feb  8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
830  * numbers to be specified on the command line.
831  *
832  * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
833  * Omit the call to connect() for Linux version 1.3.11 or later.
834  *
835  * Wed Oct  1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
836  * Implemented the "bg", "fg" and "retry" mount options for NFS.
837  *
838  * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
839  * - added Native Language Support
840  *
841  * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
842  * plus NFSv3 stuff.
843  */
844 
845 #define MOUNTPORT 635
846 #define MNTPATHLEN 1024
847 #define MNTNAMLEN 255
848 #define FHSIZE 32
849 #define FHSIZE3 64
850 
851 typedef char fhandle[FHSIZE];
852 
853 typedef struct {
854 	unsigned int fhandle3_len;
855 	char *fhandle3_val;
856 } fhandle3;
857 
858 enum mountstat3 {
859 	MNT_OK = 0,
860 	MNT3ERR_PERM = 1,
861 	MNT3ERR_NOENT = 2,
862 	MNT3ERR_IO = 5,
863 	MNT3ERR_ACCES = 13,
864 	MNT3ERR_NOTDIR = 20,
865 	MNT3ERR_INVAL = 22,
866 	MNT3ERR_NAMETOOLONG = 63,
867 	MNT3ERR_NOTSUPP = 10004,
868 	MNT3ERR_SERVERFAULT = 10006,
869 };
870 typedef enum mountstat3 mountstat3;
871 
872 struct fhstatus {
873 	unsigned int fhs_status;
874 	union {
875 		fhandle fhs_fhandle;
876 	} fhstatus_u;
877 };
878 typedef struct fhstatus fhstatus;
879 
880 struct mountres3_ok {
881 	fhandle3 fhandle;
882 	struct {
883 		unsigned int auth_flavours_len;
884 		char *auth_flavours_val;
885 	} auth_flavours;
886 };
887 typedef struct mountres3_ok mountres3_ok;
888 
889 struct mountres3 {
890 	mountstat3 fhs_status;
891 	union {
892 		mountres3_ok mountinfo;
893 	} mountres3_u;
894 };
895 typedef struct mountres3 mountres3;
896 
897 typedef char *dirpath;
898 
899 typedef char *name;
900 
901 typedef struct mountbody *mountlist;
902 
903 struct mountbody {
904 	name ml_hostname;
905 	dirpath ml_directory;
906 	mountlist ml_next;
907 };
908 typedef struct mountbody mountbody;
909 
910 typedef struct groupnode *groups;
911 
912 struct groupnode {
913 	name gr_name;
914 	groups gr_next;
915 };
916 typedef struct groupnode groupnode;
917 
918 typedef struct exportnode *exports;
919 
920 struct exportnode {
921 	dirpath ex_dir;
922 	groups ex_groups;
923 	exports ex_next;
924 };
925 typedef struct exportnode exportnode;
926 
927 struct ppathcnf {
928 	int pc_link_max;
929 	short pc_max_canon;
930 	short pc_max_input;
931 	short pc_name_max;
932 	short pc_path_max;
933 	short pc_pipe_buf;
934 	uint8_t pc_vdisable;
935 	char pc_xxx;
936 	short pc_mask[2];
937 };
938 typedef struct ppathcnf ppathcnf;
939 
940 #define MOUNTPROG 100005
941 #define MOUNTVERS 1
942 
943 #define MOUNTPROC_NULL 0
944 #define MOUNTPROC_MNT 1
945 #define MOUNTPROC_DUMP 2
946 #define MOUNTPROC_UMNT 3
947 #define MOUNTPROC_UMNTALL 4
948 #define MOUNTPROC_EXPORT 5
949 #define MOUNTPROC_EXPORTALL 6
950 
951 #define MOUNTVERS_POSIX 2
952 
953 #define MOUNTPROC_PATHCONF 7
954 
955 #define MOUNT_V3 3
956 
957 #define MOUNTPROC3_NULL 0
958 #define MOUNTPROC3_MNT 1
959 #define MOUNTPROC3_DUMP 2
960 #define MOUNTPROC3_UMNT 3
961 #define MOUNTPROC3_UMNTALL 4
962 #define MOUNTPROC3_EXPORT 5
963 
964 enum {
965 #ifndef NFS_FHSIZE
966 	NFS_FHSIZE = 32,
967 #endif
968 #ifndef NFS_PORT
969 	NFS_PORT = 2049
970 #endif
971 };
972 
973 /*
974  * We want to be able to compile mount on old kernels in such a way
975  * that the binary will work well on more recent kernels.
976  * Thus, if necessary we teach nfsmount.c the structure of new fields
977  * that will come later.
978  *
979  * Moreover, the new kernel includes conflict with glibc includes
980  * so it is easiest to ignore the kernel altogether (at compile time).
981  */
982 
983 struct nfs2_fh {
984 	char            data[32];
985 };
986 struct nfs3_fh {
987 	unsigned short  size;
988 	unsigned char   data[64];
989 };
990 
991 struct nfs_mount_data {
992 	int		version;	/* 1 */
993 	int		fd;		/* 1 */
994 	struct nfs2_fh	old_root;	/* 1 */
995 	int		flags;		/* 1 */
996 	int		rsize;		/* 1 */
997 	int		wsize;		/* 1 */
998 	int		timeo;		/* 1 */
999 	int		retrans;	/* 1 */
1000 	int		acregmin;	/* 1 */
1001 	int		acregmax;	/* 1 */
1002 	int		acdirmin;	/* 1 */
1003 	int		acdirmax;	/* 1 */
1004 	struct sockaddr_in addr;	/* 1 */
1005 	char		hostname[256];	/* 1 */
1006 	int		namlen;		/* 2 */
1007 	unsigned int	bsize;		/* 3 */
1008 	struct nfs3_fh	root;		/* 4 */
1009 };
1010 
1011 /* bits in the flags field */
1012 enum {
1013 	NFS_MOUNT_SOFT = 0x0001,	/* 1 */
1014 	NFS_MOUNT_INTR = 0x0002,	/* 1 */
1015 	NFS_MOUNT_SECURE = 0x0004,	/* 1 */
1016 	NFS_MOUNT_POSIX = 0x0008,	/* 1 */
1017 	NFS_MOUNT_NOCTO = 0x0010,	/* 1 */
1018 	NFS_MOUNT_NOAC = 0x0020,	/* 1 */
1019 	NFS_MOUNT_TCP = 0x0040,		/* 2 */
1020 	NFS_MOUNT_VER3 = 0x0080,	/* 3 */
1021 	NFS_MOUNT_KERBEROS = 0x0100,	/* 3 */
1022 	NFS_MOUNT_NONLM = 0x0200,	/* 3 */
1023 	NFS_MOUNT_NOACL = 0x0800,	/* 4 */
1024 	NFS_MOUNT_NORDIRPLUS = 0x4000
1025 };
1026 
1027 
1028 /*
1029  * We need to translate between nfs status return values and
1030  * the local errno values which may not be the same.
1031  *
1032  * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
1033  * "after #include <errno.h> the symbol errno is reserved for any use,
1034  *  it cannot even be used as a struct tag or field name".
1035  */
1036 #ifndef EDQUOT
1037 # define EDQUOT ENOSPC
1038 #endif
1039 /* Convert each NFSERR_BLAH into EBLAH */
1040 static const uint8_t nfs_err_stat[] ALIGN1 = {
1041 	 1,  2,  5,  6, 13, 17,
1042 	19, 20, 21, 22, 27, 28,
1043 	30, 63, 66, 69, 70, 71
1044 };
1045 #if ( \
1046 	EPERM | ENOENT      | EIO      | ENXIO | EACCES| EEXIST | \
1047 	ENODEV| ENOTDIR     | EISDIR   | EINVAL| EFBIG | ENOSPC | \
1048 	EROFS | ENAMETOOLONG| ENOTEMPTY| EDQUOT| ESTALE| EREMOTE) < 256
1049 typedef uint8_t nfs_err_type;
1050 #else
1051 typedef uint16_t nfs_err_type;
1052 #endif
1053 static const nfs_err_type nfs_err_errnum[] ALIGN2 = {
1054 	EPERM , ENOENT      , EIO      , ENXIO , EACCES, EEXIST,
1055 	ENODEV, ENOTDIR     , EISDIR   , EINVAL, EFBIG , ENOSPC,
1056 	EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE
1057 };
nfs_strerror(int status)1058 static char *nfs_strerror(int status)
1059 {
1060 	int i;
1061 
1062 	for (i = 0; i < ARRAY_SIZE(nfs_err_stat); i++) {
1063 		if (nfs_err_stat[i] == status)
1064 			return strerror(nfs_err_errnum[i]);
1065 	}
1066 	return xasprintf("unknown nfs status return value: %d", status);
1067 }
1068 
xdr_fhandle(XDR * xdrs,fhandle objp)1069 static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
1070 {
1071 	return xdr_opaque(xdrs, objp, FHSIZE);
1072 }
1073 
xdr_fhstatus(XDR * xdrs,fhstatus * objp)1074 static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
1075 {
1076 	if (!xdr_u_int(xdrs, &objp->fhs_status))
1077 		return FALSE;
1078 	if (objp->fhs_status == 0)
1079 		return xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle);
1080 	return TRUE;
1081 }
1082 
xdr_dirpath(XDR * xdrs,dirpath * objp)1083 static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
1084 {
1085 	return xdr_string(xdrs, objp, MNTPATHLEN);
1086 }
1087 
xdr_fhandle3(XDR * xdrs,fhandle3 * objp)1088 static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
1089 {
1090 	return xdr_bytes(xdrs, (char **)&objp->fhandle3_val,
1091 			(unsigned int *) &objp->fhandle3_len,
1092 			FHSIZE3);
1093 }
1094 
xdr_mountres3_ok(XDR * xdrs,mountres3_ok * objp)1095 static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
1096 {
1097 	if (!xdr_fhandle3(xdrs, &objp->fhandle))
1098 		return FALSE;
1099 	return xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val),
1100 			&(objp->auth_flavours.auth_flavours_len),
1101 			~0,
1102 			sizeof(int),
1103 			(xdrproc_t) xdr_int);
1104 }
1105 
xdr_mountstat3(XDR * xdrs,mountstat3 * objp)1106 static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
1107 {
1108 	return xdr_enum(xdrs, (enum_t *) objp);
1109 }
1110 
xdr_mountres3(XDR * xdrs,mountres3 * objp)1111 static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
1112 {
1113 	if (!xdr_mountstat3(xdrs, &objp->fhs_status))
1114 		return FALSE;
1115 	if (objp->fhs_status == MNT_OK)
1116 		return xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo);
1117 	return TRUE;
1118 }
1119 
1120 #define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
1121 
1122 /*
1123  * Unfortunately, the kernel prints annoying console messages
1124  * in case of an unexpected nfs mount version (instead of
1125  * just returning some error).  Therefore we'll have to try
1126  * and figure out what version the kernel expects.
1127  *
1128  * Variables:
1129  *	KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
1130  *	NFS_MOUNT_VERSION: these nfsmount sources at compile time
1131  *	nfs_mount_version: version this source and running kernel can handle
1132  */
1133 static void
find_kernel_nfs_mount_version(void)1134 find_kernel_nfs_mount_version(void)
1135 {
1136 	int kernel_version;
1137 
1138 	if (nfs_mount_version)
1139 		return;
1140 
1141 	nfs_mount_version = 4; /* default */
1142 
1143 	kernel_version = get_linux_version_code();
1144 	if (kernel_version) {
1145 		if (kernel_version < KERNEL_VERSION(2,2,18))
1146 			nfs_mount_version = 3;
1147 		/* else v4 since 2.3.99pre4 */
1148 	}
1149 }
1150 
1151 static void
get_mountport(struct pmap * pm_mnt,struct sockaddr_in * server_addr,long unsigned prog,long unsigned version,long unsigned proto,long unsigned port)1152 get_mountport(struct pmap *pm_mnt,
1153 	struct sockaddr_in *server_addr,
1154 	long unsigned prog,
1155 	long unsigned version,
1156 	long unsigned proto,
1157 	long unsigned port)
1158 {
1159 	struct pmaplist *pmap;
1160 
1161 	server_addr->sin_port = PMAPPORT;
1162 /* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
1163  * I understand it like "IPv6 for this is not 100% ready" */
1164 	pmap = pmap_getmaps(server_addr);
1165 
1166 	if (version > MAX_NFSPROT)
1167 		version = MAX_NFSPROT;
1168 	if (!prog)
1169 		prog = MOUNTPROG;
1170 	pm_mnt->pm_prog = prog;
1171 	pm_mnt->pm_vers = version;
1172 	pm_mnt->pm_prot = proto;
1173 	pm_mnt->pm_port = port;
1174 
1175 	while (pmap) {
1176 		if (pmap->pml_map.pm_prog != prog)
1177 			goto next;
1178 		if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
1179 			goto next;
1180 		if (version > 2 && pmap->pml_map.pm_vers != version)
1181 			goto next;
1182 		if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
1183 			goto next;
1184 		if (pmap->pml_map.pm_vers > MAX_NFSPROT
1185 		 || (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto)
1186 		 || (port && pmap->pml_map.pm_port != port)
1187 		) {
1188 			goto next;
1189 		}
1190 		memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
1191  next:
1192 		pmap = pmap->pml_next;
1193 	}
1194 	if (!pm_mnt->pm_vers)
1195 		pm_mnt->pm_vers = MOUNTVERS;
1196 	if (!pm_mnt->pm_port)
1197 		pm_mnt->pm_port = MOUNTPORT;
1198 	if (!pm_mnt->pm_prot)
1199 		pm_mnt->pm_prot = IPPROTO_TCP;
1200 }
1201 
1202 #if BB_MMU
daemonize(void)1203 static int daemonize(void)
1204 {
1205 	int pid = fork();
1206 	if (pid < 0) /* error */
1207 		return -errno;
1208 	if (pid > 0) /* parent */
1209 		return 0;
1210 	/* child */
1211 	close(0);
1212 	xopen(bb_dev_null, O_RDWR);
1213 	xdup2(0, 1);
1214 	xdup2(0, 2);
1215 	setsid();
1216 	openlog(applet_name, LOG_PID, LOG_DAEMON);
1217 	logmode = LOGMODE_SYSLOG;
1218 	return 1;
1219 }
1220 #else
daemonize(void)1221 static inline int daemonize(void)
1222 {
1223 	return -ENOSYS;
1224 }
1225 #endif
1226 
1227 /* TODO */
we_saw_this_host_before(const char * hostname UNUSED_PARAM)1228 static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
1229 {
1230 	return 0;
1231 }
1232 
1233 /* RPC strerror analogs are terminally idiotic:
1234  * *mandatory* prefix and \n at end.
1235  * This hopefully helps. Usage:
1236  * error_msg_rpc(clnt_*error*(" ")) */
error_msg_rpc(const char * msg)1237 static void error_msg_rpc(const char *msg)
1238 {
1239 	int len;
1240 	while (msg[0] == ' ' || msg[0] == ':') msg++;
1241 	len = strlen(msg);
1242 	while (len && msg[len-1] == '\n') len--;
1243 	bb_error_msg("%.*s", len, msg);
1244 }
1245 
1246 /* NB: mp->xxx fields may be trashed on exit */
nfsmount(struct mntent * mp,unsigned long vfsflags,char * filteropts)1247 static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
1248 {
1249 	CLIENT *mclient;
1250 	char *hostname;
1251 	char *pathname;
1252 	char *mounthost;
1253 	/* prior to 2.6.23, kernel took NFS options in a form of this struct
1254 	 * only. 2.6.23+ looks at data->version, and if it's not 1..6,
1255 	 * then data pointer is interpreted as a string. */
1256 	struct nfs_mount_data data;
1257 	char *opt;
1258 	char *tokstate;
1259 	struct hostent *hp;
1260 	struct sockaddr_in server_addr;
1261 	struct sockaddr_in mount_server_addr;
1262 	int msock, fsock;
1263 	union {
1264 		struct fhstatus nfsv2;
1265 		struct mountres3 nfsv3;
1266 	} status;
1267 	int daemonized;
1268 	char *s;
1269 	int port;
1270 	int mountport;
1271 	int proto;
1272 #if BB_MMU
1273 	smallint bg = 0;
1274 #else
1275 	enum { bg = 0 };
1276 #endif
1277 	int retry;
1278 	int mountprog;
1279 	int mountvers;
1280 	int nfsprog;
1281 	int nfsvers;
1282 	int retval;
1283 	/* these all are one-bit really. gcc 4.3.1 likes this combination: */
1284 	smallint tcp;
1285 	smallint soft;
1286 	int intr;
1287 	int posix;
1288 	int nocto;
1289 	int noac;
1290 	int nordirplus;
1291 	int nolock;
1292 	int noacl;
1293 
1294 	find_kernel_nfs_mount_version();
1295 
1296 	daemonized = 0;
1297 	mounthost = NULL;
1298 	retval = ETIMEDOUT;
1299 	msock = fsock = -1;
1300 	mclient = NULL;
1301 
1302 	/* NB: hostname, mounthost, filteropts must be free()d prior to return */
1303 
1304 	filteropts = xstrdup(filteropts); /* going to trash it later... */
1305 
1306 	hostname = xstrdup(mp->mnt_fsname);
1307 	/* mount_main() guarantees that ':' is there */
1308 	s = strchr(hostname, ':');
1309 	pathname = s + 1;
1310 	*s = '\0';
1311 	/* Ignore all but first hostname in replicated mounts
1312 	 * until they can be fully supported. (mack@sgi.com) */
1313 	s = strchr(hostname, ',');
1314 	if (s) {
1315 		*s = '\0';
1316 		bb_simple_error_msg("warning: multiple hostnames not supported");
1317 	}
1318 
1319 	server_addr.sin_family = AF_INET;
1320 	if (!inet_aton(hostname, &server_addr.sin_addr)) {
1321 		hp = gethostbyname(hostname);
1322 		if (hp == NULL) {
1323 			bb_simple_herror_msg(hostname);
1324 			goto fail;
1325 		}
1326 		if (hp->h_length != (int)sizeof(struct in_addr)) {
1327 			bb_simple_error_msg_and_die("only IPv4 is supported");
1328 		}
1329 		memcpy(&server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
1330 	}
1331 
1332 	memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1333 
1334 	/* add IP address to mtab options for use when unmounting */
1335 
1336 	if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1337 		mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1338 	} else {
1339 		char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1340 					mp->mnt_opts[0] ? "," : "",
1341 					inet_ntoa(server_addr.sin_addr));
1342 		free(mp->mnt_opts);
1343 		mp->mnt_opts = tmp;
1344 	}
1345 
1346 	/* Set default options.
1347 	 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1348 	 * let the kernel decide.
1349 	 * timeo is filled in after we know whether it'll be TCP or UDP. */
1350 	memset(&data, 0, sizeof(data));
1351 	data.retrans  = 3;
1352 	data.acregmin = 3;
1353 	data.acregmax = 60;
1354 	data.acdirmin = 30;
1355 	data.acdirmax = 60;
1356 	data.namlen   = NAME_MAX;
1357 
1358 	soft = 0;
1359 	intr = 0;
1360 	posix = 0;
1361 	nocto = 0;
1362 	nolock = 0;
1363 	noac = 0;
1364 	nordirplus = 0;
1365 	noacl = 0;
1366 	retry = 10000;		/* 10000 minutes ~ 1 week */
1367 	tcp = 1;			/* nfs-utils uses tcp per default */
1368 
1369 	mountprog = MOUNTPROG;
1370 	mountvers = 0;
1371 	port = 0;
1372 	mountport = 0;
1373 	nfsprog = 100003;
1374 	nfsvers = 0;
1375 
1376 	/* parse options */
1377 	if (filteropts)	for (opt = strtok_r(filteropts, ",", &tokstate); opt; opt = strtok_r(NULL, ",", &tokstate)) {
1378 		char *opteq = strchr(opt, '=');
1379 		if (opteq) {
1380 			int val, idx;
1381 			static const char options[] ALIGN1 =
1382 				/* 0 */ "rsize\0"
1383 				/* 1 */ "wsize\0"
1384 				/* 2 */ "timeo\0"
1385 				/* 3 */ "retrans\0"
1386 				/* 4 */ "acregmin\0"
1387 				/* 5 */ "acregmax\0"
1388 				/* 6 */ "acdirmin\0"
1389 				/* 7 */ "acdirmax\0"
1390 				/* 8 */ "actimeo\0"
1391 				/* 9 */ "retry\0"
1392 				/* 10 */ "port\0"
1393 				/* 11 */ "mountport\0"
1394 				/* 12 */ "mounthost\0"
1395 				/* 13 */ "mountprog\0"
1396 				/* 14 */ "mountvers\0"
1397 				/* 15 */ "nfsprog\0"
1398 				/* 16 */ "nfsvers\0"
1399 				/* 17 */ "vers\0"
1400 				/* 18 */ "proto\0"
1401 				/* 19 */ "namlen\0"
1402 				/* 20 */ "addr\0";
1403 
1404 			*opteq++ = '\0';
1405 			idx = index_in_strings(options, opt);
1406 			switch (idx) {
1407 			case 12: // "mounthost"
1408 				mounthost = xstrndup(opteq,
1409 						strcspn(opteq, " \t\n\r,"));
1410 				continue;
1411 			case 18: // "proto"
1412 				if (is_prefixed_with(opteq, "tcp"))
1413 					tcp = 1;
1414 				else if (is_prefixed_with(opteq, "udp"))
1415 					tcp = 0;
1416 				else
1417 					bb_simple_error_msg("warning: unrecognized proto= option");
1418 				continue;
1419 			case 20: // "addr" - ignore
1420 				continue;
1421 			case -1: // unknown
1422 				if (vfsflags & MS_REMOUNT)
1423 					continue;
1424 			}
1425 
1426 			val = xatoi_positive(opteq);
1427 			switch (idx) {
1428 			case 0: // "rsize"
1429 				data.rsize = val;
1430 				continue;
1431 			case 1: // "wsize"
1432 				data.wsize = val;
1433 				continue;
1434 			case 2: // "timeo"
1435 				data.timeo = val;
1436 				continue;
1437 			case 3: // "retrans"
1438 				data.retrans = val;
1439 				continue;
1440 			case 4: // "acregmin"
1441 				data.acregmin = val;
1442 				continue;
1443 			case 5: // "acregmax"
1444 				data.acregmax = val;
1445 				continue;
1446 			case 6: // "acdirmin"
1447 				data.acdirmin = val;
1448 				continue;
1449 			case 7: // "acdirmax"
1450 				data.acdirmax = val;
1451 				continue;
1452 			case 8: // "actimeo"
1453 				data.acregmin = val;
1454 				data.acregmax = val;
1455 				data.acdirmin = val;
1456 				data.acdirmax = val;
1457 				continue;
1458 			case 9: // "retry"
1459 				retry = val;
1460 				continue;
1461 			case 10: // "port"
1462 				port = val;
1463 				continue;
1464 			case 11: // "mountport"
1465 				mountport = val;
1466 				continue;
1467 			case 13: // "mountprog"
1468 				mountprog = val;
1469 				continue;
1470 			case 14: // "mountvers"
1471 				mountvers = val;
1472 				continue;
1473 			case 15: // "nfsprog"
1474 				nfsprog = val;
1475 				continue;
1476 			case 16: // "nfsvers"
1477 			case 17: // "vers"
1478 				nfsvers = val;
1479 				continue;
1480 			case 19: // "namlen"
1481 				//if (nfs_mount_version >= 2)
1482 					data.namlen = val;
1483 				//else
1484 				//	bb_error_msg("warning: option namlen is not supported\n");
1485 				continue;
1486 			default:
1487 				bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1488 				goto fail;
1489 			}
1490 		}
1491 		else { /* not of the form opt=val */
1492 			static const char options[] ALIGN1 =
1493 				"bg\0"
1494 				"fg\0"
1495 				"soft\0"
1496 				"hard\0"
1497 				"intr\0"
1498 				"posix\0"
1499 				"cto\0"
1500 				"ac\0"
1501 				"tcp\0"
1502 				"udp\0"
1503 				"lock\0"
1504 				"rdirplus\0"
1505 				"acl\0";
1506 			int val = 1;
1507 			if (is_prefixed_with(opt, "no")) {
1508 				val = 0;
1509 				opt += 2;
1510 			}
1511 			switch (index_in_strings(options, opt)) {
1512 			case 0: // "bg"
1513 #if BB_MMU
1514 				bg = val;
1515 #endif
1516 				break;
1517 			case 1: // "fg"
1518 #if BB_MMU
1519 				bg = !val;
1520 #endif
1521 				break;
1522 			case 2: // "soft"
1523 				soft = val;
1524 				break;
1525 			case 3: // "hard"
1526 				soft = !val;
1527 				break;
1528 			case 4: // "intr"
1529 				intr = val;
1530 				break;
1531 			case 5: // "posix"
1532 				posix = val;
1533 				break;
1534 			case 6: // "cto"
1535 				nocto = !val;
1536 				break;
1537 			case 7: // "ac"
1538 				noac = !val;
1539 				break;
1540 			case 8: // "tcp"
1541 				tcp = val;
1542 				break;
1543 			case 9: // "udp"
1544 				tcp = !val;
1545 				break;
1546 			case 10: // "lock"
1547 				if (nfs_mount_version >= 3)
1548 					nolock = !val;
1549 				else
1550 					bb_simple_error_msg("warning: option nolock is not supported");
1551 				break;
1552 			case 11: //rdirplus
1553 				nordirplus = !val;
1554 				break;
1555 			case 12: // acl
1556 				noacl = !val;
1557 				break;
1558 			default:
1559 				bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1560 				goto fail;
1561 			}
1562 		}
1563 	}
1564 	proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1565 
1566 	data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1567 		| (intr ? NFS_MOUNT_INTR : 0)
1568 		| (posix ? NFS_MOUNT_POSIX : 0)
1569 		| (nocto ? NFS_MOUNT_NOCTO : 0)
1570 		| (noac ? NFS_MOUNT_NOAC : 0)
1571 		| (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0)
1572 		| (noacl ? NFS_MOUNT_NOACL : 0);
1573 	if (nfs_mount_version >= 2)
1574 		data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1575 	if (nfs_mount_version >= 3)
1576 		data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1577 	if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1578 		bb_error_msg("NFSv%d not supported", nfsvers);
1579 		goto fail;
1580 	}
1581 	if (nfsvers && !mountvers)
1582 		mountvers = (nfsvers < 3) ? 1 : nfsvers;
1583 	if (nfsvers && nfsvers < mountvers) {
1584 		mountvers = nfsvers;
1585 	}
1586 
1587 	/* Adjust options if none specified */
1588 	if (!data.timeo)
1589 		data.timeo = tcp ? 70 : 7;
1590 
1591 	data.version = nfs_mount_version;
1592 
1593 	if (vfsflags & MS_REMOUNT)
1594 		goto do_mount;
1595 
1596 	/*
1597 	 * If the previous mount operation on the same host was
1598 	 * backgrounded, and the "bg" for this mount is also set,
1599 	 * give up immediately, to avoid the initial timeout.
1600 	 */
1601 	if (bg && we_saw_this_host_before(hostname)) {
1602 		daemonized = daemonize();
1603 		if (daemonized <= 0) { /* parent or error */
1604 			retval = -daemonized;
1605 			goto ret;
1606 		}
1607 	}
1608 
1609 	/* Create mount daemon client */
1610 	/* See if the nfs host = mount host. */
1611 	if (mounthost) {
1612 		if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1613 			mount_server_addr.sin_family = AF_INET;
1614 			mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1615 		} else {
1616 			hp = gethostbyname(mounthost);
1617 			if (hp == NULL) {
1618 				bb_simple_herror_msg(mounthost);
1619 				goto fail;
1620 			}
1621 			if (hp->h_length != (int)sizeof(struct in_addr)) {
1622 				bb_simple_error_msg_and_die("only IPv4 is supported");
1623 			}
1624 			mount_server_addr.sin_family = AF_INET;
1625 			memcpy(&mount_server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
1626 		}
1627 	}
1628 
1629 	/*
1630 	 * The following loop implements the mount retries. When the mount
1631 	 * times out, and the "bg" option is set, we background ourself
1632 	 * and continue trying.
1633 	 *
1634 	 * The case where the mount point is not present and the "bg"
1635 	 * option is set, is treated as a timeout. This is done to
1636 	 * support nested mounts.
1637 	 *
1638 	 * The "retry" count specified by the user is the number of
1639 	 * minutes to retry before giving up.
1640 	 */
1641 	{
1642 		struct timeval total_timeout;
1643 		struct timeval retry_timeout;
1644 		struct pmap pm_mnt;
1645 		time_t t;
1646 		time_t prevt;
1647 		time_t timeout;
1648 
1649 		retry_timeout.tv_sec = 3;
1650 		retry_timeout.tv_usec = 0;
1651 		total_timeout.tv_sec = 20;
1652 		total_timeout.tv_usec = 0;
1653 /* FIXME: use monotonic()? */
1654 		timeout = time(NULL) + 60 * retry;
1655 		prevt = 0;
1656 		t = 30;
1657  retry:
1658 		/* Be careful not to use too many CPU cycles */
1659 		if (t - prevt < 30)
1660 			sleep(30);
1661 
1662 		get_mountport(&pm_mnt, &mount_server_addr,
1663 				mountprog,
1664 				mountvers,
1665 				proto,
1666 				mountport);
1667 		nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
1668 
1669 		/* contact the mount daemon via TCP */
1670 		mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1671 		msock = RPC_ANYSOCK;
1672 
1673 		switch (pm_mnt.pm_prot) {
1674 		case IPPROTO_UDP:
1675 			mclient = clntudp_create(&mount_server_addr,
1676 						pm_mnt.pm_prog,
1677 						pm_mnt.pm_vers,
1678 						retry_timeout,
1679 						&msock);
1680 			if (mclient)
1681 				break;
1682 			mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1683 			msock = RPC_ANYSOCK;
1684 		case IPPROTO_TCP:
1685 			mclient = clnttcp_create(&mount_server_addr,
1686 						pm_mnt.pm_prog,
1687 						pm_mnt.pm_vers,
1688 						&msock, 0, 0);
1689 			break;
1690 		default:
1691 			mclient = NULL;
1692 		}
1693 		if (!mclient) {
1694 			if (!daemonized && prevt == 0)
1695 				error_msg_rpc(clnt_spcreateerror(" "));
1696 		} else {
1697 			enum clnt_stat clnt_stat;
1698 
1699 			/* Try to mount hostname:pathname */
1700 			mclient->cl_auth = authunix_create_default();
1701 
1702 			/* Make pointers in xdr_mountres3 NULL so
1703 			 * that xdr_array allocates memory for us
1704 			 */
1705 			memset(&status, 0, sizeof(status));
1706 
1707 			if (pm_mnt.pm_vers == 3)
1708 				clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1709 						(xdrproc_t) xdr_dirpath,
1710 						(caddr_t) &pathname,
1711 						(xdrproc_t) xdr_mountres3,
1712 						(caddr_t) &status,
1713 						total_timeout);
1714 			else
1715 				clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1716 						(xdrproc_t) xdr_dirpath,
1717 						(caddr_t) &pathname,
1718 						(xdrproc_t) xdr_fhstatus,
1719 						(caddr_t) &status,
1720 						total_timeout);
1721 
1722 			if (clnt_stat == RPC_SUCCESS)
1723 				goto prepare_kernel_data; /* we're done */
1724 			if (errno != ECONNREFUSED) {
1725 				error_msg_rpc(clnt_sperror(mclient, " "));
1726 				goto fail;	/* don't retry */
1727 			}
1728 			/* Connection refused */
1729 			if (!daemonized && prevt == 0) /* print just once */
1730 				error_msg_rpc(clnt_sperror(mclient, " "));
1731 			auth_destroy(mclient->cl_auth);
1732 			clnt_destroy(mclient);
1733 			mclient = NULL;
1734 			close(msock);
1735 			msock = -1;
1736 		}
1737 
1738 		/* Timeout. We are going to retry... maybe */
1739 		if (!bg)
1740 			goto fail;
1741 		if (!daemonized) {
1742 			daemonized = daemonize();
1743 			if (daemonized <= 0) { /* parent or error */
1744 				retval = -daemonized;
1745 				goto ret;
1746 			}
1747 		}
1748 		prevt = t;
1749 		t = time(NULL);
1750 		if (t >= timeout)
1751 			/* TODO error message */
1752 			goto fail;
1753 
1754 		goto retry;
1755 	}
1756 
1757  prepare_kernel_data:
1758 
1759 	if (nfsvers == 2) {
1760 		if (status.nfsv2.fhs_status != 0) {
1761 			bb_error_msg("%s:%s failed, reason given by server: %s",
1762 				hostname, pathname,
1763 				nfs_strerror(status.nfsv2.fhs_status));
1764 			goto fail;
1765 		}
1766 		memcpy(data.root.data,
1767 				(char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1768 				NFS_FHSIZE);
1769 		data.root.size = NFS_FHSIZE;
1770 		memcpy(data.old_root.data,
1771 				(char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1772 				NFS_FHSIZE);
1773 	} else {
1774 		fhandle3 *my_fhandle;
1775 		if (status.nfsv3.fhs_status != 0) {
1776 			bb_error_msg("%s:%s failed, reason given by server: %s",
1777 				hostname, pathname,
1778 				nfs_strerror(status.nfsv3.fhs_status));
1779 			goto fail;
1780 		}
1781 		my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1782 		memset(data.old_root.data, 0, NFS_FHSIZE);
1783 		memset(&data.root, 0, sizeof(data.root));
1784 		data.root.size = my_fhandle->fhandle3_len;
1785 		memcpy(data.root.data,
1786 				(char *) my_fhandle->fhandle3_val,
1787 				my_fhandle->fhandle3_len);
1788 
1789 		data.flags |= NFS_MOUNT_VER3;
1790 	}
1791 
1792 	/* Create nfs socket for kernel */
1793 	if (tcp) {
1794 		if (nfs_mount_version < 3) {
1795 			bb_simple_error_msg("NFS over TCP is not supported");
1796 			goto fail;
1797 		}
1798 		fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1799 	} else
1800 		fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1801 	if (fsock < 0) {
1802 		bb_simple_perror_msg("nfs socket");
1803 		goto fail;
1804 	}
1805 	if (bindresvport(fsock, 0) < 0) {
1806 		bb_simple_perror_msg("nfs bindresvport");
1807 		goto fail;
1808 	}
1809 	if (port == 0) {
1810 		server_addr.sin_port = PMAPPORT;
1811 		port = pmap_getport(&server_addr, nfsprog, nfsvers,
1812 					tcp ? IPPROTO_TCP : IPPROTO_UDP);
1813 		if (port == 0)
1814 			port = NFS_PORT;
1815 	}
1816 	server_addr.sin_port = htons(port);
1817 
1818 	/* Prepare data structure for kernel */
1819 	data.fd = fsock;
1820 	memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1821 	strncpy(data.hostname, hostname, sizeof(data.hostname));
1822 
1823 	/* Clean up */
1824 	auth_destroy(mclient->cl_auth);
1825 	clnt_destroy(mclient);
1826 	close(msock);
1827 	msock = -1;
1828 
1829 	if (bg) {
1830 		/* We must wait until mount directory is available */
1831 		struct stat statbuf;
1832 		int delay = 1;
1833 		while (stat(mp->mnt_dir, &statbuf) == -1) {
1834 			if (!daemonized) {
1835 				daemonized = daemonize();
1836 				if (daemonized <= 0) { /* parent or error */
1837 /* FIXME: parent doesn't close fsock - ??! */
1838 					retval = -daemonized;
1839 					goto ret;
1840 				}
1841 			}
1842 			sleep(delay);	/* 1, 2, 4, 8, 16, 30, ... */
1843 			delay *= 2;
1844 			if (delay > 30)
1845 				delay = 30;
1846 		}
1847 	}
1848 
1849 	/* Perform actual mount */
1850  do_mount:
1851 	retval = mount_it_now(mp, vfsflags, (char*)&data);
1852 	goto ret;
1853 
1854 	/* Abort */
1855  fail:
1856 	if (msock >= 0) {
1857 		if (mclient) {
1858 			auth_destroy(mclient->cl_auth);
1859 			clnt_destroy(mclient);
1860 		}
1861 		close(msock);
1862 	}
1863 	if (fsock >= 0)
1864 		close(fsock);
1865 
1866  ret:
1867 	free(hostname);
1868 	free(mounthost);
1869 	free(filteropts);
1870 	return retval;
1871 }
1872 
1873 #else // !ENABLE_FEATURE_MOUNT_NFS
1874 
1875 /* Linux 2.6.23+ supports nfs mounts with options passed as a string.
1876  * For older kernels, you must build busybox with ENABLE_FEATURE_MOUNT_NFS.
1877  * (However, note that then you lose any chances that NFS over IPv6 would work).
1878  */
nfsmount(struct mntent * mp,unsigned long vfsflags,char * filteropts)1879 static int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
1880 {
1881 	len_and_sockaddr *lsa;
1882 	char *opts;
1883 	char *end;
1884 	char *dotted;
1885 	int ret;
1886 
1887 # if ENABLE_FEATURE_IPV6
1888 	end = strchr(mp->mnt_fsname, ']');
1889 	if (end && end[1] == ':')
1890 		end++;
1891 	else
1892 # endif
1893 		/* mount_main() guarantees that ':' is there */
1894 		end = strchr(mp->mnt_fsname, ':');
1895 
1896 	*end = '\0';
1897 	lsa = xhost2sockaddr(mp->mnt_fsname, /*port:*/ 0);
1898 	*end = ':';
1899 	dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1900 	if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
1901 	opts = xasprintf("%s%saddr=%s",
1902 		filteropts ? filteropts : "",
1903 		filteropts ? "," : "",
1904 		dotted
1905 	);
1906 	if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
1907 	ret = mount_it_now(mp, vfsflags, opts);
1908 	if (ENABLE_FEATURE_CLEAN_UP) free(opts);
1909 
1910 	return ret;
1911 }
1912 
1913 #endif // !ENABLE_FEATURE_MOUNT_NFS
1914 
1915 // Find "...,NAME=NUM,..." in the option string, remove "NAME=NUM" option
1916 // and return NUM.
1917 // Return 0 if not found.
1918 // All instances must be parsed and removed (for example, since kernel 5.4
1919 //      squashfs: Unknown parameter 'sizelimit'
1920 // will result if loopback mount option "sizelimit=NNN" is not removed
1921 // and squashfs sees it in option string).
cut_out_ull_opt(char * opts,const char * name_eq)1922 static unsigned long long cut_out_ull_opt(char *opts, const char *name_eq)
1923 {
1924 	unsigned long long ret = 0;
1925 
1926 	if (!opts) // allow NULL opts (simplifies callers' work)
1927 		return ret;
1928 
1929 	for (;;) {
1930 		char *end;
1931 		char *opt;
1932 
1933 		// Find comma-delimited "NAME="
1934 		for (;;) {
1935 			opt = strstr(opts, name_eq);
1936 			if (!opt)
1937 				return ret;
1938 			if (opt == opts)
1939 				break; // found it (it's first opt)
1940 			if (opt[-1] == ',') {
1941 				opts = opt - 1;
1942 				break; // found it (it's not a first opt)
1943 			}
1944 			// False positive like "VNAME=", we are at "N".
1945 			// - skip it, loop back to searching
1946 			opts = opt + 1;
1947 		}
1948 
1949 		ret = bb_strtoull(opt + strlen(name_eq), &end, 0);
1950 		if (errno && errno != EINVAL) {
1951  err:
1952 			bb_error_msg_and_die("bad option '%s'", opt);
1953 		}
1954 		if (*end == '\0') {
1955 			// It is "[,]NAME=NUM\0" - truncate it and return
1956 			*opts = '\0';
1957 			return ret;
1958 		}
1959 		if (*end != ',')
1960 			goto err;
1961 		// We are at trailing comma
1962 		// Remove "NAME=NUM," and loop back to check for duplicate opts
1963 		overlapping_strcpy(opt, end + 1);
1964 	}
1965 }
1966 
1967 // Mount one directory.  Handles CIFS, NFS, loopback, autobind, and filesystem
1968 // type detection.  Returns 0 for success, nonzero for failure.
1969 // NB: mp->xxx fields may be trashed on exit
singlemount(struct mntent * mp,int ignore_busy)1970 static int singlemount(struct mntent *mp, int ignore_busy)
1971 {
1972 	int loopfd = -1;
1973 	int rc = -1;
1974 	unsigned long vfsflags;
1975 	char *loopFile = NULL, *filteropts = NULL;
1976 	llist_t *fl = NULL;
1977 	struct stat st;
1978 
1979 	errno = 0;
1980 
1981 	vfsflags = parse_mount_options(mp->mnt_opts, &filteropts, NULL);
1982 
1983 	// Treat fstype "auto" as unspecified
1984 	if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1985 		mp->mnt_type = NULL;
1986 
1987 	// Might this be a virtual filesystem?
1988 	if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
1989 		char *args[35];
1990 		char *s;
1991 		int n;
1992 		// fsname: "cmd#arg1#arg2..."
1993 		// WARNING: allows execution of arbitrary commands!
1994 		// Try "mount 'sh#-c#sh' bogus_dir".
1995 		// It is safe ONLY because non-root
1996 		// cannot use two-argument mount command
1997 		// and using one-argument "mount 'sh#-c#sh'" doesn't work:
1998 		// "mount: can't find sh#-c#sh in /etc/fstab"
1999 		// (if /etc/fstab has it, it's ok: root sets up /etc/fstab).
2000 
2001 		s = mp->mnt_fsname;
2002 		n = 0;
2003 		args[n++] = s;
2004 		while (*s && n < 35 - 2) {
2005 			if (*s++ == '#' && *s != '#') {
2006 				s[-1] = '\0';
2007 				args[n++] = s;
2008 			}
2009 		}
2010 		args[n++] = mp->mnt_dir;
2011 		args[n] = NULL;
2012 		rc = spawn_and_wait(args);
2013 		goto report_error;
2014 	}
2015 
2016 	// Might this be an CIFS filesystem?
2017 	if (ENABLE_FEATURE_MOUNT_CIFS
2018 	 && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
2019 	 && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
2020 	 && mp->mnt_fsname[0] == mp->mnt_fsname[1]
2021 	) {
2022 		int len;
2023 		char c;
2024 		char *hostname, *share;
2025 		len_and_sockaddr *lsa;
2026 
2027 		// Parse mp->mnt_fsname of the form "//hostname/share[/dir1/dir2]"
2028 
2029 		hostname = mp->mnt_fsname + 2;
2030 		len = strcspn(hostname, "/\\");
2031 		share = hostname + len + 1;
2032 		if (len == 0          // 3rd char is a [back]slash (IOW: empty hostname)
2033 		 || share[-1] == '\0' // no [back]slash after hostname
2034 		 || share[0] == '\0'  // empty share name
2035 		) {
2036 			goto report_error;
2037 		}
2038 		c = share[-1];
2039 		share[-1] = '\0';
2040 		len = strcspn(share, "/\\");
2041 
2042 		// "unc=\\hostname\share" option is mandatory
2043 		// after CIFS option parsing was rewritten in Linux 3.4.
2044 		// Must use backslashes.
2045 		// If /dir1/dir2 is present, also add "prefixpath=dir1/dir2"
2046 		{
2047 			char *unc = xasprintf(
2048 				share[len] != '\0'  /* "/dir1/dir2" exists? */
2049 					? "unc=\\\\%s\\%.*s,prefixpath=%s"
2050 					: "unc=\\\\%s\\%.*s",
2051 				hostname,
2052 				len, share,
2053 				share + len + 1  /* "dir1/dir2" */
2054 			);
2055 			parse_mount_options(unc, &filteropts, NULL);
2056 			if (ENABLE_FEATURE_CLEAN_UP) free(unc);
2057 		}
2058 
2059 		lsa = host2sockaddr(hostname, 0);
2060 		share[-1] = c;
2061 		if (!lsa)
2062 			goto report_error;
2063 
2064 		// If there is no "ip=..." option yet
2065 		if (!is_prefixed_with(filteropts, ",ip="+1)
2066 		 && !strstr(filteropts, ",ip=")
2067 		) {
2068 			char *dotted, *ip;
2069 			// Insert "ip=..." option into options
2070 			dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
2071 			if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
2072 			ip = xasprintf("ip=%s", dotted);
2073 			if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
2074 // Note: IPv6 scoped addresses ("host%iface", see RFC 4007) should be
2075 // handled by libc in getnameinfo() (inside xmalloc_sockaddr2dotted_noport()).
2076 // Currently, glibc does not support that (has no NI_NUMERICSCOPE),
2077 // musl apparently does. This results in "ip=numericIPv6%iface_name"
2078 // (instead of _numeric_ iface_id) with glibc.
2079 // This probably should be fixed in glibc, not here.
2080 // The workaround is to manually specify correct "ip=ADDR%n" option.
2081 			parse_mount_options(ip, &filteropts, NULL);
2082 			if (ENABLE_FEATURE_CLEAN_UP) free(ip);
2083 		}
2084 
2085 		mp->mnt_type = (char*)"cifs";
2086 		rc = mount_it_now(mp, vfsflags, filteropts);
2087 
2088 		goto report_error;
2089 	}
2090 
2091 	// Might this be an NFS filesystem?
2092 	if (!(vfsflags & (MS_BIND | MS_MOVE))
2093 	 && (!mp->mnt_type || is_prefixed_with(mp->mnt_type, "nfs"))
2094 	) {
2095 		char *colon = strchr(mp->mnt_fsname, ':');
2096 		if (colon // looks like "hostname:..."
2097 		 && strchrnul(mp->mnt_fsname, '/') > colon // "hostname:" has no slashes
2098 		) {
2099 			if (!mp->mnt_type)
2100 				mp->mnt_type = (char*)"nfs";
2101 			rc = nfsmount(mp, vfsflags, filteropts);
2102 			goto report_error;
2103 		}
2104 	}
2105 
2106 	// Look at the file.  (Not found isn't a failure for remount, or for
2107 	// a synthetic filesystem like proc or sysfs.)
2108 	// (We use stat, not lstat, in order to allow
2109 	// mount symlink_to_file_or_blkdev dir)
2110 	if (!stat(mp->mnt_fsname, &st)
2111 	 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
2112 	) {
2113 		// Do we need to allocate a loopback device for it?
2114 		if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
2115 			unsigned long long offset;
2116 			unsigned long long sizelimit;
2117 
2118 			loopFile = bb_simplify_path(mp->mnt_fsname);
2119 			mp->mnt_fsname = NULL; // will receive malloced loop dev name
2120 
2121 			// Parse and remove loopback options
2122 			offset = cut_out_ull_opt(filteropts, "offset=");
2123 			sizelimit = cut_out_ull_opt(filteropts, "sizelimit=");
2124 
2125 			// mount always creates AUTOCLEARed loopdevs, so that umounting
2126 			// drops them without any code in the userspace.
2127 			// This happens since circa linux-2.6.25:
2128 			// commit 96c5865559cee0f9cbc5173f3c949f6ce3525581
2129 			// Date:    Wed Feb 6 01:36:27 2008 -0800
2130 			// Subject: Allow auto-destruction of loop devices
2131 			loopfd = set_loop(&mp->mnt_fsname,
2132 					loopFile,
2133 					offset,
2134 					sizelimit,
2135 					((vfsflags & MS_RDONLY) ? BB_LO_FLAGS_READ_ONLY : 0)
2136 						| BB_LO_FLAGS_AUTOCLEAR
2137 			);
2138 			if (loopfd < 0) {
2139 				if (errno == EPERM || errno == EACCES)
2140 					bb_simple_error_msg(bb_msg_perm_denied_are_you_root);
2141 				else
2142 					bb_simple_perror_msg("can't setup loop device");
2143 				return loopfd; // was "return errno", but it can be 0 here
2144 			}
2145 
2146 		// Autodetect bind mounts
2147 		} else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
2148 			vfsflags |= MS_BIND;
2149 	}
2150 
2151 	// If we know the fstype (or don't need to), jump straight
2152 	// to the actual mount.
2153 	if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
2154 		char *next;
2155 		for (;;) {
2156 			next = mp->mnt_type ? strchr(mp->mnt_type, ',') : NULL;
2157 			if (next)
2158 				*next = '\0';
2159 			rc = mount_it_now(mp, vfsflags, filteropts);
2160 			if (rc == 0 || !next)
2161 				break;
2162 			mp->mnt_type = next + 1;
2163 		}
2164 	} else {
2165 		// Loop through filesystem types until mount succeeds
2166 		// or we run out
2167 
2168 		// Initialize list of block backed filesystems.
2169 		// This has to be done here so that during "mount -a",
2170 		// mounts after /proc shows up can autodetect.
2171 		if (!fslist) {
2172 			fslist = get_block_backed_filesystems();
2173 			if (ENABLE_FEATURE_CLEAN_UP && fslist)
2174 				atexit(delete_block_backed_filesystems);
2175 		}
2176 
2177 		for (fl = fslist; fl; fl = fl->link) {
2178 			mp->mnt_type = fl->data;
2179 			rc = mount_it_now(mp, vfsflags, filteropts);
2180 			if (rc == 0)
2181 				break;
2182 		}
2183 	}
2184 
2185 	// If mount failed, clean up loop file (if any).
2186 	// (Newer kernels which support LO_FLAGS_AUTOCLEAR should not need this,
2187 	// merely "close(loopfd)" should do it?)
2188 	if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
2189 		del_loop(mp->mnt_fsname);
2190 		if (ENABLE_FEATURE_CLEAN_UP) {
2191 			free(loopFile);
2192 			/* No, "rc != 0" needs it: free(mp->mnt_fsname); */
2193 		}
2194 	}
2195 
2196  report_error:
2197 	if (ENABLE_FEATURE_CLEAN_UP)
2198 		free(filteropts);
2199 
2200 	if (loopfd >= 0)
2201 		close(loopfd);
2202 
2203 	if (errno == EBUSY && ignore_busy)
2204 		return 0;
2205 	if (errno == ENOENT && (vfsflags & MOUNT_NOFAIL))
2206 		return 0;
2207 	if (rc != 0)
2208 		bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
2209 	return rc;
2210 }
2211 
2212 // -O support
2213 //    -O interprets a list of filter options which select whether a mount
2214 // point will be mounted: only mounts with options matching *all* filtering
2215 // options will be selected.
2216 //    By default each -O filter option must be present in the list of mount
2217 // options, but if it is prefixed by "no" then it must be absent.
2218 // For example,
2219 //  -O a,nob,c  matches  -o a,c  but fails to match  -o a,b,c
2220 //              (and also fails to match  -o a  because  -o c  is absent).
2221 //
2222 // It is different from -t in that each option is matched exactly; a leading
2223 // "no" at the beginning of one option does not negate the rest.
match_opt(const char * fs_opt_in,const char * O_opt)2224 static int match_opt(const char *fs_opt_in, const char *O_opt)
2225 {
2226 	if (!O_opt)
2227 		return 1;
2228 
2229 	while (*O_opt) {
2230 		const char *fs_opt = fs_opt_in;
2231 		int O_len;
2232 		int match;
2233 
2234 		// If option begins with "no" then treat as an inverted match:
2235 		// matching is a failure
2236 		match = 0;
2237 		if (O_opt[0] == 'n' && O_opt[1] == 'o') {
2238 			match = 1;
2239 			O_opt += 2;
2240 		}
2241 		// Isolate the current O option
2242 		O_len = strchrnul(O_opt, ',') - O_opt;
2243 		// Check for a match against existing options
2244 		while (1) {
2245 			if (strncmp(fs_opt, O_opt, O_len) == 0
2246 			 && (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',')
2247 			) {
2248 				if (match)
2249 					return 0;  // "no" prefix, but option found
2250 				match = 1;  // current O option found, go check next one
2251 				break;
2252 			}
2253 			fs_opt = strchr(fs_opt, ',');
2254 			if (!fs_opt)
2255 				break;
2256 			fs_opt++;
2257 		}
2258 		if (match == 0)
2259 			return 0;     // match wanted but not found
2260 		if (O_opt[O_len] == '\0') // end?
2261 			break;
2262 		// Step to the next O option
2263 		O_opt += O_len + 1;
2264 	}
2265 	// If we get here then everything matched
2266 	return 1;
2267 }
2268 
2269 // Parse options, if necessary parse fstab/mtab, and call singlemount for
2270 // each directory to be mounted.
2271 int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
mount_main(int argc UNUSED_PARAM,char ** argv)2272 int mount_main(int argc UNUSED_PARAM, char **argv)
2273 {
2274 	char *cmdopts = xzalloc(1);
2275 	char *fstype = NULL;
2276 	char *O_optmatch = NULL;
2277 	char *storage_path;
2278 	llist_t *lst_o = NULL;
2279 	const char *fstabname = "/etc/fstab";
2280 	FILE *fstab;
2281 	int i, j;
2282 	int rc = EXIT_SUCCESS;
2283 	unsigned long cmdopt_flags;
2284 	unsigned opt;
2285 	struct mntent mtpair[2], *mtcur = mtpair;
2286 	IF_NOT_DESKTOP(const int nonroot = 0;)
2287 
2288 	IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
2289 
2290 	INIT_G();
2291 
2292 	// Parse long options, like --bind and --move.  Note that -o option
2293 	// and --option are synonymous.  Yes, this means --remount,rw works.
2294 	for (i = j = 1; argv[i]; i++) {
2295 		if (argv[i][0] == '-' && argv[i][1] == '-')
2296 			append_mount_options(&cmdopts, argv[i] + 2);
2297 		else
2298 			argv[j++] = argv[i];
2299 	}
2300 	argv[j] = NULL;
2301 
2302 	// Parse remaining options
2303 	// Max 2 params; -o is a list, -v is a counter
2304 	opt = getopt32(argv, "^"
2305 		OPTION_STR
2306 		"\0" "?2"IF_FEATURE_MOUNT_VERBOSE("vv"),
2307 		&lst_o, &fstype, &O_optmatch
2308 		IF_FEATURE_MOUNT_OTHERTAB(, &fstabname)
2309 		IF_FEATURE_MOUNT_VERBOSE(, &verbose)
2310 	);
2311 
2312 	while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
2313 	if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
2314 	if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
2315 	argv += optind;
2316 
2317 	// If we have no arguments, show currently mounted filesystems
2318 	if (!argv[0]) {
2319 		if (!(opt & OPT_a)) {
2320 			FILE *mountTable = setmntent(bb_path_mtab_file, "r");
2321 
2322 			if (!mountTable)
2323 				bb_error_msg_and_die("no %s", bb_path_mtab_file);
2324 
2325 			while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
2326 								GETMNTENT_BUFSIZE))
2327 			{
2328 				// Don't show rootfs. FIXME: why??
2329 				// util-linux 2.12a happily shows rootfs...
2330 				//if (strcmp(mtpair->mnt_fsname, "rootfs") == 0) continue;
2331 
2332 				if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0)
2333 					printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
2334 							mtpair->mnt_dir, mtpair->mnt_type,
2335 							mtpair->mnt_opts);
2336 			}
2337 			if (ENABLE_FEATURE_CLEAN_UP)
2338 				endmntent(mountTable);
2339 			return EXIT_SUCCESS;
2340 		}
2341 		storage_path = NULL;
2342 	} else {
2343 		// When we have two arguments, the second is the directory and we can
2344 		// skip looking at fstab entirely.  We can always abspath() the directory
2345 		// argument when we get it.
2346 		if (argv[1]) {
2347 			if (nonroot)
2348 				bb_simple_error_msg_and_die(bb_msg_you_must_be_root);
2349 			mtpair->mnt_fsname = argv[0];
2350 			mtpair->mnt_dir = argv[1];
2351 			mtpair->mnt_type = fstype;
2352 			mtpair->mnt_opts = cmdopts;
2353 			resolve_mount_spec(&mtpair->mnt_fsname);
2354 			rc = singlemount(mtpair, /*ignore_busy:*/ 0);
2355 			return rc;
2356 		}
2357 		storage_path = bb_simplify_path(argv[0]); // malloced
2358 	}
2359 
2360 	// Past this point, we are handling either "mount -a [opts]"
2361 	// or "mount [opts] single_param"
2362 
2363 	cmdopt_flags = parse_mount_options(cmdopts, NULL, &option_mask32);
2364 	if (nonroot && (cmdopt_flags & ~MS_SILENT)) // Non-root users cannot specify flags
2365 		bb_simple_error_msg_and_die(bb_msg_you_must_be_root);
2366 
2367 	// If we have a shared subtree flag, don't worry about fstab or mtab.
2368 	if (ENABLE_FEATURE_MOUNT_FLAGS
2369 	 && (cmdopt_flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
2370 	) {
2371 		// verbose_mount(source, target, type, flags, data)
2372 		rc = verbose_mount("", argv[0], "", cmdopt_flags, "");
2373 		if (rc)
2374 			bb_simple_perror_msg_and_die(argv[0]);
2375 		return rc;
2376 	}
2377 
2378 	// A malicious user could overmount /usr without this.
2379 	if (ENABLE_FEATURE_MOUNT_OTHERTAB && nonroot)
2380 		fstabname = "/etc/fstab";
2381 	// Open either fstab or mtab
2382 	if (cmdopt_flags & MS_REMOUNT) {
2383 		// WARNING. I am not sure this matches util-linux's
2384 		// behavior. It's possible util-linux does not
2385 		// take -o opts from mtab (takes only mount source).
2386 		fstabname = bb_path_mtab_file;
2387 	}
2388 	fstab = setmntent(fstabname, "r");
2389 	if (!fstab)
2390 		bb_perror_msg_and_die("can't read '%s'", fstabname);
2391 
2392 	// Loop through entries until we find what we're looking for
2393 	memset(mtpair, 0, sizeof(mtpair));
2394 	for (;;) {
2395 		struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
2396 
2397 		// Get next fstab entry
2398 		if (!getmntent_r(fstab, mtcur, getmntent_buf
2399 					+ (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
2400 				GETMNTENT_BUFSIZE/2)
2401 		) { // End of fstab/mtab is reached
2402 			mtcur = mtother; // the thing we found last time
2403 			break;
2404 		}
2405 
2406 		// If we're trying to mount something specific and this isn't it,
2407 		// skip it.  Note we must match the exact text in fstab (ala
2408 		// "proc") or a full path from root
2409 		if (argv[0]) {
2410 
2411 			// Is this what we're looking for?
2412 			if (strcmp(argv[0], mtcur->mnt_fsname) != 0
2413 			 && strcmp(storage_path, mtcur->mnt_fsname) != 0
2414 			 && strcmp(argv[0], mtcur->mnt_dir) != 0
2415 			 && strcmp(storage_path, mtcur->mnt_dir) != 0
2416 			) {
2417 				continue; // no
2418 			}
2419 
2420 			// Remember this entry.  Something later may have
2421 			// overmounted it, and we want the _last_ match.
2422 			mtcur = mtother;
2423 
2424 		// If we're mounting all
2425 		} else {
2426 			struct mntent *mp;
2427 			// No, mount -a won't mount anything,
2428 			// even user mounts, for mere humans
2429 			if (nonroot)
2430 				bb_simple_error_msg_and_die(bb_msg_you_must_be_root);
2431 
2432 			// Does type match? (NULL matches always)
2433 			if (!fstype_matches(mtcur->mnt_type, fstype))
2434 				continue;
2435 
2436 			// Skip noauto and swap anyway
2437 			if ((parse_mount_options(mtcur->mnt_opts, NULL, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
2438 			// swap is bogus "fstype", parse_mount_options can't check fstypes
2439 			 || strcasecmp(mtcur->mnt_type, "swap") == 0
2440 			) {
2441 				continue;
2442 			}
2443 
2444 			// Does (at least one) option match?
2445 			// (NULL matches always)
2446 			if (!match_opt(mtcur->mnt_opts, O_optmatch))
2447 				continue;
2448 
2449 			resolve_mount_spec(&mtcur->mnt_fsname);
2450 
2451 			// NFS mounts want this to be xrealloc-able
2452 			mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2453 
2454 			// If nothing is mounted on this directory...
2455 			// (otherwise repeated "mount -a" mounts everything again)
2456 			mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2457 			// We do not check fsname match of found mount point -
2458 			// "/" may have fsname of "/dev/root" while fstab
2459 			// says "/dev/something_else".
2460 			if (mp) {
2461 				if (verbose) {
2462 					bb_error_msg("according to %s, "
2463 						"%s is already mounted on %s",
2464 						bb_path_mtab_file,
2465 						mp->mnt_fsname, mp->mnt_dir);
2466 				}
2467 			} else {
2468 				// ...mount this thing
2469 				if (singlemount(mtcur, /*ignore_busy:*/ 1)) {
2470 					// Count number of failed mounts
2471 					rc++;
2472 				}
2473 			}
2474 			free(mtcur->mnt_opts);
2475 		}
2476 	}
2477 
2478 	// End of fstab/mtab is reached.
2479 	// Were we looking for something specific?
2480 	if (argv[0]) { // yes
2481 		unsigned long l;
2482 
2483 		// If we didn't find anything, complain
2484 		if (!mtcur->mnt_fsname)
2485 			bb_error_msg_and_die("can't find %s in %s",
2486 				argv[0], fstabname);
2487 
2488 		// What happens when we try to "mount swap_partition"?
2489 		// (fstab containts "swap_partition swap swap defaults 0 0")
2490 		// util-linux-ng 2.13.1 does this:
2491 		// stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
2492 		// mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
2493 		// lstat("swap", 0x7fff62a3a640)           = -1 ENOENT (No such file or directory)
2494 		// write(2, "mount: mount point swap does not exist\n", 39) = 39
2495 		// exit_group(32)                          = ?
2496 #if 0
2497 		// In case we want to simply skip swap partitions:
2498 		l = parse_mount_options(mtcur->mnt_opts, NULL, NULL);
2499 		if ((l & MOUNT_SWAP)
2500 		// swap is bogus "fstype", parse_mount_options can't check fstypes
2501 		 || strcasecmp(mtcur->mnt_type, "swap") == 0
2502 		) {
2503 			goto ret;
2504 		}
2505 #endif
2506 		if (nonroot) {
2507 			// fstab must have "users" or "user"
2508 			l = parse_mount_options(mtcur->mnt_opts, NULL, NULL);
2509 			if (!(l & MOUNT_USERS))
2510 				bb_simple_error_msg_and_die(bb_msg_you_must_be_root);
2511 		}
2512 
2513 		//util-linux-2.12 does not do this check.
2514 		//// If nothing is mounted on this directory...
2515 		//// (otherwise repeated "mount FOO" mounts FOO again)
2516 		//mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2517 		//if (mp) {
2518 		//	bb_error_msg("according to %s, "
2519 		//		"%s is already mounted on %s",
2520 		//		bb_path_mtab_file,
2521 		//		mp->mnt_fsname, mp->mnt_dir);
2522 		//} else {
2523 			// ...mount the last thing we found
2524 			mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2525 			append_mount_options(&(mtcur->mnt_opts), cmdopts);
2526 			resolve_mount_spec(&mtpair->mnt_fsname);
2527 			rc = singlemount(mtcur, /*ignore_busy:*/ 0);
2528 			if (ENABLE_FEATURE_CLEAN_UP)
2529 				free(mtcur->mnt_opts);
2530 		//}
2531 	}
2532 
2533  //ret:
2534 	if (ENABLE_FEATURE_CLEAN_UP)
2535 		endmntent(fstab);
2536 	if (ENABLE_FEATURE_CLEAN_UP) {
2537 		free(storage_path);
2538 		free(cmdopts);
2539 	}
2540 
2541 //TODO: exitcode should be ORed mask of (from "man mount"):
2542 // 0 success
2543 // 1 incorrect invocation or permissions
2544 // 2 system error (out of memory, cannot fork, no more loop devices)
2545 // 4 internal mount bug or missing nfs support in mount
2546 // 8 user interrupt
2547 //16 problems writing or locking /etc/mtab
2548 //32 mount failure
2549 //64 some mount succeeded
2550 	return rc;
2551 }
2552