OSDN Git Service

am 8f4e7976: Merge "Do free and fclose when error occur."
[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 "wipe.h"
22
23 #include <sparse/sparse.h>
24
25 #include <assert.h>
26 #include <dirent.h>
27 #include <fcntl.h>
28 #include <inttypes.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(const char *dir_path,
82                                              struct selabel_handle *sehnd)
83 {
84         u32 inode;
85         u32 root_inode;
86         struct dentry dentries = {
87                         .filename = "lost+found",
88                         .file_type = EXT4_FT_DIR,
89                         .mode = S_IRWXU,
90                         .uid = 0,
91                         .gid = 0,
92                         .mtime = 0,
93         };
94         root_inode = make_directory(0, 1, &dentries, 1);
95         inode = make_directory(root_inode, 0, NULL, 0);
96         *dentries.inode = inode;
97         inode_set_permissions(inode, dentries.mode,
98                 dentries.uid, dentries.gid, dentries.mtime);
99
100 #ifndef USE_MINGW
101         if (sehnd) {
102                 char *path = NULL;
103                 char *secontext = NULL;
104
105                 asprintf(&path, "%slost+found", dir_path);
106                 if (selabel_lookup(sehnd, &secontext, path, S_IFDIR) < 0) {
107                         error("cannot lookup security context for %s", path);
108                 } else {
109                         inode_set_selinux(inode, secontext);
110                         freecon(secontext);
111                 }
112                 free(path);
113         }
114 #endif
115
116         return root_inode;
117 }
118
119 #ifndef USE_MINGW
120 /* Read a local directory and create the same tree in the generated filesystem.
121    Calls itself recursively with each directory in the given directory.
122    full_path is an absolute or relative path, with a trailing slash, to the
123    directory on disk that should be copied, or NULL if this is a directory
124    that does not exist on disk (e.g. lost+found).
125    dir_path is an absolute path, with trailing slash, to the same directory
126    if the image were mounted at the specified mount point */
127 static u32 build_directory_structure(const char *full_path, const char *dir_path,
128                 u32 dir_inode, fs_config_func_t fs_config_func,
129                 struct selabel_handle *sehnd, int verbose, time_t fixed_time)
130 {
131         int entries = 0;
132         struct dentry *dentries;
133         struct dirent **namelist = NULL;
134         struct stat stat;
135         int ret;
136         int i;
137         u32 inode;
138         u32 entry_inode;
139         u32 dirs = 0;
140         bool needs_lost_and_found = false;
141
142         if (full_path) {
143                 entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort);
144                 if (entries < 0) {
145 #ifdef __GLIBC__
146                         /* The scandir function implemented in glibc has a bug that makes it
147                            erroneously fail with ENOMEM under certain circumstances.
148                            As a workaround we can retry the scandir call with the same arguments.
149                            GLIBC BZ: https://sourceware.org/bugzilla/show_bug.cgi?id=17804 */
150                         if (errno == ENOMEM)
151                                 entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort);
152 #endif
153                         if (entries < 0) {
154                                 error_errno("scandir");
155                                 return EXT4_ALLOCATE_FAILED;
156                         }
157                 }
158         }
159
160         if (dir_inode == 0) {
161                 /* root directory, check if lost+found already exists */
162                 for (i = 0; i < entries; i++)
163                         if (strcmp(namelist[i]->d_name, "lost+found") == 0)
164                                 break;
165                 if (i == entries)
166                         needs_lost_and_found = true;
167         }
168
169         dentries = calloc(entries, sizeof(struct dentry));
170         if (dentries == NULL)
171                 critical_error_errno("malloc");
172
173         for (i = 0; i < entries; i++) {
174                 dentries[i].filename = strdup(namelist[i]->d_name);
175                 if (dentries[i].filename == NULL)
176                         critical_error_errno("strdup");
177
178                 asprintf(&dentries[i].path, "%s%s", dir_path, namelist[i]->d_name);
179                 asprintf(&dentries[i].full_path, "%s%s", full_path, namelist[i]->d_name);
180
181                 free(namelist[i]);
182
183                 ret = lstat(dentries[i].full_path, &stat);
184                 if (ret < 0) {
185                         error_errno("lstat");
186                         i--;
187                         entries--;
188                         continue;
189                 }
190
191                 dentries[i].size = stat.st_size;
192                 dentries[i].mode = stat.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
193                 if (fixed_time == -1) {
194                         dentries[i].mtime = stat.st_mtime;
195                 } else {
196                         dentries[i].mtime = fixed_time;
197                 }
198                 uint64_t capabilities;
199                 if (fs_config_func != NULL) {
200 #ifdef ANDROID
201                         unsigned int mode = 0;
202                         unsigned int uid = 0;
203                         unsigned int gid = 0;
204                         int dir = S_ISDIR(stat.st_mode);
205                         fs_config_func(dentries[i].path, dir, &uid, &gid, &mode, &capabilities);
206                         dentries[i].mode = mode;
207                         dentries[i].uid = uid;
208                         dentries[i].gid = gid;
209                         dentries[i].capabilities = capabilities;
210 #else
211                         error("can't set android permissions - built without android support");
212 #endif
213                 }
214 #ifndef USE_MINGW
215                 if (sehnd) {
216                         if (selabel_lookup(sehnd, &dentries[i].secon, dentries[i].path, stat.st_mode) < 0) {
217                                 error("cannot lookup security context for %s", dentries[i].path);
218                         }
219
220                         if (dentries[i].secon && verbose)
221                                 printf("Labeling %s as %s\n", dentries[i].path, dentries[i].secon);
222                 }
223 #endif
224
225                 if (S_ISREG(stat.st_mode)) {
226                         dentries[i].file_type = EXT4_FT_REG_FILE;
227                 } else if (S_ISDIR(stat.st_mode)) {
228                         dentries[i].file_type = EXT4_FT_DIR;
229                         dirs++;
230                 } else if (S_ISCHR(stat.st_mode)) {
231                         dentries[i].file_type = EXT4_FT_CHRDEV;
232                 } else if (S_ISBLK(stat.st_mode)) {
233                         dentries[i].file_type = EXT4_FT_BLKDEV;
234                 } else if (S_ISFIFO(stat.st_mode)) {
235                         dentries[i].file_type = EXT4_FT_FIFO;
236                 } else if (S_ISSOCK(stat.st_mode)) {
237                         dentries[i].file_type = EXT4_FT_SOCK;
238                 } else if (S_ISLNK(stat.st_mode)) {
239                         dentries[i].file_type = EXT4_FT_SYMLINK;
240                         dentries[i].link = calloc(info.block_size, 1);
241                         readlink(dentries[i].full_path, dentries[i].link, info.block_size - 1);
242                 } else {
243                         error("unknown file type on %s", dentries[i].path);
244                         i--;
245                         entries--;
246                 }
247         }
248         free(namelist);
249
250         if (needs_lost_and_found) {
251                 /* insert a lost+found directory at the beginning of the dentries */
252                 struct dentry *tmp = calloc(entries + 1, sizeof(struct dentry));
253                 memset(tmp, 0, sizeof(struct dentry));
254                 memcpy(tmp + 1, dentries, entries * sizeof(struct dentry));
255                 dentries = tmp;
256
257                 dentries[0].filename = strdup("lost+found");
258                 asprintf(&dentries[0].path, "%slost+found", dir_path);
259                 dentries[0].full_path = NULL;
260                 dentries[0].size = 0;
261                 dentries[0].mode = S_IRWXU;
262                 dentries[0].file_type = EXT4_FT_DIR;
263                 dentries[0].uid = 0;
264                 dentries[0].gid = 0;
265                 if (sehnd) {
266                         if (selabel_lookup(sehnd, &dentries[0].secon, dentries[0].path, dentries[0].mode) < 0)
267                                 error("cannot lookup security context for %s", dentries[0].path);
268                 }
269                 entries++;
270                 dirs++;
271         }
272
273         inode = make_directory(dir_inode, entries, dentries, dirs);
274
275         for (i = 0; i < entries; i++) {
276                 if (dentries[i].file_type == EXT4_FT_REG_FILE) {
277                         entry_inode = make_file(dentries[i].full_path, dentries[i].size);
278                 } else if (dentries[i].file_type == EXT4_FT_DIR) {
279                         char *subdir_full_path = NULL;
280                         char *subdir_dir_path;
281                         if (dentries[i].full_path) {
282                                 ret = asprintf(&subdir_full_path, "%s/", dentries[i].full_path);
283                                 if (ret < 0)
284                                         critical_error_errno("asprintf");
285                         }
286                         ret = asprintf(&subdir_dir_path, "%s/", dentries[i].path);
287                         if (ret < 0)
288                                 critical_error_errno("asprintf");
289                         entry_inode = build_directory_structure(subdir_full_path,
290                                         subdir_dir_path, inode, fs_config_func, sehnd, verbose, fixed_time);
291                         free(subdir_full_path);
292                         free(subdir_dir_path);
293                 } else if (dentries[i].file_type == EXT4_FT_SYMLINK) {
294                         entry_inode = make_link(dentries[i].link);
295                 } else {
296                         error("unknown file type on %s", dentries[i].path);
297                         entry_inode = 0;
298                 }
299                 *dentries[i].inode = entry_inode;
300
301                 ret = inode_set_permissions(entry_inode, dentries[i].mode,
302                         dentries[i].uid, dentries[i].gid,
303                         dentries[i].mtime);
304                 if (ret)
305                         error("failed to set permissions on %s\n", dentries[i].path);
306
307                 /*
308                  * It's important to call inode_set_selinux() before
309                  * inode_set_capabilities(). Extended attributes need to
310                  * be stored sorted order, and we guarantee this by making
311                  * the calls in the proper order.
312                  * Please see xattr_assert_sane() in contents.c
313                  */
314                 ret = inode_set_selinux(entry_inode, dentries[i].secon);
315                 if (ret)
316                         error("failed to set SELinux context on %s\n", dentries[i].path);
317                 ret = inode_set_capabilities(entry_inode, dentries[i].capabilities);
318                 if (ret)
319                         error("failed to set capability on %s\n", dentries[i].path);
320
321                 free(dentries[i].path);
322                 free(dentries[i].full_path);
323                 free(dentries[i].link);
324                 free((void *)dentries[i].filename);
325                 free(dentries[i].secon);
326         }
327
328         free(dentries);
329         return inode;
330 }
331 #endif
332
333 static u32 compute_block_size()
334 {
335         return 4096;
336 }
337
338 static u32 compute_journal_blocks()
339 {
340         u32 journal_blocks = DIV_ROUND_UP(info.len, info.block_size) / 64;
341         if (journal_blocks < 1024)
342                 journal_blocks = 1024;
343         if (journal_blocks > 32768)
344                 journal_blocks = 32768;
345         return journal_blocks;
346 }
347
348 static u32 compute_blocks_per_group()
349 {
350         return info.block_size * 8;
351 }
352
353 static u32 compute_inodes()
354 {
355         return DIV_ROUND_UP(info.len, info.block_size) / 4;
356 }
357
358 static u32 compute_inodes_per_group()
359 {
360         u32 blocks = DIV_ROUND_UP(info.len, info.block_size);
361         u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group);
362         u32 inodes = DIV_ROUND_UP(info.inodes, block_groups);
363         inodes = EXT4_ALIGN(inodes, (info.block_size / info.inode_size));
364
365         /* After properly rounding up the number of inodes/group,
366          * make sure to update the total inodes field in the info struct.
367          */
368         info.inodes = inodes * block_groups;
369
370         return inodes;
371 }
372
373 static u32 compute_bg_desc_reserve_blocks()
374 {
375         u32 blocks = DIV_ROUND_UP(info.len, info.block_size);
376         u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group);
377         u32 bg_desc_blocks = DIV_ROUND_UP(block_groups * sizeof(struct ext2_group_desc),
378                         info.block_size);
379
380         u32 bg_desc_reserve_blocks =
381                         DIV_ROUND_UP(block_groups * 1024 * sizeof(struct ext2_group_desc),
382                                         info.block_size) - bg_desc_blocks;
383
384         if (bg_desc_reserve_blocks > info.block_size / sizeof(u32))
385                 bg_desc_reserve_blocks = info.block_size / sizeof(u32);
386
387         return bg_desc_reserve_blocks;
388 }
389
390 void reset_ext4fs_info() {
391         // Reset all the global data structures used by make_ext4fs so it
392         // can be called again.
393         memset(&info, 0, sizeof(info));
394         memset(&aux_info, 0, sizeof(aux_info));
395
396         if (ext4_sparse_file) {
397                 sparse_file_destroy(ext4_sparse_file);
398                 ext4_sparse_file = NULL;
399         }
400 }
401
402 int make_ext4fs_sparse_fd(int fd, long long len,
403                                 const char *mountpoint, struct selabel_handle *sehnd)
404 {
405         reset_ext4fs_info();
406         info.len = len;
407
408         return make_ext4fs_internal(fd, NULL, mountpoint, NULL, 0, 1, 0, 0, 0, sehnd, 0, -1, NULL);
409 }
410
411 int make_ext4fs(const char *filename, long long len,
412                                 const char *mountpoint, struct selabel_handle *sehnd)
413 {
414         int fd;
415         int status;
416
417         reset_ext4fs_info();
418         info.len = len;
419
420         fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
421         if (fd < 0) {
422                 error_errno("open");
423                 return EXIT_FAILURE;
424         }
425
426         status = make_ext4fs_internal(fd, NULL, mountpoint, NULL, 0, 0, 0, 1, 0, sehnd, 0, -1, NULL);
427         close(fd);
428
429         return status;
430 }
431
432 /* return a newly-malloc'd string that is a copy of str.  The new string
433    is guaranteed to have a trailing slash.  If absolute is true, the new string
434    is also guaranteed to have a leading slash.
435 */
436 static char *canonicalize_slashes(const char *str, bool absolute)
437 {
438         char *ret;
439         int len = strlen(str);
440         int newlen = len;
441         char *ptr;
442
443         if (len == 0) {
444                 if (absolute)
445                         return strdup("/");
446                 else
447                         return strdup("");
448         }
449
450         if (str[0] != '/' && absolute) {
451                 newlen++;
452         }
453         if (str[len - 1] != '/') {
454                 newlen++;
455         }
456         ret = malloc(newlen + 1);
457         if (!ret) {
458                 critical_error("malloc");
459         }
460
461         ptr = ret;
462         if (str[0] != '/' && absolute) {
463                 *ptr++ = '/';
464         }
465
466         strcpy(ptr, str);
467         ptr += len;
468
469         if (str[len - 1] != '/') {
470                 *ptr++ = '/';
471         }
472
473         if (ptr != ret + newlen) {
474                 critical_error("assertion failed\n");
475         }
476
477         *ptr = '\0';
478
479         return ret;
480 }
481
482 static char *canonicalize_abs_slashes(const char *str)
483 {
484         return canonicalize_slashes(str, true);
485 }
486
487 static char *canonicalize_rel_slashes(const char *str)
488 {
489         return canonicalize_slashes(str, false);
490 }
491
492 int make_ext4fs_internal(int fd, const char *_directory,
493                                                  const char *_mountpoint, fs_config_func_t fs_config_func, int gzip,
494                                                  int sparse, int crc, int wipe, int real_uuid,
495                                                  struct selabel_handle *sehnd, int verbose, time_t fixed_time,
496                                                  FILE* block_list_file)
497 {
498         u32 root_inode_num;
499         u16 root_mode;
500         char *mountpoint;
501         char *directory = NULL;
502
503         if (setjmp(setjmp_env))
504                 return EXIT_FAILURE; /* Handle a call to longjmp() */
505
506         if (_mountpoint == NULL) {
507                 mountpoint = strdup("");
508         } else {
509                 mountpoint = canonicalize_abs_slashes(_mountpoint);
510         }
511
512         if (_directory) {
513                 directory = canonicalize_rel_slashes(_directory);
514         }
515
516         if (info.len <= 0)
517                 info.len = get_file_size(fd);
518
519         if (info.len <= 0) {
520                 fprintf(stderr, "Need size of filesystem\n");
521                 return EXIT_FAILURE;
522         }
523
524         if (info.block_size <= 0)
525                 info.block_size = compute_block_size();
526
527         /* Round down the filesystem length to be a multiple of the block size */
528         info.len &= ~((u64)info.block_size - 1);
529
530         if (info.journal_blocks == 0)
531                 info.journal_blocks = compute_journal_blocks();
532
533         if (info.no_journal == 0)
534                 info.feat_compat = EXT4_FEATURE_COMPAT_HAS_JOURNAL;
535         else
536                 info.journal_blocks = 0;
537
538         if (info.blocks_per_group <= 0)
539                 info.blocks_per_group = compute_blocks_per_group();
540
541         if (info.inodes <= 0)
542                 info.inodes = compute_inodes();
543
544         if (info.inode_size <= 0)
545                 info.inode_size = 256;
546
547         if (info.label == NULL)
548                 info.label = "";
549
550         info.inodes_per_group = compute_inodes_per_group();
551
552         info.feat_compat |=
553                         EXT4_FEATURE_COMPAT_RESIZE_INODE |
554                         EXT4_FEATURE_COMPAT_EXT_ATTR;
555
556         info.feat_ro_compat |=
557                         EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER |
558                         EXT4_FEATURE_RO_COMPAT_LARGE_FILE |
559                         EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
560
561         info.feat_incompat |=
562                         EXT4_FEATURE_INCOMPAT_EXTENTS |
563                         EXT4_FEATURE_INCOMPAT_FILETYPE;
564
565
566         info.bg_desc_reserve_blocks = compute_bg_desc_reserve_blocks();
567
568         printf("Creating filesystem with parameters:\n");
569         printf("    Size: %"PRIu64"\n", info.len);
570         printf("    Block size: %d\n", info.block_size);
571         printf("    Blocks per group: %d\n", info.blocks_per_group);
572         printf("    Inodes per group: %d\n", info.inodes_per_group);
573         printf("    Inode size: %d\n", info.inode_size);
574         printf("    Journal blocks: %d\n", info.journal_blocks);
575         printf("    Label: %s\n", info.label);
576
577         ext4_create_fs_aux_info();
578
579         printf("    Blocks: %"PRIu64"\n", aux_info.len_blocks);
580         printf("    Block groups: %d\n", aux_info.groups);
581         printf("    Reserved block group size: %d\n", info.bg_desc_reserve_blocks);
582
583         ext4_sparse_file = sparse_file_new(info.block_size, info.len);
584
585         block_allocator_init();
586
587         ext4_fill_in_sb(real_uuid);
588
589         if (reserve_inodes(0, 10) == EXT4_ALLOCATE_FAILED)
590                 error("failed to reserve first 10 inodes");
591
592         if (info.feat_compat & EXT4_FEATURE_COMPAT_HAS_JOURNAL)
593                 ext4_create_journal_inode();
594
595         if (info.feat_compat & EXT4_FEATURE_COMPAT_RESIZE_INODE)
596                 ext4_create_resize_inode();
597
598 #ifdef USE_MINGW
599         // Windows needs only 'create an empty fs image' functionality
600         assert(!directory);
601         root_inode_num = build_default_directory_structure(mountpoint, sehnd);
602 #else
603         if (directory)
604                 root_inode_num = build_directory_structure(directory, mountpoint, 0,
605                         fs_config_func, sehnd, verbose, fixed_time);
606         else
607                 root_inode_num = build_default_directory_structure(mountpoint, sehnd);
608 #endif
609
610         root_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
611         inode_set_permissions(root_inode_num, root_mode, 0, 0, 0);
612
613 #ifndef USE_MINGW
614         if (sehnd) {
615                 char *secontext = NULL;
616
617                 if (selabel_lookup(sehnd, &secontext, mountpoint, S_IFDIR) < 0) {
618                         error("cannot lookup security context for %s", mountpoint);
619                 }
620                 if (secontext) {
621                         if (verbose) {
622                                 printf("Labeling %s as %s\n", mountpoint, secontext);
623                         }
624                         inode_set_selinux(root_inode_num, secontext);
625                 }
626                 freecon(secontext);
627         }
628 #endif
629
630         ext4_update_free();
631
632         ext4_queue_sb();
633
634         if (block_list_file) {
635                 size_t dirlen = directory ? strlen(directory) : 0;
636                 struct block_allocation* p = get_saved_allocation_chain();
637                 while (p) {
638                         if (directory && strncmp(p->filename, directory, dirlen) == 0) {
639                                 // substitute mountpoint for the leading directory in the filename, in the output file
640                                 fprintf(block_list_file, "%s%s", mountpoint, p->filename + dirlen);
641                         } else {
642                                 fprintf(block_list_file, "%s", p->filename);
643                         }
644                         print_blocks(block_list_file, p);
645                         struct block_allocation* pn = p->next;
646                         free_alloc(p);
647                         p = pn;
648                 }
649         }
650
651         printf("Created filesystem with %d/%d inodes and %d/%d blocks\n",
652                         aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count,
653                         aux_info.sb->s_inodes_count,
654                         aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo,
655                         aux_info.sb->s_blocks_count_lo);
656
657         if (wipe && WIPE_IS_SUPPORTED) {
658                 wipe_block_device(fd, info.len);
659         }
660
661         write_ext4_image(fd, gzip, sparse, crc);
662
663         sparse_file_destroy(ext4_sparse_file);
664         ext4_sparse_file = NULL;
665
666         free(mountpoint);
667         free(directory);
668
669         return 0;
670 }