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