Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6af8e11f authored by Elliott Hughes's avatar Elliott Hughes Committed by Gerrit Code Review
Browse files

Merge "Switch to toybox mount(1)."

parents fa76ffcc 24d41e76
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -46,7 +46,6 @@ OUR_TOOLS := \
    log \
    ls \
    lsof \
    mount \
    nandread \
    newfs_msdos \
    ps \

toolbox/mount.c

deleted100644 → 0
+0 −360
Original line number Diff line number Diff line
/*
 * mount.c, by rmk
 */

#include <sys/mount.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <linux/loop.h>

#define ARRAY_SIZE(x)	(sizeof(x) / sizeof(x[0]))

#define DEFAULT_LOOP_DEVICE "/dev/block/loop0"
#define LOOPDEV_MAXLEN 64

struct mount_opts {
	const char str[16];
	unsigned long rwmask;
	unsigned long rwset;
	unsigned long rwnoset;
};

struct extra_opts {
	char *str;
	char *end;
	int used_size;
	int alloc_size;
};

/*
 * These options define the function of "mount(2)".
 */
#define MS_TYPE	(MS_REMOUNT|MS_BIND|MS_MOVE)


static const struct mount_opts options[] = {
	/* name		mask		set		noset		*/
	{ "async",	MS_SYNCHRONOUS,	0,		MS_SYNCHRONOUS	},
	{ "atime",	MS_NOATIME,	0,		MS_NOATIME	},
	{ "bind",	MS_TYPE,	MS_BIND,	0,		},
	{ "dev",	MS_NODEV,	0,		MS_NODEV	},
	{ "diratime",	MS_NODIRATIME,	0,		MS_NODIRATIME	},
	{ "dirsync",	MS_DIRSYNC,	MS_DIRSYNC,	0		},
	{ "exec",	MS_NOEXEC,	0,		MS_NOEXEC	},
	{ "move",	MS_TYPE,	MS_MOVE,	0		},
	{ "recurse",	MS_REC,		MS_REC,		0		},
	{ "rec",	MS_REC,		MS_REC,		0		},
	{ "remount",	MS_TYPE,	MS_REMOUNT,	0		},
	{ "ro",		MS_RDONLY,	MS_RDONLY,	0		},
	{ "rw",		MS_RDONLY,	0,		MS_RDONLY	},
	{ "suid",	MS_NOSUID,	0,		MS_NOSUID	},
	{ "sync",	MS_SYNCHRONOUS,	MS_SYNCHRONOUS,	0		},
	{ "verbose",	MS_VERBOSE,	MS_VERBOSE,	0		},
	{ "unbindable",	MS_UNBINDABLE,	MS_UNBINDABLE,	0		},
	{ "private",	MS_PRIVATE,	MS_PRIVATE,	0		},
	{ "slave",	MS_SLAVE,	MS_SLAVE,	0		},
	{ "shared",	MS_SHARED,	MS_SHARED,	0		},
};

static void add_extra_option(struct extra_opts *extra, char *s)
{
	int len = strlen(s);
	int newlen;

	if (extra->str)
	       len++;			/* +1 for ',' */
	newlen = extra->used_size + len;

	if (newlen >= extra->alloc_size) {
		char *new;

		new = realloc(extra->str, newlen + 1);	/* +1 for NUL */
		if (!new)
			return;

		extra->str = new;
		extra->end = extra->str + extra->used_size;
		extra->alloc_size = newlen + 1;
	}

	if (extra->used_size) {
		*extra->end = ',';
		extra->end++;
	}
	strcpy(extra->end, s);
	extra->used_size += len;

}

static unsigned long
parse_mount_options(char *arg, unsigned long rwflag, struct extra_opts *extra, int* loop, char *loopdev)
{
	char *s;
    
    *loop = 0;
	while ((s = strsep(&arg, ",")) != NULL) {
		char *opt = s;
		unsigned int i;
		int res, no = s[0] == 'n' && s[1] == 'o';

		if (no)
			s += 2;

        if (strncmp(s, "loop=", 5) == 0) {
            *loop = 1;
            strlcpy(loopdev, s + 5, LOOPDEV_MAXLEN);
            continue;
        }

        if (strcmp(s, "loop") == 0) {
            *loop = 1;
            strlcpy(loopdev, DEFAULT_LOOP_DEVICE, LOOPDEV_MAXLEN);
            continue;
        }
		for (i = 0, res = 1; i < ARRAY_SIZE(options); i++) {
			res = strcmp(s, options[i].str);

			if (res == 0) {
				rwflag &= ~options[i].rwmask;
				if (no)
					rwflag |= options[i].rwnoset;
				else
					rwflag |= options[i].rwset;
			}
			if (res <= 0)
				break;
		}

		if (res != 0 && s[0])
			add_extra_option(extra, opt);
	}

	return rwflag;
}

/*
 * Mark the given block device as read-write, using the BLKROSET ioctl.
 */
static void fs_set_blk_rw(const char *blockdev)
{
    int fd;
    int OFF = 0;

    fd = open(blockdev, O_RDONLY);
    if (fd < 0) {
        // should never happen
        return;
    }

    ioctl(fd, BLKROSET, &OFF);
    close(fd);
}

