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