8 #include <ext2fs/ext2fs.h>
12 #include "block_list.h"
13 #include "basefs_allocator.h"
14 #include "create_inode.h"
16 #ifndef UID_GID_MAP_MAX_EXTENTS
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).
22 #define UID_GID_MAP_MAX_EXTENTS 340
25 static char *prog_name = "e2fsdroid";
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;
37 static int android_configure;
38 static int android_sparse_file = 1;
40 static void usage(int ret)
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",
50 static char *absolute_path(const char *file)
56 if (getcwd(cwd, PATH_MAX) == NULL) {
57 fprintf(stderr, "Failed to getcwd\n");
60 ret = malloc(strlen(cwd) + 1 + strlen(file) + 1);
62 sprintf(ret, "%s/%s", cwd, file);
68 static int parse_ugid_map_entry(char* line, struct ugid_map_entry* result)
70 char *token, *token_saveptr;
72 unsigned int *parsed[] = {&result->child_id,
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) {
79 unsigned long t = strtoul(token, &endptr, 10);
80 if ((t == ULONG_MAX && errno) || (t > UINT_MAX) || *endptr) {
81 fprintf(stderr, "Malformed u/gid mapping line\n");
84 *parsed[num_tokens] = (unsigned int) t;
86 if (num_tokens < 3 || strtok_r(NULL, " ", &token_saveptr) != NULL) {
87 fprintf(stderr, "Malformed u/gid mapping line\n");
90 if (result->child_id + result->length < result->child_id ||
91 result->parent_id + result->length < result->parent_id) {
92 fprintf(stderr, "u/gid mapping overflow\n");
99 * Returns 1 if [begin1, begin1+length1) and [begin2, begin2+length2) have
100 * overlapping range. Otherwise 0.
102 static int is_overlapping(unsigned int begin1, unsigned int length1,
103 unsigned int begin2, unsigned int length2)
105 unsigned int end1 = begin1 + length1;
106 unsigned int end2 = begin2 + length2;
107 return !(end1 <= begin2 || end2 <= begin1);
111 * Verifies if the given mapping works.
112 * - Checks if the number of entries is less than or equals to
113 * UID_GID_MAP_MAX_EXTENTS.
114 * - Checks if there is no overlapped ranges.
115 * Returns 1 if valid, otherwise 0.
117 static int is_valid_ugid_map(const struct ugid_map* mapping)
121 if (mapping->size > UID_GID_MAP_MAX_EXTENTS) {
122 fprintf(stderr, "too many u/gid mapping entries\n");
126 for (i = 0; i < mapping->size; ++i) {
127 const struct ugid_map_entry *entry1 = &mapping->entries[i];
128 for (j = i + 1; j < mapping->size; ++j) {
129 const struct ugid_map_entry *entry2 =
130 &mapping->entries[j];
131 if (is_overlapping(entry1->child_id, entry1->length,
132 entry2->child_id, entry2->length)) {
134 "Overlapping child u/gid: [%d %d %d],"
136 entry1->child_id, entry1->parent_id,
137 entry1->length, entry2->child_id,
138 entry2->parent_id, entry2->length);
141 if (is_overlapping(entry1->parent_id, entry1->length,
142 entry2->parent_id, entry2->length)) {
144 "Overlapping parent u/gid: [%d %d %d],"
146 entry1->child_id, entry1->parent_id,
147 entry1->length, entry2->child_id,
148 entry2->parent_id, entry2->length);
157 * Parses the UID/GID mapping argument. The argument could be a multi-line
158 * string (separated by '\n', no trailing '\n' is allowed). Each line must
159 * contain exact three integer tokens; the first token is |child_id|,
160 * the second is |parent_id|, and the last is |length| of the mapping range.
161 * See also user_namespace(7) man page.
162 * On success, the parsed entries are stored in |result|, and it returns 1.
163 * Otherwise, returns 0.
165 static int parse_ugid_map(char* arg, struct ugid_map* result)
168 char *line, *line_saveptr;
169 size_t current_index;
171 /* Count the number of lines. */
173 for (i = 0; arg[i]; ++i) {
178 /* Allocate memory for entries. */
179 result->entries = malloc(sizeof(struct ugid_map_entry) * result->size);
180 if (!result->entries) {
185 /* Parse each line */
186 for (line = strtok_r(arg, "\n", &line_saveptr), current_index = 0;
188 line = strtok_r(NULL, "\n", &line_saveptr), ++current_index) {
189 if (!parse_ugid_map_entry(
190 line, &result->entries[current_index])) {
195 return is_valid_ugid_map(result);
198 int main(int argc, char *argv[])
202 int flags = EXT2_FLAG_RW;
205 ext2_filsys fs = NULL;
206 struct fs_ops_callbacks fs_callbacks = { NULL, NULL };
209 ext2_ino_t inodes_count;
210 ext2_ino_t free_inodes_count;
211 blk64_t blocks_count;
212 blk64_t free_blocks_count;
213 struct ugid_map uid_map = { 0, NULL }, gid_map = { 0, NULL };
215 add_error_table(&et_ext2_error_table);
217 while ((c = getopt (argc, argv, "T:C:S:p:a:D:d:B:f:esu:g:")) != EOF) {
220 fixed_time = strtoul(optarg, &p, 0);
221 android_configure = 1;
224 fs_config_file = absolute_path(optarg);
225 android_configure = 1;
228 token = strtok(optarg, ",");
230 if (nr_opt == max_nr_opt) {
231 fprintf(stderr, "Expected at most %d selinux opts\n",
235 seopt_file[nr_opt].type = SELABEL_OPT_PATH;
236 seopt_file[nr_opt].value = absolute_path(token);
238 token = strtok(NULL, ",");
240 android_configure = 1;
243 product_out = absolute_path(optarg);
244 android_configure = 1;
247 mountpoint = strdup(optarg);
250 basefs_out = absolute_path(optarg);
253 basefs_in = absolute_path(optarg);
256 block_list = absolute_path(optarg);
259 src_dir = absolute_path(optarg);
262 android_sparse_file = 0;
265 flags |= EXT2_FLAG_SHARE_DUP;
268 if (!parse_ugid_map(optarg, &uid_map))
270 android_configure = 1;
273 if (!parse_ugid_map(optarg, &gid_map))
275 android_configure = 1;
281 if (optind >= argc) {
282 fprintf(stderr, "Expected filename after options\n");
286 if (android_sparse_file) {
287 io_mgr = sparse_io_manager;
288 if (asprintf(&in_file, "(%s)", argv[optind]) == -1) {
289 fprintf(stderr, "Failed to allocate file name\n");
293 io_mgr = unix_io_manager;
294 in_file = strdup(argv[optind]);
296 retval = ext2fs_open(in_file, flags, 0, 0, io_mgr, &fs);
298 com_err(prog_name, retval, "while opening file %s\n", in_file);
303 ext2fs_read_bitmaps(fs);
305 retval = base_fs_alloc_load(fs, basefs_in, mountpoint,
308 com_err(prog_name, retval, "%s",
309 "while reading base_fs file");
312 fs_callbacks.create_new_inode =
313 base_fs_alloc_set_target;
314 fs_callbacks.end_create_new_inode =
315 base_fs_alloc_unset_target;
317 retval = populate_fs2(fs, EXT2_ROOT_INO, src_dir,
318 EXT2_ROOT_INO, &fs_callbacks);
320 com_err(prog_name, retval, "%s",
321 "while populating file system");
325 base_fs_alloc_cleanup(fs);
328 if (android_configure) {
329 retval = android_configure_fs(
330 fs, src_dir, product_out, mountpoint, seopt_file,
331 nr_opt, fs_config_file, fixed_time, &uid_map, &gid_map);
333 com_err(prog_name, retval, "%s",
334 "while configuring the file system");
340 retval = fsmap_iter_filsys(fs, &block_list_format, block_list,
343 com_err(prog_name, retval, "%s",
344 "while creating the block_list");
350 retval = fsmap_iter_filsys(fs, &base_fs_format,
351 basefs_out, mountpoint);
353 com_err(prog_name, retval, "%s",
354 "while creating the basefs file");
359 inodes_count = fs->super->s_inodes_count;
360 free_inodes_count = fs->super->s_free_inodes_count;
361 blocks_count = ext2fs_blocks_count(fs->super);
362 free_blocks_count = ext2fs_free_blocks_count(fs->super);
364 retval = ext2fs_close_free(&fs);
366 com_err(prog_name, retval, "%s",
367 "while writing superblocks");
371 printf("Created filesystem with %u/%u inodes and %llu/%llu blocks\n",
372 inodes_count - free_inodes_count, inodes_count,
373 blocks_count - free_blocks_count, blocks_count);
375 remove_error_table(&et_ext2_error_table);