• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
filter_dot(const struct dirent * d)76 static int filter_dot(const struct dirent *d)
77 {
78 	return (strcmp(d->d_name, "..") && strcmp(d->d_name, "."));
79 }
80 
build_default_directory_structure()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 */
build_directory_structure(const char * full_path,const char * dir_path,u32 dir_inode,fs_config_func_t fs_config_func,struct selabel_handle * sehnd,int verbose)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 		uint64_t capabilities;
168 		if (fs_config_func != NULL) {
169 #ifdef ANDROID
170 			unsigned int mode = 0;
171 			unsigned int uid = 0;
172 			unsigned int gid = 0;
173 			int dir = S_ISDIR(stat.st_mode);
174 			fs_config_func(dentries[i].path, dir, &uid, &gid, &mode, &capabilities);
175 			dentries[i].mode = mode;
176 			dentries[i].uid = uid;
177 			dentries[i].gid = gid;
178 			dentries[i].capabilities = capabilities;
179 #else
180 			error("can't set android permissions - built without android support");
181 #endif
182 		}
183 #ifndef USE_MINGW
184 		if (sehnd) {
185 			if (selabel_lookup(sehnd, &dentries[i].secon, dentries[i].path, stat.st_mode) < 0) {
186 				error("cannot lookup security context for %s", dentries[i].path);
187 			}
188 
189 			if (dentries[i].secon && verbose)
190 				printf("Labeling %s as %s\n", dentries[i].path, dentries[i].secon);
191 		}
192 #endif
193 
194 		if (S_ISREG(stat.st_mode)) {
195 			dentries[i].file_type = EXT4_FT_REG_FILE;
196 		} else if (S_ISDIR(stat.st_mode)) {
197 			dentries[i].file_type = EXT4_FT_DIR;
198 			dirs++;
199 		} else if (S_ISCHR(stat.st_mode)) {
200 			dentries[i].file_type = EXT4_FT_CHRDEV;
201 		} else if (S_ISBLK(stat.st_mode)) {
202 			dentries[i].file_type = EXT4_FT_BLKDEV;
203 		} else if (S_ISFIFO(stat.st_mode)) {
204 			dentries[i].file_type = EXT4_FT_FIFO;
205 		} else if (S_ISSOCK(stat.st_mode)) {
206 			dentries[i].file_type = EXT4_FT_SOCK;
207 		} else if (S_ISLNK(stat.st_mode)) {
208 			dentries[i].file_type = EXT4_FT_SYMLINK;
209 			dentries[i].link = calloc(info.block_size, 1);
210 			readlink(dentries[i].full_path, dentries[i].link, info.block_size - 1);
211 		} else {
212 			error("unknown file type on %s", dentries[i].path);
213 			i--;
214 			entries--;
215 		}
216 	}
217 	free(namelist);
218 
219 	if (needs_lost_and_found) {
220 		/* insert a lost+found directory at the beginning of the dentries */
221 		struct dentry *tmp = calloc(entries + 1, sizeof(struct dentry));
222 		memset(tmp, 0, sizeof(struct dentry));
223 		memcpy(tmp + 1, dentries, entries * sizeof(struct dentry));
224 		dentries = tmp;
225 
226 		dentries[0].filename = strdup("lost+found");
227 		asprintf(&dentries[0].path, "%slost+found", dir_path);
228 		dentries[0].full_path = NULL;
229 		dentries[0].size = 0;
230 		dentries[0].mode = S_IRWXU;
231 		dentries[0].file_type = EXT4_FT_DIR;
232 		dentries[0].uid = 0;
233 		dentries[0].gid = 0;
234 		if (sehnd) {
235 			if (selabel_lookup(sehnd, &dentries[0].secon, dentries[0].path, dentries[0].mode) < 0)
236 				error("cannot lookup security context for %s", dentries[0].path);
237 		}
238 		entries++;
239 		dirs++;
240 	}
241 
242 	inode = make_directory(dir_inode, entries, dentries, dirs);
243 
244 	for (i = 0; i < entries; i++) {
245 		if (dentries[i].file_type == EXT4_FT_REG_FILE) {
246 			entry_inode = make_file(dentries[i].full_path, dentries[i].size);
247 		} else if (dentries[i].file_type == EXT4_FT_DIR) {
248 			char *subdir_full_path = NULL;
249 			char *subdir_dir_path;
250 			if (dentries[i].full_path) {
251 				ret = asprintf(&subdir_full_path, "%s/", dentries[i].full_path);
252 				if (ret < 0)
253 					critical_error_errno("asprintf");
254 			}
255 			ret = asprintf(&subdir_dir_path, "%s/", dentries[i].path);
256 			if (ret < 0)
257 				critical_error_errno("asprintf");
258 			entry_inode = build_directory_structure(subdir_full_path,
259 					subdir_dir_path, inode, fs_config_func, sehnd, verbose);
260 			free(subdir_full_path);
261 			free(subdir_dir_path);
262 		} else if (dentries[i].file_type == EXT4_FT_SYMLINK) {
263 			entry_inode = make_link(dentries[i].link);
264 		} else {
265 			error("unknown file type on %s", dentries[i].path);
266 			entry_inode = 0;
267 		}
268 		*dentries[i].inode = entry_inode;
269 
270 		ret = inode_set_permissions(entry_inode, dentries[i].mode,
271 			dentries[i].uid, dentries[i].gid,
272 			dentries[i].mtime);
273 		if (ret)
274 			error("failed to set permissions on %s\n", dentries[i].path);
275 
276 		/*
277 		 * It's important to call inode_set_selinux() before
278 		 * inode_set_capabilities(). Extended attributes need to
279 		 * be stored sorted order, and we guarantee this by making
280 		 * the calls in the proper order.
281 		 * Please see xattr_assert_sane() in contents.c
282 		 */
283 		ret = inode_set_selinux(entry_inode, dentries[i].secon);
284 		if (ret)
285 			error("failed to set SELinux context on %s\n", dentries[i].path);
286 		ret = inode_set_capabilities(entry_inode, dentries[i].capabilities);
287 		if (ret)
288 			error("failed to set capability on %s\n", dentries[i].path);
289 
290 		free(dentries[i].path);
291 		free(dentries[i].full_path);
292 		free(dentries[i].link);
293 		free((void *)dentries[i].filename);
294 		free(dentries[i].secon);
295 	}
296 
297 	free(dentries);
298 	return inode;
299 }
300 #endif
301 
compute_block_size()302 static u32 compute_block_size()
303 {
304 	return 4096;
305 }
306 
compute_journal_blocks()307 static u32 compute_journal_blocks()
308 {
309 	u32 journal_blocks = DIV_ROUND_UP(info.len, info.block_size) / 64;
310 	if (journal_blocks < 1024)
311 		journal_blocks = 1024;
312 	if (journal_blocks > 32768)
313 		journal_blocks = 32768;
314 	return journal_blocks;
315 }
316 
compute_blocks_per_group()317 static u32 compute_blocks_per_group()
318 {
319 	return info.block_size * 8;
320 }
321 
compute_inodes()322 static u32 compute_inodes()
323 {
324 	return DIV_ROUND_UP(info.len, info.block_size) / 4;
325 }
326 
compute_inodes_per_group()327 static u32 compute_inodes_per_group()
328 {
329 	u32 blocks = DIV_ROUND_UP(info.len, info.block_size);
330 	u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group);
331 	u32 inodes = DIV_ROUND_UP(info.inodes, block_groups);
332 	inodes = ALIGN(inodes, (info.block_size / info.inode_size));
333 
334 	/* After properly rounding up the number of inodes/group,
335 	 * make sure to update the total inodes field in the info struct.
336 	 */
337 	info.inodes = inodes * block_groups;
338 
339 	return inodes;
340 }
341 
compute_bg_desc_reserve_blocks()342 static u32 compute_bg_desc_reserve_blocks()
343 {
344 	u32 blocks = DIV_ROUND_UP(info.len, info.block_size);
345 	u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group);
346 	u32 bg_desc_blocks = DIV_ROUND_UP(block_groups * sizeof(struct ext2_group_desc),
347 			info.block_size);
348 
349 	u32 bg_desc_reserve_blocks =
350 			DIV_ROUND_UP(block_groups * 1024 * sizeof(struct ext2_group_desc),
351 					info.block_size) - bg_desc_blocks;
352 
353 	if (bg_desc_reserve_blocks > info.block_size / sizeof(u32))
354 		bg_desc_reserve_blocks = info.block_size / sizeof(u32);
355 
356 	return bg_desc_reserve_blocks;
357 }
358 
reset_ext4fs_info()359 void reset_ext4fs_info() {
360     // Reset all the global data structures used by make_ext4fs so it
361     // can be called again.
362     memset(&info, 0, sizeof(info));
363     memset(&aux_info, 0, sizeof(aux_info));
364 
365     if (info.sparse_file) {
366         sparse_file_destroy(info.sparse_file);
367         info.sparse_file = NULL;
368     }
369 }
370 
make_ext4fs_sparse_fd(int fd,long long len,const char * mountpoint,struct selabel_handle * sehnd)371 int make_ext4fs_sparse_fd(int fd, long long len,
372                 const char *mountpoint, struct selabel_handle *sehnd)
373 {
374 	reset_ext4fs_info();
375 	info.len = len;
376 
377 	return make_ext4fs_internal(fd, NULL, mountpoint, NULL, 0, 1, 0, 0, sehnd, 0);
378 }
379 
make_ext4fs(const char * filename,long long len,const char * mountpoint,struct selabel_handle * sehnd)380 int make_ext4fs(const char *filename, long long len,
381                 const char *mountpoint, struct selabel_handle *sehnd)
382 {
383 	int fd;
384 	int status;
385 
386 	reset_ext4fs_info();
387 	info.len = len;
388 
389 	fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
390 	if (fd < 0) {
391 		error_errno("open");
392 		return EXIT_FAILURE;
393 	}
394 
395 	status = make_ext4fs_internal(fd, NULL, mountpoint, NULL, 0, 0, 0, 1, sehnd, 0);
396 	close(fd);
397 
398 	return status;
399 }
400 
401 /* return a newly-malloc'd string that is a copy of str.  The new string
402    is guaranteed to have a trailing slash.  If absolute is true, the new string
403    is also guaranteed to have a leading slash.
404 */
canonicalize_slashes(const char * str,bool absolute)405 static char *canonicalize_slashes(const char *str, bool absolute)
406 {
407 	char *ret;
408 	int len = strlen(str);
409 	int newlen = len;
410 	char *ptr;
411 
412 	if (len == 0 && absolute) {
413 		return strdup("/");
414 	}
415 
416 	if (str[0] != '/' && absolute) {
417 		newlen++;
418 	}
419 	if (str[len - 1] != '/') {
420 		newlen++;
421 	}
422 	ret = malloc(newlen + 1);
423 	if (!ret) {
424 		critical_error("malloc");
425 	}
426 
427 	ptr = ret;
428 	if (str[0] != '/' && absolute) {
429 		*ptr++ = '/';
430 	}
431 
432 	strcpy(ptr, str);
433 	ptr += len;
434 
435 	if (str[len - 1] != '/') {
436 		*ptr++ = '/';
437 	}
438 
439 	if (ptr != ret + newlen) {
440 		critical_error("assertion failed\n");
441 	}
442 
443 	*ptr = '\0';
444 
445 	return ret;
446 }
447 
canonicalize_abs_slashes(const char * str)448 static char *canonicalize_abs_slashes(const char *str)
449 {
450 	return canonicalize_slashes(str, true);
451 }
452 
canonicalize_rel_slashes(const char * str)453 static char *canonicalize_rel_slashes(const char *str)
454 {
455 	return canonicalize_slashes(str, false);
456 }
457 
make_ext4fs_internal(int fd,const char * _directory,const char * _mountpoint,fs_config_func_t fs_config_func,int gzip,int sparse,int crc,int wipe,struct selabel_handle * sehnd,int verbose)458 int make_ext4fs_internal(int fd, const char *_directory,
459                          const char *_mountpoint, fs_config_func_t fs_config_func, int gzip,
460                          int sparse, int crc, int wipe,
461                          struct selabel_handle *sehnd, int verbose)
462 {
463 	u32 root_inode_num;
464 	u16 root_mode;
465 	char *mountpoint;
466 	char *directory = NULL;
467 
468 	if (setjmp(setjmp_env))
469 		return EXIT_FAILURE; /* Handle a call to longjmp() */
470 
471 	if (_mountpoint == NULL) {
472 		mountpoint = strdup("");
473 	} else {
474 		mountpoint = canonicalize_abs_slashes(_mountpoint);
475 	}
476 
477 	if (_directory) {
478 		directory = canonicalize_rel_slashes(_directory);
479 	}
480 
481 	if (info.len <= 0)
482 		info.len = get_file_size(fd);
483 
484 	if (info.len <= 0) {
485 		fprintf(stderr, "Need size of filesystem\n");
486 		return EXIT_FAILURE;
487 	}
488 
489 	if (info.block_size <= 0)
490 		info.block_size = compute_block_size();
491 
492 	/* Round down the filesystem length to be a multiple of the block size */
493 	info.len &= ~((u64)info.block_size - 1);
494 
495 	if (info.journal_blocks == 0)
496 		info.journal_blocks = compute_journal_blocks();
497 
498 	if (info.no_journal == 0)
499 		info.feat_compat = EXT4_FEATURE_COMPAT_HAS_JOURNAL;
500 	else
501 		info.journal_blocks = 0;
502 
503 	if (info.blocks_per_group <= 0)
504 		info.blocks_per_group = compute_blocks_per_group();
505 
506 	if (info.inodes <= 0)
507 		info.inodes = compute_inodes();
508 
509 	if (info.inode_size <= 0)
510 		info.inode_size = 256;
511 
512 	if (info.label == NULL)
513 		info.label = "";
514 
515 	info.inodes_per_group = compute_inodes_per_group();
516 
517 	info.feat_compat |=
518 			EXT4_FEATURE_COMPAT_RESIZE_INODE |
519 			EXT4_FEATURE_COMPAT_EXT_ATTR;
520 
521 	info.feat_ro_compat |=
522 			EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER |
523 			EXT4_FEATURE_RO_COMPAT_LARGE_FILE |
524 			EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
525 
526 	info.feat_incompat |=
527 			EXT4_FEATURE_INCOMPAT_EXTENTS |
528 			EXT4_FEATURE_INCOMPAT_FILETYPE;
529 
530 
531 	info.bg_desc_reserve_blocks = compute_bg_desc_reserve_blocks();
532 
533 	printf("Creating filesystem with parameters:\n");
534 	printf("    Size: %llu\n", info.len);
535 	printf("    Block size: %d\n", info.block_size);
536 	printf("    Blocks per group: %d\n", info.blocks_per_group);
537 	printf("    Inodes per group: %d\n", info.inodes_per_group);
538 	printf("    Inode size: %d\n", info.inode_size);
539 	printf("    Journal blocks: %d\n", info.journal_blocks);
540 	printf("    Label: %s\n", info.label);
541 
542 	ext4_create_fs_aux_info();
543 
544 	printf("    Blocks: %llu\n", aux_info.len_blocks);
545 	printf("    Block groups: %d\n", aux_info.groups);
546 	printf("    Reserved block group size: %d\n", info.bg_desc_reserve_blocks);
547 
548 	info.sparse_file = sparse_file_new(info.block_size, info.len);
549 
550 	block_allocator_init();
551 
552 	ext4_fill_in_sb();
553 
554 	if (reserve_inodes(0, 10) == EXT4_ALLOCATE_FAILED)
555 		error("failed to reserve first 10 inodes");
556 
557 	if (info.feat_compat & EXT4_FEATURE_COMPAT_HAS_JOURNAL)
558 		ext4_create_journal_inode();
559 
560 	if (info.feat_compat & EXT4_FEATURE_COMPAT_RESIZE_INODE)
561 		ext4_create_resize_inode();
562 
563 #ifdef USE_MINGW
564 	// Windows needs only 'create an empty fs image' functionality
565 	assert(!directory);
566 	root_inode_num = build_default_directory_structure();
567 #else
568 	if (directory)
569 		root_inode_num = build_directory_structure(directory, mountpoint, 0,
570                         fs_config_func, sehnd, verbose);
571 	else
572 		root_inode_num = build_default_directory_structure();
573 #endif
574 
575 	root_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
576 	inode_set_permissions(root_inode_num, root_mode, 0, 0, 0);
577 
578 #ifndef USE_MINGW
579 	if (sehnd) {
580 		char *secontext = NULL;
581 
582 		if (selabel_lookup(sehnd, &secontext, mountpoint, S_IFDIR) < 0) {
583 			error("cannot lookup security context for %s", mountpoint);
584 		}
585 		if (secontext) {
586 			if (verbose) {
587 				printf("Labeling %s as %s\n", mountpoint, secontext);
588 			}
589 			inode_set_selinux(root_inode_num, secontext);
590 		}
591 		freecon(secontext);
592 	}
593 #endif
594 
595 	ext4_update_free();
596 
597 	ext4_queue_sb();
598 
599 	printf("Created filesystem with %d/%d inodes and %d/%d blocks\n",
600 			aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count,
601 			aux_info.sb->s_inodes_count,
602 			aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo,
603 			aux_info.sb->s_blocks_count_lo);
604 
605 	if (wipe)
606 		wipe_block_device(fd, info.len);
607 
608 	write_ext4_image(fd, gzip, sparse, crc);
609 
610 	sparse_file_destroy(info.sparse_file);
611 	info.sparse_file = NULL;
612 
613 	free(mountpoint);
614 	free(directory);
615 
616 	return 0;
617 }
618