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#define O_BINARY 0
63
64#endif
65
66/* TODO: Not implemented:
67   Allocating blocks in the same block group as the file inode
68   Hash or binary tree directories
69   Special files: sockets, devices, fifos
70 */
71
72static int filter_dot(const struct dirent *d)
73{
74	return (strcmp(d->d_name, "..") && strcmp(d->d_name, "."));
75}
76
77static u32 build_default_directory_structure()
78{
79	u32 inode;
80	u32 root_inode;
81	struct dentry dentries = {
82			.filename = "lost+found",
83			.file_type = EXT4_FT_DIR,
84			.mode = S_IRWXU,
85			.uid = 0,
86			.gid = 0,
87			.mtime = 0,
88	};
89	root_inode = make_directory(0, 1, &dentries, 1);
90	inode = make_directory(root_inode, 0, NULL, 0);
91	*dentries.inode = inode;
92	inode_set_permissions(inode, dentries.mode,
93		dentries.uid, dentries.gid, dentries.mtime);
94
95	return root_inode;
96}
97
98#ifndef USE_MINGW
99/* Read a local directory and create the same tree in the generated filesystem.
100   Calls itself recursively with each directory in the given directory */
101static u32 build_directory_structure(const char *full_path, const char *dir_path,
102		u32 dir_inode, fs_config_func_t fs_config_func,
103		struct selabel_handle *sehnd)
104{
105	int entries = 0;
106	struct dentry *dentries;
107	struct dirent **namelist;
108	struct stat stat;
109	int ret;
110	int i;
111	u32 inode;
112	u32 entry_inode;
113	u32 dirs = 0;
114
115	entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort);
116	if (entries < 0) {
117		error_errno("scandir");
118		return EXT4_ALLOCATE_FAILED;
119	}
120
121	dentries = calloc(entries, sizeof(struct dentry));
122	if (dentries == NULL)
123		critical_error_errno("malloc");
124
125	for (i = 0; i < entries; i++) {
126		dentries[i].filename = strdup(namelist[i]->d_name);
127		if (dentries[i].filename == NULL)
128			critical_error_errno("strdup");
129
130		asprintf(&dentries[i].path, "%s/%s", dir_path, namelist[i]->d_name);
131		asprintf(&dentries[i].full_path, "%s/%s", full_path, namelist[i]->d_name);
132
133		free(namelist[i]);
134
135		ret = lstat(dentries[i].full_path, &stat);
136		if (ret < 0) {
137			error_errno("lstat");
138			i--;
139			entries--;
140			continue;
141		}
142
143		dentries[i].size = stat.st_size;
144		dentries[i].mode = stat.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
145		dentries[i].mtime = stat.st_mtime;
146		if (fs_config_func != NULL) {
147#ifdef ANDROID
148			unsigned int mode = 0;
149			unsigned int uid = 0;
150			unsigned int gid = 0;
151			int dir = S_ISDIR(stat.st_mode);
152			fs_config_func(dentries[i].path, dir, &uid, &gid, &mode);
153			dentries[i].mode = mode;
154			dentries[i].uid = uid;
155			dentries[i].gid = gid;
156#else
157			error("can't set android permissions - built without android support");
158#endif
159		}
160#ifdef HAVE_SELINUX
161		if (sehnd) {
162			char *sepath = NULL;
163			asprintf(&sepath, "/%s", dentries[i].path);
164			if (selabel_lookup(sehnd, &dentries[i].secon, sepath, stat.st_mode) < 0) {
165				error("cannot lookup security context for %s", sepath);
166			}
167			if (dentries[i].secon)
168				printf("Labeling %s as %s\n", sepath, dentries[i].secon);
169			free(sepath);
170		}
171#endif
172
173		if (S_ISREG(stat.st_mode)) {
174			dentries[i].file_type = EXT4_FT_REG_FILE;
175		} else if (S_ISDIR(stat.st_mode)) {
176			dentries[i].file_type = EXT4_FT_DIR;
177			dirs++;
178		} else if (S_ISCHR(stat.st_mode)) {
179			dentries[i].file_type = EXT4_FT_CHRDEV;
180		} else if (S_ISBLK(stat.st_mode)) {
181			dentries[i].file_type = EXT4_FT_BLKDEV;
182		} else if (S_ISFIFO(stat.st_mode)) {
183			dentries[i].file_type = EXT4_FT_FIFO;
184		} else if (S_ISSOCK(stat.st_mode)) {
185			dentries[i].file_type = EXT4_FT_SOCK;
186		} else if (S_ISLNK(stat.st_mode)) {
187			dentries[i].file_type = EXT4_FT_SYMLINK;
188			dentries[i].link = calloc(info.block_size, 1);
189			readlink(dentries[i].full_path, dentries[i].link, info.block_size - 1);
190		} else {
191			error("unknown file type on %s", dentries[i].path);
192			i--;
193			entries--;
194		}
195	}
196	free(namelist);
197
198	inode = make_directory(dir_inode, entries, dentries, dirs);
199
200	for (i = 0; i < entries; i++) {
201		if (dentries[i].file_type == EXT4_FT_REG_FILE) {
202			entry_inode = make_file(dentries[i].full_path, dentries[i].size);
203		} else if (dentries[i].file_type == EXT4_FT_DIR) {
204			entry_inode = build_directory_structure(dentries[i].full_path,
205					dentries[i].path, inode, fs_config_func, sehnd);
206		} else if (dentries[i].file_type == EXT4_FT_SYMLINK) {
207			entry_inode = make_link(dentries[i].full_path, dentries[i].link);
208		} else {
209			error("unknown file type on %s", dentries[i].path);
210			entry_inode = 0;
211		}
212		*dentries[i].inode = entry_inode;
213
214		ret = inode_set_permissions(entry_inode, dentries[i].mode,
215			dentries[i].uid, dentries[i].gid,
216			dentries[i].mtime);
217		if (ret)
218			error("failed to set permissions on %s\n", dentries[i].path);
219		ret = inode_set_selinux(entry_inode, dentries[i].secon);
220		if (ret)
221			error("failed to set SELinux context on %s\n", dentries[i].path);
222
223		free(dentries[i].path);
224		free(dentries[i].full_path);
225		free(dentries[i].link);
226		free((void *)dentries[i].filename);
227		free(dentries[i].secon);
228	}
229
230	free(dentries);
231	return inode;
232}
233#endif
234
235static u32 compute_block_size()
236{
237	return 4096;
238}
239
240static u32 compute_journal_blocks()
241{
242	u32 journal_blocks = DIV_ROUND_UP(info.len, info.block_size) / 64;
243	if (journal_blocks < 1024)
244		journal_blocks = 1024;
245	if (journal_blocks > 32768)
246		journal_blocks = 32768;
247	return journal_blocks;
248}
249
250static u32 compute_blocks_per_group()
251{
252	return info.block_size * 8;
253}
254
255static u32 compute_inodes()
256{
257	return DIV_ROUND_UP(info.len, info.block_size) / 4;
258}
259
260static u32 compute_inodes_per_group()
261{
262	u32 blocks = DIV_ROUND_UP(info.len, info.block_size);
263	u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group);
264	u32 inodes = DIV_ROUND_UP(info.inodes, block_groups);
265	inodes = ALIGN(inodes, (info.block_size / info.inode_size));
266
267	/* After properly rounding up the number of inodes/group,
268	 * make sure to update the total inodes field in the info struct.
269	 */
270	info.inodes = inodes * block_groups;
271
272	return inodes;
273}
274
275static u32 compute_bg_desc_reserve_blocks()
276{
277	u32 blocks = DIV_ROUND_UP(info.len, info.block_size);
278	u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group);
279	u32 bg_desc_blocks = DIV_ROUND_UP(block_groups * sizeof(struct ext2_group_desc),
280			info.block_size);
281
282	u32 bg_desc_reserve_blocks =
283			DIV_ROUND_UP(block_groups * 1024 * sizeof(struct ext2_group_desc),
284					info.block_size) - bg_desc_blocks;
285
286	if (bg_desc_reserve_blocks > info.block_size / sizeof(u32))
287		bg_desc_reserve_blocks = info.block_size / sizeof(u32);
288
289	return bg_desc_reserve_blocks;
290}
291
292void reset_ext4fs_info() {
293    // Reset all the global data structures used by make_ext4fs so it
294    // can be called again.
295    memset(&info, 0, sizeof(info));
296    memset(&aux_info, 0, sizeof(aux_info));
297
298    if (info.sparse_file) {
299        sparse_file_destroy(info.sparse_file);
300        info.sparse_file = NULL;
301    }
302}
303
304int make_ext4fs(const char *filename, s64 len,
305                const char *mountpoint, struct selabel_handle *sehnd)
306{
307	int fd;
308	int status;
309
310	reset_ext4fs_info();
311	info.len = len;
312
313	fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
314	if (fd < 0) {
315		error_errno("open");
316		return EXIT_FAILURE;
317	}
318
319	status = make_ext4fs_internal(fd, NULL, mountpoint, NULL, 0, 0, 0, 1, 0, sehnd);
320	close(fd);
321
322	return status;
323}
324
325int make_ext4fs_internal(int fd, const char *directory,
326                         char *mountpoint, fs_config_func_t fs_config_func, int gzip, int sparse,
327                         int crc, int wipe, int init_itabs, struct selabel_handle *sehnd)
328{
329	u32 root_inode_num;
330	u16 root_mode;
331
332	if (setjmp(setjmp_env))
333		return EXIT_FAILURE; /* Handle a call to longjmp() */
334
335	if (info.len <= 0)
336		info.len = get_file_size(fd);
337
338	if (info.len <= 0) {
339		fprintf(stderr, "Need size of filesystem\n");
340		return EXIT_FAILURE;
341	}
342
343	if (info.block_size <= 0)
344		info.block_size = compute_block_size();
345
346	/* Round down the filesystem length to be a multiple of the block size */
347	info.len &= ~((u64)info.block_size - 1);
348
349	if (info.journal_blocks == 0)
350		info.journal_blocks = compute_journal_blocks();
351
352	if (info.no_journal == 0)
353		info.feat_compat = EXT4_FEATURE_COMPAT_HAS_JOURNAL;
354	else
355		info.journal_blocks = 0;
356
357	if (info.blocks_per_group <= 0)
358		info.blocks_per_group = compute_blocks_per_group();
359
360	if (info.inodes <= 0)
361		info.inodes = compute_inodes();
362
363	if (info.inode_size <= 0)
364		info.inode_size = 256;
365
366	if (info.label == NULL)
367		info.label = "";
368
369	info.inodes_per_group = compute_inodes_per_group();
370
371	info.feat_compat |=
372			EXT4_FEATURE_COMPAT_RESIZE_INODE;
373
374	info.feat_ro_compat |=
375			EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER |
376			EXT4_FEATURE_RO_COMPAT_LARGE_FILE;
377
378	info.feat_incompat |=
379			EXT4_FEATURE_INCOMPAT_EXTENTS |
380			EXT4_FEATURE_INCOMPAT_FILETYPE;
381
382
383	info.bg_desc_reserve_blocks = compute_bg_desc_reserve_blocks();
384
385	printf("Creating filesystem with parameters:\n");
386	printf("    Size: %llu\n", info.len);
387	printf("    Block size: %d\n", info.block_size);
388	printf("    Blocks per group: %d\n", info.blocks_per_group);
389	printf("    Inodes per group: %d\n", info.inodes_per_group);
390	printf("    Inode size: %d\n", info.inode_size);
391	printf("    Journal blocks: %d\n", info.journal_blocks);
392	printf("    Label: %s\n", info.label);
393
394	ext4_create_fs_aux_info();
395
396	printf("    Blocks: %llu\n", aux_info.len_blocks);
397	printf("    Block groups: %d\n", aux_info.groups);
398	printf("    Reserved block group size: %d\n", info.bg_desc_reserve_blocks);
399
400	info.sparse_file = sparse_file_new(info.block_size, info.len);
401
402	block_allocator_init();
403
404	ext4_fill_in_sb();
405
406	if (reserve_inodes(0, 10) == EXT4_ALLOCATE_FAILED)
407		error("failed to reserve first 10 inodes");
408
409	if (info.feat_compat & EXT4_FEATURE_COMPAT_HAS_JOURNAL)
410		ext4_create_journal_inode();
411
412	if (info.feat_compat & EXT4_FEATURE_COMPAT_RESIZE_INODE)
413		ext4_create_resize_inode();
414
415#ifdef USE_MINGW
416	// Windows needs only 'create an empty fs image' functionality
417	assert(!directory);
418	root_inode_num = build_default_directory_structure();
419#else
420	if (directory)
421		root_inode_num = build_directory_structure(directory, mountpoint, 0,
422                        fs_config_func, sehnd);
423	else
424		root_inode_num = build_default_directory_structure();
425#endif
426
427	root_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
428	inode_set_permissions(root_inode_num, root_mode, 0, 0, 0);
429
430#ifdef HAVE_SELINUX
431	if (sehnd) {
432		char *sepath = NULL;
433		char *secontext = NULL;
434
435		if (mountpoint[0] == '/')
436			sepath = strdup(mountpoint);
437		else
438			asprintf(&sepath, "/%s", mountpoint);
439		if (!sepath)
440			critical_error_errno("malloc");
441		if (selabel_lookup(sehnd, &secontext, sepath, S_IFDIR) < 0) {
442			error("cannot lookup security context for %s", sepath);
443		}
444		if (secontext) {
445			printf("Labeling %s as %s\n", sepath, secontext);
446			inode_set_selinux(root_inode_num, secontext);
447		}
448		free(sepath);
449		freecon(secontext);
450	}
451#endif
452
453	ext4_update_free();
454
455	if (init_itabs)
456		init_unused_inode_tables();
457
458	ext4_queue_sb();
459
460	printf("Created filesystem with %d/%d inodes and %d/%d blocks\n",
461			aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count,
462			aux_info.sb->s_inodes_count,
463			aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo,
464			aux_info.sb->s_blocks_count_lo);
465
466	if (wipe)
467		wipe_block_device(fd, info.len);
468
469	write_ext4_image(fd, gzip, sparse, crc);
470
471	sparse_file_destroy(info.sparse_file);
472	info.sparse_file = NULL;
473
474	return 0;
475}
476