| The Android Open Source Project | 4f6e8d7 | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * mount.c, by rmk | 
 | 3 |  */ | 
 | 4 |  | 
 | 5 | #include <sys/mount.h> | 
 | 6 | #include <sys/stat.h> | 
 | 7 | #include <fcntl.h> | 
 | 8 | #include <errno.h> | 
 | 9 | #include <stdio.h> | 
 | 10 | #include <stdlib.h> | 
 | 11 | #include <string.h> | 
 | 12 | #include <unistd.h> | 
 | 13 |  | 
 | 14 | #include <linux/loop.h> | 
 | 15 |  | 
 | 16 | #define ARRAY_SIZE(x)	(sizeof(x) / sizeof(x[0])) | 
 | 17 |  | 
| Ken Sumrall | 940c810 | 2011-07-12 19:47:06 -0700 | [diff] [blame] | 18 | #define DEFAULT_LOOP_DEVICE "/dev/block/loop0" | 
 | 19 | #define LOOPDEV_MAXLEN 64 | 
| The Android Open Source Project | 4f6e8d7 | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 20 |  | 
 | 21 | struct mount_opts { | 
| Nick Kralevich | 29a5298 | 2013-04-24 16:31:02 -0700 | [diff] [blame] | 22 | 	const char str[16]; | 
| The Android Open Source Project | 4f6e8d7 | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 23 | 	unsigned long rwmask; | 
 | 24 | 	unsigned long rwset; | 
 | 25 | 	unsigned long rwnoset; | 
 | 26 | }; | 
 | 27 |  | 
 | 28 | struct extra_opts { | 
 | 29 | 	char *str; | 
 | 30 | 	char *end; | 
 | 31 | 	int used_size; | 
 | 32 | 	int alloc_size; | 
 | 33 | }; | 
 | 34 |  | 
 | 35 | /* | 
 | 36 |  * These options define the function of "mount(2)". | 
 | 37 |  */ | 
 | 38 | #define MS_TYPE	(MS_REMOUNT|MS_BIND|MS_MOVE) | 
 | 39 |  | 
 | 40 |  | 
 | 41 | static const struct mount_opts options[] = { | 
 | 42 | 	/* name		mask		set		noset		*/ | 
 | 43 | 	{ "async",	MS_SYNCHRONOUS,	0,		MS_SYNCHRONOUS	}, | 
 | 44 | 	{ "atime",	MS_NOATIME,	0,		MS_NOATIME	}, | 
 | 45 | 	{ "bind",	MS_TYPE,	MS_BIND,	0,		}, | 
 | 46 | 	{ "dev",	MS_NODEV,	0,		MS_NODEV	}, | 
 | 47 | 	{ "diratime",	MS_NODIRATIME,	0,		MS_NODIRATIME	}, | 
 | 48 | 	{ "dirsync",	MS_DIRSYNC,	MS_DIRSYNC,	0		}, | 
 | 49 | 	{ "exec",	MS_NOEXEC,	0,		MS_NOEXEC	}, | 
 | 50 | 	{ "move",	MS_TYPE,	MS_MOVE,	0		}, | 
 | 51 | 	{ "recurse",	MS_REC,		MS_REC,		0		}, | 
| Jeff Sharkey | bfcd810 | 2012-08-22 13:57:25 -0700 | [diff] [blame] | 52 | 	{ "rec",	MS_REC,		MS_REC,		0		}, | 
| The Android Open Source Project | 4f6e8d7 | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 53 | 	{ "remount",	MS_TYPE,	MS_REMOUNT,	0		}, | 
 | 54 | 	{ "ro",		MS_RDONLY,	MS_RDONLY,	0		}, | 
 | 55 | 	{ "rw",		MS_RDONLY,	0,		MS_RDONLY	}, | 
 | 56 | 	{ "suid",	MS_NOSUID,	0,		MS_NOSUID	}, | 
 | 57 | 	{ "sync",	MS_SYNCHRONOUS,	MS_SYNCHRONOUS,	0		}, | 
 | 58 | 	{ "verbose",	MS_VERBOSE,	MS_VERBOSE,	0		}, | 
| Jeff Sharkey | bfcd810 | 2012-08-22 13:57:25 -0700 | [diff] [blame] | 59 | 	{ "unbindable",	MS_UNBINDABLE,	MS_UNBINDABLE,	0		}, | 
 | 60 | 	{ "private",	MS_PRIVATE,	MS_PRIVATE,	0		}, | 
 | 61 | 	{ "slave",	MS_SLAVE,	MS_SLAVE,	0		}, | 
 | 62 | 	{ "shared",	MS_SHARED,	MS_SHARED,	0		}, | 
| The Android Open Source Project | 4f6e8d7 | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 63 | }; | 
 | 64 |  | 
 | 65 | static void add_extra_option(struct extra_opts *extra, char *s) | 
 | 66 | { | 
 | 67 | 	int len = strlen(s); | 
| Nick Kralevich | 29a5298 | 2013-04-24 16:31:02 -0700 | [diff] [blame] | 68 | 	int newlen; | 
| The Android Open Source Project | 4f6e8d7 | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 69 |  | 
 | 70 | 	if (extra->str) | 
 | 71 | 	       len++;			/* +1 for ',' */ | 
| Nick Kralevich | 29a5298 | 2013-04-24 16:31:02 -0700 | [diff] [blame] | 72 | 	newlen = extra->used_size + len; | 
| The Android Open Source Project | 4f6e8d7 | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 73 |  | 
 | 74 | 	if (newlen >= extra->alloc_size) { | 
 | 75 | 		char *new; | 
 | 76 |  | 
 | 77 | 		new = realloc(extra->str, newlen + 1);	/* +1 for NUL */ | 
 | 78 | 		if (!new) | 
 | 79 | 			return; | 
 | 80 |  | 
 | 81 | 		extra->str = new; | 
 | 82 | 		extra->end = extra->str + extra->used_size; | 
| Nick Kralevich | 29a5298 | 2013-04-24 16:31:02 -0700 | [diff] [blame] | 83 | 		extra->alloc_size = newlen + 1; | 
| The Android Open Source Project | 4f6e8d7 | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 84 | 	} | 
 | 85 |  | 
 | 86 | 	if (extra->used_size) { | 
 | 87 | 		*extra->end = ','; | 
 | 88 | 		extra->end++; | 
 | 89 | 	} | 
 | 90 | 	strcpy(extra->end, s); | 
 | 91 | 	extra->used_size += len; | 
 | 92 |  | 
 | 93 | } | 
 | 94 |  | 
 | 95 | static unsigned long | 
