Beispiel #1
0
// Mount one directory.  Handles CIFS, NFS, loopback, autobind, and filesystem
// type detection.  Returns 0 for success, nonzero for failure.
// NB: mp->xxx fields may be trashed on exit
static int singlemount(struct mntent *mp, int ignore_busy)
{
	int rc = -1, vfsflags;
	char *loopFile = 0, *filteropts = 0;
	llist_t *fl = 0;
	struct stat st;

	vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);

	// Treat fstype "auto" as unspecified.

	if (mp->mnt_type && strcmp(mp->mnt_type,"auto") == 0)
		mp->mnt_type = 0;

	// Might this be an CIFS filesystem?

	if (ENABLE_FEATURE_MOUNT_CIFS
	 && (!mp->mnt_type || strcmp(mp->mnt_type,"cifs") == 0)
	 && (mp->mnt_fsname[0]=='/' || mp->mnt_fsname[0]=='\\')
	 && mp->mnt_fsname[0]==mp->mnt_fsname[1]
	) {
		len_and_sockaddr *lsa;
		char *ip, *dotted;
		char *s;

		rc = 1;
		// Replace '/' with '\' and verify that unc points to "//server/share".

		for (s = mp->mnt_fsname; *s; ++s)
			if (*s == '/') *s = '\\';

		// get server IP

		s = strrchr(mp->mnt_fsname, '\\');
		if (s <= mp->mnt_fsname+1) goto report_error;
		*s = '\0';
		lsa = host2sockaddr(mp->mnt_fsname+2, 0);
		*s = '\\';
		if (!lsa) goto report_error;

		// insert ip=... option into string flags.

		dotted = xmalloc_sockaddr2dotted_noport(&lsa->sa, lsa->len);
		ip = xasprintf("ip=%s", dotted);
		parse_mount_options(ip, &filteropts);

		// compose new unc '\\server-ip\share'
		// (s => slash after hostname)

		mp->mnt_fsname = xasprintf("\\\\%s%s", dotted, s);

		// lock is required
		vfsflags |= MS_MANDLOCK;

		mp->mnt_type = (char*)"cifs";
		rc = mount_it_now(mp, vfsflags, filteropts);
		if (ENABLE_FEATURE_CLEAN_UP) {
			free(mp->mnt_fsname);
			free(ip);
			free(dotted);
			free(lsa);
		}
		goto report_error;
	}

	// Might this be an NFS filesystem?

	if (ENABLE_FEATURE_MOUNT_NFS
	 && (!mp->mnt_type || !strcmp(mp->mnt_type,"nfs"))
	 && strchr(mp->mnt_fsname, ':') != NULL
	) {
		rc = nfsmount(mp, vfsflags, filteropts);
		goto report_error;
	}

	// Look at the file.  (Not found isn't a failure for remount, or for
	// a synthetic filesystem like proc or sysfs.)
	// (We use stat, not lstat, in order to allow
	// mount symlink_to_file_or_blkdev dir)

	if (!stat(mp->mnt_fsname, &st)
	 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
	) {
		// Do we need to allocate a loopback device for it?

		if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
			loopFile = bb_simplify_path(mp->mnt_fsname);
			mp->mnt_fsname = 0;
			switch (set_loop(&(mp->mnt_fsname), loopFile, 0)) {
			case 0:
			case 1:
				break;
			default:
				bb_error_msg( errno == EPERM || errno == EACCES
					? bb_msg_perm_denied_are_you_root
					: "cannot setup loop device");
				return errno;
			}

		// Autodetect bind mounts

		} else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
			vfsflags |= MS_BIND;
	}

	/* If we know the fstype (or don't need to), jump straight
	 * to the actual mount. */

	if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
		rc = mount_it_now(mp, vfsflags, filteropts);
	else {
		// Loop through filesystem types until mount succeeds
		// or we run out

		/* Initialize list of block backed filesystems.  This has to be
		 * done here so that during "mount -a", mounts after /proc shows up
		 * can autodetect. */

		if (!fslist) {
			fslist = get_block_backed_filesystems();
			if (ENABLE_FEATURE_CLEAN_UP && fslist)
				atexit(delete_block_backed_filesystems);
		}

		for (fl = fslist; fl; fl = fl->link) {
			mp->mnt_type = fl->data;
			rc = mount_it_now(mp, vfsflags, filteropts);
			if (!rc) break;
		}
	}

	// If mount failed, clean up loop file (if any).

	if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
		del_loop(mp->mnt_fsname);
		if (ENABLE_FEATURE_CLEAN_UP) {
			free(loopFile);
			free(mp->mnt_fsname);
		}
	}

 report_error:
	if (ENABLE_FEATURE_CLEAN_UP)
		free(filteropts);

	if (rc && errno == EBUSY && ignore_busy) rc = 0;
	if (rc < 0)
		/* perror here sometimes says "mounting ... on ... failed: Success" */
		bb_error_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);

	return rc;
}
Beispiel #2
0
// NB: mp->xxx fields may be trashed on exit
static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts)
{
	CLIENT *mclient;
	char *hostname;
	char *pathname;
	char *mounthost;
	struct nfs_mount_data data;
	char *opt;
	struct hostent *hp;
	struct sockaddr_in server_addr;
	struct sockaddr_in mount_server_addr;
	int msock, fsock;
	union {
		struct fhstatus nfsv2;
		struct mountres3 nfsv3;
	} status;
	int daemonized;
	char *s;
	int port;
	int mountport;
	int proto;
	int bg;
	int soft;
	int intr;
	int posix;
	int nocto;
	int noac;
	int nolock;
	int retry;
	int tcp;
	int mountprog;
	int mountvers;
	int nfsprog;
	int nfsvers;
	int retval;

	find_kernel_nfs_mount_version();

	daemonized = 0;
	mounthost = NULL;
	retval = ETIMEDOUT;
	msock = fsock = -1;
	mclient = NULL;

	/* NB: hostname, mounthost, filteropts must be free()d prior to return */

	filteropts = xstrdup(filteropts); /* going to trash it later... */

	hostname = xstrdup(mp->mnt_fsname);
	/* mount_main() guarantees that ':' is there */
	s = strchr(hostname, ':');
	pathname = s + 1;
	*s = '\0';
	/* Ignore all but first hostname in replicated mounts
	   until they can be fully supported. ([email protected]) */
	s = strchr(hostname, ',');
	if (s) {
		*s = '\0';
		bb_error_msg("warning: multiple hostnames not supported");
	}

	server_addr.sin_family = AF_INET;
	if (!inet_aton(hostname, &server_addr.sin_addr)) {
		hp = gethostbyname(hostname);
		if (hp == NULL) {
			bb_herror_msg("%s", hostname);
			goto fail;
		}
		if (hp->h_length > sizeof(struct in_addr)) {
			bb_error_msg("got bad hp->h_length");
			hp->h_length = sizeof(struct in_addr);
		}
		memcpy(&server_addr.sin_addr,
				hp->h_addr, hp->h_length);
	}

	memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));

	/* add IP address to mtab options for use when unmounting */

	if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
		mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
	} else {
		char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
					mp->mnt_opts[0] ? "," : "",
					inet_ntoa(server_addr.sin_addr));
		free(mp->mnt_opts);
		mp->mnt_opts = tmp;
	}

	/* Set default options.
	 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
	 * let the kernel decide.
	 * timeo is filled in after we know whether it'll be TCP or UDP. */
	memset(&data, 0, sizeof(data));
	data.retrans	= 3;
	data.acregmin	= 3;
	data.acregmax	= 60;
	data.acdirmin	= 30;
	data.acdirmax	= 60;
	data.namlen	= NAME_MAX;

	bg = 0;
	soft = 0;
	intr = 0;
	posix = 0;
	nocto = 0;
	nolock = 0;
	noac = 0;
	retry = 10000;		/* 10000 minutes ~ 1 week */
	tcp = 0;

	mountprog = MOUNTPROG;
	mountvers = 0;
	port = 0;
	mountport = 0;
	nfsprog = 100003;
	nfsvers = 0;

	/* parse options */
	if (filteropts)	for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
		char *opteq = strchr(opt, '=');
		if (opteq) {
			static const char *const options[] = {
				/* 0 */ "rsize",
				/* 1 */ "wsize",
				/* 2 */ "timeo",
				/* 3 */ "retrans",
				/* 4 */ "acregmin",
				/* 5 */ "acregmax",
				/* 6 */ "acdirmin",
				/* 7 */ "acdirmax",
				/* 8 */ "actimeo",
				/* 9 */ "retry",
				/* 10 */ "port",
				/* 11 */ "mountport",
				/* 12 */ "mounthost",
				/* 13 */ "mountprog",
				/* 14 */ "mountvers",
				/* 15 */ "nfsprog",
				/* 16 */ "nfsvers",
				/* 17 */ "vers",
				/* 18 */ "proto",
				/* 19 */ "namlen",
				/* 20 */ "addr",
				NULL
			};
			int val = xatoi_u(opteq + 1);
			*opteq = '\0';
			switch (index_in_str_array(options, opt)) {
			case 0: // "rsize"
				data.rsize = val;
				break;
			case 1: // "wsize"
				data.wsize = val;
				break;
			case 2: // "timeo"
				data.timeo = val;
				break;
			case 3: // "retrans"
				data.retrans = val;
				break;
			case 4: // "acregmin"
				data.acregmin = val;
				break;
			case 5: // "acregmax"
				data.acregmax = val;
				break;
			case 6: // "acdirmin"
				data.acdirmin = val;
				break;
			case 7: // "acdirmax"
				data.acdirmax = val;
				break;
			case 8: // "actimeo"
				data.acregmin = val;
				data.acregmax = val;
				data.acdirmin = val;
				data.acdirmax = val;
				break;
			case 9: // "retry"
				retry = val;
				break;
			case 10: // "port"
				port = val;
				break;
			case 11: // "mountport"
				mountport = val;
				break;
			case 12: // "mounthost"
				mounthost = xstrndup(opteq+1,
						strcspn(opteq+1," \t\n\r,"));
				break;
			case 13: // "mountprog"
				mountprog = val;
				break;
			case 14: // "mountvers"
				mountvers = val;
				break;
			case 15: // "nfsprog"
				nfsprog = val;
				break;
			case 16: // "nfsvers"
			case 17: // "vers"
				nfsvers = val;
				break;
			case 18: // "proto"
				if (!strncmp(opteq+1, "tcp", 3))
					tcp = 1;
				else if (!strncmp(opteq+1, "udp", 3))
					tcp = 0;
				else
					bb_error_msg("warning: unrecognized proto= option");
				break;
			case 19: // "namlen"
				if (nfs_mount_version >= 2)
					data.namlen = val;
				else
					bb_error_msg("warning: option namlen is not supported\n");
				break;
			case 20: // "addr" - ignore
				break;
			default:
				bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
				goto fail;
			}
		}
		else {
			static const char *const options[] = {
				"bg",
				"fg",
				"soft",
				"hard",
				"intr",
				"posix",
				"cto",
				"ac",
				"tcp",
				"udp",
				"lock",
				NULL
			};
			int val = 1;
			if (!strncmp(opt, "no", 2)) {
				val = 0;
				opt += 2;
			}
			switch (index_in_str_array(options, opt)) {
			case 0: // "bg"
				bg = val;
				break;
			case 1: // "fg"
				bg = !val;
				break;
			case 2: // "soft"
				soft = val;
				break;
			case 3: // "hard"
				soft = !val;
				break;
			case 4: // "intr"
				intr = val;
				break;
			case 5: // "posix"
				posix = val;
				break;
			case 6: // "cto"
				nocto = !val;
				break;
			case 7: // "ac"
				noac = !val;
				break;
			case 8: // "tcp"
				tcp = val;
				break;
			case 9: // "udp"
				tcp = !val;
				break;
			case 10: // "lock"
				if (nfs_mount_version >= 3)
					nolock = !val;
				else
					bb_error_msg("warning: option nolock is not supported");
				break;
			default:
				bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
				goto fail;
			}
		}
	}
	proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;

	data.flags = (soft ? NFS_MOUNT_SOFT : 0)
		| (intr ? NFS_MOUNT_INTR : 0)
		| (posix ? NFS_MOUNT_POSIX : 0)
		| (nocto ? NFS_MOUNT_NOCTO : 0)
		| (noac ? NFS_MOUNT_NOAC : 0);
	if (nfs_mount_version >= 2)
		data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
	if (nfs_mount_version >= 3)
		data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
	if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
		bb_error_msg("NFSv%d not supported", nfsvers);
		goto fail;
	}
	if (nfsvers && !mountvers)
		mountvers = (nfsvers < 3) ? 1 : nfsvers;
	if (nfsvers && nfsvers < mountvers) {
		mountvers = nfsvers;
	}

	/* Adjust options if none specified */
	if (!data.timeo)
		data.timeo = tcp ? 70 : 7;

	data.version = nfs_mount_version;

	if (vfsflags & MS_REMOUNT)
		goto do_mount;

	/*
	 * If the previous mount operation on the same host was
	 * backgrounded, and the "bg" for this mount is also set,
	 * give up immediately, to avoid the initial timeout.
	 */
	if (bg && we_saw_this_host_before(hostname)) {
		daemonized = daemonize(); /* parent or error */
		if (daemonized <= 0) { /* parent or error */
			retval = -daemonized;
			goto ret;
		}
	}

	/* create mount daemon client */
	/* See if the nfs host = mount host. */
	if (mounthost) {
		if (mounthost[0] >= '0' && mounthost[0] <= '9') {
			mount_server_addr.sin_family = AF_INET;
			mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
		} else {
			hp = gethostbyname(mounthost);
			if (hp == NULL) {
				bb_herror_msg("%s", mounthost);
				goto fail;
			} else {
				if (hp->h_length > sizeof(struct in_addr)) {
					bb_error_msg("got bad hp->h_length?");
					hp->h_length = sizeof(struct in_addr);
				}
				mount_server_addr.sin_family = AF_INET;
				memcpy(&mount_server_addr.sin_addr,
						hp->h_addr, hp->h_length);
			}
		}
	}

	/*
	 * The following loop implements the mount retries. When the mount
	 * times out, and the "bg" option is set, we background ourself
	 * and continue trying.
	 *
	 * The case where the mount point is not present and the "bg"
	 * option is set, is treated as a timeout. This is done to
	 * support nested mounts.
	 *
	 * The "retry" count specified by the user is the number of
	 * minutes to retry before giving up.
	 */
	{
		struct timeval total_timeout;
		struct timeval retry_timeout;
		struct pmap* pm_mnt;
		time_t t;
		time_t prevt;
		time_t timeout;

		retry_timeout.tv_sec = 3;
		retry_timeout.tv_usec = 0;
		total_timeout.tv_sec = 20;
		total_timeout.tv_usec = 0;
		timeout = time(NULL) + 60 * retry;
		prevt = 0;
		t = 30;
retry:
		/* be careful not to use too many CPU cycles */
		if (t - prevt < 30)
			sleep(30);

		pm_mnt = get_mountport(&mount_server_addr,
				mountprog,
				mountvers,
				proto,
				mountport);
		nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers;

		/* contact the mount daemon via TCP */
		mount_server_addr.sin_port = htons(pm_mnt->pm_port);
		msock = RPC_ANYSOCK;

		switch (pm_mnt->pm_prot) {
		case IPPROTO_UDP:
			mclient = clntudp_create(&mount_server_addr,
						 pm_mnt->pm_prog,
						 pm_mnt->pm_vers,
						 retry_timeout,
						 &msock);
			if (mclient)
				break;
			mount_server_addr.sin_port = htons(pm_mnt->pm_port);
			msock = RPC_ANYSOCK;
		case IPPROTO_TCP:
			mclient = clnttcp_create(&mount_server_addr,
						 pm_mnt->pm_prog,
						 pm_mnt->pm_vers,
						 &msock, 0, 0);
			break;
		default:
			mclient = 0;
		}
		if (!mclient) {
			if (!daemonized && prevt == 0)
				error_msg_rpc(clnt_spcreateerror(" "));
		} else {
			enum clnt_stat clnt_stat;
			/* try to mount hostname:pathname */
			mclient->cl_auth = authunix_create_default();

			/* make pointers in xdr_mountres3 NULL so
			 * that xdr_array allocates memory for us
			 */
			memset(&status, 0, sizeof(status));

			if (pm_mnt->pm_vers == 3)
				clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
					      (xdrproc_t) xdr_dirpath,
					      (caddr_t) &pathname,
					      (xdrproc_t) xdr_mountres3,
					      (caddr_t) &status,
					      total_timeout);
			else
				clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
					      (xdrproc_t) xdr_dirpath,
					      (caddr_t) &pathname,
					      (xdrproc_t) xdr_fhstatus,
					      (caddr_t) &status,
					      total_timeout);

			if (clnt_stat == RPC_SUCCESS)
				goto prepare_kernel_data; /* we're done */
			if (errno != ECONNREFUSED) {
				error_msg_rpc(clnt_sperror(mclient, " "));
				goto fail;	/* don't retry */
			}
			/* Connection refused */
			if (!daemonized && prevt == 0) /* print just once */
				error_msg_rpc(clnt_sperror(mclient, " "));
			auth_destroy(mclient->cl_auth);
			clnt_destroy(mclient);
			mclient = 0;
			close(msock);
		}

		/* Timeout. We are going to retry... maybe */

		if (!bg)
			goto fail;
		if (!daemonized) {
			daemonized = daemonize();
			if (daemonized <= 0) { /* parent or error */
				retval = -daemonized;
				goto ret;
			}
		}
		prevt = t;
		t = time(NULL);
		if (t >= timeout)
			/* TODO error message */
			goto fail;

		goto retry;
	}

