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 
40 static int filter_dot(const struct dirent *d)
41 {
42 	return (strcmp(d->d_name, "..") && strcmp(d->d_name, "."));
43 }
44 
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
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 
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 
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 
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 
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 
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