| Ken Sumrall | 940c810 | 2011-07-12 19:47:06 -0700 | [diff] [blame] | 96 | parse_mount_options(char *arg, unsigned long rwflag, struct extra_opts *extra, int* loop, char *loopdev) | 
| The Android Open Source Project | 4f6e8d7 | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 97 | { | 
 | 98 | 	char *s; | 
 | 99 |      | 
 | 100 |     *loop = 0; | 
 | 101 | 	while ((s = strsep(&arg, ",")) != NULL) { | 
 | 102 | 		char *opt = s; | 
 | 103 | 		unsigned int i; | 
 | 104 | 		int res, no = s[0] == 'n' && s[1] == 'o'; | 
 | 105 |  | 
 | 106 | 		if (no) | 
 | 107 | 			s += 2; | 
 | 108 |  | 
| Ken Sumrall | 940c810 | 2011-07-12 19:47:06 -0700 | [diff] [blame] | 109 |         if (strncmp(s, "loop=", 5) == 0) { | 
 | 110 |             *loop = 1; | 
 | 111 |             strlcpy(loopdev, s + 5, LOOPDEV_MAXLEN); | 
 | 112 |             continue; | 
 | 113 |         } | 
 | 114 |  | 
| The Android Open Source Project | 4f6e8d7 | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 115 |         if (strcmp(s, "loop") == 0) { | 
 | 116 |             *loop = 1; | 
| Ken Sumrall | 940c810 | 2011-07-12 19:47:06 -0700 | [diff] [blame] | 117 |             strlcpy(loopdev, DEFAULT_LOOP_DEVICE, LOOPDEV_MAXLEN); | 
| The Android Open Source Project | 4f6e8d7 | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 118 |             continue; | 
 | 119 |         } | 
 | 120 | 		for (i = 0, res = 1; i < ARRAY_SIZE(options); i++) { | 
 | 121 | 			res = strcmp(s, options[i].str); | 
 | 122 |  | 
 | 123 | 			if (res == 0) { | 
 | 124 | 				rwflag &= ~options[i].rwmask; | 
 | 125 | 				if (no) | 
 | 126 | 					rwflag |= options[i].rwnoset; | 
 | 127 | 				else | 
 | 128 | 					rwflag |= options[i].rwset; | 
 | 129 | 			} | 
 | 130 | 			if (res <= 0) | 
 | 131 | 				break; | 
 | 132 | 		} | 
 | 133 |  | 
 | 134 | 		if (res != 0 && s[0]) | 
 | 135 | 			add_extra_option(extra, opt); | 
 | 136 | 	} | 
 | 137 |  | 
 | 138 | 	return rwflag; | 
 | 139 | } | 
 | 140 |  | 
