Remove unused mountpoint support
[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 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 *directory = NULL;
383
384 if (setjmp(setjmp_env))
385 return EXIT_FAILURE; /* Handle a call to longjmp() */
386
387 if (_directory == NULL) {
388 fprintf(stderr, "Need a source directory\n");
389 return EXIT_FAILURE;
390 }
391
392 directory = canonicalize_rel_slashes(_directory);
393
394 if (info.len <= 0)
395 info.len = get_file_size(fd);
396
397 if (info.len <= 0) {
398 fprintf(stderr, "Need size of filesystem\n");
399 return EXIT_FAILURE;
400 }
401
402 if (info.block_size <= 0)
403 info.block_size = compute_block_size();
404
405 /* Round down the filesystem length to be a multiple of the block size */
406 info.len &= ~((u64)info.block_size - 1);
407
408 if (info.journal_blocks == 0)
409 info.journal_blocks = compute_journal_blocks();
410
411 if (info.no_journal == 0)
412 info.feat_compat = EXT4_FEATURE_COMPAT_HAS_JOURNAL;
413 else
414 info.journal_blocks = 0;
415
416 if (info.blocks_per_group <= 0)
417 info.blocks_per_group = compute_blocks_per_group();
418
419 if (info.inodes <= 0)
420 info.inodes = compute_inodes();
421
422 if (info.inode_size <= 0)
423 info.inode_size = 256;
424
425 if (info.label == NULL)
426 info.label = "";
427
428 info.inodes_per_group = compute_inodes_per_group();
429
430 info.feat_compat |=
431 EXT4_FEATURE_COMPAT_RESIZE_INODE |
432 EXT4_FEATURE_COMPAT_EXT_ATTR;
433
434 info.feat_ro_compat |=
435 EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER |
436 EXT4_FEATURE_RO_COMPAT_LARGE_FILE |
437 EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
438
439 info.feat_incompat |=
440 EXT4_FEATURE_INCOMPAT_EXTENTS |
441 EXT4_FEATURE_INCOMPAT_FILETYPE;
442
443
444 info.bg_desc_reserve_blocks = compute_bg_desc_reserve_blocks();
445
446 printf("Creating filesystem with parameters:\n");
447 printf(" Size: %"PRIu64"\n", info.len);
448 printf(" Block size: %d\n", info.block_size);
449 printf(" Blocks per group: %d\n", info.blocks_per_group);
450 printf(" Inodes per group: %d\n", info.inodes_per_group);
451 printf(" Inode size: %d\n", info.inode_size);
452 printf(" Journal blocks: %d\n", info.journal_blocks);
453 printf(" Label: %s\n", info.label);
454
455 ext4_create_fs_aux_info();
456
457 printf(" Blocks: %"PRIu64"\n", aux_info.len_blocks);
458 printf(" Block groups: %d\n", aux_info.groups);
459 printf(" Reserved block group size: %d\n", info.bg_desc_reserve_blocks);
460
461 ext4_sparse_file = sparse_file_new(info.block_size, info.len);
462
463 block_allocator_init();
464
465 ext4_fill_in_sb();
466
467 if (reserve_inodes(0, 10) == EXT4_ALLOCATE_FAILED)
468 error("failed to reserve first 10 inodes");
469
470 if (info.feat_compat & EXT4_FEATURE_COMPAT_HAS_JOURNAL)
471 ext4_create_journal_inode();
472
473 if (info.feat_compat & EXT4_FEATURE_COMPAT_RESIZE_INODE)
474 ext4_create_resize_inode();
475
476 root_inode_num = build_directory_structure(directory, "", 0,
477 fs_config_func, verbose, fixed_time);
478
479 root_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
480 inode_set_permissions(root_inode_num, root_mode, 0, 0, 0);
481
482 ext4_update_free();
483
484 ext4_queue_sb();
485
486 if (block_list_file) {
487 size_t dirlen = strlen(directory);
488 struct block_allocation* p = get_saved_allocation_chain();
489 while (p) {
490 if (strncmp(p->filename, directory, dirlen) == 0) {
491 fprintf(block_list_file, "%s", p->filename + dirlen);
492 } else {
493 fprintf(block_list_file, "%s", p->filename);
494 }
495 print_blocks(block_list_file, p);
496 struct block_allocation* pn = p->next;
497 free_alloc(p);
498 p = pn;
499 }
500 }
501
502 printf("Created filesystem with %d/%d inodes and %d/%d blocks\n",
503 aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count,
504 aux_info.sb->s_inodes_count,
505 aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo,
506 aux_info.sb->s_blocks_count_lo);
507
508 if (wipe && WIPE_IS_SUPPORTED) {
509 wipe_block_device(fd, info.len);
510 }
511
512 write_ext4_image(fd, gzip, sparse, crc);
513
514 sparse_file_destroy(ext4_sparse_file);
515 ext4_sparse_file = NULL;
516
517 free(directory);
518
519 return 0;
520 }