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