OSDN Git Service

ext4_utils: mark uninitialized inode tables in block groups
[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 "make_ext4fs.h"
18 #include "ext4_utils.h"
19 #include "allocate.h"
20 #include "contents.h"
21 #include "uuid.h"
22 #include "wipe.h"
23
24 #include <sparse/sparse.h>
25
26 #include <assert.h>
27 #include <dirent.h>
28 #include <fcntl.h>
29 #include <libgen.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36
37 #ifdef USE_MINGW
38
39 #include <winsock2.h>
40
41 /* These match the Linux definitions of these flags.
42    L_xx is defined to avoid conflicting with the win32 versions.
43 */
44 #define L_S_IRUSR 00400
45 #define L_S_IWUSR 00200
46 #define L_S_IXUSR 00100
47 #define S_IRWXU (L_S_IRUSR | L_S_IWUSR | L_S_IXUSR)
48 #define S_IRGRP 00040
49 #define S_IWGRP 00020
50 #define S_IXGRP 00010
51 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
52 #define S_IROTH 00004
53 #define S_IWOTH 00002
54 #define S_IXOTH 00001
55 #define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH)
56 #define S_ISUID 0004000
57 #define S_ISGID 0002000
58 #define S_ISVTX 0001000
59
60 #else
61
62 #include <selinux/selinux.h>
63 #include <selinux/label.h>
64 #include <selinux/android.h>
65
66 #define O_BINARY 0
67
68 #endif
69
70 /* TODO: Not implemented:
71    Allocating blocks in the same block group as the file inode
72    Hash or binary tree directories
73    Special files: sockets, devices, fifos
74  */
75
76 static int filter_dot(const struct dirent *d)
77 {
78         return (strcmp(d->d_name, "..") && strcmp(d->d_name, "."));
79 }
80
81 static u32 build_default_directory_structure()
82 {
83         u32 inode;
84         u32 root_inode;
85         struct dentry dentries = {
86                         .filename = "lost+found",
87                         .file_type = EXT4_FT_DIR,
88                         .mode = S_IRWXU,
89                         .uid = 0,
90                         .gid = 0,
91                         .mtime = 0,
92         };
93         root_inode = make_directory(0, 1, &dentries, 1);
94         inode = make_directory(root_inode, 0, NULL, 0);
95         *dentries.inode = inode;
96         inode_set_permissions(inode, dentries.mode,
97                 dentries.uid, dentries.gid, dentries.mtime);
98
99         return root_inode;
100 }
101
102 #ifndef USE_MINGW
103 /* Read a local directory and create the same tree in the generated filesystem.
104    Calls itself recursively with each directory in the given directory.
105    full_path is an absolute or relative path, with a trailing slash, to the
106    directory on disk that should be copied, or NULL if this is a directory
107    that does not exist on disk (e.g. lost+found).
108    dir_path is an absolute path, with trailing slash, to the same directory
109    if the image were mounted at the specified mount point */
110 static u32 build_directory_structure(const char *full_path, const char *dir_path,
111                 u32 dir_inode, fs_config_func_t fs_config_func,
112                 struct selabel_handle *sehnd, int verbose)
113 {
114         int entries = 0;
115         struct dentry *dentries;
116         struct dirent **namelist = NULL;
117         struct stat stat;
118         int ret;
119         int i;
120         u32 inode;
121         u32 entry_inode;
122         u32 dirs = 0;
123         bool needs_lost_and_found = false;
124
125         if (full_path) {
126                 entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort);
127                 if (entries < 0) {
128                         error_errno("scandir");
129                         return EXT4_ALLOCATE_FAILED;
130                 }
131         }
132
133         if (dir_inode == 0) {
134                 /* root directory, check if lost+found already exists */
135                 for (i = 0; i < entries; i++)
136                         if (strcmp(namelist[i]->d_name, "lost+found") == 0)
137                                 break;
138                 if (i == entries)
139                         needs_lost_and_found = true;
140         }
141
142         dentries = calloc(entries, sizeof(struct dentry));
143         if (dentries == NULL)
144                 critical_error_errno("malloc");
145
146         for (i = 0; i < entries; i++) {
147                 dentries[i].filename = strdup(namelist[i]->d_name);
148                 if (dentries[i].filename == NULL)
149                         critical_error_errno("strdup");
150
151                 asprintf(&dentries[i].path, "%s%s", dir_path, namelist[i]->d_name);
152                 asprintf(&dentries[i].full_path, "%s%s", full_path, namelist[i]->d_name);
153
154                 free(namelist[i]);
155
156                 ret = lstat(dentries[i].full_path, &stat);
157                 if (ret < 0) {
158                         error_errno("lstat");
159                         i--;
160                         entries--;
161                         continue;
162                 }
163
164                 dentries[i].size = stat.st_size;
165                 dentries[i].mode = stat.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
166                 dentries[i].mtime = stat.st_mtime;
167                 if (fs_config_func != NULL) {
168 #ifdef ANDROID
169                         unsigned int mode = 0;
170                         unsigned int uid = 0;
171                         unsigned int gid = 0;
172                         int dir = S_ISDIR(stat.st_mode);
173                         fs_config_func(dentries[i].path, dir, &uid, &gid, &mode);
174                         dentries[i].mode = mode;
175                         dentries[i].uid = uid;
176                         dentries[i].gid = gid;
177 #else
178                         error("can't set android permissions - built without android support");
179 #endif
180                 }
181 #ifndef USE_MINGW
182                 if (sehnd) {
183                         if (selabel_lookup(sehnd, &dentries[i].secon, dentries[i].path, stat.st_mode) < 0) {
184                                 error("cannot lookup security context for %s", dentries[i].path);
185                         }
186
187                         if (dentries[i].secon && verbose)
188                                 printf("Labeling %s as %s\n", dentries[i].path, dentries[i].secon);
189                 }
190 #endif
191
192                 if (S_ISREG(stat.st_mode)) {
193                         dentries[i].file_type = EXT4_FT_REG_FILE;
194                 } else if (S_ISDIR(stat.st_mode)) {
195                         dentries[i].file_type = EXT4_FT_DIR;
196                         dirs++;
197                 } else if (S_ISCHR(stat.st_mode)) {
198                         dentries[i].file_type = EXT4_FT_CHRDEV;
199                 } else if (S_ISBLK(stat.st_mode)) {
200                         dentries[i].file_type = EXT4_FT_BLKDEV;
201                 } else if (S_ISFIFO(stat.st_mode)) {
202                         dentries[i].file_type = EXT4_FT_FIFO;
203                 } else if (S_ISSOCK(stat.st_mode)) {
204                         dentries[i].file_type = EXT4_FT_SOCK;
205                 } else if (S_ISLNK(stat.st_mode)) {
206                         dentries[i].file_type = EXT4_FT_SYMLINK;
207                         dentries[i].link = calloc(info.block_size, 1);
208                         readlink(dentries[i].full_path, dentries[i].link, info.block_size - 1);
209                 } else {
210                         error("unknown file type on %s", dentries[i].path);
211                         i--;
212                         entries--;
213                 }
214         }
215         free(namelist);
216
217         if (needs_lost_and_found) {
218                 /* insert a lost+found directory at the beginning of the dentries */
219                 struct dentry *tmp = calloc(entries + 1, sizeof(struct dentry));
220                 memset(tmp, 0, sizeof(struct dentry));
221                 memcpy(tmp + 1, dentries, entries * sizeof(struct dentry));
222                 dentries = tmp;
223
224                 dentries[0].filename = strdup("lost+found");
225                 asprintf(&dentries[0].path, "%slost+found", dir_path);
226                 dentries[0].full_path = NULL;
227                 dentries[0].size = 0;
228                 dentries[0].mode = S_IRWXU;
229                 dentries[0].file_type = EXT4_FT_DIR;
230                 dentries[0].uid = 0;
231                 dentries[0].gid = 0;
232                 if (sehnd) {
233                         if (selabel_lookup(sehnd, &dentries[0].secon, dentries[0].path, dentries[0].mode) < 0)
234                                 error("cannot lookup security context for %s", dentries[0].path);
235                 }
236                 entries++;
237                 dirs++;
238         }
239
240         inode = make_directory(dir_inode, entries, dentries, dirs);
241
242         for (i = 0; i < entries; i++) {
243                 if (dentries[i].file_type == EXT4_FT_REG_FILE) {
244                         entry_inode = make_file(dentries[i].full_path, dentries[i].size);
245                 } else if (dentries[i].file_type == EXT4_FT_DIR) {
246                         char *subdir_full_path = NULL;
247                         char *subdir_dir_path;
248                         if (dentries[i].full_path) {
249                                 ret = asprintf(&subdir_full_path, "%s/", dentries[i].full_path);
250                                 if (ret < 0)
251                                         critical_error_errno("asprintf");
252                         }
253                         ret = asprintf(&subdir_dir_path, "%s/", dentries[i].path);
254                         if (ret < 0)
255                                 critical_error_errno("asprintf");
256                         entry_inode = build_directory_structure(subdir_full_path,
257                                         subdir_dir_path, inode, fs_config_func, sehnd, verbose);
258                         free(subdir_full_path);
259                         free(subdir_dir_path);
260                 } else if (dentries[i].file_type == EXT4_FT_SYMLINK) {
261                         entry_inode = make_link(dentries[i].full_path, dentries[i].link);
262                 } else {
263                         error("unknown file type on %s", dentries[i].path);
264                         entry_inode = 0;
265                 }
266                 *dentries[i].inode = entry_inode;
267
268                 ret = inode_set_permissions(entry_inode, dentries[i].mode,
269                         dentries[i].uid, dentries[i].gid,
270                         dentries[i].mtime);
271                 if (ret)
272                         error("failed to set permissions on %s\n", dentries[i].path);
273                 ret = inode_set_selinux(entry_inode, dentries[i].secon);
274                 if (ret)
275                         error("failed to set SELinux context on %s\n", dentries[i].path);
276
277                 free(dentries[i].path);
278                 free(dentries[i].full_path);
279                 free(dentries[i].link);
280                 free((void *)dentries[i].filename);
281                 free(dentries[i].secon);
282         }
283
284         free(dentries);
285         return inode;
286 }
287 #endif
288
289 static u32 compute_block_size()
290 {
291         return 4096;
292 }
293
294 static u32 compute_journal_blocks()
295 {
296         u32 journal_blocks = DIV_ROUND_UP(info.len, info.block_size) / 64;
297         if (journal_blocks < 1024)
298                 journal_blocks = 1024;
299         if (journal_blocks > 32768)
300                 journal_blocks = 32768;
301         return journal_blocks;
302 }
303
304 static u32 compute_blocks_per_group()
305 {
306         return info.block_size * 8;
307 }
308
309 static u32 compute_inodes()
310 {
311         return DIV_ROUND_UP(info.len, info.block_size) / 4;
312 }
313
314 static u32 compute_inodes_per_group()
315 {
316         u32 blocks = DIV_ROUND_UP(info.len, info.block_size);
317         u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group);
318         u32 inodes = DIV_ROUND_UP(info.inodes, block_groups);
319         inodes = ALIGN(inodes, (info.block_size / info.inode_size));
320
321         /* After properly rounding up the number of inodes/group,
322          * make sure to update the total inodes field in the info struct.
323          */
324         info.inodes = inodes * block_groups;
325
326         return inodes;
327 }
328
329 static u32 compute_bg_desc_reserve_blocks()
330 {
331         u32 blocks = DIV_ROUND_UP(info.len, info.block_size);
332         u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group);
333         u32 bg_desc_blocks = DIV_ROUND_UP(block_groups * sizeof(struct ext2_group_desc),
334                         info.block_size);
335
336         u32 bg_desc_reserve_blocks =
337                         DIV_ROUND_UP(block_groups * 1024 * sizeof(struct ext2_group_desc),
338                                         info.block_size) - bg_desc_blocks;
339
340         if (bg_desc_reserve_blocks > info.block_size / sizeof(u32))
341                 bg_desc_reserve_blocks = info.block_size / sizeof(u32);
342
343         return bg_desc_reserve_blocks;
344 }
345
346 void reset_ext4fs_info() {
347     // Reset all the global data structures used by make_ext4fs so it
348     // can be called again.
349     memset(&info, 0, sizeof(info));
350     memset(&aux_info, 0, sizeof(aux_info));
351
352     if (info.sparse_file) {
353         sparse_file_destroy(info.sparse_file);
354         info.sparse_file = NULL;
355     }
356 }
357
358 int make_ext4fs_sparse_fd(int fd, long long len,
359                 const char *mountpoint, struct selabel_handle *sehnd)
360 {
361         reset_ext4fs_info();
362         info.len = len;
363
364         return make_ext4fs_internal(fd, NULL, mountpoint, NULL, 0, 1, 0, 0, sehnd, 0);
365 }
366
367 int make_ext4fs(const char *filename, long long len,
368                 const char *mountpoint, struct selabel_handle *sehnd)
369 {
370         int fd;
371         int status;
372
373         reset_ext4fs_info();
374         info.len = len;
375
376         fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
377         if (fd < 0) {
378                 error_errno("open");
379                 return EXIT_FAILURE;
380         }
381
382         status = make_ext4fs_internal(fd, NULL, mountpoint, NULL, 0, 0, 0, 1, sehnd, 0);
383         close(fd);
384
385         return status;
386 }
387
388 /* return a newly-malloc'd string that is a copy of str.  The new string
389    is guaranteed to have a trailing slash.  If absolute is true, the new string
390    is also guaranteed to have a leading slash.
391 */
392 static char *canonicalize_slashes(const char *str, bool absolute)
393 {
394         char *ret;
395         int len = strlen(str);
396         int newlen = len;
397         char *ptr;
398
399         if (len == 0 && absolute) {
400                 return strdup("/");
401         }
402
403         if (str[0] != '/' && absolute) {
404                 newlen++;
405         }
406         if (str[len - 1] != '/') {
407                 newlen++;
408         }
409         ret = malloc(newlen + 1);
410         if (!ret) {
411                 critical_error("malloc");
412         }
413
414         ptr = ret;
415         if (str[0] != '/' && absolute) {
416                 *ptr++ = '/';
417         }
418
419         strcpy(ptr, str);
420         ptr += len;
421
422         if (str[len - 1] != '/') {
423                 *ptr++ = '/';
424         }
425
426         if (ptr != ret + newlen) {
427                 critical_error("assertion failed\n");
428         }
429
430         *ptr = '\0';
431
432         return ret;
433 }
434
435 static char *canonicalize_abs_slashes(const char *str)
436 {
437         return canonicalize_slashes(str, true);
438 }
439
440 static char *canonicalize_rel_slashes(const char *str)
441 {
442         return canonicalize_slashes(str, false);
443 }
444
445 int make_ext4fs_internal(int fd, const char *_directory,
446                          const char *_mountpoint, fs_config_func_t fs_config_func, int gzip,
447                          int sparse, int crc, int wipe,
448                          struct selabel_handle *sehnd, int verbose)
449 {
450         u32 root_inode_num;
451         u16 root_mode;
452         char *mountpoint;
453         char *directory = NULL;
454         int ret;
455
456         if (setjmp(setjmp_env))
457                 return EXIT_FAILURE; /* Handle a call to longjmp() */
458
459         if (_mountpoint == NULL) {
460                 mountpoint = strdup("");
461         } else {
462                 mountpoint = canonicalize_abs_slashes(_mountpoint);
463         }
464
465         if (_directory) {
466                 directory = canonicalize_rel_slashes(_directory);
467         }
468
469         if (info.len <= 0)
470                 info.len = get_file_size(fd);
471
472         if (info.len <= 0) {
473                 fprintf(stderr, "Need size of filesystem\n");
474                 return EXIT_FAILURE;
475         }
476
477         if (info.block_size <= 0)
478                 info.block_size = compute_block_size();
479
480         /* Round down the filesystem length to be a multiple of the block size */
481         info.len &= ~((u64)info.block_size - 1);
482
483         if (info.journal_blocks == 0)
484                 info.journal_blocks = compute_journal_blocks();
485
486         if (info.no_journal == 0)
487                 info.feat_compat = EXT4_FEATURE_COMPAT_HAS_JOURNAL;
488         else
489                 info.journal_blocks = 0;
490
491         if (info.blocks_per_group <= 0)
492                 info.blocks_per_group = compute_blocks_per_group();
493
494         if (info.inodes <= 0)
495                 info.inodes = compute_inodes();
496
497         if (info.inode_size <= 0)
498                 info.inode_size = 256;
499
500         if (info.label == NULL)
501                 info.label = "";
502
503         info.inodes_per_group = compute_inodes_per_group();
504
505         info.feat_compat |=
506                         EXT4_FEATURE_COMPAT_RESIZE_INODE;
507
508         info.feat_ro_compat |=
509                         EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER |
510                         EXT4_FEATURE_RO_COMPAT_LARGE_FILE |
511                         EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
512
513         info.feat_incompat |=
514                         EXT4_FEATURE_INCOMPAT_EXTENTS |
515                         EXT4_FEATURE_INCOMPAT_FILETYPE;
516
517
518         info.bg_desc_reserve_blocks = compute_bg_desc_reserve_blocks();
519
520         printf("Creating filesystem with parameters:\n");
521         printf("    Size: %llu\n", info.len);
522         printf("    Block size: %d\n", info.block_size);
523         printf("    Blocks per group: %d\n", info.blocks_per_group);
524         printf("    Inodes per group: %d\n", info.inodes_per_group);
525         printf("    Inode size: %d\n", info.inode_size);
526         printf("    Journal blocks: %d\n", info.journal_blocks);
527         printf("    Label: %s\n", info.label);
528
529         ext4_create_fs_aux_info();
530
531         printf("    Blocks: %llu\n", aux_info.len_blocks);
532         printf("    Block groups: %d\n", aux_info.groups);
533         printf("    Reserved block group size: %d\n", info.bg_desc_reserve_blocks);
534
535         info.sparse_file = sparse_file_new(info.block_size, info.len);
536
537         block_allocator_init();
538
539         ext4_fill_in_sb();
540
541         if (reserve_inodes(0, 10) == EXT4_ALLOCATE_FAILED)
542                 error("failed to reserve first 10 inodes");
543
544         if (info.feat_compat & EXT4_FEATURE_COMPAT_HAS_JOURNAL)
545                 ext4_create_journal_inode();
546
547         if (info.feat_compat & EXT4_FEATURE_COMPAT_RESIZE_INODE)
548                 ext4_create_resize_inode();
549
550 #ifdef USE_MINGW
551         // Windows needs only 'create an empty fs image' functionality
552         assert(!directory);
553         root_inode_num = build_default_directory_structure();
554 #else
555         if (directory)
556                 root_inode_num = build_directory_structure(directory, mountpoint, 0,
557                         fs_config_func, sehnd, verbose);
558         else
559                 root_inode_num = build_default_directory_structure();
560 #endif
561
562         root_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
563         inode_set_permissions(root_inode_num, root_mode, 0, 0, 0);
564
565 #ifndef USE_MINGW
566         if (sehnd) {
567                 char *secontext = NULL;
568
569                 if (selabel_lookup(sehnd, &secontext, mountpoint, S_IFDIR) < 0) {
570                         error("cannot lookup security context for %s", mountpoint);
571                 }
572                 if (secontext) {
573                         if (verbose) {
574                                 printf("Labeling %s as %s\n", mountpoint, secontext);
575                         }
576                         inode_set_selinux(root_inode_num, secontext);
577                 }
578                 freecon(secontext);
579         }
580 #endif
581
582         ext4_update_free();
583
584         ext4_queue_sb();
585
586         printf("Created filesystem with %d/%d inodes and %d/%d blocks\n",
587                         aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count,
588                         aux_info.sb->s_inodes_count,
589                         aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo,
590                         aux_info.sb->s_blocks_count_lo);
591
592         if (wipe)
593                 wipe_block_device(fd, info.len);
594
595         write_ext4_image(fd, gzip, sparse, crc);
596
597         sparse_file_destroy(info.sparse_file);
598         info.sparse_file = NULL;
599
600         free(mountpoint);
601         free(directory);
602
603         return 0;
604 }