prepare_kernel_data:

	if (nfsvers == 2) {
		if (status.nfsv2.fhs_status != 0) {
			bb_error_msg("%s:%s failed, reason given by server: %s",
				hostname, pathname,
				nfs_strerror(status.nfsv2.fhs_status));
			goto fail;
		}
		memcpy(data.root.data,
				(char *) status.nfsv2.fhstatus_u.fhs_fhandle,
				NFS_FHSIZE);
		data.root.size = NFS_FHSIZE;
		memcpy(data.old_root.data,
				(char *) status.nfsv2.fhstatus_u.fhs_fhandle,
				NFS_FHSIZE);
	} else {
		fhandle3 *my_fhandle;
		if (status.nfsv3.fhs_status != 0) {
			bb_error_msg("%s:%s failed, reason given by server: %s",
				hostname, pathname,
				nfs_strerror(status.nfsv3.fhs_status));
			goto fail;
		}
		my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
		memset(data.old_root.data, 0, NFS_FHSIZE);
		memset(&data.root, 0, sizeof(data.root));
		data.root.size = my_fhandle->fhandle3_len;
		memcpy(data.root.data,
				(char *) my_fhandle->fhandle3_val,
				my_fhandle->fhandle3_len);

		data.flags |= NFS_MOUNT_VER3;
	}

	/* create nfs socket for kernel */

	if (tcp) {
		if (nfs_mount_version < 3) {
			bb_error_msg("NFS over TCP is not supported");
			goto fail;
		}
		fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	} else
		fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (fsock < 0) {
		bb_perror_msg("nfs socket");
		goto fail;
	}
	if (bindresvport(fsock, 0) < 0) {
		bb_perror_msg("nfs bindresvport");
		goto fail;
	}
	if (port == 0) {
		server_addr.sin_port = PMAPPORT;
		port = pmap_getport(&server_addr, nfsprog, nfsvers,
					tcp ? IPPROTO_TCP : IPPROTO_UDP);
		if (port == 0)
			port = NFS_PORT;
	}
	server_addr.sin_port = htons(port);

	/* prepare data structure for kernel */

	data.fd = fsock;
	memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
	strncpy(data.hostname, hostname, sizeof(data.hostname));

	/* clean up */

	auth_destroy(mclient->cl_auth);
	clnt_destroy(mclient);
	close(msock);

	if (bg) {
		/* We must wait until mount directory is available */
		struct stat statbuf;
		int delay = 1;
		while (stat(mp->mnt_dir, &statbuf) == -1) {
			if (!daemonized) {
				daemonized = daemonize();
				if (daemonized <= 0) { /* parent or error */
					retval = -daemonized;
					goto ret;
				}
			}
			sleep(delay);	/* 1, 2, 4, 8, 16, 30, ... */
			delay *= 2;
			if (delay > 30)
				delay = 30;
		}
	}

