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