1 /**
2 * sload.c
3 *
4 * Copyright (C) 2015 Huawei Ltd.
5 * Witten by:
6 * Hou Pengyang <houpengyang@huawei.com>
7 * Liu Shuoran <liushuoran@huawei.com>
8 * Jaegeuk Kim <jaegeuk@kernel.org>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 */
14 #define _GNU_SOURCE
15 #include "fsck.h"
16 #include <libgen.h>
17 #include <dirent.h>
18 #ifdef HAVE_MNTENT_H
19 #include <mntent.h>
20 #endif
21
22 #ifdef HAVE_LIBSELINUX
23 static struct selabel_handle *sehnd = NULL;
24 #endif
25
26 typedef void (*fs_config_f)(const char *path, int dir,
27 const char *target_out_path,
28 unsigned *uid, unsigned *gid,
29 unsigned *mode, uint64_t *capabilities);
30
31 static fs_config_f fs_config_func = NULL;
32
33 #ifdef WITH_ANDROID
34 #include <selinux/android.h>
35 #include <private/android_filesystem_config.h>
36 #include <private/canned_fs_config.h>
37 #include <private/fs_config.h>
38 #endif
39
filter_dot(const struct dirent * d)40 static int filter_dot(const struct dirent *d)
41 {
42 return (strcmp(d->d_name, "..") && strcmp(d->d_name, "."));
43 }
44
f2fs_make_directory(struct f2fs_sb_info * sbi,int entries,struct dentry * de)45 static int f2fs_make_directory(struct f2fs_sb_info *sbi,
46 int entries, struct dentry *de)
47 {
48 int ret = 0;
49 int i = 0;
50
51 for (i = 0; i < entries; i++) {
52 if (de[i].file_type == F2FS_FT_DIR)
53 ret = f2fs_mkdir(sbi, de + i);
54 else if (de[i].file_type == F2FS_FT_REG_FILE)
55 ret = f2fs_create(sbi, de + i);
56 else if (de[i].file_type == F2FS_FT_SYMLINK)
57 ret = f2fs_symlink(sbi, de + i);
58
59 if (ret)
60 break;
61 }
62
63 return ret;
64 }
65
66 #ifdef HAVE_LIBSELINUX
set_selinux_xattr(struct f2fs_sb_info * sbi,const char * path,nid_t ino,int mode)67 static int set_selinux_xattr(struct f2fs_sb_info *sbi, const char *path,
68 nid_t ino, int mode)
69 {
70 char *secontext = NULL;
71 char *mnt_path = NULL;
72
73 if (!sehnd)
74 return 0;
75
76 if (asprintf(&mnt_path, "%s%s", c.mount_point, path) <= 0) {
77 ERR_MSG("cannot allocate security path for %s%s\n",
78 c.mount_point, path);
79 return -ENOMEM;
80 }
81
82 /* set root inode selinux context */
83 if (selabel_lookup(sehnd, &secontext, mnt_path, mode) < 0) {
84 ERR_MSG("cannot lookup security context for %s\n", mnt_path);
85 free(mnt_path);
86 return -EINVAL;
87 }
88
89 if (secontext) {
90 MSG(2, "%s (%d) -> SELinux context = %s\n",
91 mnt_path, ino, secontext);
92 inode_set_selinux(sbi, ino, secontext);
93 }
94 freecon(secontext);
95 free(mnt_path);
96 return 0;
97 }
98 #else
99 #define set_selinux_xattr(...) 0
100 #endif
101
set_perms_and_caps(struct dentry * de)102 static int set_perms_and_caps(struct dentry *de)
103 {
104 uint64_t capabilities = 0;
105 unsigned int uid = 0, gid = 0, imode = 0;
106 char *mnt_path = NULL;
107
108 if (asprintf(&mnt_path, "%s%s", c.mount_point, de->path) <= 0) {
109 ERR_MSG("cannot allocate mount path for %s%s\n",
110 c.mount_point, de->path);
111 return -ENOMEM;
112 }
113
114 /* Permissions */
115 if (fs_config_func != NULL) {
116 fs_config_func(mnt_path, de->file_type == F2FS_FT_DIR,
117 c.target_out_dir, &uid, &gid, &imode,
118 &capabilities);
119 de->uid = uid & 0xffff;
120 de->gid = gid & 0xffff;
121 de->mode = (de->mode & S_IFMT) | (imode & 0xffff);
122 de->capabilities = capabilities;
123 }
124 MSG(2, "%s -> mode = 0x%x, uid = 0x%x, gid = 0x%x, "
125 "capabilities = 0x%"PRIx64"\n",
126 mnt_path, de->mode, de->uid, de->gid, de->capabilities);
127 free(mnt_path);
128 return 0;
129 }
130
set_inode_metadata(struct dentry * de)131 static void set_inode_metadata(struct dentry *de)
132 {
133 struct stat stat;
134 int ret;
135
136 ret = lstat(de->full_path, &stat);
137 if (ret < 0) {
138 ERR_MSG("lstat failure\n");
139 ASSERT(0);
140 }
141
142 if (S_ISREG(stat.st_mode)) {
143 de->file_type = F2FS_FT_REG_FILE;
144 } else if (S_ISDIR(stat.st_mode)) {
145 de->file_type = F2FS_FT_DIR;
146 } else if (S_ISCHR(stat.st_mode)) {
147 de->file_type = F2FS_FT_CHRDEV;
148 } else if (S_ISBLK(stat.st_mode)) {
149 de->file_type = F2FS_FT_BLKDEV;
150 } else if (S_ISFIFO(stat.st_mode)) {
151 de->file_type = F2FS_FT_FIFO;
152 } else if (S_ISSOCK(stat.st_mode)) {
153 de->file_type = F2FS_FT_SOCK;
154 } else if (S_ISLNK(stat.st_mode)) {
155 de->file_type = F2FS_FT_SYMLINK;
156 de->link = calloc(F2FS_BLKSIZE, 1);
157 ASSERT(de->link);
158 ret = readlink(de->full_path, de->link, F2FS_BLKSIZE - 1);
159 ASSERT(ret >= 0);
160 } else {
161 ERR_MSG("unknown file type on %s", de->path);
162 ASSERT(0);
163 }
164
165 de->size = stat.st_size;
166 de->mode = stat.st_mode &
167 (S_IFMT|S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
168 if (c.fixed_time == -1 && c.from_dir)
169 de->mtime = stat.st_mtime;
170 else
171 de->mtime = c.fixed_time;
172
173 set_perms_and_caps(de);
174 }
175
build_directory(struct f2fs_sb_info * sbi,const char * full_path,const char * dir_path,const char * target_out_dir,nid_t dir_ino)176 static int build_directory(struct f2fs_sb_info *sbi, const char *full_path,
177 const char *dir_path, const char *target_out_dir,
178 nid_t dir_ino)
179 {
180 int entries = 0;
181 struct dentry *dentries;
182 struct dirent **namelist = NULL;
183 int i = 0, ret = 0;
184
185 entries = scandir(full_path, &namelist, filter_dot, (void *)alphasort);
186 if (entries < 0) {
187 ERR_MSG("No entries in %s\n", full_path);
188 return -ENOENT;
189 }
190
191 dentries = calloc(entries, sizeof(struct dentry));
192 ASSERT(dentries);
193
194 for (i = 0; i < entries; i++) {
195 dentries[i].name = (unsigned char *)strdup(namelist[i]->d_name);
196 if (dentries[i].name == NULL) {
197 ERR_MSG("Skip: ENOMEM\n");
198 continue;
199 }
200 dentries[i].len = strlen((char *)dentries[i].name);
201
202 ret = asprintf(&dentries[i].path, "%s%s",
203 dir_path, namelist[i]->d_name);
204 ASSERT(ret > 0);
205 ret = asprintf(&dentries[i].full_path, "%s/%s",
206 full_path, namelist[i]->d_name);
207 ASSERT(ret > 0);
208 free(namelist[i]);
209
210 set_inode_metadata(dentries + i);
211
212 dentries[i].pino = dir_ino;
213 }
214
215 free(namelist);
216
217 ret = f2fs_make_directory(sbi, entries, dentries);
218 if (ret)
219 goto out_free;
220
221 for (i = 0; i < entries; i++) {
222 if (dentries[i].file_type == F2FS_FT_REG_FILE) {
223 f2fs_build_file(sbi, dentries + i);
224 } else if (dentries[i].file_type == F2FS_FT_DIR) {
225 char *subdir_full_path = NULL;
226 char *subdir_dir_path = NULL;
227
228 ret = asprintf(&subdir_full_path, "%s",
229 dentries[i].full_path);
230 ASSERT(ret > 0);
231 ret = asprintf(&subdir_dir_path, "%s/",
232 dentries[i].path);
233 ASSERT(ret > 0);
234
235 ret = build_directory(sbi, subdir_full_path,
236 subdir_dir_path,
237 target_out_dir,
238 dentries[i].ino);
239 free(subdir_full_path);
240 free(subdir_dir_path);
241
242 if (ret)
243 goto out_free;
244 } else if (dentries[i].file_type == F2FS_FT_SYMLINK) {
245 /*
246 * It is already done in f2fs_make_directory
247 * f2fs_make_symlink(sbi, dir_ino, &dentries[i]);
248 */
249 } else {
250 MSG(1, "Error unknown file type\n");
251 }
252
253 ret = set_selinux_xattr(sbi, dentries[i].path,
254 dentries[i].ino, dentries[i].mode);
255 if (ret)
256 goto out_free;
257
258 free(dentries[i].path);
259 free(dentries[i].full_path);
260 free((void *)dentries[i].name);
261 }
262 out_free:
263 for (; i < entries; i++) {
264 free(dentries[i].path);
265 free(dentries[i].full_path);
266 free((void *)dentries[i].name);
267 }
268
269 free(dentries);
270 return 0;
271 }
272
configure_files(void)273 static int configure_files(void)
274 {
275 #ifdef HAVE_LIBSELINUX
276 if (!c.nr_opt)
277 goto skip;
278 #if !defined(__ANDROID__)
279 sehnd = selabel_open(SELABEL_CTX_FILE, c.seopt_file, c.nr_opt);
280 if (!sehnd) {
281 ERR_MSG("Failed to open file contexts \"%s\"",
282 c.seopt_file[0].value);
283 return -EINVAL;
284 }
285 #else
286 sehnd = selinux_android_file_context_handle();
287 if (!sehnd) {
288 ERR_MSG("Failed to get android file_contexts\n", c.mount_point);
289 return -EINVAL;
290 }
291 #endif
292 skip:
293 #endif
294 #ifdef WITH_ANDROID
295 /* Load the FS config */
296 if (c.fs_config_file) {
297 int ret = load_canned_fs_config(c.fs_config_file);
298
299 if (ret < 0) {
300 ERR_MSG("Failed to load fs_config \"%s\"",
301 c.fs_config_file);
302 return ret;
303 }
304 fs_config_func = canned_fs_config;
305 } else {
306 fs_config_func = fs_config;
307 }
308 #endif
309 return 0;
310 }
311
f2fs_sload(struct f2fs_sb_info * sbi)312 int f2fs_sload(struct f2fs_sb_info *sbi)
313 {
314 int ret = 0;
315
316 ret = configure_files();
317 if (ret) {
318 ERR_MSG("Failed to configure files\n");
319 return ret;
320 }
321
322 /* flush NAT/SIT journal entries */
323 flush_journal_entries(sbi);
324
325 ret = build_directory(sbi, c.from_dir, "/",
326 c.target_out_dir, F2FS_ROOT_INO(sbi));
327 if (ret) {
328 ERR_MSG("Failed to build due to %d\n", ret);
329 return ret;
330 }
331
332 ret = set_selinux_xattr(sbi, c.mount_point,
333 F2FS_ROOT_INO(sbi), S_IFDIR);
334 if (ret) {
335 ERR_MSG("Failed to set selinux for root: %d\n", ret);
336 return ret;
337 }
338
339 /* update curseg info; can update sit->types */
340 move_curseg_info(sbi, SM_I(sbi)->main_blkaddr, 0);
341 zero_journal_entries(sbi);
342 write_curseg_info(sbi);
343
344 /* flush dirty sit entries */
345 flush_sit_entries(sbi);
346
347 write_checkpoint(sbi);
348 return 0;
349 }
350