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