OSDN Git Service

Merge "Add event monitoring to Inferno."
[android-x86/system-extras.git] / ext4_utils / make_ext4fs.c
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "ext4_utils/make_ext4fs.h"
18
19 #ifndef _GNU_SOURCE
20 #define _GNU_SOURCE
21 #endif
22
23 #include <assert.h>
24 #include <dirent.h>
25 #include <fcntl.h>
26 #include <inttypes.h>
27 #include <libgen.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #include <unistd.h>
34
35 #include <sparse/sparse.h>
36
37 #include "allocate.h"
38 #include "contents.h"
39 #include "ext4_utils/ext4_utils.h"
40 #include "ext4_utils/wipe.h"
41
42 #ifdef _WIN32
43
44 #include <winsock2.h>
45
46 /* These match the Linux definitions of these flags.
47    L_xx is defined to avoid conflicting with the win32 versions.
48 */
49 #undef S_IRWXU
50 #undef S_IRGRP
51 #undef S_IWGRP
52 #undef S_IXGRP
53 #undef S_IRWXG
54 #undef S_IROTH
55 #undef S_IWOTH
56 #undef S_IXOTH
57 #undef S_IRWXO
58 #undef S_ISUID
59 #undef S_ISGID
60 #undef S_ISVTX
61
62 #define L_S_IRUSR 00400
63 #define L_S_IWUSR 00200
64 #define L_S_IXUSR 00100
65 #define S_IRWXU (L_S_IRUSR | L_S_IWUSR | L_S_IXUSR)
66 #define S_IRGRP 00040
67 #define S_IWGRP 00020
68 #define S_IXGRP 00010
69 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
70 #define S_IROTH 00004
71 #define S_IWOTH 00002
72 #define S_IXOTH 00001
73 #define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH)
74 #define S_ISUID 0004000
75 #define S_ISGID 0002000
76 #define S_ISVTX 0001000
77
78 #else
79
80 #include <selinux/selinux.h>
81 #include <selinux/label.h>
82
83 #define O_BINARY 0
84
85 #endif
86
87 #define MAX_PATH 4096
88 #define MAX_BLK_MAPPING_STR 1000
89
90 const int blk_file_major_ver = 1;
91 const int blk_file_minor_ver = 0;
92 const char *blk_file_header_fmt = "Base EXT4 version %d.%d";
93
94 /* TODO: Not implemented:
95    Allocating blocks in the same block group as the file inode
96    Hash or binary tree directories
97    Special files: sockets, devices, fifos
98  */
99
100 static int filter_dot(const struct dirent *d)
101 {
102         return (strcmp(d->d_name, "..") && strcmp(d->d_name, "."));
103 }
104
105 static u32 build_default_directory_structure(const char *dir_path,
106                                                  struct selabel_handle *sehnd)
107 {
108         u32 inode;
109         u32 root_inode;
110         struct dentry dentries = {
111                         .filename = "lost+found",
112                         .file_type = EXT4_FT_DIR,
113                         .mode = S_IRWXU,
114                         .uid = 0,
115                         .gid = 0,
116                         .mtime = 0,
117         };
118         root_inode = make_directory(0, 1, &dentries, 1);
119         inode = make_directory(root_inode, 0, NULL, 0);
120         *dentries.inode = inode;
121         inode_set_permissions(inode, dentries.mode,
122                 dentries.uid, dentries.gid, dentries.mtime);
123
124 #ifndef _WIN32
125         if (sehnd) {
126                 char *path = NULL;
127                 char *secontext = NULL;
128
129                 asprintf(&path, "%slost+found", dir_path);
130                 if (selabel_lookup(sehnd, &secontext, path, S_IFDIR) < 0) {
131                         error("cannot lookup security context for %s", path);
132                 } else {
133                         inode_set_selinux(inode, secontext);
134                         freecon(secontext);
135                 }
136                 free(path);
137         }
138 #endif
139
140         return root_inode;
141 }
142
143 #ifndef _WIN32
144 /* Read a local directory and create the same tree in the generated filesystem.
145    Calls itself recursively with each directory in the given directory.
146    full_path is an absolute or relative path, with a trailing slash, to the
147    directory on disk that should be copied, or NULL if this is a directory
148    that does not exist on disk (e.g. lost+found).
149    dir_path is an absolute path, with trailing slash, to the same directory
150    if the image were mounted at the specified mount point */
151 static u32 build_directory_structure(const char *full_path, const char *dir_path, const char *target_out_path,
152                 u32 dir_inode, fs_config_func_t fs_config_func,
153                 struct selabel_handle *sehnd, int verbose, time_t fixed_time)
154 {
155         int entries = 0;
156         struct dentry *dentries;
157         struct dirent **namelist = NULL;
158         struct stat stat;
159         int ret;
160         int i;
161         u32 inode;
162         u32 entry_inode;
163         u32 dirs = 0;
164         bool needs_lost_and_found = false;
165
166         if (full_path) {
167                 entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort);
168                 if (entries < 0) {
169 #ifdef __GLIBC__
170                         /* The scandir function implemented in glibc has a bug that makes it
171                            erroneously fail with ENOMEM under certain circumstances.
172                            As a workaround we can retry the scandir call with the same arguments.
173                            GLIBC BZ: https://sourceware.org/bugzilla/show_bug.cgi?id=17804 */
174                         if (errno == ENOMEM)
175                                 entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort);
176 #endif
177                         if (entries < 0) {
178                                 error_errno("scandir");
179                                 return EXT4_ALLOCATE_FAILED;
180                         }
181                 }
182         }
183
184         if (dir_inode == 0) {
185                 /* root directory, check if lost+found already exists */
186                 for (i = 0; i < entries; i++)
187                         if (strcmp(namelist[i]->d_name, "lost+found") == 0)
188                                 break;
189                 if (i == entries)
190                         needs_lost_and_found = true;
191         }
192
193         dentries = calloc(entries, sizeof(struct dentry));
194         if (dentries == NULL)
195                 critical_error_errno("malloc");
196
197         for (i = 0; i < entries; i++) {
198                 dentries[i].filename = strdup(namelist[i]->d_name);
199                 if (dentries[i].filename == NULL)
200                         critical_error_errno("strdup");
201
202                 asprintf(&dentries[i].path, "%s%s", dir_path, namelist[i]->d_name);
203                 asprintf(&dentries[i].full_path, "%s%s", full_path, namelist[i]->d_name);
204
205                 free(namelist[i]);
206
207                 ret = lstat(dentries[i].full_path, &stat);
208                 if (ret < 0) {
209                         error_errno("lstat");
210                         i--;
211                         entries--;
212                         continue;
213                 }
214
215                 dentries[i].size = stat.st_size;
216                 dentries[i].mode = stat.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
217                 if (fixed_time == -1) {
218                         dentries[i].mtime = stat.st_mtime;
219                 } else {
220                         dentries[i].mtime = fixed_time;
221                 }
222                 uint64_t capabilities;
223                 if (fs_config_func != NULL) {
224 #ifdef ANDROID
225                         unsigned int mode = 0;
226                         unsigned int uid = 0;
227                         unsigned int gid = 0;
228                         int dir = S_ISDIR(stat.st_mode);
229                         fs_config_func(dentries[i].path, dir, target_out_path, &uid, &gid, &mode, &capabilities);
230                         dentries[i].mode = mode;
231                         dentries[i].uid = uid;
232                         dentries[i].gid = gid;
233                         dentries[i].capabilities = capabilities;
234 #else
235                         error("can't set android permissions - built without android support");
236 #endif
237                 }
238 #ifndef _WIN32
239                 if (sehnd) {
240                         if (selabel_lookup(sehnd, &dentries[i].secon, dentries[i].path, stat.st_mode) < 0) {
241                                 error("cannot lookup security context for %s", dentries[i].path);
242                         }
243
244                         if (dentries[i].secon && verbose)
245                                 printf("Labeling %s as %s\n", dentries[i].path, dentries[i].secon);
246                 }
247 #endif
248
249                 if (S_ISREG(stat.st_mode)) {
250                         dentries[i].file_type = EXT4_FT_REG_FILE;
251                 } else if (S_ISDIR(stat.st_mode)) {
252                         dentries[i].file_type = EXT4_FT_DIR;
253                         dirs++;
254                 } else if (S_ISCHR(stat.st_mode)) {
255                         dentries[i].file_type = EXT4_FT_CHRDEV;
256                 } else if (S_ISBLK(stat.st_mode)) {
257                         dentries[i].file_type = EXT4_FT_BLKDEV;
258                 } else if (S_ISFIFO(stat.st_mode)) {
259                         dentries[i].file_type = EXT4_FT_FIFO;
260                 } else if (S_ISSOCK(stat.st_mode)) {
261                         dentries[i].file_type = EXT4_FT_SOCK;
262                 } else if (S_ISLNK(stat.st_mode)) {
263                         dentries[i].file_type = EXT4_FT_SYMLINK;
264                         dentries[i].link = calloc(info.block_size, 1);
265                         readlink(dentries[i].full_path, dentries[i].link, info.block_size - 1);
266                 } else {
267                         error("unknown file type on %s", dentries[i].path);
268                         i--;
269                         entries--;
270                 }
271         }
272         free(namelist);
273
274         if (needs_lost_and_found) {
275                 /* insert a lost+found directory at the beginning of the dentries */
276                 struct dentry *tmp = calloc(entries + 1, sizeof(struct dentry));
277                 memset(tmp, 0, sizeof(struct dentry));
278                 memcpy(tmp + 1, dentries, entries * sizeof(struct dentry));
279                 dentries = tmp;
280
281                 dentries[0].filename = strdup("lost+found");
282                 asprintf(&dentries[0].path, "%slost+found", dir_path);
283                 dentries[0].full_path = NULL;
284                 dentries[0].size = 0;
285                 dentries[0].mode = S_IRWXU;
286                 dentries[0].file_type = EXT4_FT_DIR;
287                 dentries[0].uid = 0;
288                 dentries[0].gid = 0;
289                 if (sehnd) {
290                         if (selabel_lookup(sehnd, &dentries[0].secon, dentries[0].path, dentries[0].mode) < 0)
291                                 error("cannot lookup security context for %s", dentries[0].path);
292                 }
293                 entries++;
294                 dirs++;
295         }
296
297         inode = make_directory(dir_inode, entries, dentries, dirs);
298
299         for (i = 0; i < entries; i++) {
300                 if (dentries[i].file_type == EXT4_FT_REG_FILE) {
301                         entry_inode = make_file(dentries[i].full_path, dentries[i].size);
302                 } else if (dentries[i].file_type == EXT4_FT_DIR) {
303                         char *subdir_full_path = NULL;
304                         char *subdir_dir_path;
305                         if (dentries[i].full_path) {
306                                 ret = asprintf(&subdir_full_path, "%s/", dentries[i].full_path);
307                                 if (ret < 0)
308                                         critical_error_errno("asprintf");
309                         }
310                         ret = asprintf(&subdir_dir_path, "%s/", dentries[i].path);
311                         if (ret < 0)
312                                 critical_error_errno("asprintf");
313                         entry_inode = build_directory_structure(subdir_full_path, subdir_dir_path, target_out_path,
314                                         inode, fs_config_func, sehnd, verbose, fixed_time);
315                         free(subdir_full_path);
316                         free(subdir_dir_path);
317                 } else if (dentries[i].file_type == EXT4_FT_SYMLINK) {
318                         entry_inode = make_link(dentries[i].link);
319                 } else {
320                         error("unknown file type on %s", dentries[i].path);
321                         entry_inode = 0;
322                 }
323                 *dentries[i].inode = entry_inode;
324
325                 ret = inode_set_permissions(entry_inode, dentries[i].mode,
326                         dentries[i].uid, dentries[i].gid,
327                         dentries[i].mtime);
328                 if (ret)
329                         error("failed to set permissions on %s\n", dentries[i].path);
330
331                 /*
332                  * It's important to call inode_set_selinux() before
333                  * inode_set_capabilities(). Extended attributes need to
334                  * be stored sorted order, and we guarantee this by making
335                  * the calls in the proper order.
336                  * Please see xattr_assert_sane() in contents.c
337                  */
338                 ret = inode_set_selinux(entry_inode, dentries[i].secon);
339                 if (ret)
340                         error("failed to set SELinux context on %s\n", dentries[i].path);
341                 ret = inode_set_capabilities(entry_inode, dentries[i].capabilities);
342                 if (ret)
343                         error("failed to set capability on %s\n", dentries[i].path);
344
345                 free(dentries[i].path);
346                 free(dentries[i].full_path);
347                 free(dentries[i].link);
348                 free((void *)dentries[i].filename);
349                 free(dentries[i].secon);
350         }
351
352         free(dentries);
353         return inode;
354 }
355 #endif
356
357 static u32 compute_block_size()
358 {
359         return 4096;
360 }
361
362 static u32 compute_journal_blocks()
363 {
364         u32 journal_blocks = DIV_ROUND_UP(info.len, info.block_size) / 64;
365         if (journal_blocks < 1024)
366                 journal_blocks = 1024;
367         if (journal_blocks > 32768)
368                 journal_blocks = 32768;
369         return journal_blocks;
370 }
371
372 static u32 compute_blocks_per_group()
373 {
374         return info.block_size * 8;
375 }
376
377 static u32 compute_inodes()
378 {
379         return DIV_ROUND_UP(info.len, info.block_size) / 4;
380 }
381
382 static u32 compute_inodes_per_group()
383 {
384         u32 blocks = DIV_ROUND_UP(info.len, info.block_size);
385         u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group);
386         u32 inodes = DIV_ROUND_UP(info.inodes, block_groups);
387         inodes = EXT4_ALIGN(inodes, (info.block_size / info.inode_size));
388
389         /* After properly rounding up the number of inodes/group,
390          * make sure to update the total inodes field in the info struct.
391          */
392         info.inodes = inodes * block_groups;
393
394         return inodes;
395 }
396
397 static u32 compute_bg_desc_reserve_blocks()
398 {
399         u32 blocks = DIV_ROUND_UP(info.len, info.block_size);
400         u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group);
401         u32 bg_desc_blocks = DIV_ROUND_UP(block_groups * sizeof(struct ext2_group_desc),
402                         info.block_size);
403
404         u32 bg_desc_reserve_blocks =
405                         DIV_ROUND_UP(block_groups * 1024 * sizeof(struct ext2_group_desc),
406                                         info.block_size) - bg_desc_blocks;
407
408         if (bg_desc_reserve_blocks > info.block_size / sizeof(u32))
409                 bg_desc_reserve_blocks = info.block_size / sizeof(u32);
410
411         return bg_desc_reserve_blocks;
412 }
413
414 void reset_ext4fs_info() {
415         // Reset all the global data structures used by make_ext4fs so it
416         // can be called again.
417         memset(&info, 0, sizeof(info));
418         memset(&aux_info, 0, sizeof(aux_info));
419
420         if (ext4_sparse_file) {
421                 sparse_file_destroy(ext4_sparse_file);
422                 ext4_sparse_file = NULL;
423         }
424 }
425
426 int make_ext4fs_sparse_fd(int fd, long long len,
427                                 const char *mountpoint, struct selabel_handle *sehnd)
428 {
429         return make_ext4fs_sparse_fd_align(fd, len, mountpoint, sehnd, 0, 0);
430 }
431
432 int make_ext4fs_sparse_fd_align(int fd, long long len,
433                                 const char *mountpoint, struct selabel_handle *sehnd,
434                                 unsigned eraseblk, unsigned logicalblk)
435 {
436         return make_ext4fs_sparse_fd_directory_align(fd, len, mountpoint, sehnd, NULL,
437                                                                 eraseblk, logicalblk);
438 }
439
440 int make_ext4fs_sparse_fd_directory(int fd, long long len,
441                                 const char *mountpoint, struct selabel_handle *sehnd,
442                                 const char *directory)
443 {
444         return make_ext4fs_sparse_fd_directory_align(fd, len, mountpoint, sehnd, directory, 0, 0);
445 }
446
447 int make_ext4fs_sparse_fd_directory_align(int fd, long long len,
448                                 const char *mountpoint, struct selabel_handle *sehnd,
449                                 const char *directory, unsigned eraseblk, unsigned logicalblk)
450 {
451         reset_ext4fs_info();
452         info.len = len;
453         info.flash_erase_block_size = eraseblk;
454         info.flash_logical_block_size = logicalblk;
455
456         return make_ext4fs_internal(fd, directory, NULL, mountpoint, NULL,
457                                                                 0, 1, 0, 0, 0,
458                                                                 sehnd, 0, -1, NULL, NULL, NULL);
459 }
460
461 int make_ext4fs(const char *filename, long long len,
462                                 const char *mountpoint, struct selabel_handle *sehnd)
463 {
464         return make_ext4fs_directory(filename, len, mountpoint, sehnd, NULL);
465 }
466
467 int make_ext4fs_directory(const char *filename, long long len,
468                                                   const char *mountpoint, struct selabel_handle *sehnd,
469                                                   const char *directory)
470 {
471         return make_ext4fs_directory_align(filename, len, mountpoint, sehnd, directory, 0, 0);
472 }
473
474 int make_ext4fs_directory_align(const char *filename, long long len,
475                                                   const char *mountpoint, struct selabel_handle *sehnd,
476                                                   const char *directory, unsigned eraseblk,
477                                                   unsigned logicalblk)
478 {
479         int fd;
480         int status;
481
482         reset_ext4fs_info();
483         info.len = len;
484         info.flash_erase_block_size = eraseblk;
485         info.flash_logical_block_size = logicalblk;
486
487         fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
488         if (fd < 0) {
489                 error_errno("open");
490                 return EXIT_FAILURE;
491         }
492
493         status = make_ext4fs_internal(fd, directory, NULL, mountpoint, NULL,
494                                                                   0, 0, 0, 1, 0,
495                                                                   sehnd, 0, -1, NULL, NULL, NULL);
496         close(fd);
497
498         return status;
499 }
500
501 /* return a newly-malloc'd string that is a copy of str.  The new string
502    is guaranteed to have a trailing slash.  If absolute is true, the new string
503    is also guaranteed to have a leading slash.
504 */
505 static char *canonicalize_slashes(const char *str, bool absolute)
506 {
507         char *ret;
508         int len = strlen(str);
509         int newlen = len;
510         char *ptr;
511
512         if (len == 0) {
513                 if (absolute)
514                         return strdup("/");
515                 else
516                         return strdup("");
517         }
518
519         if (str[0] != '/' && absolute) {
520                 newlen++;
521         }
522         if (str[len - 1] != '/') {
523                 newlen++;
524         }
525         ret = malloc(newlen + 1);
526         if (!ret) {
527                 critical_error("malloc");
528         }
529
530         ptr = ret;
531         if (str[0] != '/' && absolute) {
532                 *ptr++ = '/';
533         }
534
535         strcpy(ptr, str);
536         ptr += len;
537
538         if (str[len - 1] != '/') {
539                 *ptr++ = '/';
540         }
541
542         if (ptr != ret + newlen) {
543                 critical_error("assertion failed\n");
544         }
545
546         *ptr = '\0';
547
548         return ret;
549 }
550
551 static char *canonicalize_abs_slashes(const char *str)
552 {
553         return canonicalize_slashes(str, true);
554 }
555
556 static char *canonicalize_rel_slashes(const char *str)
557 {
558         return canonicalize_slashes(str, false);
559 }
560
561 static int compare_chunks(const void* chunk1, const void* chunk2) {
562         struct region* c1 = (struct region*) chunk1;
563         struct region* c2 = (struct region*) chunk2;
564         return c1->block - c2->block;
565 }
566
567 static int get_block_group(u32 block) {
568         unsigned int i, group = 0;
569
570         for(i = 0; i < aux_info.groups; i++) {
571                 if (block >= aux_info.bgs[i].first_block)
572                         group = i;
573                 else
574                         break;
575         }
576         return group;
577 }
578
579 static void extract_base_fs_allocations(const char *directory, const char *mountpoint,
580                                                                                 FILE* base_alloc_file_in) {
581 #define err_msg "base file badly formatted"
582 #ifndef _WIN32
583         // FORMAT Version 1.0: filename blk_mapping
584         const char *base_alloc_file_in_format = "%s %s";
585         const int base_file_format_param_count = 2;
586
587         char stored_file_name[MAX_PATH], real_file_name[MAX_PATH], file_map[MAX_BLK_MAPPING_STR];
588         struct block_allocation *fs_alloc;
589         struct block_group_info *bgs = aux_info.bgs;
590         int major_version = 0, minor_version = 0;
591         unsigned int i;
592         char *base_file_line = NULL;
593         size_t base_file_line_len = 0;
594
595         printf("[v%d.%d] Generating an Incremental EXT4 image\n",
596                         blk_file_major_ver, blk_file_minor_ver);
597         if (base_fs_allocations == NULL)
598                 base_fs_allocations = create_allocation();
599         fs_alloc = base_fs_allocations;
600
601         fscanf(base_alloc_file_in, blk_file_header_fmt, &major_version, &minor_version);
602         if (major_version == 0) {
603                 critical_error("Invalid base file");
604         }
605
606         if (major_version != blk_file_major_ver) {
607                 critical_error("Incompatible base file: version required is %d.X",
608                                 blk_file_major_ver);
609         }
610
611         if (minor_version < blk_file_minor_ver) {
612                 critical_error("Incompatible base file: version required is %d.%d or above",
613                                 blk_file_major_ver, blk_file_minor_ver);
614         }
615
616         while (getline(&base_file_line, &base_file_line_len, base_alloc_file_in) != -1) {
617                 if (sscanf(base_file_line, base_alloc_file_in_format, &stored_file_name, &file_map)
618                                 != base_file_format_param_count) {
619                         continue;
620                 }
621                 if (strlen(stored_file_name) < strlen(mountpoint)) {
622                         continue;
623                 }
624                 snprintf(real_file_name, MAX_PATH, "%s%s", directory, stored_file_name + strlen(mountpoint));
625                 if (!access(real_file_name, R_OK)) {
626                         char *block_range, *end_string;
627                         int real_file_fd;
628                         int start_block, end_block;
629                         u32 block_file_size;
630                         u32 real_file_block_size;
631
632                         real_file_fd = open(real_file_name, O_RDONLY);
633                         if (real_file_fd == -1) {
634                                 critical_error(err_msg);
635                         }
636                         real_file_block_size = get_file_size(real_file_fd);
637                         close(real_file_fd);
638                         real_file_block_size = DIV_ROUND_UP(real_file_block_size, info.block_size);
639                         fs_alloc->filename = strdup(real_file_name);
640                         block_range = strtok_r(file_map, ",", &end_string);
641                         while (block_range && real_file_block_size) {
642                                 int block_group;
643                                 char *range, *end_token = NULL;
644                                 range = strtok_r(block_range, "-", &end_token);
645                                 if (!range) {
646                                         critical_error(err_msg);
647                                 }
648                                 start_block = parse_num(range);
649                                 range = strtok_r(NULL, "-", &end_token);
650                                 if (!range) {
651                                         end_block = start_block;
652                                 } else {
653                                         end_block = parse_num(range);
654                                 }
655                                 // Assummption is that allocations are within the same block group
656                                 block_group = get_block_group(start_block);
657                                 if (block_group != get_block_group(end_block)) {
658                                         critical_error("base file allocation's end block is in a different "
659                                                                    "block group than start block. did you change fs params?");
660                                 }
661                                 block_range = strtok_r(NULL, ",", &end_string);
662                                 int bg_first_block = bgs[block_group].first_block;
663                                 int min_bg_bound = bgs[block_group].chunks[0].block + bgs[block_group].chunks[0].len;
664                                 int max_bg_bound = bgs[block_group].chunks[bgs[block_group].chunk_count - 1].block;
665
666                                 if (min_bg_bound >= start_block - bg_first_block ||
667                                         max_bg_bound <= end_block - bg_first_block) {
668                                         continue;
669                                 }
670                                 block_file_size = end_block - start_block + 1;
671                                 if (block_file_size > real_file_block_size) {
672                                         block_file_size = real_file_block_size;
673                                 }
674                                 append_region(fs_alloc, start_block, block_file_size, block_group);
675                                 reserve_bg_chunk(block_group, start_block - bgs[block_group].first_block, block_file_size);
676                                 real_file_block_size -= block_file_size;
677                         }
678                         if (reserve_blocks_for_allocation(fs_alloc) < 0)
679                                 critical_error("failed to reserve base fs allocation");
680                         fs_alloc->next = create_allocation();
681                         fs_alloc = fs_alloc->next;
682                 }
683         }
684
685         for (i = 0; i < aux_info.groups; i++) {
686                 qsort(bgs[i].chunks, bgs[i].chunk_count, sizeof(struct region), compare_chunks);
687         }
688
689         free(base_file_line);
690
691 #else
692     return;
693 #endif
694 #undef err_msg
695 }
696
697 void generate_base_alloc_file_out(FILE* base_alloc_file_out, char* dir, char* mountpoint,
698                                                                   struct block_allocation* p)
699 {
700         size_t dirlen = dir ? strlen(dir) : 0;
701         fprintf(base_alloc_file_out, blk_file_header_fmt, blk_file_major_ver, blk_file_minor_ver);
702         fputc('\n', base_alloc_file_out);
703         while (p) {
704                 if (dir && strncmp(p->filename, dir, dirlen) == 0) {
705                         // substitute mountpoint for the leading directory in the filename, in the output file
706                         fprintf(base_alloc_file_out, "%s%s", mountpoint, p->filename + dirlen);
707                 } else {
708                         fprintf(base_alloc_file_out, "%s", p->filename);
709                 }
710                 print_blocks(base_alloc_file_out, p, ',');
711                 struct block_allocation* pn = p->next;
712                 p = pn;
713         }
714 }
715
716 int make_ext4fs_internal(int fd, const char *_directory, const char *_target_out_directory,
717                                                  const char *_mountpoint, fs_config_func_t fs_config_func, int gzip,
718                                                  int sparse, int crc, int wipe, int real_uuid,
719                                                  struct selabel_handle *sehnd, int verbose, time_t fixed_time,
720                                                  FILE* block_list_file, FILE* base_alloc_file_in, FILE* base_alloc_file_out)
721 {
722         u32 root_inode_num;
723         u16 root_mode;
724         char *mountpoint;
725         char *directory = NULL;
726         char *target_out_directory = NULL;
727         struct block_allocation* p;
728
729         if (setjmp(setjmp_env))
730                 return EXIT_FAILURE; /* Handle a call to longjmp() */
731
732         info.block_device = is_block_device_fd(fd);
733
734         if (info.block_device && (sparse || gzip || crc)) {
735                 fprintf(stderr, "No sparse/gzip/crc allowed for block device\n");
736                 return EXIT_FAILURE;
737         }
738
739         if (_mountpoint == NULL) {
740                 mountpoint = strdup("");
741         } else {
742                 mountpoint = canonicalize_abs_slashes(_mountpoint);
743         }
744
745         if (_directory) {
746                 directory = canonicalize_rel_slashes(_directory);
747         }
748
749         if (_target_out_directory) {
750                 target_out_directory = canonicalize_rel_slashes(_target_out_directory);
751         }
752
753         if (info.len <= 0)
754                 info.len = get_file_size(fd);
755
756         if (info.block_size <= 0)
757                 info.block_size = compute_block_size();
758
759         /* Round down the filesystem length to be a multiple of the block size */
760         info.len &= ~((u64)info.block_size - 1);
761
762         if (info.len <= 0) {
763                 fprintf(stderr, "filesystem size too small\n");
764                 return EXIT_FAILURE;
765         }
766
767         if (info.journal_blocks == 0)
768                 info.journal_blocks = compute_journal_blocks();
769
770         if (info.no_journal == 0)
771                 info.feat_compat = EXT4_FEATURE_COMPAT_HAS_JOURNAL;
772         else
773                 info.journal_blocks = 0;
774
775         if (info.blocks_per_group <= 0)
776                 info.blocks_per_group = compute_blocks_per_group();
777
778         if (info.inodes <= 0)
779                 info.inodes = compute_inodes();
780
781         if (info.inode_size <= 0)
782                 info.inode_size = 256;
783
784         if (info.label == NULL)
785                 info.label = "";
786
787         info.inodes_per_group = compute_inodes_per_group();
788
789         info.feat_compat |=
790                         EXT4_FEATURE_COMPAT_RESIZE_INODE |
791                         EXT4_FEATURE_COMPAT_EXT_ATTR;
792
793         info.feat_ro_compat |=
794                         EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER |
795                         EXT4_FEATURE_RO_COMPAT_LARGE_FILE |
796                         EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
797
798         info.feat_incompat |=
799                         EXT4_FEATURE_INCOMPAT_EXTENTS |
800                         EXT4_FEATURE_INCOMPAT_FILETYPE;
801
802
803         info.bg_desc_reserve_blocks = compute_bg_desc_reserve_blocks();
804
805         printf("Creating filesystem with parameters:\n");
806         printf("    Size: %"PRIu64"\n", info.len);
807         printf("    Block size: %d\n", info.block_size);
808         printf("    Blocks per group: %d\n", info.blocks_per_group);
809         printf("    Inodes per group: %d\n", info.inodes_per_group);
810         printf("    Inode size: %d\n", info.inode_size);
811         printf("    Journal blocks: %d\n", info.journal_blocks);
812         printf("    Label: %s\n", info.label);
813
814         ext4_create_fs_aux_info();
815
816         printf("    Blocks: %"PRIu64"\n", aux_info.len_blocks);
817         printf("    Block groups: %d\n", aux_info.groups);
818         printf("    Reserved block group size: %d\n", info.bg_desc_reserve_blocks);
819
820         ext4_sparse_file = sparse_file_new(info.block_size, info.len);
821
822         block_allocator_init();
823
824         ext4_fill_in_sb(real_uuid);
825
826         if (base_alloc_file_in) {
827                 extract_base_fs_allocations(directory, mountpoint, base_alloc_file_in);
828         }
829         if (reserve_inodes(0, 10) == EXT4_ALLOCATE_FAILED)
830                 error("failed to reserve first 10 inodes");
831
832         if (info.feat_compat & EXT4_FEATURE_COMPAT_HAS_JOURNAL)
833                 ext4_create_journal_inode();
834
835         if (info.feat_compat & EXT4_FEATURE_COMPAT_RESIZE_INODE)
836                 ext4_create_resize_inode();
837
838 #ifdef _WIN32
839         // Windows needs only 'create an empty fs image' functionality
840         assert(!directory);
841         root_inode_num = build_default_directory_structure(mountpoint, sehnd);
842 #else
843         if (directory)
844                 root_inode_num = build_directory_structure(directory, mountpoint, target_out_directory, 0,
845                         fs_config_func, sehnd, verbose, fixed_time);
846         else
847                 root_inode_num = build_default_directory_structure(mountpoint, sehnd);
848 #endif
849
850         root_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
851         inode_set_permissions(root_inode_num, root_mode, 0, 0, 0);
852
853 #ifndef _WIN32
854         if (sehnd) {
855                 char *secontext = NULL;
856
857                 if (selabel_lookup(sehnd, &secontext, mountpoint, S_IFDIR) < 0) {
858                         error("cannot lookup security context for %s", mountpoint);
859                 }
860                 if (secontext) {
861                         if (verbose) {
862                                 printf("Labeling %s as %s\n", mountpoint, secontext);
863                         }
864                         inode_set_selinux(root_inode_num, secontext);
865                 }
866                 freecon(secontext);
867         }
868 #endif
869
870         ext4_update_free();
871
872         // TODO: Consider migrating the OTA tools to the new base alloc file format
873         // used for generating incremental images (see go/incremental-ext4)
874         if (block_list_file) {
875                 size_t dirlen = directory ? strlen(directory) : 0;
876                 struct block_allocation* p = get_saved_allocation_chain();
877                 while (p) {
878                         if (directory && strncmp(p->filename, directory, dirlen) == 0) {
879                                 // substitute mountpoint for the leading directory in the filename, in the output file
880                                 fprintf(block_list_file, "%s%s", mountpoint, p->filename + dirlen);
881                         } else {
882                                 fprintf(block_list_file, "%s", p->filename);
883                         }
884                         print_blocks(block_list_file, p, ' ');
885                         struct block_allocation* pn = p->next;
886                         p = pn;
887                 }
888         }
889
890         if (base_alloc_file_out) {
891                 struct block_allocation* p = get_saved_allocation_chain();
892                 generate_base_alloc_file_out(base_alloc_file_out, directory, mountpoint, p);
893         }
894
895         printf("Created filesystem with %d/%d inodes and %d/%d blocks\n",
896                         aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count,
897                         aux_info.sb->s_inodes_count,
898                         aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo,
899                         aux_info.sb->s_blocks_count_lo);
900
901         if (wipe && WIPE_IS_SUPPORTED) {
902                 wipe_block_device(fd, info.len);
903         }
904
905         write_ext4_image(fd, gzip, sparse, crc);
906
907         sparse_file_destroy(ext4_sparse_file);
908         ext4_sparse_file = NULL;
909
910         p = get_saved_allocation_chain();
911         while (p) {
912                 struct block_allocation* pn = p->next;
913                 free_alloc(p);
914                 p = pn;
915         }
916
917         free(mountpoint);
918         free(directory);
919
920         return 0;
921 }