Whamcloud - gitweb
AOSP: e2fsdroid: Correctly process the root inode
[tools/e2fsprogs.git] / contrib / android / e2fsdroid.c
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
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
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
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  */
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  */
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  */
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
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                         break;
244                 case 'a':
245                         mountpoint = strdup(optarg);
246                         break;
247                 case 'D':
248                         basefs_out = absolute_path(optarg);
249                         break;
250                 case 'd':
251                         basefs_in = absolute_path(optarg);
252                         break;
253                 case 'B':
254                         block_list = absolute_path(optarg);
255                         break;
256                 case 'f':
257                         src_dir = absolute_path(optarg);
258                         break;
259                 case 'e':
260                         android_sparse_file = 0;
261                         break;
262                 case 's':
263                         flags |= EXT2_FLAG_SHARE_DUP;
264                         break;
265                 case 'u':
266                         if (!parse_ugid_map(optarg, &uid_map))
267                                 exit(EXIT_FAILURE);
268                         android_configure = 1;
269                         break;
270                 case 'g':
271                         if (!parse_ugid_map(optarg, &gid_map))
272                                 exit(EXIT_FAILURE);
273                         android_configure = 1;
274                         break;
275                 default:
276                         usage(EXIT_FAILURE);
277                 }
278         }
279         if (optind >= argc) {
280                 fprintf(stderr, "Expected filename after options\n");
281                 exit(EXIT_FAILURE);
282         }
283
284         if (android_sparse_file) {
285                 io_mgr = sparse_io_manager;
286                 if (asprintf(&in_file, "(%s)", argv[optind]) == -1) {
287                         fprintf(stderr, "Failed to allocate file name\n");
288                         exit(EXIT_FAILURE);
289                 }
290         } else {
291                 io_mgr = unix_io_manager;
292                 in_file = strdup(argv[optind]);
293         }
294         retval = ext2fs_open(in_file, flags, 0, 0, io_mgr, &fs);
295         if (retval) {
296                 com_err(prog_name, retval, "while opening file %s\n", in_file);
297                 return retval;
298         }
299
300         if (src_dir) {
301                 ext2fs_read_bitmaps(fs);
302                 if (basefs_in) {
303                         retval = base_fs_alloc_load(fs, basefs_in, mountpoint);
304                         if (retval) {
305                                 com_err(prog_name, retval, "%s",
306                                 "while reading base_fs file");
307                             exit(1);
308                         }
309                         fs_callbacks.create_new_inode =
310                                 base_fs_alloc_set_target;
311                         fs_callbacks.end_create_new_inode =
312                                 base_fs_alloc_unset_target;
313                 }
314                 retval = populate_fs2(fs, EXT2_ROOT_INO, src_dir,
315                                       EXT2_ROOT_INO, &fs_callbacks);
316                 if (retval) {
317                         com_err(prog_name, retval, "%s",
318                         "while populating file system");
319                     exit(1);
320                 }
321                 if (basefs_in)
322                         base_fs_alloc_cleanup(fs);
323         }
324
325         if (android_configure) {
326                 retval = android_configure_fs(
327                         fs, src_dir, product_out, mountpoint, seopt_file,
328                         nr_opt, fs_config_file, fixed_time, &uid_map, &gid_map);
329                 if (retval) {
330                         com_err(prog_name, retval, "%s",
331                                 "while configuring the file system");
332                         exit(1);
333                 }
334         }
335
336         if (block_list) {
337                 retval = fsmap_iter_filsys(fs, &block_list_format, block_list,
338                                            mountpoint);
339                 if (retval) {
340                         com_err(prog_name, retval, "%s",
341                                 "while creating the block_list");
342                         exit(1);
343                 }
344         }
345
346         if (basefs_out) {
347                 retval = fsmap_iter_filsys(fs, &base_fs_format,
348                                            basefs_out, mountpoint);
349                 if (retval) {
350                         com_err(prog_name, retval, "%s",
351                                 "while creating the basefs file");
352                         exit(1);
353                 }
354         }
355
356         inodes_count = fs->super->s_inodes_count;
357         free_inodes_count = fs->super->s_free_inodes_count;
358         blocks_count = ext2fs_blocks_count(fs->super);
359         free_blocks_count = ext2fs_free_blocks_count(fs->super);
360
361         retval = ext2fs_close_free(&fs);
362         if (retval) {
363                 com_err(prog_name, retval, "%s",
364                                 "while writing superblocks");
365                 exit(1);
366         }
367
368         printf("Created filesystem with %u/%u inodes and %llu/%llu blocks\n",
369                         inodes_count - free_inodes_count, inodes_count,
370                         blocks_count - free_blocks_count, blocks_count);
371
372         remove_error_table(&et_ext2_error_table);
373         return 0;
374 }