do_mount: /* perform actual mount */

	mp->mnt_type = (char*)"nfs";
	retval = mount_it_now(mp, vfsflags, (char*)&data);
	goto ret;

fail:	/* abort */

	if (msock != -1) {
		if (mclient) {
			auth_destroy(mclient->cl_auth);
			clnt_destroy(mclient);
		}
		close(msock);
	}
	if (fsock != -1)
		close(fsock);

ret:
	free(hostname);
	free(mounthost);
	free(filteropts);
	return retval;
}
Beispiel #3
0
static int singlemount(struct mntent *mp, int ignore_busy)
{
	int rc = -1, vfsflags;
	char *loopFile = 0, *filteropts = 0;
	llist_t *fl = 0;
	struct stat st;

	vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);

	// Treat fstype "auto" as unspecified.

	if (mp->mnt_type && !strcmp(mp->mnt_type,"auto")) mp->mnt_type = 0;

	// Might this be an NFS filesystem?

	if (ENABLE_FEATURE_MOUNT_NFS &&
		(!mp->mnt_type || !strcmp(mp->mnt_type,"nfs")) &&
		strchr(mp->mnt_fsname, ':') != NULL)
	{
		if (nfsmount(mp->mnt_fsname, mp->mnt_dir, &vfsflags, &filteropts, 1)) {
			bb_perror_msg("nfsmount failed");
			goto report_error;
		} else {
			// Strangely enough, nfsmount() doesn't actually mount() anything.
			mp->mnt_type = "nfs";
			rc = mount_it_now(mp, vfsflags, filteropts);
			if (ENABLE_FEATURE_CLEAN_UP) free(filteropts);
			
			goto report_error;
		}
	}

	// Look at the file.  (Not found isn't a failure for remount, or for
	// a synthetic filesystem like proc or sysfs.)

	if (stat(mp->mnt_fsname, &st));
	else if (!(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
		// Do we need to allocate a loopback device for it?

		if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
			loopFile = bb_simplify_path(mp->mnt_fsname);
			mp->mnt_fsname = 0;
			switch(set_loop(&(mp->mnt_fsname), loopFile, 0)) {
				case 0:
				case 1:
					break;
				default:
					bb_error_msg( errno == EPERM || errno == EACCES
						? bb_msg_perm_denied_are_you_root
					   	: "Couldn't setup loop device");
					return errno;
			}

		// Autodetect bind mounts

		} else if (S_ISDIR(st.st_mode) && !mp->mnt_type) vfsflags |= MS_BIND;
	}

	/* If we know the fstype (or don't need to), jump straight
	 * to the actual mount. */

	if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
		rc = mount_it_now(mp, vfsflags, filteropts);

	// Loop through filesystem types until mount succeeds or we run out

	else {

		/* Initialize list of block backed filesystems.  This has to be
		 * done here so that during "mount -a", mounts after /proc shows up
		 * can autodetect. */

		if (!fslist) {
			fslist = get_block_backed_filesystems();
			if (ENABLE_FEATURE_CLEAN_UP && fslist)
				atexit(delete_block_backed_filesystems);
		}

		for (fl = fslist; fl; fl = fl->link) {
			mp->mnt_type = fl->data;

			if (!(rc = mount_it_now(mp,vfsflags, filteropts))) break;

			mp->mnt_type = 0;
		}
	}

	if (ENABLE_FEATURE_CLEAN_UP) free(filteropts);

	// If mount failed, clean up loop file (if any).

	if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
		del_loop(mp->mnt_fsname);
		if (ENABLE_FEATURE_CLEAN_UP) {
			free(loopFile);
			free(mp->mnt_fsname);
		}
	}
report_error:
	if (rc && errno == EBUSY && ignore_busy) rc = 0;
	if (rc < 0)
		bb_perror_msg("Mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);

	return rc;
}