Home | History | Annotate | Download | only in fsck
      1 /**
      2  * sload.c
      3  *
      4  * Copyright (C) 2015 Huawei Ltd.
      5  * Witten by:
      6  *   Hou Pengyang <houpengyang (at) huawei.com>
      7  *   Liu Shuoran <liushuoran (at) huawei.com>
      8  *   Jaegeuk Kim <jaegeuk (at) 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 
     39 static int filter_dot(const struct dirent *d)
     40 {
     41 	return (strcmp(d->d_name, "..") && strcmp(d->d_name, "."));
     42 }
     43 
     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
     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 
     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 
    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 
    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 
    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 
    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);
    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