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/make_ext4fs.h"
18
19#ifndef _GNU_SOURCE
20#define _GNU_SOURCE
21#endif
22
23#include <assert.h>
24#include <dirent.h>
25#include <fcntl.h>
26#include <inttypes.h>
27#include <libgen.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <sys/stat.h>
32#include <sys/types.h>
33#include <unistd.h>
34
35#include <sparse/sparse.h>
36
37#include "allocate.h"
38#include "contents.h"
39#include "ext4_utils/ext4_utils.h"
40#include "ext4_utils/wipe.h"
41
42#ifdef _WIN32
43
44#include <winsock2.h>
45
46/* These match the Linux definitions of these flags.
47   L_xx is defined to avoid conflicting with the win32 versions.
48*/
49#undef S_IRWXU
50#undef S_IRGRP
51#undef S_IWGRP
52#undef S_IXGRP
53#undef S_IRWXG
54#undef S_IROTH
55#undef S_IWOTH
56#undef S_IXOTH
57#undef S_IRWXO
58#undef S_ISUID
59#undef S_ISGID
60#undef S_ISVTX
61
62#define L_S_IRUSR 00400
63#define L_S_IWUSR 00200
64#define L_S_IXUSR 00100
65#define S_IRWXU (L_S_IRUSR | L_S_IWUSR | L_S_IXUSR)
66#define S_IRGRP 00040
67#define S_IWGRP 00020
68#define S_IXGRP 00010
69#define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
70#define S_IROTH 00004
71#define S_IWOTH 00002
72#define S_IXOTH 00001
73#define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH)
74#define S_ISUID 0004000
75#define S_ISGID 0002000
76#define S_ISVTX 0001000
77
78#else
79
80#include <selinux/selinux.h>
81#include <selinux/label.h>
82
83#define O_BINARY 0
84
85#endif
86
87#define MAX_PATH 4096
88#define MAX_BLK_MAPPING_STR 1000
89
90const int blk_file_major_ver = 1;
91const int blk_file_minor_ver = 0;
92const char *blk_file_header_fmt = "Base EXT4 version %d.%d";
93
94/* TODO: Not implemented:
95   Allocating blocks in the same block group as the file inode
96   Hash or binary tree directories
97   Special files: sockets, devices, fifos
98 */
99
100static int filter_dot(const struct dirent *d)
101{
102	return (strcmp(d->d_name, "..") && strcmp(d->d_name, "."));
103}
104
105static u32 build_default_directory_structure(const char *dir_path,
106						 struct selabel_handle *sehnd)
107{
108	u32 inode;
109	u32 root_inode;
110	struct dentry dentries = {
111			.filename = "lost+found",
112			.file_type = EXT4_FT_DIR,
113			.mode = S_IRWXU,
114			.uid = 0,
115			.gid = 0,
116			.mtime = 0,
117	};
118	root_inode = make_directory(0, 1, &dentries, 1);
119	inode = make_directory(root_inode, 0, NULL, 0);
120	*dentries.inode = inode;
121	inode_set_permissions(inode, dentries.mode,
122		dentries.uid, dentries.gid, dentries.mtime);
123
124#ifndef _WIN32
125	if (sehnd) {
126		char *path = NULL;
127		char *secontext = NULL;
128
129		asprintf(&path, "%slost+found", dir_path);
130		if (selabel_lookup(sehnd, &secontext, path, S_IFDIR) < 0) {
131			error("cannot lookup security context for %s", path);
132		} else {
133			inode_set_selinux(inode, secontext);
134			freecon(secontext);
135		}
136		free(path);
137	}
138#endif
139
140	return root_inode;
141}
142
143#ifndef _WIN32
144/* Read a local directory and create the same tree in the generated filesystem.
145   Calls itself recursively with each directory in the given directory.
146   full_path is an absolute or relative path, with a trailing slash, to the
147   directory on disk that should be copied, or NULL if this is a directory
148   that does not exist on disk (e.g. lost+found).
149   dir_path is an absolute path, with trailing slash, to the same directory
150   if the image were mounted at the specified mount point */
151static u32 build_directory_structure(const char *full_path, const char *dir_path, const char *target_out_path,
152		u32 dir_inode, fs_config_func_t fs_config_func,
153		struct selabel_handle *sehnd, int verbose, time_t fixed_time)
154{
155	int entries = 0;
156	struct dentry *dentries;
157	struct dirent **namelist = NULL;
158	struct stat stat;
159	int ret;
160	int i;
161	u32 inode;
162	u32 entry_inode;
163	u32 dirs = 0;
164	bool needs_lost_and_found = false;
165
166	if (full_path) {
167		entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort);
168		if (entries < 0) {
169#ifdef __GLIBC__
170			/* The scandir function implemented in glibc has a bug that makes it
171			   erroneously fail with ENOMEM under certain circumstances.
172			   As a workaround we can retry the scandir call with the same arguments.
173			   GLIBC BZ: https://sourceware.org/bugzilla/show_bug.cgi?id=17804 */
174			if (errno == ENOMEM)
175				entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort);
176#endif
177			if (entries < 0) {
178				error_errno("scandir");
179				return EXT4_ALLOCATE_FAILED;
180			}
181		}
182	}
183
184	if (dir_inode == 0) {
185		/* root directory, check if lost+found already exists */
186		for (i = 0; i < entries; i++)
187			if (strcmp(namelist[i]->d_name, "lost+found") == 0)
188				break;
189		if (i == entries)
190			needs_lost_and_found = true;
191	}
192
193	dentries = calloc(entries, sizeof(struct dentry));
194	if (dentries == NULL)
195		critical_error_errno("malloc");
196
197	for (i = 0; i < entries; i++) {
198		dentries[i].filename = strdup(namelist[i]->d_name);
199		if (dentries[i].filename == NULL)
200			critical_error_errno("strdup");
201
202		asprintf(&dentries[i].path, "%s%s", dir_path, namelist[i]->d_name);
203		asprintf(&dentries[i].full_path, "%s%s", full_path, namelist[i]->d_name);
204
205		free(namelist[i]);
206
207		ret = lstat(dentries[i].full_path, &stat);
208		if (ret < 0) {
209			error_errno("lstat");
210			i--;
211			entries--;
212			continue;
213		}
214
215		dentries[i].size = stat.st_size;
216		dentries[i].mode = stat.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
217		if (fixed_time == -1) {
218			dentries[i].mtime = stat.st_mtime;
219		} else {
220			dentries[i].mtime = fixed_time;
221		}
222		uint64_t capabilities;
223		if (fs_config_func != NULL) {
224#ifdef ANDROID
225			unsigned int mode = 0;
226			unsigned int uid = 0;
227			unsigned int gid = 0;
228			int dir = S_ISDIR(stat.st_mode);
229			fs_config_func(dentries[i].path, dir, target_out_path, &uid, &gid, &mode, &capabilities);
230			dentries[i].mode = mode;
231			dentries[i].uid = uid;
232			dentries[i].gid = gid;
233			dentries[i].capabilities = capabilities;
234#else
235			error("can't set android permissions - built without android support");
236#endif
237		}
238#ifndef _WIN32
239		if (sehnd) {
240			if (selabel_lookup(sehnd, &dentries[i].secon, dentries[i].path, stat.st_mode) < 0) {
241				error("cannot lookup security context for %s", dentries[i].path);
242			}
243
244			if (dentries[i].secon && verbose)
245				printf("Labeling %s as %s\n", dentries[i].path, dentries[i].secon);
246		}
247#endif
248
249		if (S_ISREG(stat.st_mode)) {
250			dentries[i].file_type = EXT4_FT_REG_FILE;
251		} else if (S_ISDIR(stat.st_mode)) {
252			dentries[i].file_type = EXT4_FT_DIR;
253			dirs++;
254		} else if (S_ISCHR(stat.st_mode)) {
255			dentries[i].file_type = EXT4_FT_CHRDEV;
256		} else if (S_ISBLK(stat.st_mode)) {
257			dentries[i].file_type = EXT4_FT_BLKDEV;
258		} else if (S_ISFIFO(stat.st_mode)) {
259			dentries[i].file_type = EXT4_FT_FIFO;
260		} else if (S_ISSOCK(stat.st_mode)) {
261			dentries[i].file_type = EXT4_FT_SOCK;
262		} else if (S_ISLNK(stat.st_mode)) {
263			dentries[i].file_type = EXT4_FT_SYMLINK;
264			dentries[i].link = calloc(info.block_size, 1);
265			readlink(dentries[i].full_path, dentries[i].link, info.block_size - 1);
266		} else {
267			error("unknown file type on %s", dentries[i].path);
268			i--;
269			entries--;
270		}
271	}
272	free(namelist);
273
274	if (needs_lost_and_found) {
275		/* insert a lost+found directory at the beginning of the dentries */
276		struct dentry *tmp = calloc(entries + 1, sizeof(struct dentry));
277		memset(tmp, 0, sizeof(struct dentry));
278		memcpy(tmp + 1, dentries, entries * sizeof(struct dentry));
279		dentries = tmp;
280
281		dentries[0].filename = strdup("lost+found");
282		asprintf(&dentries[0].path, "%slost+found", dir_path);
283		dentries[0].full_path = NULL;
284		dentries[0].size = 0;
285		dentries[0].mode = S_IRWXU;
286		dentries[0].file_type = EXT4_FT_DIR;
287		dentries[0].uid = 0;
288		dentries[0].gid = 0;
289		if (sehnd) {
290			if (selabel_lookup(sehnd, &dentries[0].secon, dentries[0].path, dentries[0].mode) < 0)
291				error("cannot lookup security context for %s", dentries[0].path);
292		}
293		entries++;
294		dirs++;
295	}
296
297	inode = make_directory(dir_inode, entries, dentries, dirs);
298
299	for (i = 0; i < entries; i++) {
300		if (dentries[i].file_type == EXT4_FT_REG_FILE) {
301			entry_inode = make_file(dentries[i].full_path, dentries[i].size);
302		} else if (dentries[i].file_type == EXT4_FT_DIR) {
303			char *subdir_full_path = NULL;
304			char *subdir_dir_path;
305			if (dentries[i].full_path) {
306				ret = asprintf(&subdir_full_path, "%s/", dentries[i].full_path);
307				if (ret < 0)
308					critical_error_errno("asprintf");
309			}
310			ret = asprintf(&subdir_dir_path, "%s/", dentries[i].path);
311			if (ret < 0)
312				critical_error_errno("asprintf");
313			entry_inode = build_directory_structure(subdir_full_path, subdir_dir_path, target_out_path,
314					inode, fs_config_func, sehnd, verbose, fixed_time);
315			free(subdir_full_path);
316			free(subdir_dir_path);
317		} else if (dentries[i].file_type == EXT4_FT_SYMLINK) {
318			entry_inode = make_link(dentries[i].link);
319		} else {
320			error("unknown file type on %s", dentries[i].path);
321			entry_inode = 0;
322		}
323		*dentries[i].inode = entry_inode;
324
325		ret = inode_set_permissions(entry_inode, dentries[i].mode,
326			dentries[i].uid, dentries[i].gid,
327			dentries[i].mtime);
328		if (ret)
329			error("failed to set permissions on %s\n", dentries[i].path);
330
331		/*
332		 * It's important to call inode_set_selinux() before
333		 * inode_set_capabilities(). Extended attributes need to
334		 * be stored sorted order, and we guarantee this by making
335		 * the calls in the proper order.
336		 * Please see xattr_assert_sane() in contents.c
337		 */
338		ret = inode_set_selinux(entry_inode, dentries[i].secon);
339		if (ret)
340			error("failed to set SELinux context on %s\n", dentries[i].path);
341		ret = inode_set_capabilities(entry_inode, dentries[i].capabilities);
342		if (ret)
343			error("failed to set capability on %s\n", dentries[i].path);
344
345		free(dentries[i].path);
346		free(dentries[i].full_path);
347		free(dentries[i].link);
348		free((void *)dentries[i].filename);
349		free(dentries[i].secon);
350	}
351
352	free(dentries);
353	return inode;
354}
355#endif
356
357static u32 compute_block_size()
358{
359	return 4096;
360}
361
362static u32 compute_journal_blocks()
363{
364	u32 journal_blocks = DIV_ROUND_UP(info.len, info.block_size) / 64;
365	if (journal_blocks < 1024)
366		journal_blocks = 1024;
367	if (journal_blocks > 32768)
368		journal_blocks = 32768;
369	return journal_blocks;
370}
371
372static u32 compute_blocks_per_group()
373{
374	return info.block_size * 8;
375}
376
377static u32 compute_inodes()
378{
379	return DIV_ROUND_UP(info.len, info.block_size) / 4;
380}
381
382static u32 compute_inodes_per_group()
383{
384	u32 blocks = DIV_ROUND_UP(info.len, info.block_size);
385	u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group);
386	u32 inodes = DIV_ROUND_UP(info.inodes, block_groups);
387	inodes = EXT4_ALIGN(inodes, (info.block_size / info.inode_size));
388
389	/* After properly rounding up the number of inodes/group,
390	 * make sure to update the total inodes field in the info struct.
391	 */
392	info.inodes = inodes * block_groups;
393
394	return inodes;
395}
396
397static u32 compute_bg_desc_reserve_blocks()
398{
399	u32 blocks = DIV_ROUND_UP(info.len, info.block_size);
400	u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group);
401	u32 bg_desc_blocks = DIV_ROUND_UP(block_groups * sizeof(struct ext2_group_desc),
402			info.block_size);
403
404	u32 bg_desc_reserve_blocks =
405			DIV_ROUND_UP(block_groups * 1024 * sizeof(struct ext2_group_desc),
406					info.block_size) - bg_desc_blocks;
407
408	if (bg_desc_reserve_blocks > info.block_size / sizeof(u32))
409		bg_desc_reserve_blocks = info.block_size / sizeof(u32);
410
411	return bg_desc_reserve_blocks;
412}
413
414void reset_ext4fs_info() {
415	// Reset all the global data structures used by make_ext4fs so it
416	// can be called again.
417	memset(&info, 0, sizeof(info));
418	memset(&aux_info, 0, sizeof(aux_info));
419
420	if (ext4_sparse_file) {
421		sparse_file_destroy(ext4_sparse_file);
422		ext4_sparse_file = NULL;
423	}
424}
425
426int make_ext4fs_sparse_fd(int fd, long long len,
427				const char *mountpoint, struct selabel_handle *sehnd)
428{
429	return make_ext4fs_sparse_fd_align(fd, len, mountpoint, sehnd, 0, 0);
430}
431
432int make_ext4fs_sparse_fd_align(int fd, long long len,
433				const char *mountpoint, struct selabel_handle *sehnd,
434				unsigned eraseblk, unsigned logicalblk)
435{
436	return make_ext4fs_sparse_fd_directory_align(fd, len, mountpoint, sehnd, NULL,
437								eraseblk, logicalblk);
438}
439
440int make_ext4fs_sparse_fd_directory(int fd, long long len,
441				const char *mountpoint, struct selabel_handle *sehnd,
442				const char *directory)
443{
444	return make_ext4fs_sparse_fd_directory_align(fd, len, mountpoint, sehnd, directory, 0, 0);
445}
446
447int make_ext4fs_sparse_fd_directory_align(int fd, long long len,
448				const char *mountpoint, struct selabel_handle *sehnd,
449				const char *directory, unsigned eraseblk, unsigned logicalblk)
450{
451	reset_ext4fs_info();
452	info.len = len;
453	info.flash_erase_block_size = eraseblk;
454	info.flash_logical_block_size = logicalblk;
455
456	return make_ext4fs_internal(fd, directory, NULL, mountpoint, NULL,
457								0, 1, 0, 0, 0,
458								sehnd, 0, -1, NULL, NULL, NULL);
459}
460
461int make_ext4fs(const char *filename, long long len,
462				const char *mountpoint, struct selabel_handle *sehnd)
463{
464	return make_ext4fs_directory(filename, len, mountpoint, sehnd, NULL);
465}
466
467int make_ext4fs_directory(const char *filename, long long len,
468						  const char *mountpoint, struct selabel_handle *sehnd,
469						  const char *directory)
470{
471	return make_ext4fs_directory_align(filename, len, mountpoint, sehnd, directory, 0, 0);
472}
473
474int make_ext4fs_directory_align(const char *filename, long long len,
475						  const char *mountpoint, struct selabel_handle *sehnd,
476						  const char *directory, unsigned eraseblk,
477						  unsigned logicalblk)
478{
479	int fd;
480	int status;
481
482	reset_ext4fs_info();
483	info.len = len;
484	info.flash_erase_block_size = eraseblk;
485	info.flash_logical_block_size = logicalblk;
486
487	fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
488	if (fd < 0) {
489		error_errno("open");
490		return EXIT_FAILURE;
491	}
492
493	status = make_ext4fs_internal(fd, directory, NULL, mountpoint, NULL,
494								  0, 0, 0, 1, 0,
495								  sehnd, 0, -1, NULL, NULL, NULL);
496	close(fd);
497
498	return status;
499}
500
501/* return a newly-malloc'd string that is a copy of str.  The new string
502   is guaranteed to have a trailing slash.  If absolute is true, the new string
503   is also guaranteed to have a leading slash.
504*/
505static char *canonicalize_slashes(const char *str, bool absolute)
506{
507	char *ret;
508	int len = strlen(str);
509	int newlen = len;
510	char *ptr;
511
512	if (len == 0) {
513		if (absolute)
514			return strdup("/");
515		else
516			return strdup("");
517	}
518
519	if (str[0] != '/' && absolute) {
520		newlen++;
521	}
522	if (str[len - 1] != '/') {
523		newlen++;
524	}
525	ret = malloc(newlen + 1);
526	if (!ret) {
527		critical_error("malloc");
528	}
529
530	ptr = ret;
531	if (str[0] != '/' && absolute) {
532		*ptr++ = '/';
533	}
534
535	strcpy(ptr, str);
536	ptr += len;
537
538	if (str[len - 1] != '/') {
539		*ptr++ = '/';
540	}
541
542	if (ptr != ret + newlen) {
543		critical_error("assertion failed\n");
544	}
545
546	*ptr = '\0';
547
548	return ret;
549}
550
551static char *canonicalize_abs_slashes(const char *str)
552{
553	return canonicalize_slashes(str, true);
554}
555
556static char *canonicalize_rel_slashes(const char *str)
557{
558	return canonicalize_slashes(str, false);
559}
560
561static int compare_chunks(const void* chunk1, const void* chunk2) {
562	struct region* c1 = (struct region*) chunk1;
563	struct region* c2 = (struct region*) chunk2;
564	return c1->block - c2->block;
565}
566
567static int get_block_group(u32 block) {
568	unsigned int i, group = 0;
569
570	for(i = 0; i < aux_info.groups; i++) {
571		if (block >= aux_info.bgs[i].first_block)
572			group = i;
573		else
574			break;
575	}
576	return group;
577}
578
579static void extract_base_fs_allocations(const char *directory, const char *mountpoint,
580										FILE* base_alloc_file_in) {
581#define err_msg "base file badly formatted"
582#ifndef _WIN32
583	// FORMAT Version 1.0: filename blk_mapping
584	const char *base_alloc_file_in_format = "%s %s";
585	const int base_file_format_param_count = 2;
586
587	char stored_file_name[MAX_PATH], real_file_name[MAX_PATH], file_map[MAX_BLK_MAPPING_STR];
588	struct block_allocation *fs_alloc;
589	struct block_group_info *bgs = aux_info.bgs;
590	int major_version = 0, minor_version = 0;
591	unsigned int i;
592	char *base_file_line = NULL;
593	size_t base_file_line_len = 0;
594
595	printf("[v%d.%d] Generating an Incremental EXT4 image\n",
596			blk_file_major_ver, blk_file_minor_ver);
597	if (base_fs_allocations == NULL)
598		base_fs_allocations = create_allocation();
599	fs_alloc = base_fs_allocations;
600
601	fscanf(base_alloc_file_in, blk_file_header_fmt, &major_version, &minor_version);
602	if (major_version == 0) {
603		critical_error("Invalid base file");
604	}
605
606	if (major_version != blk_file_major_ver) {
607		critical_error("Incompatible base file: version required is %d.X",
608				blk_file_major_ver);
609	}
610
611	if (minor_version < blk_file_minor_ver) {
612		critical_error("Incompatible base file: version required is %d.%d or above",
613				blk_file_major_ver, blk_file_minor_ver);
614	}
615
616	while (getline(&base_file_line, &base_file_line_len, base_alloc_file_in) != -1) {
617		if (sscanf(base_file_line, base_alloc_file_in_format, &stored_file_name, &file_map)
618				!= base_file_format_param_count) {
619			continue;
620		}
621		if (strlen(stored_file_name) < strlen(mountpoint)) {
622			continue;
623		}
624		snprintf(real_file_name, MAX_PATH, "%s%s", directory, stored_file_name + strlen(mountpoint));
625		if (!access(real_file_name, R_OK)) {
626			char *block_range, *end_string;
627			int real_file_fd;
628			int start_block, end_block;
629			u32 block_file_size;
630			u32 real_file_block_size;
631
632			real_file_fd = open(real_file_name, O_RDONLY);
633			if (real_file_fd == -1) {
634				critical_error(err_msg);
635			}
636			real_file_block_size = get_file_size(real_file_fd);
637			close(real_file_fd);
638			real_file_block_size = DIV_ROUND_UP(real_file_block_size, info.block_size);
639			fs_alloc->filename = strdup(real_file_name);
640			block_range = strtok_r(file_map, ",", &end_string);
641			while (block_range && real_file_block_size) {
642				int block_group;
643				char *range, *end_token = NULL;
644				range = strtok_r(block_range, "-", &end_token);
645				if (!range) {
646					critical_error(err_msg);
647				}
648				start_block = parse_num(range);
649				range = strtok_r(NULL, "-", &end_token);
650				if (!range) {
651					end_block = start_block;
652				} else {
653					end_block = parse_num(range);
654				}
655				// Assummption is that allocations are within the same block group
656				block_group = get_block_group(start_block);
657				if (block_group != get_block_group(end_block)) {
658					critical_error("base file allocation's end block is in a different "
659								   "block group than start block. did you change fs params?");
660				}
661				block_range = strtok_r(NULL, ",", &end_string);
662				int bg_first_block = bgs[block_group].first_block;
663				int min_bg_bound = bgs[block_group].chunks[0].block + bgs[block_group].chunks[0].len;
664				int max_bg_bound = bgs[block_group].chunks[bgs[block_group].chunk_count - 1].block;
665
666				if (min_bg_bound >= start_block - bg_first_block ||
667					max_bg_bound <= end_block - bg_first_block) {
668					continue;
669				}
670				block_file_size = end_block - start_block + 1;
671				if (block_file_size > real_file_block_size) {
672					block_file_size = real_file_block_size;
673				}
674				append_region(fs_alloc, start_block, block_file_size, block_group);
675				reserve_bg_chunk(block_group, start_block - bgs[block_group].first_block, block_file_size);
676				real_file_block_size -= block_file_size;
677			}
678			if (reserve_blocks_for_allocation(fs_alloc) < 0)
679				critical_error("failed to reserve base fs allocation");
680			fs_alloc->next = create_allocation();
681			fs_alloc = fs_alloc->next;
682		}
683	}
684
685	for (i = 0; i < aux_info.groups; i++) {
686		qsort(bgs[i].chunks, bgs[i].chunk_count, sizeof(struct region), compare_chunks);
687	}
688
689	free(base_file_line);
690
691#else
692    return;
693#endif
694#undef err_msg
695}
696
697void generate_base_alloc_file_out(FILE* base_alloc_file_out, char* dir, char* mountpoint,
698								  struct block_allocation* p)
699{
700	size_t dirlen = dir ? strlen(dir) : 0;
701	fprintf(base_alloc_file_out, blk_file_header_fmt, blk_file_major_ver, blk_file_minor_ver);
702	fputc('\n', base_alloc_file_out);
703	while (p) {
704		if (dir && strncmp(p->filename, dir, dirlen) == 0) {
705			// substitute mountpoint for the leading directory in the filename, in the output file
706			fprintf(base_alloc_file_out, "%s%s", mountpoint, p->filename + dirlen);
707		} else {
708			fprintf(base_alloc_file_out, "%s", p->filename);
709		}
710		print_blocks(base_alloc_file_out, p, ',');
711		struct block_allocation* pn = p->next;
712		p = pn;
713	}
714}
715
716int make_ext4fs_internal(int fd, const char *_directory, const char *_target_out_directory,
717						 const char *_mountpoint, fs_config_func_t fs_config_func, int gzip,
718						 int sparse, int crc, int wipe, int real_uuid,
719						 struct selabel_handle *sehnd, int verbose, time_t fixed_time,
720						 FILE* block_list_file, FILE* base_alloc_file_in, FILE* base_alloc_file_out)
721{
722	u32 root_inode_num;
723	u16 root_mode;
724	char *mountpoint;
725	char *directory = NULL;
726	char *target_out_directory = NULL;
727	struct block_allocation* p;
728
729	if (setjmp(setjmp_env))
730		return EXIT_FAILURE; /* Handle a call to longjmp() */
731
732	info.block_device = is_block_device_fd(fd);
733
734	if (info.block_device && (sparse || gzip || crc)) {
735		fprintf(stderr, "No sparse/gzip/crc allowed for block device\n");
736		return EXIT_FAILURE;
737	}
738
739	if (_mountpoint == NULL) {
740		mountpoint = strdup("");
741	} else {
742		mountpoint = canonicalize_abs_slashes(_mountpoint);
743	}
744
745	if (_directory) {
746		directory = canonicalize_rel_slashes(_directory);
747	}
748
749	if (_target_out_directory) {
750		target_out_directory = canonicalize_rel_slashes(_target_out_directory);
751	}
752
753	if (info.len <= 0)
754		info.len = get_file_size(fd);
755
756	if (info.block_size <= 0)
757		info.block_size = compute_block_size();
758
759	/* Round down the filesystem length to be a multiple of the block size */
760	info.len &= ~((u64)info.block_size - 1);
761
762	if (info.len <= 0) {
763		fprintf(stderr, "filesystem size too small\n");
764		return EXIT_FAILURE;
765	}
766
767	if (info.journal_blocks == 0)
768		info.journal_blocks = compute_journal_blocks();
769
770	if (info.no_journal == 0)
771		info.feat_compat = EXT4_FEATURE_COMPAT_HAS_JOURNAL;
772	else
773		info.journal_blocks = 0;
774
775	if (info.blocks_per_group <= 0)
776		info.blocks_per_group = compute_blocks_per_group();
777
778	if (info.inodes <= 0)
779		info.inodes = compute_inodes();
780
781	if (info.inode_size <= 0)
782		info.inode_size = 256;
783
784	if (info.label == NULL)
785		info.label = "";
786
787	info.inodes_per_group = compute_inodes_per_group();
788
789	info.feat_compat |=
790			EXT4_FEATURE_COMPAT_RESIZE_INODE |
791			EXT4_FEATURE_COMPAT_EXT_ATTR;
792
793	info.feat_ro_compat |=
794			EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER |
795			EXT4_FEATURE_RO_COMPAT_LARGE_FILE |
796			EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
797
798	info.feat_incompat |=
799			EXT4_FEATURE_INCOMPAT_EXTENTS |
800			EXT4_FEATURE_INCOMPAT_FILETYPE;
801
802
803	info.bg_desc_reserve_blocks = compute_bg_desc_reserve_blocks();
804
805	printf("Creating filesystem with parameters:\n");
806	printf("    Size: %"PRIu64"\n", info.len);
807	printf("    Block size: %d\n", info.block_size);
808	printf("    Blocks per group: %d\n", info.blocks_per_group);
809	printf("    Inodes per group: %d\n", info.inodes_per_group);
810	printf("    Inode size: %d\n", info.inode_size);
811	printf("    Journal blocks: %d\n", info.journal_blocks);
812	printf("    Label: %s\n", info.label);
813
814	ext4_create_fs_aux_info();
815
816	printf("    Blocks: %"PRIu64"\n", aux_info.len_blocks);
817	printf("    Block groups: %d\n", aux_info.groups);
818	printf("    Reserved block group size: %d\n", info.bg_desc_reserve_blocks);
819
820	ext4_sparse_file = sparse_file_new(info.block_size, info.len);
821
822	block_allocator_init();
823
824	ext4_fill_in_sb(real_uuid);
825
826	if (base_alloc_file_in) {
827		extract_base_fs_allocations(directory, mountpoint, base_alloc_file_in);
828	}
829	if (reserve_inodes(0, 10) == EXT4_ALLOCATE_FAILED)
830		error("failed to reserve first 10 inodes");
831
832	if (info.feat_compat & EXT4_FEATURE_COMPAT_HAS_JOURNAL)
833		ext4_create_journal_inode();
834
835	if (info.feat_compat & EXT4_FEATURE_COMPAT_RESIZE_INODE)
836		ext4_create_resize_inode();
837
838#ifdef _WIN32
839	// Windows needs only 'create an empty fs image' functionality
840	assert(!directory);
841	root_inode_num = build_default_directory_structure(mountpoint, sehnd);
842#else
843	if (directory)
844		root_inode_num = build_directory_structure(directory, mountpoint, target_out_directory, 0,
845			fs_config_func, sehnd, verbose, fixed_time);
846	else
847		root_inode_num = build_default_directory_structure(mountpoint, sehnd);
848#endif
849
850	root_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
851	inode_set_permissions(root_inode_num, root_mode, 0, 0, 0);
852
853#ifndef _WIN32
854	if (sehnd) {
855		char *secontext = NULL;
856
857		if (selabel_lookup(sehnd, &secontext, mountpoint, S_IFDIR) < 0) {
858			error("cannot lookup security context for %s", mountpoint);
859		}
860		if (secontext) {
861			if (verbose) {
862				printf("Labeling %s as %s\n", mountpoint, secontext);
863			}
864			inode_set_selinux(root_inode_num, secontext);
865		}
866		freecon(secontext);
867	}
868#endif
869
870	ext4_update_free();
871
872	// TODO: Consider migrating the OTA tools to the new base alloc file format
873	// used for generating incremental images (see go/incremental-ext4)
874	if (block_list_file) {
875		size_t dirlen = directory ? strlen(directory) : 0;
876		struct block_allocation* p = get_saved_allocation_chain();
877		while (p) {
878			if (directory && strncmp(p->filename, directory, dirlen) == 0) {
879				// substitute mountpoint for the leading directory in the filename, in the output file
880				fprintf(block_list_file, "%s%s", mountpoint, p->filename + dirlen);
881			} else {
882				fprintf(block_list_file, "%s", p->filename);
883			}
884			print_blocks(block_list_file, p, ' ');
885			struct block_allocation* pn = p->next;
886			p = pn;
887		}
888	}
889
890	if (base_alloc_file_out) {
891		struct block_allocation* p = get_saved_allocation_chain();
892		generate_base_alloc_file_out(base_alloc_file_out, directory, mountpoint, p);
893	}
894
895	printf("Created filesystem with %d/%d inodes and %d/%d blocks\n",
896			aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count,
897			aux_info.sb->s_inodes_count,
898			aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo,
899			aux_info.sb->s_blocks_count_lo);
900
901	if (wipe && WIPE_IS_SUPPORTED) {
902		wipe_block_device(fd, info.len);
903	}
904
905	write_ext4_image(fd, gzip, sparse, crc);
906
907	sparse_file_destroy(ext4_sparse_file);
908	ext4_sparse_file = NULL;
909
910	p = get_saved_allocation_chain();
911	while (p) {
912		struct block_allocation* pn = p->next;
913		free_alloc(p);
914		p = pn;
915	}
916
917	free(mountpoint);
918	free(directory);
919
920	return 0;
921}
922