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