1 #define _GNU_SOURCE
2
3 #include <stdio.h>
4 #include <getopt.h>
5 #include <string.h>
6 #include <unistd.h>
7 #include <limits.h>
8 #include <ext2fs/ext2fs.h>
9
10 #include "perms.h"
11 #include "base_fs.h"
12 #include "block_list.h"
13 #include "basefs_allocator.h"
14 #include "create_inode.h"
15
16 static char *prog_name = "e2fsdroid";
17 static char *in_file;
18 static char *block_list;
19 static char *basefs_out;
20 static char *basefs_in;
21 static char *mountpoint = "";
22 static time_t fixed_time = -1;
23 static char *fs_config_file;
24 static struct selinux_opt seopt_file[8];
25 static int max_nr_opt = (int)sizeof(seopt_file) / sizeof(seopt_file[0]);
26 static char *product_out;
27 static char *src_dir;
28 static int android_configure;
29 static int android_sparse_file = 1;
30
usage(int ret)31 static void usage(int ret)
32 {
33 fprintf(stderr, "%s [-B block_list] [-D basefs_out] [-T timestamp]\n"
34 "\t[-C fs_config] [-S file_contexts] [-p product_out]\n"
35 "\t[-a mountpoint] [-d basefs_in] [-f src_dir] [-e] [-s] image\n",
36 prog_name);
37 exit(ret);
38 }
39
absolute_path(const char * file)40 static char *absolute_path(const char *file)
41 {
42 char *ret;
43 char cwd[PATH_MAX];
44
45 if (file[0] != '/') {
46 if (getcwd(cwd, PATH_MAX) == NULL) {
47 fprintf(stderr, "Failed to getcwd\n");
48 exit(EXIT_FAILURE);
49 }
50 ret = malloc(strlen(cwd) + 1 + strlen(file) + 1);
51 if (ret)
52 sprintf(ret, "%s/%s", cwd, file);
53 } else
54 ret = strdup(file);
55 return ret;
56 }
57
main(int argc,char * argv[])58 int main(int argc, char *argv[])
59 {
60 int c;
61 char *p;
62 int flags = EXT2_FLAG_RW;
63 errcode_t retval;
64 io_manager io_mgr;
65 ext2_filsys fs = NULL;
66 struct fs_ops_callbacks fs_callbacks = { NULL, NULL };
67 char *token;
68 int nr_opt = 0;
69 ext2_ino_t inodes_count;
70 ext2_ino_t free_inodes_count;
71 blk64_t blocks_count;
72 blk64_t free_blocks_count;
73
74 add_error_table(&et_ext2_error_table);
75
76 while ((c = getopt (argc, argv, "T:C:S:p:a:D:d:B:f:es")) != EOF) {
77 switch (c) {
78 case 'T':
79 fixed_time = strtoul(optarg, &p, 0);
80 android_configure = 1;
81 break;
82 case 'C':
83 fs_config_file = absolute_path(optarg);
84 android_configure = 1;
85 break;
86 case 'S':
87 token = strtok(optarg, ",");
88 while (token) {
89 if (nr_opt == max_nr_opt) {
90 fprintf(stderr, "Expected at most %d selinux opts\n",
91 max_nr_opt);
92 exit(EXIT_FAILURE);
93 }
94 seopt_file[nr_opt].type = SELABEL_OPT_PATH;
95 seopt_file[nr_opt].value = absolute_path(token);
96 nr_opt++;
97 token = strtok(NULL, ",");
98 }
99 android_configure = 1;
100 break;
101 case 'p':
102 product_out = absolute_path(optarg);
103 break;
104 case 'a':
105 mountpoint = strdup(optarg);
106 break;
107 case 'D':
108 basefs_out = absolute_path(optarg);
109 break;
110 case 'd':
111 basefs_in = absolute_path(optarg);
112 break;
113 case 'B':
114 block_list = absolute_path(optarg);
115 break;
116 case 'f':
117 src_dir = absolute_path(optarg);
118 break;
119 case 'e':
120 android_sparse_file = 0;
121 break;
122 case 's':
123 flags |= EXT2_FLAG_SHARE_DUP;
124 break;
125 default:
126 usage(EXIT_FAILURE);
127 }
128 }
129 if (optind >= argc) {
130 fprintf(stderr, "Expected filename after options\n");
131 exit(EXIT_FAILURE);
132 }
133
134 if (android_sparse_file) {
135 io_mgr = sparse_io_manager;
136 if (asprintf(&in_file, "(%s)", argv[optind]) == -1) {
137 fprintf(stderr, "Failed to allocate file name\n");
138 exit(EXIT_FAILURE);
139 }
140 } else {
141 io_mgr = unix_io_manager;
142 in_file = strdup(argv[optind]);
143 }
144 retval = ext2fs_open(in_file, flags, 0, 0, io_mgr, &fs);
145 if (retval) {
146 com_err(prog_name, retval, "while opening file %s\n", in_file);
147 return retval;
148 }
149
150 if (src_dir) {
151 ext2fs_read_bitmaps(fs);
152 if (basefs_in) {
153 retval = base_fs_alloc_load(fs, basefs_in, mountpoint);
154 if (retval) {
155 com_err(prog_name, retval, "%s",
156 "while reading base_fs file");
157 exit(1);
158 }
159 fs_callbacks.create_new_inode =
160 base_fs_alloc_set_target;
161 fs_callbacks.end_create_new_inode =
162 base_fs_alloc_unset_target;
163 }
164 retval = populate_fs2(fs, EXT2_ROOT_INO, src_dir,
165 EXT2_ROOT_INO, &fs_callbacks);
166 if (retval) {
167 com_err(prog_name, retval, "%s",
168 "while populating file system");
169 exit(1);
170 }
171 if (basefs_in)
172 base_fs_alloc_cleanup(fs);
173 }
174
175 if (android_configure) {
176 retval = android_configure_fs(fs, src_dir, product_out, mountpoint,
177 seopt_file, nr_opt, fs_config_file, fixed_time);
178 if (retval) {
179 com_err(prog_name, retval, "%s",
180 "while configuring the file system");
181 exit(1);
182 }
183 }
184
185 if (block_list) {
186 retval = fsmap_iter_filsys(fs, &block_list_format, block_list,
187 mountpoint);
188 if (retval) {
189 com_err(prog_name, retval, "%s",
190 "while creating the block_list");
191 exit(1);
192 }
193 }
194
195 if (basefs_out) {
196 retval = fsmap_iter_filsys(fs, &base_fs_format,
197 basefs_out, mountpoint);
198 if (retval) {
199 com_err(prog_name, retval, "%s",
200 "while creating the basefs file");
201 exit(1);
202 }
203 }
204
205 inodes_count = fs->super->s_inodes_count;
206 free_inodes_count = fs->super->s_free_inodes_count;
207 blocks_count = ext2fs_blocks_count(fs->super);
208 free_blocks_count = ext2fs_free_blocks_count(fs->super);
209
210 retval = ext2fs_close_free(&fs);
211 if (retval) {
212 com_err(prog_name, retval, "%s",
213 "while writing superblocks");
214 exit(1);
215 }
216
217 printf("Created filesystem with %u/%u inodes and %llu/%llu blocks\n",
218 inodes_count - free_inodes_count, inodes_count,
219 blocks_count - free_blocks_count, blocks_count);
220
221 remove_error_table(&et_ext2_error_table);
222 return 0;
223 }
224