| Nick Kralevich | e18c0d5 | 2013-04-16 16:41:32 -0700 | [diff] [blame] | 141 | /* | 
 | 142 |  * Mark the given block device as read-write, using the BLKROSET ioctl. | 
 | 143 |  */ | 
 | 144 | static void fs_set_blk_rw(const char *blockdev) | 
 | 145 | { | 
 | 146 |     int fd; | 
 | 147 |     int OFF = 0; | 
 | 148 |  | 
 | 149 |     fd = open(blockdev, O_RDONLY); | 
 | 150 |     if (fd < 0) { | 
 | 151 |         // should never happen | 
 | 152 |         return; | 
 | 153 |     } | 
 | 154 |  | 
 | 155 |     ioctl(fd, BLKROSET, &OFF); | 
 | 156 |     close(fd); | 
 | 157 | } | 
 | 158 |  | 
| The Android Open Source Project | 4f6e8d7 | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 159 | static char *progname; | 
 | 160 |  | 
 | 161 | static struct extra_opts extra; | 
 | 162 | static unsigned long rwflag; | 
 | 163 |  | 
 | 164 | static int | 
| Ken Sumrall | 940c810 | 2011-07-12 19:47:06 -0700 | [diff] [blame] | 165 | do_mount(char *dev, char *dir, char *type, unsigned long rwflag, void *data, int loop, | 
 | 166 |          char *loopdev) | 
| The Android Open Source Project | 4f6e8d7 | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 167 | { | 
 | 168 | 	char *s; | 
 | 169 | 	int error = 0; | 
 | 170 |  | 
 | 171 |     if (loop) { | 
 | 172 |         int file_fd, device_fd; | 
| Jay Freeman (saurik) | bc7b0cb | 2008-11-16 13:25:10 +0000 | [diff] [blame] | 173 |         int flags; | 
 | 174 |  | 
 | 175 |         flags = (rwflag & MS_RDONLY) ? O_RDONLY : O_RDWR; | 
| The Android Open Source Project | 4f6e8d7 | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 176 |          | 
| Jay Freeman (saurik) | bc7b0cb | 2008-11-16 13:25:10 +0000 | [diff] [blame] | 177 |         file_fd = open(dev, flags); | 
| Ken Sumrall | 940c810 | 2011-07-12 19:47:06 -0700 | [diff] [blame] | 178 |         if (file_fd < 0) { | 
| The Android Open Source Project | 4f6e8d7 | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 179 |             perror("open backing file failed"); | 
 | 180 |             return 1; | 
 | 181 |         } | 
| Ken Sumrall | 940c810 | 2011-07-12 19:47:06 -0700 | [diff] [blame] | 182 |         device_fd = open(loopdev, flags); | 
 | 183 |         if (device_fd < 0) { | 
| The Android Open Source Project | 4f6e8d7 | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 184 |             perror("open loop device failed"); | 
 | 185 |             close(file_fd); | 
 | 186 |             return 1; | 
 | 187 |         } | 
 | 188 |         if (ioctl(device_fd, LOOP_SET_FD, file_fd) < 0) { | 
 | 189 |             perror("ioctl LOOP_SET_FD failed"); | 
 | 190 |             close(file_fd); | 
 | 191 |             close(device_fd); | 
 | 192 |             return 1; | 
 | 193 |         } | 
 | 194 |  | 
 | 195 |         close(file_fd); | 
 | 196 |         close(device_fd); | 
| Ken Sumrall | 940c810 | 2011-07-12 19:47:06 -0700 | [diff] [blame] | 197 |         dev = loopdev; | 
| The Android Open Source Project | 4f6e8d7 | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 198 |     } | 
 | 199 |  | 
| Nick Kralevich | e18c0d5 | 2013-04-16 16:41:32 -0700 | [diff] [blame] | 200 |     if ((rwflag & MS_RDONLY) == 0) { | 
 | 201 |         fs_set_blk_rw(dev); | 
 | 202 |     } | 
 | 203 |  | 
| The Android Open Source Project | 4f6e8d7 | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 204 | 	while ((s = strsep(&type, ",")) != NULL) { | 
 | 205 | retry: | 
 | 206 | 		if (mount(dev, dir, s, rwflag, data) == -1) { | 
 | 207 | 			error = errno; | 
 | 208 | 			/* | 
 | 209 | 			 * If the filesystem is not found, or the | 
 | 210 | 			 * superblock is invalid, try the next. | 
 | 211 | 			 */ | 
 | 212 | 			if (error == ENODEV || error == EINVAL) | 
 | 213 | 				continue; | 
 | 214 |  | 
 | 215 | 			/* | 
 | 216 | 			 * If we get EACCESS, and we're trying to | 
 | 217 | 			 * mount readwrite and this isn't a remount, | 
 | 218 | 			 * try read only. | 
 | 219 | 			 */ | 
 | 220 | 			if (error == EACCES && | 
 | 221 | 			    (rwflag & (MS_REMOUNT|MS_RDONLY)) == 0) { | 
 | 222 | 				rwflag |= MS_RDONLY; | 
 | 223 | 				goto retry; | 
 | 224 | 			} | 
 | 225 | 			break; | 
 | 226 | 		} | 
 | 227 | 	} | 
 | 228 |  | 
 | 229 | 	if (error) { | 
 | 230 | 		errno = error; | 
 | 231 | 		perror("mount"); | 
 | 232 | 		return 255; | 
 | 233 | 	} | 
 | 234 |  | 
 | 235 | 	return 0; | 
 | 236 | } | 
 | 237 |  | 
 | 238 | static int print_mounts() | 
 | 239 | { | 
 | 240 |     FILE* f; | 
 | 241 |     int length; | 
 | 242 |     char buffer[100]; | 
 | 243 |      | 
 | 244 |     f = fopen("/proc/mounts", "r"); | 
 | 245 |     if (!f) { | 
 | 246 |         fprintf(stdout, "could not open /proc/mounts\n"); | 
 | 247 |         return -1; | 
 | 248 |     } | 
 | 249 |  | 
 | 250 |     do { | 
 | 251 |         length = fread(buffer, 1, 100, f); | 
 | 252 |         if (length > 0) | 
 | 253 |             fwrite(buffer, 1, length, stdout); | 
 | 254 |     } while (length > 0); | 
 | 255 |  | 
 | 256 |     fclose(f); | 
 | 257 |     return 0; | 
 | 258 | } | 
 | 259 |  | 