static char *progname;

static struct extra_opts extra;
static unsigned long rwflag;

static int
do_mount(char *dev, char *dir, char *type, unsigned long rwflag, void *data, int loop,
         char *loopdev)
{
	char *s;
	int error = 0;

    if (loop) {
        int file_fd, device_fd;
        int flags;

        flags = (rwflag & MS_RDONLY) ? O_RDONLY : O_RDWR;
        
        file_fd = open(dev, flags);
        if (file_fd < 0) {
            perror("open backing file failed");
            return 1;
        }
        device_fd = open(loopdev, flags);
        if (device_fd < 0) {
            perror("open loop device failed");
            close(file_fd);
            return 1;
        }
        if (ioctl(device_fd, LOOP_SET_FD, file_fd) < 0) {
            perror("ioctl LOOP_SET_FD failed");
            close(file_fd);
            close(device_fd);
            return 1;
        }

        close(file_fd);
        close(device_fd);
        dev = loopdev;
    }

    if ((rwflag & MS_RDONLY) == 0) {
        fs_set_blk_rw(dev);
    }

	while ((s = strsep(&type, ",")) != NULL) {
retry:
		if (mount(dev, dir, s, rwflag, data) == -1) {
			error = errno;
			/*
			 * If the filesystem is not found, or the
			 * superblock is invalid, try the next.
			 */
			if (error == ENODEV || error == EINVAL)
				continue;

			/*
			 * If we get EACCESS, and we're trying to
			 * mount readwrite and this isn't a remount,
			 * try read only.
			 */
			if (error == EACCES &&
			    (rwflag & (MS_REMOUNT|MS_RDONLY)) == 0) {
				rwflag |= MS_RDONLY;
				goto retry;
			}
			break;
		}
	}

	if (error) {
		errno = error;
		perror("mount");
		return 255;
	}

	return 0;
}

static int print_mounts()
{
    FILE* f;
    int length;
    char buffer[100];
    
    f = fopen("/proc/mounts", "r");
    if (!f) {
        fprintf(stdout, "could not open /proc/mounts\n");
        return -1;
    }

    do {
        length = fread(buffer, 1, 100, f);
        if (length > 0)
            fwrite(buffer, 1, length, stdout);
    } while (length > 0);

    fclose(f);
    return 0;
}

static int get_mounts_dev_dir(const char *arg, char **dev, char **dir)
{
	FILE *f;
	char mount_dev[256];
	char mount_dir[256];
	char mount_type[256];
	char mount_opts[256];
	int mount_freq;
	int mount_passno;
	int match;

	f = fopen("/proc/mounts", "r");
	if (!f) {
		fprintf(stdout, "could not open /proc/mounts\n");
		return -1;
	}

	do {
		match = fscanf(f, "%255s %255s %255s %255s %d %d\n",
					   mount_dev, mount_dir, mount_type,
					   mount_opts, &mount_freq, &mount_passno);
		mount_dev[255] = 0;
		mount_dir[255] = 0;
		mount_type[255] = 0;
		mount_opts[255] = 0;
		if (match == 6 &&
			(strcmp(arg, mount_dev) == 0 ||
			 strcmp(arg, mount_dir) == 0)) {
			*dev = strdup(mount_dev);
			*dir = strdup(mount_dir);
			fclose(f);
			return 0;
		}
	} while (match != EOF);

	fclose(f);
	return -1;
}

int mount_main(int argc, char *argv[])
{
	char *type = NULL;
	char *dev = NULL;
	char *dir = NULL;
	int c;
	int loop = 0;
	char loopdev[LOOPDEV_MAXLEN];

	progname = argv[0];
	rwflag = MS_VERBOSE;
	
	// mount with no arguments is equivalent to "cat /proc/mounts"
	if (argc == 1) return print_mounts();

	do {
		c = getopt(argc, argv, "o:rt:w");
		if (c == EOF)
			break;
		switch (c) {
		case 'o':
			rwflag = parse_mount_options(optarg, rwflag, &extra, &loop, loopdev);
			break;
		case 'r':
			rwflag |= MS_RDONLY;
			break;
		case 't':
			type = optarg;
			break;
		case 'w':
			rwflag &= ~MS_RDONLY;
			break;
		case '?':
			fprintf(stderr, "%s: invalid option -%c\n",
				progname, optopt);
			exit(1);
		}
	} while (1);

	/*
	 * If remount, bind or move was specified, then we don't
	 * have a "type" as such.  Use the dummy "none" type.
	 */
	if (rwflag & MS_TYPE)
		type = "none";

	if (optind + 2 == argc) {
		dev = argv[optind];
		dir = argv[optind + 1];
	} else if (optind + 1 == argc && rwflag & MS_REMOUNT) {
		get_mounts_dev_dir(argv[optind], &dev, &dir);
	}

	if (dev == NULL || dir == NULL || type == NULL) {
		fprintf(stderr, "Usage: %s [-r] [-w] [-o options] [-t type] "
			"device directory\n", progname);
		exit(1);
	}

	return do_mount(dev, dir, type, rwflag, extra.str, loop, loopdev);
	/* We leak dev and dir in some cases, but we're about to exit */
}