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