/** * sload.c * * Copyright (C) 2015 Huawei Ltd. * Witten by: * Hou Pengyang <houpengyang@huawei.com> * Liu Shuoran <liushuoran@huawei.com> * Jaegeuk Kim <jaegeuk@kernel.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #define _GNU_SOURCE #include "fsck.h" #include <libgen.h> #include <dirent.h> #ifdef HAVE_MNTENT_H #include <mntent.h> #endif #ifdef HAVE_LIBSELINUX static struct selabel_handle *sehnd = NULL; #endif typedef void (*fs_config_f)(const char *path, int dir, const char *target_out_path, unsigned *uid, unsigned *gid, unsigned *mode, uint64_t *capabilities); static fs_config_f fs_config_func = NULL; #ifdef WITH_ANDROID #include <selinux/android.h> #include <private/android_filesystem_config.h> #include <private/canned_fs_config.h> #endif static int filter_dot(const struct dirent *d) { return (strcmp(d->d_name, "..") && strcmp(d->d_name, ".")); } static void f2fs_make_directory(struct f2fs_sb_info *sbi, int entries, struct dentry *de) { int i = 0; for (i = 0; i < entries; i++) { if (de[i].file_type == F2FS_FT_DIR) f2fs_mkdir(sbi, de + i); else if (de[i].file_type == F2FS_FT_REG_FILE) f2fs_create(sbi, de + i); else if (de[i].file_type == F2FS_FT_SYMLINK) f2fs_symlink(sbi, de + i); } } #ifdef HAVE_LIBSELINUX static int set_selinux_xattr(struct f2fs_sb_info *sbi, const char *path, nid_t ino, int mode) { char *secontext = NULL; char *mnt_path = NULL; if (!sehnd) return 0; if (asprintf(&mnt_path, "%s%s", c.mount_point, path) <= 0) { ERR_MSG("cannot allocate security path for %s%s\n", c.mount_point, path); return -ENOMEM; } /* set root inode selinux context */ if (selabel_lookup(sehnd, &secontext, mnt_path, mode) < 0) { ERR_MSG("cannot lookup security context for %s\n", mnt_path); free(mnt_path); return -EINVAL; } if (secontext) { MSG(2, "%s (%d) -> SELinux context = %s\n", mnt_path, ino, secontext); inode_set_selinux(sbi, ino, secontext); } freecon(secontext); free(mnt_path); return 0; } #else #define set_selinux_xattr(...) 0 #endif static int set_perms_and_caps(struct dentry *de) { uint64_t capabilities = 0; unsigned int uid = 0, gid = 0, imode = 0; char *mnt_path = NULL; if (asprintf(&mnt_path, "%s%s", c.mount_point, de->path) <= 0) { ERR_MSG("cannot allocate mount path for %s%s\n", c.mount_point, de->path); return -ENOMEM; } /* Permissions */ if (fs_config_func != NULL) { fs_config_func(mnt_path, de->file_type == F2FS_FT_DIR, c.target_out_dir, &uid, &gid, &imode, &capabilities); de->uid = uid & 0xffff; de->gid = gid & 0xffff; de->mode = (de->mode & S_IFMT) | (imode & 0xffff); de->capabilities = capabilities; } MSG(2, "%s -> mode = 0x%x, uid = 0x%x, gid = 0x%x, " "capabilities = 0x%"PRIx64"\n", mnt_path, de->mode, de->uid, de->gid, de->capabilities); free(mnt_path); return 0; } static void set_inode_metadata(struct dentry *de) { struct stat stat; int ret; ret = lstat(de->full_path, &stat); if (ret < 0) { ERR_MSG("lstat failure\n"); ASSERT(0); } if (S_ISREG(stat.st_mode)) { de->file_type = F2FS_FT_REG_FILE; } else if (S_ISDIR(stat.st_mode)) { de->file_type = F2FS_FT_DIR; } else if (S_ISCHR(stat.st_mode)) { de->file_type = F2FS_FT_CHRDEV; } else if (S_ISBLK(stat.st_mode)) { de->file_type = F2FS_FT_BLKDEV; } else if (S_ISFIFO(stat.st_mode)) { de->file_type = F2FS_FT_FIFO; } else if (S_ISSOCK(stat.st_mode)) { de->file_type = F2FS_FT_SOCK; } else if (S_ISLNK(stat.st_mode)) { de->file_type = F2FS_FT_SYMLINK; de->link = calloc(F2FS_BLKSIZE, 1); ASSERT(de->link); ret = readlink(de->full_path, de->link, F2FS_BLKSIZE - 1); ASSERT(ret >= 0); } else { ERR_MSG("unknown file type on %s", de->path); ASSERT(0); } de->size = stat.st_size; de->mode = stat.st_mode & (S_IFMT|S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); if (c.fixed_time == -1 && c.from_dir) de->mtime = stat.st_mtime; else de->mtime = c.fixed_time; set_perms_and_caps(de); } static int build_directory(struct f2fs_sb_info *sbi, const char *full_path, const char *dir_path, const char *target_out_dir, nid_t dir_ino) { int entries = 0; struct dentry *dentries; struct dirent **namelist = NULL; int i, ret = 0; entries = scandir(full_path, &namelist, filter_dot, (void *)alphasort); if (entries < 0) { ERR_MSG("No entries in %s\n", full_path); return -ENOENT; } dentries = calloc(entries, sizeof(struct dentry)); if (dentries == NULL) return -ENOMEM; for (i = 0; i < entries; i++) { dentries[i].name = (unsigned char *)strdup(namelist[i]->d_name); if (dentries[i].name == NULL) { ERR_MSG("Skip: ENOMEM\n"); continue; } dentries[i].len = strlen((char *)dentries[i].name); ret = asprintf(&dentries[i].path, "%s%s", dir_path, namelist[i]->d_name); ASSERT(ret > 0); ret = asprintf(&dentries[i].full_path, "%s/%s", full_path, namelist[i]->d_name); ASSERT(ret > 0); free(namelist[i]); set_inode_metadata(dentries + i); dentries[i].pino = dir_ino; } free(namelist); f2fs_make_directory(sbi, entries, dentries); for (i = 0; i < entries; i++) { if (dentries[i].file_type == F2FS_FT_REG_FILE) { f2fs_build_file(sbi, dentries + i); } else if (dentries[i].file_type == F2FS_FT_DIR) { char *subdir_full_path = NULL; char *subdir_dir_path = NULL; ret = asprintf(&subdir_full_path, "%s", dentries[i].full_path); ASSERT(ret > 0); ret = asprintf(&subdir_dir_path, "%s/", dentries[i].path); ASSERT(ret > 0); build_directory(sbi, subdir_full_path, subdir_dir_path, target_out_dir, dentries[i].ino); free(subdir_full_path); free(subdir_dir_path); } else if (dentries[i].file_type == F2FS_FT_SYMLINK) { /* * It is already done in f2fs_make_directory * f2fs_make_symlink(sbi, dir_ino, &dentries[i]); */ } else { MSG(1, "Error unknown file type\n"); } ret = set_selinux_xattr(sbi, dentries[i].path, dentries[i].ino, dentries[i].mode); if (ret) return ret; free(dentries[i].path); free(dentries[i].full_path); free((void *)dentries[i].name); } free(dentries); return 0; } static int configure_files(void) { #ifdef HAVE_LIBSELINUX if (!c.nr_opt) goto skip; #if !defined(__ANDROID__) sehnd = selabel_open(SELABEL_CTX_FILE, c.seopt_file, c.nr_opt); if (!sehnd) { ERR_MSG("Failed to open file contexts \"%s\"", c.seopt_file[0].value); return -EINVAL; } #else sehnd = selinux_android_file_context_handle(); if (!sehnd) { ERR_MSG("Failed to get android file_contexts\n", c.mount_point); return -EINVAL; } #endif skip: #endif #ifdef WITH_ANDROID /* Load the FS config */ if (c.fs_config_file) { int ret = load_canned_fs_config(c.fs_config_file); if (ret < 0) { ERR_MSG("Failed to load fs_config \"%s\"", c.fs_config_file); return ret; } fs_config_func = canned_fs_config; } else { fs_config_func = fs_config; } #endif return 0; } int f2fs_sload(struct f2fs_sb_info *sbi) { int ret = 0; ret = configure_files(); if (ret) { ERR_MSG("Failed to configure files\n"); return ret; } /* flush NAT/SIT journal entries */ flush_journal_entries(sbi); ret = build_directory(sbi, c.from_dir, "/", c.target_out_dir, F2FS_ROOT_INO(sbi)); if (ret) { ERR_MSG("Failed to build due to %d\n", ret); return ret; } ret = set_selinux_xattr(sbi, c.mount_point, F2FS_ROOT_INO(sbi), S_IFDIR); if (ret) { ERR_MSG("Failed to set selinux for root: %d\n", ret); return ret; } /* update curseg info; can update sit->types */ move_curseg_info(sbi, SM_I(sbi)->main_blkaddr, 0); zero_journal_entries(sbi); write_curseg_info(sbi); /* flush dirty sit entries */ flush_sit_entries(sbi); write_checkpoint(sbi); return 0; }