1 #define _GNU_SOURCE
2 
3 #include <stdio.h>
4 #include <getopt.h>
5 #include <string.h>
6 #include <unistd.h>
7 #include <limits.h>
8 #include <ext2fs/ext2fs.h>
9 
10 #include "perms.h"
11 #include "base_fs.h"
12 #include "block_list.h"
13 #include "basefs_allocator.h"
14 #include "create_inode.h"
15 
16 #ifndef UID_GID_MAP_MAX_EXTENTS
17 /*
18  * The value is defined in linux/user_namspace.h.
19  * The value is (arbitrarily) 5 in 4.14 and earlier, or 340 in 4.15 and later.
20  * Here, the bigger value is taken. See also man user_namespace(7).
21  */
22 #define UID_GID_MAP_MAX_EXTENTS 340
23 #endif
24 
25 static char *prog_name = "e2fsdroid";
26 static char *in_file;
27 static char *block_list;
28 static char *basefs_out;
29 static char *basefs_in;
30 static char *mountpoint = "";
31 static time_t fixed_time = -1;
32 static char *fs_config_file;
33 static struct selinux_opt seopt_file[8];
34 static int max_nr_opt = (int)sizeof(seopt_file) / sizeof(seopt_file[0]);
35 static char *product_out;
36 static char *src_dir;
37 static int android_configure;
38 static int android_sparse_file = 1;
39 
usage(int ret)40 static void usage(int ret)
41 {
42 	fprintf(stderr, "%s [-B block_list] [-D basefs_out] [-T timestamp]\n"
43 			"\t[-C fs_config] [-S file_contexts] [-p product_out]\n"
44 			"\t[-a mountpoint] [-d basefs_in] [-f src_dir] [-e] [-s]\n"
45 			"\t[-u uid-mapping] [-g gid-mapping] image\n",
46                 prog_name);
47 	exit(ret);
48 }
49 
absolute_path(const char * file)50 static char *absolute_path(const char *file)
51 {
52 	char *ret;
53 	char cwd[PATH_MAX];
54 
55 	if (file[0] != '/') {
56 		if (getcwd(cwd, PATH_MAX) == NULL) {
57 			fprintf(stderr, "Failed to getcwd\n");
58 			exit(EXIT_FAILURE);
59 		}
60 		ret = malloc(strlen(cwd) + 1 + strlen(file) + 1);
61 		if (ret)
62 			sprintf(ret, "%s/%s", cwd, file);
63 	} else
64 		ret = strdup(file);
65 	return ret;
66 }
67 
parse_ugid_map_entry(char * line,struct ugid_map_entry * result)68 static int parse_ugid_map_entry(char* line, struct ugid_map_entry* result)
69 {
70 	char *token, *token_saveptr;
71 	size_t num_tokens;
72 	unsigned int *parsed[] = {&result->child_id,
73 				  &result->parent_id,
74 				  &result->length};
75 	for (token = strtok_r(line, " ", &token_saveptr), num_tokens = 0;
76 	     token && num_tokens < 3;
77 	     token = strtok_r(NULL, " ", &token_saveptr), ++num_tokens) {
78 		char* endptr = NULL;
79 		*parsed[num_tokens] = strtoul(token, &endptr, 10);
80 		if ((*parsed[num_tokens] == ULONG_MAX && errno) || *endptr) {
81 			fprintf(stderr, "Malformed u/gid mapping line\n");
82 			return 0;
83 		}
84 	}
85 	if (num_tokens < 3 || strtok_r(NULL, " ", &token_saveptr) != NULL) {
86 		fprintf(stderr, "Malformed u/gid mapping line\n");
87 		return 0;
88 	}
89 	if (result->child_id + result->length < result->child_id ||
90 	    result->parent_id + result->length < result->parent_id) {
91 		fprintf(stderr, "u/gid mapping overflow\n");
92 		return 0;
93 	}
94 	return 1;
95 }
96 
97 /*
98  * Returns 1 if [begin1, begin1+length1) and [begin2, begin2+length2) have
99  * overlapping range. Otherwise 0.
100  */
is_overlapping(unsigned int begin1,unsigned int length1,unsigned int begin2,unsigned int length2)101 static int is_overlapping(unsigned int begin1, unsigned int length1,
102 			  unsigned int begin2, unsigned int length2)
103 {
104 	unsigned int end1 = begin1 + length1;
105 	unsigned int end2 = begin2 + length2;
106 	return !(end1 <= begin2 || end2 <= begin1);
107 }
108 
109 /*
110  * Verifies if the given mapping works.
111  * - Checks if the number of entries is less than or equals to
112  *   UID_GID_MAP_MAX_EXTENTS.
113  * - Checks if there is no overlapped ranges.
114  * Returns 1 if valid, otherwise 0.
115  */
is_valid_ugid_map(const struct ugid_map * mapping)116 static int is_valid_ugid_map(const struct ugid_map* mapping)
117 {
118 	size_t i, j;
119 
120 	if (mapping->size > UID_GID_MAP_MAX_EXTENTS) {
121 		fprintf(stderr, "too many u/gid mapping entries\n");
122 		return 0;
123 	}
124 
125 	for (i = 0; i < mapping->size; ++i) {
126 		const struct ugid_map_entry *entry1 = &mapping->entries[i];
127 		for (j = i + 1; j < mapping->size; ++j) {
128 			const struct ugid_map_entry *entry2 =
129 				&mapping->entries[j];
130 			if (is_overlapping(entry1->child_id, entry1->length,
131 					   entry2->child_id, entry2->length)) {
132 				fprintf(stderr,
133 					"Overlapping child u/gid: [%d %d %d],"
134 					" [%d %d %d]\n",
135 					entry1->child_id, entry1->parent_id,
136 					entry1->length, entry2->child_id,
137 					entry2->parent_id, entry2->length);
138 				return 0;
139 			}
140 			if (is_overlapping(entry1->parent_id, entry1->length,
141 					   entry2->parent_id, entry2->length)) {
142 				fprintf(stderr,
143 					"Overlapping parent u/gid: [%d %d %d],"
144 					" [%d %d %d]\n",
145 					entry1->child_id, entry1->parent_id,
146 					entry1->length, entry2->child_id,
147 					entry2->parent_id, entry2->length);
148 				return 0;
149 			}
150 		}
151 	}
152 	return 1;
153 }
154 
155 /*
156  * Parses the UID/GID mapping argument. The argument could be a multi-line
157  * string (separated by '\n', no trailing '\n' is allowed). Each line must
158  * contain exact three integer tokens; the first token is |child_id|,
159  * the second is |parent_id|, and the last is |length| of the mapping range.
160  * See also user_namespace(7) man page.
161  * On success, the parsed entries are stored in |result|, and it returns 1.
162  * Otherwise, returns 0.
163  */
parse_ugid_map(char * arg,struct ugid_map * result)164 static int parse_ugid_map(char* arg, struct ugid_map* result)
165 {
166 	int i;
167 	char *line, *line_saveptr;
168 	size_t current_index;
169 
170 	/* Count the number of lines. */
171 	result->size = 1;
172 	for (i = 0; arg[i]; ++i) {
173 		if (arg[i] == '\n')
174 			++result->size;
175 	}
176 
177 	/* Allocate memory for entries. */
178 	result->entries = malloc(sizeof(struct ugid_map_entry) * result->size);
179 	if (!result->entries) {
180 		result->size = 0;
181 		return 0;
182 	}
183 
184 	/* Parse each line */
185 	for (line = strtok_r(arg, "\n", &line_saveptr), current_index = 0;
186 	     line;
187 	     line = strtok_r(NULL, "\n", &line_saveptr), ++current_index) {
188 		if (!parse_ugid_map_entry(
189 			line, &result->entries[current_index])) {
190 			return 0;
191 		}
192 	}
193 
194 	return is_valid_ugid_map(result);
195 }
196 
main(int argc,char * argv[])197 int main(int argc, char *argv[])
198 {
199 	int c;
200 	char *p;
201 	int flags = EXT2_FLAG_RW;
202 	errcode_t retval;
203 	io_manager io_mgr;
204 	ext2_filsys fs = NULL;
205 	struct fs_ops_callbacks fs_callbacks = { NULL, NULL };
206 	char *token;
207 	int nr_opt = 0;
208 	ext2_ino_t inodes_count;
209 	ext2_ino_t free_inodes_count;
210 	blk64_t blocks_count;
211 	blk64_t free_blocks_count;
212 	struct ugid_map uid_map = { 0, NULL }, gid_map = { 0, NULL };
213 
214 	add_error_table(&et_ext2_error_table);
215 
216 	while ((c = getopt (argc, argv, "T:C:S:p:a:D:d:B:f:esu:g:")) != EOF) {
217 		switch (c) {
218 		case 'T':
219 			fixed_time = strtoul(optarg, &p, 0);
220 			android_configure = 1;
221 			break;
222 		case 'C':
223 			fs_config_file = absolute_path(optarg);
224 			android_configure = 1;
225 			break;
226 		case 'S':
227 			token = strtok(optarg, ",");
228 			while (token) {
229 				if (nr_opt == max_nr_opt) {
230 					fprintf(stderr, "Expected at most %d selinux opts\n",
231 						max_nr_opt);
232 					exit(EXIT_FAILURE);
233 				}
234 				seopt_file[nr_opt].type = SELABEL_OPT_PATH;
235 				seopt_file[nr_opt].value = absolute_path(token);
236 				nr_opt++;
237 				token = strtok(NULL, ",");
238 			}
239 			android_configure = 1;
240 			break;
241 		case 'p':
242 			product_out = absolute_path(optarg);
243 			android_configure = 1;
244 			break;
245 		case 'a':
246 			mountpoint = strdup(optarg);
247 			break;
248 		case 'D':
249 			basefs_out = absolute_path(optarg);
250 			break;
251 		case 'd':
252 			basefs_in = absolute_path(optarg);
253 			break;
254 		case 'B':
255 			block_list = absolute_path(optarg);
256 			break;
257 		case 'f':
258 			src_dir = absolute_path(optarg);
259 			break;
260 		case 'e':
261 			android_sparse_file = 0;
262 			break;
263 		case 's':
264 			flags |= EXT2_FLAG_SHARE_DUP;
265 			break;
266 		case 'u':
267 			if (!parse_ugid_map(optarg, &uid_map))
268 				exit(EXIT_FAILURE);
269 			android_configure = 1;
270 			break;
271 		case 'g':
272 			if (!parse_ugid_map(optarg, &gid_map))
273 				exit(EXIT_FAILURE);
274 			android_configure = 1;
275 			break;
276 		default:
277 			usage(EXIT_FAILURE);
278 		}
279 	}
280 	if (optind >= argc) {
281 		fprintf(stderr, "Expected filename after options\n");
282 		exit(EXIT_FAILURE);
283 	}
284 
285 	if (android_sparse_file) {
286 		io_mgr = sparse_io_manager;
287 		if (asprintf(&in_file, "(%s)", argv[optind]) == -1) {
288 			fprintf(stderr, "Failed to allocate file name\n");
289 			exit(EXIT_FAILURE);
290 		}
291 	} else {
292 		io_mgr = unix_io_manager;
293 		in_file = strdup(argv[optind]);
294 	}
295 	retval = ext2fs_open(in_file, flags, 0, 0, io_mgr, &fs);
296 	if (retval) {
297 		com_err(prog_name, retval, "while opening file %s\n", in_file);
298 		return retval;
299 	}
300 
301 	if (src_dir) {
302 		ext2fs_read_bitmaps(fs);
303 		if (basefs_in) {
304 			retval = base_fs_alloc_load(fs, basefs_in, mountpoint);
305 			if (retval) {
306 				com_err(prog_name, retval, "%s",
307 				"while reading base_fs file");
308 			    exit(1);
309 			}
310 			fs_callbacks.create_new_inode =
311 				base_fs_alloc_set_target;
312 			fs_callbacks.end_create_new_inode =
313 				base_fs_alloc_unset_target;
314 		}
315 		retval = populate_fs2(fs, EXT2_ROOT_INO, src_dir,
316 				      EXT2_ROOT_INO, &fs_callbacks);
317 		if (retval) {
318 			com_err(prog_name, retval, "%s",
319 			"while populating file system");
320 		    exit(1);
321 		}
322 		if (basefs_in)
323 			base_fs_alloc_cleanup(fs);
324 	}
325 
326 	if (android_configure) {
327 		retval = android_configure_fs(
328 			fs, src_dir, product_out, mountpoint, seopt_file,
329 			nr_opt, fs_config_file, fixed_time, &uid_map, &gid_map);
330 		if (retval) {
331 			com_err(prog_name, retval, "%s",
332 				"while configuring the file system");
333 			exit(1);
334 		}
335 	}
336 
337 	if (block_list) {
338 		retval = fsmap_iter_filsys(fs, &block_list_format, block_list,
339 					   mountpoint);
340 		if (retval) {
341 			com_err(prog_name, retval, "%s",
342 				"while creating the block_list");
343 			exit(1);
344 		}
345 	}
346 
347 	if (basefs_out) {
348 		retval = fsmap_iter_filsys(fs, &base_fs_format,
349 					   basefs_out, mountpoint);
350 		if (retval) {
351 			com_err(prog_name, retval, "%s",
352 				"while creating the basefs file");
353 			exit(1);
354 		}
355 	}
356 
357 	inodes_count = fs->super->s_inodes_count;
358 	free_inodes_count = fs->super->s_free_inodes_count;
359 	blocks_count = ext2fs_blocks_count(fs->super);
360 	free_blocks_count = ext2fs_free_blocks_count(fs->super);
361 
362 	retval = ext2fs_close_free(&fs);
363 	if (retval) {
364 		com_err(prog_name, retval, "%s",
365 				"while writing superblocks");
366 		exit(1);
367 	}
368 
369 	printf("Created filesystem with %u/%u inodes and %llu/%llu blocks\n",
370 			inodes_count - free_inodes_count, inodes_count,
371 			blocks_count - free_blocks_count, blocks_count);
372 
373 	remove_error_table(&et_ext2_error_table);
374 	return 0;
375 }
376