1 #ifndef _GNU_SOURCE
2 # define _GNU_SOURCE //asprintf
3 #endif
4 #include "perms.h"
5 #include "support/nls-enable.h"
6 #include <time.h>
7 #include <sys/stat.h>
8
9 #ifndef XATTR_SELINUX_SUFFIX
10 # define XATTR_SELINUX_SUFFIX "selinux"
11 #endif
12 #ifndef XATTR_CAPS_SUFFIX
13 # define XATTR_CAPS_SUFFIX "capability"
14 #endif
15
16 struct inode_params {
17 ext2_filsys fs;
18 char *path;
19 char *filename;
20 char *src_dir;
21 char *target_out;
22 char *mountpoint;
23 fs_config_f fs_config_func;
24 struct selabel_handle *sehnd;
25 time_t fixed_time;
26 };
27
ino_add_xattr(ext2_filsys fs,ext2_ino_t ino,const char * name,const void * value,int value_len)28 static errcode_t ino_add_xattr(ext2_filsys fs, ext2_ino_t ino, const char *name,
29 const void *value, int value_len)
30 {
31 errcode_t retval, close_retval;
32 struct ext2_xattr_handle *xhandle;
33
34 retval = ext2fs_xattrs_open(fs, ino, &xhandle);
35 if (retval) {
36 com_err(__func__, retval, _("while opening inode %u"), ino);
37 return retval;
38 }
39 retval = ext2fs_xattrs_read(xhandle);
40 if (retval) {
41 com_err(__func__, retval,
42 _("while reading xattrs of inode %u"), ino);
43 goto xattrs_close;
44 }
45 retval = ext2fs_xattr_set(xhandle, name, value, value_len);
46 if (retval) {
47 com_err(__func__, retval,
48 _("while setting xattrs of inode %u"), ino);
49 goto xattrs_close;
50 }
51 retval = ext2fs_xattrs_write(xhandle);
52 if (retval) {
53 com_err(__func__, retval,
54 _("while writting xattrs of inode %u"), ino);
55 goto xattrs_close;
56 }
57 xattrs_close:
58 close_retval = ext2fs_xattrs_close(&xhandle);
59 if (close_retval) {
60 com_err(__func__, close_retval,
61 _("while closing xattrs of inode %u"), ino);
62 return retval ? retval : close_retval;
63 }
64 return retval;
65 }
66
set_selinux_xattr(ext2_filsys fs,ext2_ino_t ino,struct inode_params * params)67 static errcode_t set_selinux_xattr(ext2_filsys fs, ext2_ino_t ino,
68 struct inode_params *params)
69 {
70 errcode_t retval;
71 char *secontext = NULL;
72 struct ext2_inode inode;
73
74 if (params->sehnd == NULL)
75 return 0;
76
77 retval = ext2fs_read_inode(fs, ino, &inode);
78 if (retval) {
79 com_err(__func__, retval,
80 _("while reading inode %u"), ino);
81 return retval;
82 }
83
84 retval = selabel_lookup(params->sehnd, &secontext, params->filename,
85 inode.i_mode);
86 if (retval < 0) {
87 com_err(__func__, retval,
88 _("searching for label \"%s\""), params->filename);
89 exit(1);
90 }
91
92 retval = ino_add_xattr(fs, ino, "security." XATTR_SELINUX_SUFFIX,
93 secontext, strlen(secontext) + 1);
94
95 freecon(secontext);
96 return retval;
97 }
98
set_perms_and_caps(ext2_filsys fs,ext2_ino_t ino,struct inode_params * params)99 static errcode_t set_perms_and_caps(ext2_filsys fs, ext2_ino_t ino,
100 struct inode_params *params)
101 {
102 errcode_t retval;
103 uint64_t capabilities = 0;
104 struct ext2_inode inode;
105 struct vfs_cap_data cap_data;
106 unsigned int uid = 0, gid = 0, imode = 0;
107
108 retval = ext2fs_read_inode(fs, ino, &inode);
109 if (retval) {
110 com_err(__func__, retval, _("while reading inode %u"), ino);
111 return retval;
112 }
113
114 /* Permissions */
115 if (params->fs_config_func != NULL) {
116 params->fs_config_func(params->filename, S_ISDIR(inode.i_mode),
117 params->target_out, &uid, &gid, &imode,
118 &capabilities);
119 inode.i_uid = uid & 0xffff;
120 inode.i_gid = gid & 0xffff;
121 inode.i_mode = (inode.i_mode & S_IFMT) | (imode & 0xffff);
122 retval = ext2fs_write_inode(fs, ino, &inode);
123 if (retval) {
124 com_err(__func__, retval,
125 _("while writting inode %u"), ino);
126 return retval;
127 }
128 }
129
130 /* Capabilities */
131 if (!capabilities)
132 return 0;
133 memset(&cap_data, 0, sizeof(cap_data));
134 cap_data.magic_etc = VFS_CAP_REVISION | VFS_CAP_FLAGS_EFFECTIVE;
135 cap_data.data[0].permitted = (uint32_t) (capabilities & 0xffffffff);
136 cap_data.data[1].permitted = (uint32_t) (capabilities >> 32);
137 return ino_add_xattr(fs, ino, "security." XATTR_CAPS_SUFFIX,
138 &cap_data, sizeof(cap_data));
139 }
140
set_timestamp(ext2_filsys fs,ext2_ino_t ino,struct inode_params * params)141 static errcode_t set_timestamp(ext2_filsys fs, ext2_ino_t ino,
142 struct inode_params *params)
143 {
144 errcode_t retval;
145 struct ext2_inode inode;
146 struct stat stat;
147 char *src_filename = NULL;
148
149 retval = ext2fs_read_inode(fs, ino, &inode);
150 if (retval) {
151 com_err(__func__, retval,
152 _("while reading inode %u"), ino);
153 return retval;
154 }
155
156 if (params->fixed_time == -1) {
157 /* replace mountpoint from filename with src_dir */
158 if (asprintf(&src_filename, "%s/%s", params->src_dir,
159 params->filename + strlen(params->mountpoint)) < 0)
160 return -ENOMEM;
161 retval = lstat(src_filename, &stat);
162 if (retval < 0) {
163 com_err(__func__, retval,
164 _("while lstat file %s"), src_filename);
165 goto end;
166 }
167 inode.i_atime = inode.i_ctime = inode.i_mtime = stat.st_mtime;
168 } else {
169 inode.i_atime = inode.i_ctime = inode.i_mtime = params->fixed_time;
170 }
171
172 retval = ext2fs_write_inode(fs, ino, &inode);
173 if (retval) {
174 com_err(__func__, retval,
175 _("while writting inode %u"), ino);
176 goto end;
177 }
178
179 end:
180 free(src_filename);
181 return retval;
182 }
183
is_dir(ext2_filsys fs,ext2_ino_t ino)184 static int is_dir(ext2_filsys fs, ext2_ino_t ino)
185 {
186 struct ext2_inode inode;
187
188 if (ext2fs_read_inode(fs, ino, &inode))
189 return 0;
190 return S_ISDIR(inode.i_mode);
191 }
192
androidify_inode(ext2_filsys fs,ext2_ino_t ino,struct inode_params * params)193 static errcode_t androidify_inode(ext2_filsys fs, ext2_ino_t ino,
194 struct inode_params *params)
195 {
196 errcode_t retval;
197
198 retval = set_timestamp(fs, ino, params);
199 if (retval)
200 return retval;
201
202 retval = set_selinux_xattr(fs, ino, params);
203 if (retval)
204 return retval;
205
206 return set_perms_and_caps(fs, ino, params);
207 }
208
walk_dir(ext2_ino_t dir EXT2FS_ATTR ((unused)),int flags EXT2FS_ATTR ((unused)),struct ext2_dir_entry * de,int offset EXT2FS_ATTR ((unused)),int blocksize EXT2FS_ATTR ((unused)),char * buf EXT2FS_ATTR ((unused)),void * priv_data)209 static int walk_dir(ext2_ino_t dir EXT2FS_ATTR((unused)),
210 int flags EXT2FS_ATTR((unused)),
211 struct ext2_dir_entry *de,
212 int offset EXT2FS_ATTR((unused)),
213 int blocksize EXT2FS_ATTR((unused)),
214 char *buf EXT2FS_ATTR((unused)), void *priv_data)
215 {
216 __u16 name_len;
217 errcode_t retval;
218 struct inode_params *params = (struct inode_params *)priv_data;
219
220 name_len = de->name_len & 0xff;
221 if (!strncmp(de->name, ".", name_len)
222 || (!strncmp(de->name, "..", name_len)))
223 return 0;
224
225 if (asprintf(¶ms->filename, "%s/%.*s", params->path, name_len,
226 de->name) < 0)
227 return -ENOMEM;
228
229 if (!strncmp(de->name, "lost+found", 10)) {
230 retval = set_selinux_xattr(params->fs, de->inode, params);
231 if (retval)
232 goto end;
233 } else {
234 retval = androidify_inode(params->fs, de->inode, params);
235 if (retval)
236 goto end;
237 if (is_dir(params->fs, de->inode)) {
238 char *cur_path = params->path;
239 char *cur_filename = params->filename;
240 params->path = params->filename;
241 ext2fs_dir_iterate2(params->fs, de->inode, 0, NULL,
242 walk_dir, params);
243 params->path = cur_path;
244 params->filename = cur_filename;
245 }
246 }
247
248 end:
249 free(params->filename);
250 return retval;
251 }
252
__android_configure_fs(ext2_filsys fs,char * src_dir,char * target_out,char * mountpoint,fs_config_f fs_config_func,struct selabel_handle * sehnd,time_t fixed_time)253 errcode_t __android_configure_fs(ext2_filsys fs, char *src_dir,
254 char *target_out,
255 char *mountpoint,
256 fs_config_f fs_config_func,
257 struct selabel_handle *sehnd,
258 time_t fixed_time)
259 {
260 errcode_t retval;
261 struct inode_params params = {
262 .fs = fs,
263 .src_dir = src_dir,
264 .target_out = target_out,
265 .fs_config_func = fs_config_func,
266 .sehnd = sehnd,
267 .fixed_time = fixed_time,
268 .path = mountpoint,
269 .filename = mountpoint,
270 .mountpoint = mountpoint,
271 };
272
273 /* walk_dir will add the "/". Don't add it twice. */
274 if (strlen(mountpoint) == 1 && mountpoint[0] == '/')
275 params.path = "";
276
277 retval = set_selinux_xattr(fs, EXT2_ROOT_INO, ¶ms);
278 if (retval)
279 return retval;
280 retval = set_timestamp(fs, EXT2_ROOT_INO, ¶ms);
281 if (retval)
282 return retval;
283
284 return ext2fs_dir_iterate2(fs, EXT2_ROOT_INO, 0, NULL, walk_dir,
285 ¶ms);
286 }
287
android_configure_fs(ext2_filsys fs,char * src_dir,char * target_out,char * mountpoint,char * file_contexts,char * fs_config_file,time_t fixed_time)288 errcode_t android_configure_fs(ext2_filsys fs, char *src_dir, char *target_out,
289 char *mountpoint,
290 char *file_contexts,
291 char *fs_config_file, time_t fixed_time)
292 {
293 errcode_t retval;
294 fs_config_f fs_config_func = NULL;
295 struct selabel_handle *sehnd = NULL;
296
297 /* Retrieve file contexts */
298 if (file_contexts) {
299 struct selinux_opt seopts[] = { { SELABEL_OPT_PATH, "" } };
300 seopts[0].value = file_contexts;
301 sehnd = selabel_open(SELABEL_CTX_FILE, seopts, 1);
302 if (!sehnd) {
303 com_err(__func__, -EINVAL,
304 _("while opening file contexts \"%s\""),
305 seopts[0].value);
306 return -EINVAL;
307 }
308 }
309
310 /* Load the FS config */
311 if (fs_config_file) {
312 retval = load_canned_fs_config(fs_config_file);
313 if (retval < 0) {
314 com_err(__func__, retval,
315 _("while loading fs_config \"%s\""),
316 fs_config_file);
317 return retval;
318 }
319 fs_config_func = canned_fs_config;
320 } else if (mountpoint)
321 fs_config_func = fs_config;
322
323 return __android_configure_fs(fs, src_dir, target_out, mountpoint,
324 fs_config_func, sehnd, fixed_time);
325 }
326