Remove unused functions
[project/make_ext4fs.git] / 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.h"
18 #include "allocate.h"
19 #include "contents.h"
20 #include "uuid.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 /* TODO: Not implemented:
38 Allocating blocks in the same block group as the file inode
39 Hash or binary tree directories
40 */
41
42 static int filter_dot(const struct dirent *d)
43 {
44 return (strcmp(d->d_name, "..") && strcmp(d->d_name, "."));
45 }
46
47 static u32 build_default_directory_structure(const char *dir_path)
48 {
49 u32 inode;
50 u32 root_inode;
51 struct dentry dentries = {
52 .filename = "lost+found",
53 .file_type = EXT4_FT_DIR,
54 .mode = S_IRWXU,
55 .uid = 0,
56 .gid = 0,
57 .mtime = 0,
58 };
59 root_inode = make_directory(0, 1, &dentries, 1);
60 inode = make_directory(root_inode, 0, NULL, 0);
61 *dentries.inode = inode;
62 inode_set_permissions(inode, dentries.mode,
63 dentries.uid, dentries.gid, dentries.mtime);
64
65 return root_inode;
66 }
67
68 /* Read a local directory and create the same tree in the generated filesystem.
69 Calls itself recursively with each directory in the given directory.
70 full_path is an absolute or relative path, with a trailing slash, to the
71 directory on disk that should be copied, or NULL if this is a directory
72 that does not exist on disk (e.g. lost+found).
73 dir_path is an absolute path, with trailing slash, to the same directory
74 if the image were mounted at the specified mount point */
75 static u32 build_directory_structure(const char *full_path, const char *dir_path,
76 u32 dir_inode, fs_config_func_t fs_config_func,
77 int verbose, time_t fixed_time)
78 {
79 int entries = 0;
80 struct dentry *dentries;
81 struct dirent **namelist = NULL;
82 struct stat stat;
83 int ret;
84 int i;
85 u32 inode;
86 u32 entry_inode;
87 u32 dirs = 0;
88 bool needs_lost_and_found = false;
89
90 if (full_path) {
91 entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort);
92 if (entries < 0) {
93 #ifdef __GLIBC__
94 /* The scandir function implemented in glibc has a bug that makes it
95 erroneously fail with ENOMEM under certain circumstances.
96 As a workaround we can retry the scandir call with the same arguments.
97 GLIBC BZ: https://sourceware.org/bugzilla/show_bug.cgi?id=17804 */
98 if (errno == ENOMEM)
99 entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort);
100 #endif
101 if (entries < 0) {
102 error_errno("scandir");
103 return EXT4_ALLOCATE_FAILED;
104 }
105 }
106 }
107
108 if (dir_inode == 0) {
109 /* root directory, check if lost+found already exists */
110 for (i = 0; i < entries; i++)
111 if (strcmp(namelist[i]->d_name, "lost+found") == 0)
112 break;
113 if (i == entries)
114 needs_lost_and_found = true;
115 }
116
117 dentries = calloc(entries, sizeof(struct dentry));
118 if (dentries == NULL)
119 critical_error_errno("malloc");
120
121 for (i = 0; i < entries; i++) {
122 dentries[i].filename = strdup(namelist[i]->d_name);
123 if (dentries[i].filename == NULL)
124 critical_error_errno("strdup");
125
126 asprintf(&dentries[i].path, "%s%s", dir_path, namelist[i]->d_name);
127 asprintf(&dentries[i].full_path, "%s%s", full_path, namelist[i]->d_name);
128
129 free(namelist[i]);
130
131 ret = lstat(dentries[i].full_path, &stat);
132 if (ret < 0) {
133 error_errno("lstat");
134 i--;
135 entries--;
136 continue;
137 }
138
139 dentries[i].size = stat.st_size;
140 dentries[i].mode = stat.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
141 if (fixed_time == -1) {
142 dentries[i].mtime = stat.st_mtime;
143 } else {
144 dentries[i].mtime = fixed_time;
145 }
146 uint64_t capabilities;
147 if (fs_config_func != NULL) {
148 unsigned int mode = 0;
149 unsigned int uid = 0;
150 unsigned int gid = 0;
151 int dir = S_ISDIR(stat.st_mode);
152 if (fs_config_func(dentries[i].path, dir, &uid, &gid, &mode, &capabilities)) {
153 dentries[i].mode = mode;
154 dentries[i].uid = uid;
155 dentries[i].gid = gid;
156 dentries[i].capabilities = capabilities;
157 }
158 }
159
160 if (S_ISREG(stat.st_mode)) {
161 dentries[i].file_type = EXT4_FT_REG_FILE;
162 } else if (S_ISDIR(stat.st_mode)) {
163 dentries[i].file_type = EXT4_FT_DIR;
164 dirs++;
165 } else if (S_ISCHR(stat.st_mode)) {
166 dentries[i].file_type = EXT4_FT_CHRDEV;
167 } else if (S_ISBLK(stat.st_mode)) {
168 dentries[i].file_type = EXT4_FT_BLKDEV;
169 } else if (S_ISFIFO(stat.st_mode)) {
170 dentries[i].file_type = EXT4_FT_FIFO;
171 } else if (S_ISSOCK(stat.st_mode)) {
172 dentries[i].file_type = EXT4_FT_SOCK;
173 } else if (S_ISLNK(stat.st_mode)) {
174 dentries[i].file_type = EXT4_FT_SYMLINK;
175 dentries[i].link = calloc(info.block_size, 1);
176 readlink(dentries[i].full_path, dentries[i].link, info.block_size - 1);
177 } else {
178 error("unknown file type on %s", dentries[i].path);
179 i--;
180 entries--;
181 }
182 }
183 free(namelist);
184
185 if (needs_lost_and_found) {
186 /* insert a lost+found directory at the beginning of the dentries */
187 struct dentry *tmp = calloc(entries + 1, sizeof(struct dentry));
188 memset(tmp, 0, sizeof(struct dentry));
189 memcpy(tmp + 1, dentries, entries * sizeof(struct dentry));
190 dentries = tmp;
191
192 dentries[0].filename = strdup("lost+found");
193 asprintf(&dentries[0].path, "%slost+found", dir_path);
194 dentries[0].full_path = NULL;
195 dentries[0].size = 0;
196 dentries[0].mode = S_IRWXU;
197 dentries[0].file_type = EXT4_FT_DIR;
198 dentries[0].uid = 0;
199 dentries[0].gid = 0;
200 entries++;
201 dirs++;
202 }
203
204 inode = make_directory(dir_inode, entries, dentries, dirs);
205
206 for (i = 0; i < entries; i++) {
207 if (dentries[i].file_type == EXT4_FT_REG_FILE) {
208 entry_inode = make_file(dentries[i].full_path, dentries[i].size);
209 } else if (dentries[i].file_type == EXT4_FT_DIR) {
210 char *subdir_full_path = NULL;
211 char *subdir_dir_path;
212 if (dentries[i].full_path) {
213 ret = asprintf(&subdir_full_path, "%s/", dentries[i].full_path);
214 if (ret < 0)
215 critical_error_errno("asprintf");
216 }
217 ret = asprintf(&subdir_dir_path, "%s/", dentries[i].path);
218 if (ret < 0)
219 critical_error_errno("asprintf");
220 entry_inode = build_directory_structure(subdir_full_path,
221 subdir_dir_path, inode, fs_config_func, verbose, fixed_time);
222 free(subdir_full_path);
223 free(subdir_dir_path);
224 } else if (dentries[i].file_type == EXT4_FT_SYMLINK) {
225 entry_inode = make_link(dentries[i].link);
226 } else if (dentries[i].file_type == EXT4_FT_CHRDEV ||
227 dentries[i].file_type == EXT4_FT_BLKDEV ||
228 dentries[i].file_type == EXT4_FT_SOCK ||
229 dentries[i].file_type == EXT4_FT_FIFO) {
230 entry_inode = make_special(dentries[i].full_path);
231 } else {
232 error("unknown file type on %s", dentries[i].path);
233 entry_inode = 0;
234 }
235 *dentries[i].inode = entry_inode;
236
237 ret = inode_set_permissions(entry_inode, dentries[i].mode,
238 dentries[i].uid, dentries[i].gid,
239 dentries[i].mtime);
240 if (ret)
241 error("failed to set permissions on %s\n", dentries[i].path);
242
243 ret = inode_set_capabilities(entry_inode, dentries[i].capabilities);
244 if (ret)
245 error("failed to set capability on %s\n", dentries[i].path);
246
247 free(dentries[i].path);
248 free(dentries[i].full_path);
249 free(dentries[i].link);
250 free((void *)dentries[i].filename);
251 }
252
253 free(dentries);
254 return inode;
255 }
256
257 static u32 compute_block_size()
258 {
259 return 4096;
260 }
261
262 static u32 compute_journal_blocks()
263 {
264 u32 journal_blocks = DIV_ROUND_UP(info.len, info.block_size) / 64;
265 if (journal_blocks < 1024)
266 journal_blocks = 1024;
267 if (journal_blocks > 32768)
268 journal_blocks = 32768;
269 return journal_blocks;
270 }
271
272 static u32 compute_blocks_per_group()
273 {
274 return info.block_size * 8;
275 }
276
277 static u32 compute_inodes()
278 {
279 return DIV_ROUND_UP(info.len, info.block_size) / 4;
280 }
281
282 static u32 compute_inodes_per_group()
283 {
284 u32 blocks = DIV_ROUND_UP(info.len, info.block_size);
285 u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group);
286 u32 inodes = DIV_ROUND_UP(info.inodes, block_groups);
287 inodes = EXT4_ALIGN(inodes, (info.block_size / info.inode_size));
288
289 /* After properly rounding up the number of inodes/group,
290 * make sure to update the total inodes field in the info struct.
291 */
292 info.inodes = inodes * block_groups;
293
294 return inodes;
295 }
296
297 static u32 compute_bg_desc_reserve_blocks()
298 {
299 u32 blocks = DIV_ROUND_UP(info.len, info.block_size);
300 u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group);
301 u32 bg_desc_blocks = DIV_ROUND_UP(block_groups * sizeof(struct ext2_group_desc),
302 info.block_size);
303
304 u32 bg_desc_reserve_blocks =
305 DIV_ROUND_UP(block_groups * 1024 * sizeof(struct ext2_group_desc),
306 info.block_size) - bg_desc_blocks;
307
308 if (bg_desc_reserve_blocks > info.block_size / sizeof(u32))
309 bg_desc_reserve_blocks = info.block_size / sizeof(u32);
310
311 return bg_desc_reserve_blocks;
312 }
313
314 /* return a newly-malloc'd string that is a copy of str. The new string
315 is guaranteed to have a trailing slash. If absolute is true, the new string
316 is also guaranteed to have a leading slash.
317 */
318 static char *canonicalize_slashes(const char *str, bool absolute)
319 {
320 char *ret;
321 int len = strlen(str);
322 int newlen = len;
323 char *ptr;
324
325 if (len == 0) {
326 if (absolute)
327 return strdup("/");
328 else
329 return strdup("");
330 }
331
332 if (str[0] != '/' && absolute) {
333 newlen++;
334 }
335 if (str[len - 1] != '/') {
336 newlen++;
337 }
338 ret = malloc(newlen + 1);
339 if (!ret) {
340 critical_error("malloc");
341 }
342
343 ptr = ret;
344 if (str[0] != '/' && absolute) {
345 *ptr++ = '/';
346 }
347
348 strcpy(ptr, str);
349 ptr += len;
350
351 if (str[len - 1] != '/') {
352 *ptr++ = '/';
353 }
354
355 if (ptr != ret + newlen) {
356 critical_error("assertion failed\n");
357 }
358
359 *ptr = '\0';
360
361 return ret;
362 }
363
364 static char *canonicalize_abs_slashes(const char *str)
365 {
366 return canonicalize_slashes(str, true);
367 }
368
369 static char *canonicalize_rel_slashes(const char *str)
370 {
371 return canonicalize_slashes(str, false);
372 }
373
374 int make_ext4fs_internal(int fd, const char *_directory,
375 const char *_mountpoint, fs_config_func_t fs_config_func, int gzip,
376 int sparse, int crc, int wipe,
377 int verbose, time_t fixed_time,
378 FILE* block_list_file)
379 {
380 u32 root_inode_num;
381 u16 root_mode;
382 char *mountpoint;
383 char *directory = NULL;
384
385 if (setjmp(setjmp_env))
386 return EXIT_FAILURE; /* Handle a call to longjmp() */
387
388 if (_mountpoint == NULL) {
389 mountpoint = strdup("");
390 } else {
391 mountpoint = canonicalize_abs_slashes(_mountpoint);
392 }
393
394 if (_directory) {
395 directory = canonicalize_rel_slashes(_directory);
396 }
397
398 if (info.len <= 0)
399 info.len = get_file_size(fd);
400
401 if (info.len <= 0) {
402 fprintf(stderr, "Need size of filesystem\n");
403 return EXIT_FAILURE;
404 }
405
406 if (info.block_size <= 0)
407 info.block_size = compute_block_size();
408
409 /* Round down the filesystem length to be a multiple of the block size */
410 info.len &= ~((u64)info.block_size - 1);
411
412 if (info.journal_blocks == 0)
413 info.journal_blocks = compute_journal_blocks();
414
415 if (info.no_journal == 0)
416 info.feat_compat = EXT4_FEATURE_COMPAT_HAS_JOURNAL;
417 else
418 info.journal_blocks = 0;
419
420 if (info.blocks_per_group <= 0)
421 info.blocks_per_group = compute_blocks_per_group();
422
423 if (info.inodes <= 0)
424 info.inodes = compute_inodes();
425
426 if (info.inode_size <= 0)
427 info.inode_size = 256;
428
429 if (info.label == NULL)
430 info.label = "";
431
432 info.inodes_per_group = compute_inodes_per_group();
433
434 info.feat_compat |=
435 EXT4_FEATURE_COMPAT_RESIZE_INODE |
436 EXT4_FEATURE_COMPAT_EXT_ATTR;
437
438 info.feat_ro_compat |=
439 EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER |
440 EXT4_FEATURE_RO_COMPAT_LARGE_FILE |
441 EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
442
443 info.feat_incompat |=
444 EXT4_FEATURE_INCOMPAT_EXTENTS |
445 EXT4_FEATURE_INCOMPAT_FILETYPE;
446
447
448 info.bg_desc_reserve_blocks = compute_bg_desc_reserve_blocks();
449
450 printf("Creating filesystem with parameters:\n");
451 printf(" Size: %"PRIu64"\n", info.len);
452 printf(" Block size: %d\n", info.block_size);
453 printf(" Blocks per group: %d\n", info.blocks_per_group);
454 printf(" Inodes per group: %d\n", info.inodes_per_group);
455 printf(" Inode size: %d\n", info.inode_size);
456 printf(" Journal blocks: %d\n", info.journal_blocks);
457 printf(" Label: %s\n", info.label);
458
459 ext4_create_fs_aux_info();
460
461 printf(" Blocks: %"PRIu64"\n", aux_info.len_blocks);
462 printf(" Block groups: %d\n", aux_info.groups);
463 printf(" Reserved block group size: %d\n", info.bg_desc_reserve_blocks);
464
465 ext4_sparse_file = sparse_file_new(info.block_size, info.len);
466
467 block_allocator_init();
468
469 ext4_fill_in_sb();
470
471 if (reserve_inodes(0, 10) == EXT4_ALLOCATE_FAILED)
472 error("failed to reserve first 10 inodes");
473
474 if (info.feat_compat & EXT4_FEATURE_COMPAT_HAS_JOURNAL)
475 ext4_create_journal_inode();
476
477 if (info.feat_compat & EXT4_FEATURE_COMPAT_RESIZE_INODE)
478 ext4_create_resize_inode();
479
480 if (directory)
481 root_inode_num = build_directory_structure(directory, mountpoint, 0,
482 fs_config_func, verbose, fixed_time);
483 else
484 root_inode_num = build_default_directory_structure(mountpoint);
485
486 root_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
487 inode_set_permissions(root_inode_num, root_mode, 0, 0, 0);
488
489 ext4_update_free();
490
491 ext4_queue_sb();
492
493 if (block_list_file) {
494 size_t dirlen = directory ? strlen(directory) : 0;
495 struct block_allocation* p = get_saved_allocation_chain();
496 while (p) {
497 if (directory && strncmp(p->filename, directory, dirlen) == 0) {
498 // substitute mountpoint for the leading directory in the filename, in the output file
499 fprintf(block_list_file, "%s%s", mountpoint, p->filename + dirlen);
500 } else {
501 fprintf(block_list_file, "%s", p->filename);
502 }
503 print_blocks(block_list_file, p);
504 struct block_allocation* pn = p->next;
505 free_alloc(p);
506 p = pn;
507 }
508 }
509
510 printf("Created filesystem with %d/%d inodes and %d/%d blocks\n",
511 aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count,
512 aux_info.sb->s_inodes_count,
513 aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo,
514 aux_info.sb->s_blocks_count_lo);
515
516 if (wipe && WIPE_IS_SUPPORTED) {
517 wipe_block_device(fd, info.len);
518 }
519
520 write_ext4_image(fd, gzip, sparse, crc);
521
522 sparse_file_destroy(ext4_sparse_file);
523 ext4_sparse_file = NULL;
524
525 free(mountpoint);
526 free(directory);
527
528 return 0;
529 }