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