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