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