mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-05-06 07:26:56 +00:00
1. Rename folder name with "_" 2. Category acrn_crashlog, acrn_log, acrn_trace into debug_tools folder and acrn_bridge, life_mngr, acrn_manager into services. Tracked-On: #5644 Signed-off-by: Xie, nanlin <nanlin.xie@intel.com>
646 lines
13 KiB
C
646 lines
13 KiB
C
/*
|
|
* Copyright (C) 2018 Intel Corporation
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#define _LARGEFILE64_SOURCE
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <linux/loop.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/ioctl.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <utime.h>
|
|
#include <string.h>
|
|
#include <blkid/blkid.h>
|
|
#include <ext2fs/ext2fs.h>
|
|
#include "fsutils.h"
|
|
#include "log_sys.h"
|
|
|
|
#define DEV_LOOP_CTL "/dev/loop-control"
|
|
|
|
struct walking_inode_data {
|
|
const char *current_out_native_dirpath;
|
|
int dumped_count;
|
|
};
|
|
|
|
static int get_par_startaddr_from_img(const char *img,
|
|
const char *target_parname,
|
|
unsigned long long *start)
|
|
{
|
|
blkid_probe pr;
|
|
blkid_partlist ls;
|
|
blkid_partition par;
|
|
int i;
|
|
int nparts;
|
|
const char *par_name;
|
|
unsigned int sector_size;
|
|
unsigned long long par_start;
|
|
|
|
if (!img || !target_parname || !start)
|
|
return -1;
|
|
|
|
pr = blkid_new_probe_from_filename(img);
|
|
if (!pr) {
|
|
LOGE("blkid new probe failed\n");
|
|
return -1;
|
|
}
|
|
ls = blkid_probe_get_partitions(pr);
|
|
if (!ls) {
|
|
LOGE("blkid get partitions failed\n");
|
|
goto err;
|
|
}
|
|
nparts = blkid_partlist_numof_partitions(ls);
|
|
if (nparts <= 0) {
|
|
LOGE("(%d) partitions in (%s)??\n", nparts, img);
|
|
goto err;
|
|
}
|
|
|
|
for (i = 0; i < nparts; i++) {
|
|
par = blkid_partlist_get_partition(ls, i);
|
|
par_name = blkid_partition_get_name(par);
|
|
if (!par_name) {
|
|
LOGW("A partition in (%s) don't have name??\n", img);
|
|
continue;
|
|
}
|
|
if (!strcmp(par_name, target_parname))
|
|
goto found;
|
|
}
|
|
LOGE("no partition of (%s) is named %s\n", img, target_parname);
|
|
err:
|
|
blkid_free_probe(pr);
|
|
return -1;
|
|
found:
|
|
sector_size = blkid_probe_get_sectorsize(pr);
|
|
par_start = (unsigned long long)blkid_partition_get_start(par);
|
|
*start = par_start * sector_size;
|
|
blkid_free_probe(pr);
|
|
return 0;
|
|
}
|
|
|
|
int loopdev_num_get_free(void)
|
|
{
|
|
int loopctlfd;
|
|
int devnr;
|
|
|
|
loopctlfd = open(DEV_LOOP_CTL, O_RDONLY);
|
|
if (loopctlfd == -1) {
|
|
LOGE("failed to open %s, error (%s)\n", DEV_LOOP_CTL,
|
|
strerror(errno));
|
|
return -errno;
|
|
}
|
|
|
|
devnr = ioctl(loopctlfd, LOOP_CTL_GET_FREE);
|
|
if (devnr == -1) {
|
|
LOGE("failed to get free loopdev, error (%s)\n",
|
|
strerror(errno));
|
|
close(loopctlfd);
|
|
return -errno;
|
|
}
|
|
|
|
close(loopctlfd);
|
|
return devnr;
|
|
}
|
|
|
|
static int loopdev_set_status(const char *loopdev,
|
|
const struct loop_info64 *info)
|
|
{
|
|
int loopfd;
|
|
int res;
|
|
|
|
if (!loopdev || !info)
|
|
return -EINVAL;
|
|
|
|
loopfd = open(loopdev, O_RDWR);
|
|
if (loopfd == -1) {
|
|
LOGE("failed to open (%s), error(%s)\n", loopdev,
|
|
strerror(errno));
|
|
return -errno;
|
|
}
|
|
|
|
res = ioctl(loopfd, LOOP_SET_STATUS64, info);
|
|
if (res == -1) {
|
|
LOGE("failed to set info to (%s), error(%s)\n", loopdev,
|
|
strerror(errno));
|
|
close(loopfd);
|
|
return -errno;
|
|
}
|
|
|
|
close(loopfd);
|
|
return 0;
|
|
}
|
|
|
|
static int loopdev_set_img(const char *loopdev, const char *img_path)
|
|
{
|
|
int loopfd;
|
|
int imgfd;
|
|
int res;
|
|
|
|
if (!loopdev || !img_path)
|
|
return -EINVAL;
|
|
|
|
loopfd = open(loopdev, O_WRONLY);
|
|
if (loopfd == -1) {
|
|
LOGE("failed to open %s, error (%s)\n", loopdev,
|
|
strerror(errno));
|
|
return -errno;
|
|
}
|
|
|
|
imgfd = open(img_path, O_RDONLY);
|
|
if (imgfd == -1) {
|
|
LOGE("failed to open %s, error (%s)\n", img_path,
|
|
strerror(errno));
|
|
close(loopfd);
|
|
return -errno;
|
|
}
|
|
|
|
res = ioctl(loopfd, LOOP_SET_FD, imgfd);
|
|
if (res == -1) {
|
|
LOGE("failed to set (%s) to (%s), error (%s)\n", img_path,
|
|
loopdev, strerror(errno));
|
|
close(loopfd);
|
|
close(imgfd);
|
|
return -errno;
|
|
}
|
|
|
|
close(loopfd);
|
|
close(imgfd);
|
|
return 0;
|
|
}
|
|
|
|
int loopdev_set_img_par(const char *loopdev, const char *img_path,
|
|
const char *parname)
|
|
{
|
|
struct loop_info64 info;
|
|
unsigned long long par_start;
|
|
int res;
|
|
|
|
if (!loopdev || !img_path || !parname)
|
|
return -1;
|
|
|
|
res = get_par_startaddr_from_img(img_path, parname, &par_start);
|
|
if (res == -1) {
|
|
LOGE("failed to get data par startaddr\n");
|
|
return -1;
|
|
}
|
|
|
|
res = loopdev_set_img(loopdev, img_path);
|
|
if (res) {
|
|
LOGE("failed to set img (%s) to (%s), error (%s)\n",
|
|
img_path, loopdev, strerror(-res));
|
|
return -1;
|
|
}
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
info.lo_offset = par_start;
|
|
|
|
res = loopdev_set_status(loopdev, &info);
|
|
if (res < 0) {
|
|
LOGE("failed to set loopdev, error (%s)\n", strerror(-res));
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int loopdev_check_parname(const char *loopdev, const char *parname)
|
|
{
|
|
struct ext2_super_block super;
|
|
int fd;
|
|
const int skiprate = 512;
|
|
loff_t sk = 0;
|
|
|
|
if (!loopdev || !parname)
|
|
return -ENOENT;
|
|
|
|
/* quickly find super block */
|
|
fd = open(loopdev, O_RDONLY);
|
|
if (fd == -1) {
|
|
LOGE("failed to open (%s), error(%s)\n", loopdev,
|
|
strerror(errno));
|
|
return -errno;
|
|
}
|
|
for (; lseek64(fd, sk, SEEK_SET) != -1 &&
|
|
read(fd, &super, 512) == 512; sk += skiprate) {
|
|
if (super.s_magic != EXT2_SUPER_MAGIC)
|
|
continue;
|
|
|
|
LOGD("find super block at +%ld\n", sk);
|
|
/* only look into the primary super block */
|
|
if (super.s_volume_name[0]) {
|
|
close(fd);
|
|
return !strcmp((const char *)super.s_volume_name, parname);
|
|
}
|
|
break;
|
|
}
|
|
|
|
close(fd);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Align the file's perms, only print WARNING if errors occurred in this
|
|
* function.
|
|
*
|
|
* Note: Drop user and group.
|
|
*/
|
|
static void align_props(int fd, const char *name,
|
|
const struct ext2_inode *inode)
|
|
{
|
|
int res;
|
|
struct utimbuf ut;
|
|
|
|
if (!inode || !name)
|
|
return;
|
|
|
|
if (fd >= 0)
|
|
res = fchmod(fd, inode->i_mode);
|
|
else
|
|
res = chmod(name, inode->i_mode);
|
|
|
|
if (res == -1)
|
|
LOGW("failed to exec (xchmod), error (%s)\n", strerror(errno));
|
|
|
|
ut.actime = inode->i_atime;
|
|
ut.modtime = inode->i_mtime;
|
|
res = utime(name, &ut);
|
|
if (res == -1)
|
|
LOGW("failed to exec (utime), error (%s)\n", strerror(errno));
|
|
}
|
|
|
|
static int e2fs_get_inodenum_by_fpath(ext2_filsys fs, const char *fpath,
|
|
ext2_ino_t *out_ino)
|
|
{
|
|
ext2_ino_t root;
|
|
ext2_ino_t cwd;
|
|
errcode_t res;
|
|
|
|
if (!fs || !fpath || !out_ino)
|
|
return -1;
|
|
|
|
root = EXT2_ROOT_INO;
|
|
cwd = EXT2_ROOT_INO;
|
|
|
|
res = ext2fs_namei(fs, root, cwd, fpath, out_ino);
|
|
if (res) {
|
|
LOGE("ext2fs failed to get ino, path (%s), error (%s)\n",
|
|
fpath, error_message(res));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int e2fs_read_inode_by_inodenum(ext2_filsys fs, ext2_ino_t ino,
|
|
struct ext2_inode *inode)
|
|
{
|
|
errcode_t res;
|
|
|
|
if (!fs || !ino || !inode)
|
|
return -1;
|
|
|
|
res = ext2fs_read_inode(fs, ino, inode);
|
|
if (res) {
|
|
LOGE("ext2fs failed to get inode, ino (%d), error (%s)\n",
|
|
ino, error_message(res));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int e2fs_dump_file_by_inodenum(ext2_filsys fs, ext2_ino_t ino,
|
|
const char *out_fp)
|
|
{
|
|
errcode_t res;
|
|
int ret = 0;
|
|
int fd;
|
|
unsigned int got;
|
|
struct ext2_inode inode;
|
|
ext2_file_t e2_file;
|
|
char *buf;
|
|
ssize_t write_b;
|
|
|
|
if (!fs || !ino || !out_fp)
|
|
return -1;
|
|
|
|
res = e2fs_read_inode_by_inodenum(fs, ino, &inode);
|
|
if (res) {
|
|
LOGE("ext2fs failed to read inode, error (%s)\n",
|
|
error_message(res));
|
|
return -1;
|
|
}
|
|
|
|
fd = open(out_fp, O_CREAT | O_WRONLY | O_TRUNC | O_LARGEFILE, 0666);
|
|
if (fd == -1) {
|
|
LOGE("open (%s) failed, error (%s)\n", out_fp, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
/* open with read only */
|
|
res = ext2fs_file_open2(fs, ino, &inode, 0, &e2_file);
|
|
if (res) {
|
|
LOGE("ext2fs failed to open file, ino (%d), error (%s)\n",
|
|
ino, error_message(res));
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
res = ext2fs_get_mem(fs->blocksize, &buf);
|
|
if (res) {
|
|
LOGE("ext2fs failed to get mem, error (%s)\n",
|
|
error_message(res));
|
|
close(fd);
|
|
ext2fs_file_close(e2_file);
|
|
return -1;
|
|
}
|
|
|
|
while (1) {
|
|
res = ext2fs_file_read(e2_file, buf, fs->blocksize, &got);
|
|
/* got equals zero in failed case */
|
|
if (res) {
|
|
LOGE("ext2fs failed to read (%u), error (%s)\n",
|
|
ino, error_message(res));
|
|
ret = -1;
|
|
}
|
|
if (!got)
|
|
break;
|
|
|
|
write_b = write(fd, buf, got);
|
|
if ((unsigned int)write_b != got) {
|
|
LOGE("failed to write file (%s), error (%s)\n",
|
|
out_fp, strerror(errno));
|
|
ret = -1;
|
|
break;
|
|
}
|
|
}
|
|
align_props(fd, out_fp, &inode);
|
|
if (buf)
|
|
ext2fs_free_mem(&buf);
|
|
/* ext2fs_file_close only failed in flush process */
|
|
ext2fs_file_close(e2_file);
|
|
close(fd);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int e2fs_dump_file_by_fpath(ext2_filsys fs, const char *in_fp,
|
|
const char *out_fp)
|
|
{
|
|
int res;
|
|
ext2_ino_t ino;
|
|
|
|
if (!fs || !in_fp || !out_fp)
|
|
return -1;
|
|
|
|
res = e2fs_get_inodenum_by_fpath(fs, in_fp, &ino);
|
|
if (res)
|
|
return res;
|
|
|
|
return e2fs_dump_file_by_inodenum(fs, ino, out_fp);
|
|
}
|
|
|
|
static int e2fs_read_file_by_inodenum(ext2_filsys fs, ext2_ino_t ino,
|
|
void **out_data, unsigned long *size)
|
|
{
|
|
errcode_t res;
|
|
unsigned int got;
|
|
struct ext2_inode inode;
|
|
ext2_file_t e2_file;
|
|
__u64 _size;
|
|
char *buf;
|
|
|
|
if (!fs || !ino || !out_data || !size)
|
|
return -1;
|
|
|
|
res = e2fs_read_inode_by_inodenum(fs, ino, &inode);
|
|
if (res) {
|
|
LOGE("ext2fs failed to read inode, error (%s)\n",
|
|
error_message(res));
|
|
return -1;
|
|
}
|
|
|
|
_size = EXT2_I_SIZE(&inode);
|
|
if (!_size) {
|
|
LOGW("try to read a empty file\n");
|
|
*size = 0;
|
|
*out_data = 0;
|
|
return 0;
|
|
}
|
|
|
|
/* open with read only */
|
|
res = ext2fs_file_open2(fs, ino, &inode, 0, &e2_file);
|
|
if (res) {
|
|
LOGE("ext2fs failed to open file, ino (%d), error (%s)\n",
|
|
ino, error_message(res));
|
|
return -1;
|
|
}
|
|
|
|
res = ext2fs_get_mem(_size + 1, &buf);
|
|
if (res) {
|
|
LOGE("ext2fs failed to get mem, error (%s)\n",
|
|
error_message(res));
|
|
ext2fs_file_close(e2_file);
|
|
return -1;
|
|
}
|
|
|
|
res = ext2fs_file_read(e2_file, buf, _size, &got);
|
|
/* got equals zero in failed case */
|
|
if (res) {
|
|
LOGE("ext2fs failed to read (%u), error (%s)\n",
|
|
ino, error_message(res));
|
|
goto err;
|
|
}
|
|
|
|
/* ext2fs_file_close only failed in flush process */
|
|
ext2fs_file_close(e2_file);
|
|
|
|
*size = _size;
|
|
buf[_size] = 0;
|
|
*out_data = buf;
|
|
|
|
return 0;
|
|
err:
|
|
free(buf);
|
|
ext2fs_file_close(e2_file);
|
|
return -1;
|
|
}
|
|
|
|
int e2fs_read_file_by_fpath(ext2_filsys fs, const char *in_fp,
|
|
void **out_data, unsigned long *size)
|
|
{
|
|
int res;
|
|
ext2_ino_t ino;
|
|
|
|
if (!fs || !in_fp || !out_data || !size)
|
|
return -1;
|
|
|
|
res = e2fs_get_inodenum_by_fpath(fs, in_fp, &ino);
|
|
if (res)
|
|
return res;
|
|
|
|
return e2fs_read_file_by_inodenum(fs, ino, out_data, size);
|
|
}
|
|
|
|
static int dump_inode_recursively_by_inodenum(ext2_filsys fs, ext2_ino_t ino,
|
|
struct walking_inode_data *data,
|
|
const char *fname);
|
|
static int callback_for_subentries(struct ext2_dir_entry *dirent,
|
|
int offset EXT2FS_ATTR((unused)),
|
|
int blocksize EXT2FS_ATTR((unused)),
|
|
char *buf EXT2FS_ATTR((unused)),
|
|
void *private)
|
|
{
|
|
char fname[EXT2_NAME_LEN + 1];
|
|
struct walking_inode_data *data = private;
|
|
int len;
|
|
|
|
len = dirent->name_len & 0xFF; /* EXT2_NAME_LEN = 255 */
|
|
strncpy(fname, dirent->name, len);
|
|
fname[len] = 0;
|
|
|
|
return dump_inode_recursively_by_inodenum(NULL, dirent->inode,
|
|
data, fname);
|
|
}
|
|
|
|
static int dump_inode_recursively_by_inodenum(ext2_filsys fs, ext2_ino_t ino,
|
|
struct walking_inode_data *data,
|
|
const char *fname)
|
|
{
|
|
int res;
|
|
char *out_fpath;
|
|
errcode_t err;
|
|
static ext2_filsys fs_for_dump;
|
|
struct ext2_inode inode;
|
|
|
|
if (!ino || !data || !data->current_out_native_dirpath || !fname)
|
|
goto abort;
|
|
|
|
/* caller is not callback_for_subentries */
|
|
if (fs)
|
|
fs_for_dump = fs;
|
|
|
|
if (!strcmp(fname, ".") || !strcmp(fname, ".."))
|
|
return 0;
|
|
|
|
res = asprintf(&out_fpath, "%s/%s", data->current_out_native_dirpath,
|
|
fname);
|
|
if (res == -1) {
|
|
LOGE("failed to construct target file name, ");
|
|
goto abort;
|
|
}
|
|
|
|
res = e2fs_read_inode_by_inodenum(fs_for_dump, ino, &inode);
|
|
if (res) {
|
|
LOGE("ext2fs failed to read inode, ");
|
|
goto abort_free;
|
|
}
|
|
|
|
if (LINUX_S_ISREG(inode.i_mode)) {
|
|
/* do dump for file */
|
|
res = e2fs_dump_file_by_inodenum(fs_for_dump, ino, out_fpath);
|
|
if (res) {
|
|
LOGE("ext2fs failed to dump file, ");
|
|
goto abort_free;
|
|
}
|
|
data->dumped_count++;
|
|
} else if (LINUX_S_ISDIR(inode.i_mode)) {
|
|
/* mkdir for directory and dump the subentry */
|
|
res = mkdir(out_fpath, 0700);
|
|
if (res == -1 && errno != EEXIST) {
|
|
LOGE("failed to mkdir (%s), error (%s), ", out_fpath,
|
|
strerror(errno));
|
|
goto abort_free;
|
|
}
|
|
data->dumped_count++;
|
|
|
|
data->current_out_native_dirpath = out_fpath;
|
|
err = ext2fs_dir_iterate(fs_for_dump, ino, 0, 0,
|
|
callback_for_subentries,
|
|
(void *)data);
|
|
if (err) {
|
|
LOGE("ext2fs failed to iterate dir, errno (%s), ",
|
|
error_message(err));
|
|
goto abort_free;
|
|
}
|
|
align_props(-1, out_fpath, &inode);
|
|
}
|
|
/* else ignore the rest types, such as link, socket, fifo, ... */
|
|
|
|
free(out_fpath);
|
|
return 0;
|
|
|
|
abort_free:
|
|
free(out_fpath);
|
|
abort:
|
|
LOGE("dump dir aborted...\n");
|
|
return DIRENT_ABORT;
|
|
}
|
|
|
|
int e2fs_dump_dir_by_dpath(ext2_filsys fs, const char *in_dp,
|
|
const char *out_dp, int *count)
|
|
{
|
|
ext2_ino_t ino;
|
|
struct walking_inode_data dump_needed;
|
|
const char *dname;
|
|
int res;
|
|
|
|
if (!fs || !in_dp || !count)
|
|
return -1;
|
|
|
|
*count = 0;
|
|
if (!directory_exists(out_dp)) {
|
|
LOGE("dir need dump into an existed dir\n");
|
|
return -1;
|
|
}
|
|
|
|
res = e2fs_get_inodenum_by_fpath(fs, in_dp, &ino);
|
|
if (res == -1)
|
|
return -1;
|
|
|
|
dname = strrchr(in_dp, '/');
|
|
if (dname)
|
|
dname++;
|
|
else
|
|
dname = in_dp;
|
|
|
|
dump_needed.dumped_count = 0;
|
|
dump_needed.current_out_native_dirpath = out_dp;
|
|
res = dump_inode_recursively_by_inodenum(fs, ino, &dump_needed, dname);
|
|
*count = dump_needed.dumped_count;
|
|
if (res) {
|
|
LOGE("ext2fs failed to dump dir\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int e2fs_open(const char *dev, ext2_filsys *outfs)
|
|
{
|
|
errcode_t res;
|
|
|
|
if (!dev || !outfs)
|
|
return -1;
|
|
|
|
add_error_table(&et_ext2_error_table);
|
|
res = ext2fs_open(dev, EXT2_FLAG_64BITS, 0, 0,
|
|
unix_io_manager, outfs);
|
|
if (res) {
|
|
LOGE("ext2fs fail to open (%s), error (%s)\n", dev,
|
|
error_message(res));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void e2fs_close(ext2_filsys fs)
|
|
{
|
|
if (fs)
|
|
ext2fs_close(fs);
|
|
remove_error_table(&et_ext2_error_table);
|
|
}
|