blob: 4dea78bc3cc64a1c8ba9b9abc3b9b1084d9b992f [file] [log] [blame]
/**
* 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>
#include <private/fs_config.h>
#endif
static int filter_dot(const struct dirent *d)
{
return (strcmp(d->d_name, "..") && strcmp(d->d_name, "."));
}
static int f2fs_make_directory(struct f2fs_sb_info *sbi,
int entries, struct dentry *de)
{
int ret = 0;
int i = 0;
for (i = 0; i < entries; i++) {
if (de[i].file_type == F2FS_FT_DIR)
ret = f2fs_mkdir(sbi, de + i);
else if (de[i].file_type == F2FS_FT_REG_FILE)
ret = f2fs_create(sbi, de + i);
else if (de[i].file_type == F2FS_FT_SYMLINK)
ret = f2fs_symlink(sbi, de + i);
if (ret)
break;
}
return ret;
}
#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;
char *mount_path = c.mount_point;
/*
* de->path already has "/" in the beginning of it.
* Need to remove "/" when c.mount_point is "/", not to add it twice.
*/
if (strlen(c.mount_point) == 1 && c.mount_point[0] == '/')
mount_path = "";
if (asprintf(&mnt_path, "%s%s", mount_path, de->path) <= 0) {
ERR_MSG("cannot allocate mount path for %s%s\n",
mount_path, 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)) {
if (stat.st_nlink > 1) {
/*
* This file might have multiple links to it, so remember
* device and inode.
*/
de->from_devino = stat.st_dev;
de->from_devino <<= 32;
de->from_devino |= stat.st_ino;
}
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 = 0, 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));
ASSERT(dentries);
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);
ret = f2fs_make_directory(sbi, entries, dentries);
if (ret)
goto out_free;
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);
ret = build_directory(sbi, subdir_full_path,
subdir_dir_path,
target_out_dir,
dentries[i].ino);
free(subdir_full_path);
free(subdir_dir_path);
if (ret)
goto out_free;
} 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)
goto out_free;
free(dentries[i].path);
free(dentries[i].full_path);
free((void *)dentries[i].name);
}
out_free:
for (; i < entries; i++) {
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;
/* this requires for the below sanity checks */
fsck_init(sbi);
ret = configure_files();
if (ret) {
ERR_MSG("Failed to configure files\n");
return ret;
}
/* flush NAT/SIT journal entries */
flush_journal_entries(sbi);
/* initialize empty hardlink cache */
sbi->hardlink_cache = 0;
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;
}