| Colin Cross | 4e7dd3d | 2010-05-06 16:33:30 -0700 | [diff] [blame] | 260 | static int get_mounts_dev_dir(const char *arg, char **dev, char **dir) | 
 | 261 | { | 
 | 262 | 	FILE *f; | 
 | 263 | 	char mount_dev[256]; | 
 | 264 | 	char mount_dir[256]; | 
 | 265 | 	char mount_type[256]; | 
 | 266 | 	char mount_opts[256]; | 
 | 267 | 	int mount_freq; | 
 | 268 | 	int mount_passno; | 
 | 269 | 	int match; | 
 | 270 |  | 
 | 271 | 	f = fopen("/proc/mounts", "r"); | 
 | 272 | 	if (!f) { | 
 | 273 | 		fprintf(stdout, "could not open /proc/mounts\n"); | 
 | 274 | 		return -1; | 
 | 275 | 	} | 
 | 276 |  | 
 | 277 | 	do { | 
 | 278 | 		match = fscanf(f, "%255s %255s %255s %255s %d %d\n", | 
 | 279 | 					   mount_dev, mount_dir, mount_type, | 
 | 280 | 					   mount_opts, &mount_freq, &mount_passno); | 
 | 281 | 		mount_dev[255] = 0; | 
 | 282 | 		mount_dir[255] = 0; | 
 | 283 | 		mount_type[255] = 0; | 
 | 284 | 		mount_opts[255] = 0; | 
 | 285 | 		if (match == 6 && | 
 | 286 | 			(strcmp(arg, mount_dev) == 0 || | 
 | 287 | 			 strcmp(arg, mount_dir) == 0)) { | 
 | 288 | 			*dev = strdup(mount_dev); | 
 | 289 | 			*dir = strdup(mount_dir); | 
 | 290 | 			fclose(f); | 
 | 291 | 			return 0; | 
 | 292 | 		} | 
 | 293 | 	} while (match != EOF); | 
 | 294 |  | 
 | 295 | 	fclose(f); | 
 | 296 | 	return -1; | 
 | 297 | } | 
 | 298 |  | 
