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 char *mount_path = c.mount_point;
108
109 /*
110 * de->path already has "/" in the beginning of it.
111 * Need to remove "/" when c.mount_point is "/", not to add it twice.
112 */
113 if (strlen(c.mount_point) == 1 && c.mount_point[0] == '/')
114 mount_path = "";
115
116 if (asprintf(&mnt_path, "%s%s", mount_path, de->path) <= 0) {
117 ERR_MSG("cannot allocate mount path for %s%s\n",
118 mount_path, de->path);
119 return -ENOMEM;
120 }
121
122 /* Permissions */
123 if (fs_config_func != NULL) {
124 fs_config_func(mnt_path, de->file_type == F2FS_FT_DIR,
125 c.target_out_dir, &uid, &gid, &imode,
126 &capabilities);
127 de->uid = uid & 0xffff;
128 de->gid = gid & 0xffff;
129 de->mode = (de->mode & S_IFMT) | (imode & 0xffff);
130 de->capabilities = capabilities;
131 }
132 MSG(2, "%s -> mode = 0x%x, uid = 0x%x, gid = 0x%x, "
133 "capabilities = 0x%"PRIx64"\n",
134 mnt_path, de->mode, de->uid, de->gid, de->capabilities);
135 free(mnt_path);
136 return 0;
137 }
138
set_inode_metadata(struct dentry * de)139 static void set_inode_metadata(struct dentry *de)
140 {
141 struct stat stat;
142 int ret;
143
144 ret = lstat(de->full_path, &stat);
145 if (ret < 0) {
146 ERR_MSG("lstat failure\n");
147 ASSERT(0);
148 }
149
150 if (S_ISREG(stat.st_mode)) {
151 if (stat.st_nlink > 1) {
152 /*
153 * This file might have multiple links to it, so remember
154 * device and inode.
155 */
156 de->from_devino = stat.st_dev;
157 de->from_devino <<= 32;
158 de->from_devino |= stat.st_ino;
159 }
160 de->file_type = F2FS_FT_REG_FILE;
161 } else if (S_ISDIR(stat.st_mode)) {
162 de->file_type = F2FS_FT_DIR;
163 } else if (S_ISCHR(stat.st_mode)) {
164 de->file_type = F2FS_FT_CHRDEV;
165 } else if (S_ISBLK(stat.st_mode)) {
166 de->file_type = F2FS_FT_BLKDEV;
167 } else if (S_ISFIFO(stat.st_mode)) {
168 de->file_type = F2FS_FT_FIFO;
169 } else if (S_ISSOCK(stat.st_mode)) {
170 de->file_type = F2FS_FT_SOCK;
171 } else if (S_ISLNK(stat.st_mode)) {
172 de->file_type = F2FS_FT_SYMLINK;
173 de->link = calloc(F2FS_BLKSIZE, 1);
174 ASSERT(de->link);
175 ret = readlink(de->full_path, de->link, F2FS_BLKSIZE - 1);
176 ASSERT(ret >= 0);
177 } else {
178 ERR_MSG("unknown file type on %s", de->path);
179 ASSERT(0);
180 }
181
182 de->size = stat.st_size;
183 de->mode = stat.st_mode &
184 (S_IFMT|S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
185 if (c.fixed_time == -1 && c.from_dir)
186 de->mtime = stat.st_mtime;
187 else
188 de->mtime = c.fixed_time;
189
190 if (c.preserve_perms) {
191 de->uid = stat.st_uid;
192 de->gid = stat.st_gid;
193 }
194
195 set_perms_and_caps(de);
196 }
197
build_directory(struct f2fs_sb_info * sbi,const char * full_path,const char * dir_path,const char * target_out_dir,nid_t dir_ino)198 static int build_directory(struct f2fs_sb_info *sbi, const char *full_path,
199 const char *dir_path, const char *target_out_dir,
200 nid_t dir_ino)
201 {
202 int entries = 0;
203 struct dentry *dentries;
204 struct dirent **namelist = NULL;
205 int i = 0, ret = 0;
206
207 entries = scandir(full_path, &namelist, filter_dot, (void *)alphasort);
208 if (entries < 0) {
209 ERR_MSG("No entries in %s\n", full_path);
210 return -ENOENT;
211 }
212
213 dentries = calloc(entries, sizeof(struct dentry));
214 ASSERT(dentries);
215
216 for (i = 0; i < entries; i++) {
217 dentries[i].name = (unsigned char *)strdup(namelist[i]->d_name);
218 if (dentries[i].name == NULL) {
219 ERR_MSG("Skip: ENOMEM\n");
220 continue;
221 }
222 dentries[i].len = strlen((char *)dentries[i].name);
223
224 ret = asprintf(&dentries[i].path, "%s%s",
225 dir_path, namelist[i]->d_name);
226 ASSERT(ret > 0);
227 ret = asprintf(&dentries[i].full_path, "%s/%s",
228 full_path, namelist[i]->d_name);
229 ASSERT(ret > 0);
230 free(namelist[i]);
231
232 set_inode_metadata(dentries + i);
233
234 dentries[i].pino = dir_ino;
235 }
236
237 free(namelist);
238
239 ret = f2fs_make_directory(sbi, entries, dentries);
240 if (ret)
241 goto out_free;
242
243 for (i = 0; i < entries; i++) {
244 if (dentries[i].file_type == F2FS_FT_REG_FILE) {
245 f2fs_build_file(sbi, dentries + i);
246 } else if (dentries[i].file_type == F2FS_FT_DIR) {
247 char *subdir_full_path = NULL;
248 char *subdir_dir_path = NULL;
249
250 ret = asprintf(&subdir_full_path, "%s",
251 dentries[i].full_path);
252 ASSERT(ret > 0);
253 ret = asprintf(&subdir_dir_path, "%s/",
254 dentries[i].path);
255 ASSERT(ret > 0);
256
257 ret = build_directory(sbi, subdir_full_path,
258 subdir_dir_path,
259 target_out_dir,
260 dentries[i].ino);
261 free(subdir_full_path);
262 free(subdir_dir_path);
263
264 if (ret)
265 goto out_free;
266 } else if (dentries[i].file_type == F2FS_FT_SYMLINK) {
267 /*
268 * It is already done in f2fs_make_directory
269 * f2fs_make_symlink(sbi, dir_ino, &dentries[i]);
270 */
271 } else {
272 MSG(1, "Error unknown file type\n");
273 }
274
275 ret = set_selinux_xattr(sbi, dentries[i].path,
276 dentries[i].ino, dentries[i].mode);
277 if (ret)
278 goto out_free;
279
280 free(dentries[i].path);
281 free(dentries[i].full_path);
282 free((void *)dentries[i].name);
283 }
284 out_free:
285 for (; i < entries; i++) {
286 free(dentries[i].path);
287 free(dentries[i].full_path);
288 free((void *)dentries[i].name);
289 }
290
291 free(dentries);
292 return 0;
293 }
294
configure_files(void)295 static int configure_files(void)
296 {
297 #ifdef HAVE_LIBSELINUX
298 if (!c.nr_opt)
299 goto skip;
300 #if !defined(__ANDROID__)
301 sehnd = selabel_open(SELABEL_CTX_FILE, c.seopt_file, c.nr_opt);
302 if (!sehnd) {
303 ERR_MSG("Failed to open file contexts \"%s\"",
304 c.seopt_file[0].value);
305 return -EINVAL;
306 }
307 #else
308 sehnd = selinux_android_file_context_handle();
309 if (!sehnd) {
310 ERR_MSG("Failed to get android file_contexts\n", c.mount_point);
311 return -EINVAL;
312 }
313 #endif
314 skip:
315 #endif
316 #ifdef WITH_ANDROID
317 /* Load the FS config */
318 if (c.fs_config_file) {
319 int ret = load_canned_fs_config(c.fs_config_file);
320
321 if (ret < 0) {
322 ERR_MSG("Failed to load fs_config \"%s\"",
323 c.fs_config_file);
324 return ret;
325 }
326 fs_config_func = canned_fs_config;
327 } else {
328 fs_config_func = fs_config;
329 }
330 #endif
331 return 0;
332 }
333
f2fs_sload(struct f2fs_sb_info * sbi)334 int f2fs_sload(struct f2fs_sb_info *sbi)
335 {
336 int ret = 0;
337
338 /* this requires for the below sanity checks */
339 fsck_init(sbi);
340
341 ret = configure_files();
342 if (ret) {
343 ERR_MSG("Failed to configure files\n");
344 return ret;
345 }
346
347 /* flush NAT/SIT journal entries */
348 flush_journal_entries(sbi);
349
350 /* initialize empty hardlink cache */
351 sbi->hardlink_cache = 0;
352
353 ret = build_directory(sbi, c.from_dir, "/",
354 c.target_out_dir, F2FS_ROOT_INO(sbi));
355 if (ret) {
356 ERR_MSG("Failed to build due to %d\n", ret);
357 return ret;
358 }
359
360 ret = set_selinux_xattr(sbi, c.mount_point,
361 F2FS_ROOT_INO(sbi), S_IFDIR);
362 if (ret) {
363 ERR_MSG("Failed to set selinux for root: %d\n", ret);
364 return ret;
365 }
366
367 /* update curseg info; can update sit->types */
368 move_curseg_info(sbi, SM_I(sbi)->main_blkaddr, 0);
369 zero_journal_entries(sbi);
370 write_curseg_info(sbi);
371
372 /* flush dirty sit entries */
373 flush_sit_entries(sbi);
374
375 write_checkpoint(sbi);
376 return 0;
377 }
378