| The Android Open Source Project | 4f6e8d7 | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 299 | int mount_main(int argc, char *argv[]) | 
 | 300 | { | 
 | 301 | 	char *type = NULL; | 
| Colin Cross | 4e7dd3d | 2010-05-06 16:33:30 -0700 | [diff] [blame] | 302 | 	char *dev = NULL; | 
 | 303 | 	char *dir = NULL; | 
| The Android Open Source Project | 4f6e8d7 | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 304 | 	int c; | 
| Dima Zavin | 383688b | 2009-06-29 15:50:20 -0700 | [diff] [blame] | 305 | 	int loop = 0; | 
| Ken Sumrall | 940c810 | 2011-07-12 19:47:06 -0700 | [diff] [blame] | 306 | 	char loopdev[LOOPDEV_MAXLEN]; | 
| The Android Open Source Project | 4f6e8d7 | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 307 |  | 
 | 308 | 	progname = argv[0]; | 
 | 309 | 	rwflag = MS_VERBOSE; | 
 | 310 | 	 | 
 | 311 | 	// mount with no arguments is equivalent to "cat /proc/mounts" | 
 | 312 | 	if (argc == 1) return print_mounts(); | 
 | 313 |  | 
 | 314 | 	do { | 
 | 315 | 		c = getopt(argc, argv, "o:rt:w"); | 
 | 316 | 		if (c == EOF) | 
 | 317 | 			break; | 
 | 318 | 		switch (c) { | 
 | 319 | 		case 'o': | 
| Ken Sumrall | 940c810 | 2011-07-12 19:47:06 -0700 | [diff] [blame] | 320 | 			rwflag = parse_mount_options(optarg, rwflag, &extra, &loop, loopdev); | 
| The Android Open Source Project | 4f6e8d7 | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 321 | 			break; | 
 | 322 | 		case 'r': | 
 | 323 | 			rwflag |= MS_RDONLY; | 
 | 324 | 			break; | 
 | 325 | 		case 't': | 
 | 326 | 			type = optarg; | 
 | 327 | 			break; | 
 | 328 | 		case 'w': | 
 | 329 | 			rwflag &= ~MS_RDONLY; | 
 | 330 | 			break; | 
 | 331 | 		case '?': | 
 | 332 | 			fprintf(stderr, "%s: invalid option -%c\n", | 
 | 333 | 				progname, optopt); | 
 | 334 | 			exit(1); | 
 | 335 | 		} | 
 | 336 | 	} while (1); | 
 | 337 |  | 
 | 338 | 	/* | 
 | 339 | 	 * If remount, bind or move was specified, then we don't | 
 | 340 | 	 * have a "type" as such.  Use the dummy "none" type. | 
 | 341 | 	 */ | 
 | 342 | 	if (rwflag & MS_TYPE) | 
 | 343 | 		type = "none"; | 
 | 344 |  | 
| Colin Cross | 4e7dd3d | 2010-05-06 16:33:30 -0700 | [diff] [blame] | 345 | 	if (optind + 2 == argc) { | 
 | 346 | 		dev = argv[optind]; | 
 | 347 | 		dir = argv[optind + 1]; | 
 | 348 | 	} else if (optind + 1 == argc && rwflag & MS_REMOUNT) { | 
 | 349 | 		get_mounts_dev_dir(argv[optind], &dev, &dir); | 
 | 350 | 	} | 
 | 351 |  | 
 | 352 | 	if (dev == NULL || dir == NULL || type == NULL) { | 
| The Android Open Source Project | 4f6e8d7 | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 353 | 		fprintf(stderr, "Usage: %s [-r] [-w] [-o options] [-t type] " | 
 | 354 | 			"device directory\n", progname); | 
 | 355 | 		exit(1); | 
 | 356 | 	} | 
 | 357 |  | 
| Ken Sumrall | 940c810 | 2011-07-12 19:47:06 -0700 | [diff] [blame] | 358 | 	return do_mount(dev, dir, type, rwflag, extra.str, loop, loopdev); | 
| Colin Cross | 4e7dd3d | 2010-05-06 16:33:30 -0700 | [diff] [blame] | 359 | 	/* We leak dev and dir in some cases, but we're about to exit */ | 
| The Android Open Source Project | 4f6e8d7 | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 360 | } |