From e57dec9ffbf35ba88408a39a6f3912440d87c76b Mon Sep 17 00:00:00 2001 From: Hongzhen Luo Date: Sun, 11 Aug 2024 14:14:53 +0800 Subject: [PATCH 1/2] [EROFS]: cleanup: get rid of the global static variables in liberofs.cpp Remove the global variables `_target` and `_source`, and introduce `struct liberofs_file` to wrap the source and target files. Signed-off-by: Hongzhen Luo --- src/overlaybd/tar/erofs/liberofs.cpp | 106 +++++++++++++++++---------- 1 file changed, 67 insertions(+), 39 deletions(-) diff --git a/src/overlaybd/tar/erofs/liberofs.cpp b/src/overlaybd/tar/erofs/liberofs.cpp index a0d5a219..9bd1674d 100644 --- a/src/overlaybd/tar/erofs/liberofs.cpp +++ b/src/overlaybd/tar/erofs/liberofs.cpp @@ -27,7 +27,9 @@ struct liberofs_inmem_sector { class ErofsCache { public: - ErofsCache() {} + ErofsCache(photon::fs::IFile *file, unsigned long int capacity): + file(file), capacity(capacity) + {} ~ErofsCache() {} ssize_t write_sector(u64 addr, char *buf); ssize_t read_sector(u64 addr, char *buf); @@ -39,11 +41,11 @@ class ErofsCache { std::set dirty; }; -static struct erofs_vfops target_vfops; -static struct erofs_vfops source_vfops; -static ErofsCache erofs_cache; -static photon::fs::IFile *_target; -static photon::fs::IFile *_source; +struct liberofs_file { + struct erofs_vfops ops; + photon::fs::IFile *file; + ErofsCache *cache; +}; ssize_t ErofsCache::write_sector(u64 addr, char *buf) { @@ -297,7 +299,13 @@ static ssize_t erofs_read_photon_file(void *buf, u64 offset, size_t len, static ssize_t erofs_target_pread(struct erofs_vfile *vf, void *buf, u64 offset, size_t len) { - if (erofs_read_photon_file(buf, offset, len, &erofs_cache) != (ssize_t)len) + struct liberofs_file *target_file = + reinterpret_cast(vf->ops); + + if (!target_file) + return -EINVAL; + if (erofs_read_photon_file(buf, offset, len, target_file->cache) + != (ssize_t)len) return -1; return len; @@ -306,15 +314,25 @@ static ssize_t erofs_target_pread(struct erofs_vfile *vf, void *buf, u64 offset, static ssize_t erofs_target_pwrite(struct erofs_vfile *vf, const void *buf, u64 offset, size_t len) { + struct liberofs_file *target_file = + reinterpret_cast(vf->ops); + + if (!target_file) + return -EINVAL; if (!buf) return -EINVAL; - return erofs_write_photon_file(buf, offset, len, &erofs_cache); + return erofs_write_photon_file(buf, offset, len, target_file->cache); } static int erofs_target_fsync(struct erofs_vfile *vf) { - return erofs_cache.flush(); + struct liberofs_file *target_file = + reinterpret_cast(vf->ops); + + if (!target_file) + return -EINVAL; + return target_file->cache->flush(); } static int erofs_target_fallocate(struct erofs_vfile *vf, u64 offset, @@ -384,12 +402,21 @@ static int erofs_source_ftruncate(struct erofs_vfile *vf, u64 length) static ssize_t erofs_source_read(struct erofs_vfile *vf, void *buf, size_t bytes) { - return _source->read(buf, bytes); + struct liberofs_file *source_file = + reinterpret_cast(vf->ops); + + if (!source_file) + return -EINVAL; + return source_file->file->read(buf, bytes); } static off_t erofs_source_lseek(struct erofs_vfile *vf, u64 offset, int whence) { - return _source->lseek(offset, whence); + struct liberofs_file *source_file = + reinterpret_cast(vf->ops); + if (!source_file) + return -EINVAL; + return source_file->file->lseek(offset, whence); } struct erofs_mkfs_cfg { @@ -594,41 +621,41 @@ int LibErofs::extract_tar(photon::fs::IFile *source, bool meta_only, bool first_ struct erofs_tarfile erofstar = {}; struct erofs_mkfs_cfg cfg; struct erofs_configure *erofs_cfg; + struct liberofs_file target_file, source_file; int err; - _target = target; - _source = source; - - target_vfops.pread = erofs_target_pread; - target_vfops.pwrite = erofs_target_pwrite; - target_vfops.pread = erofs_target_pread; - target_vfops.pwrite = erofs_target_pwrite; - target_vfops.fsync = erofs_target_fsync; - target_vfops.fallocate = erofs_target_fallocate; - target_vfops.ftruncate = erofs_target_ftruncate; - target_vfops.read = erofs_target_read; - target_vfops.lseek = erofs_target_lseek; - - source_vfops.pread = erofs_source_pread; - source_vfops.pwrite = erofs_source_pwrite; - source_vfops.fsync = erofs_source_fsync; - source_vfops.fallocate = erofs_source_fallocate; - source_vfops.ftruncate = erofs_source_ftruncate; - source_vfops.read = erofs_source_read; - source_vfops.lseek = erofs_source_lseek; - - erofs_cache.file = _target; - erofs_cache.capacity = 128; + target_file.ops.pread = erofs_target_pread; + target_file.ops.pwrite = erofs_target_pwrite; + target_file.ops.pread = erofs_target_pread; + target_file.ops.pwrite = erofs_target_pwrite; + target_file.ops.fsync = erofs_target_fsync; + target_file.ops.fallocate = erofs_target_fallocate; + target_file.ops.ftruncate = erofs_target_ftruncate; + target_file.ops.read = erofs_target_read; + target_file.ops.lseek = erofs_target_lseek; + target_file.file = target; + target_file.cache = new ErofsCache(target, 128); + + source_file.ops.pread = erofs_source_pread; + source_file.ops.pwrite = erofs_source_pwrite; + source_file.ops.fsync = erofs_source_fsync; + source_file.ops.fallocate = erofs_source_fallocate; + source_file.ops.ftruncate = erofs_source_ftruncate; + source_file.ops.read = erofs_source_read; + source_file.ops.lseek = erofs_source_lseek; + source_file.file = source; + source_file.cache = NULL; /* initialization of sbi */ - err = erofs_init_sbi(&sbi, _target, &target_vfops, ilog2(blksize)); + err = erofs_init_sbi(&sbi, target_file.file, &target_file.ops, ilog2(blksize)); if (err) { - erofs_close_sbi(&sbi, &erofs_cache); + erofs_close_sbi(&sbi, target_file.cache); + delete target_file.cache; LOG_ERROR("Failed to init sbi."); return err; } /* initialization of erofstar */ - err = erofs_init_tar(&erofstar, &source_vfops); + err = erofs_init_tar(&erofstar, &source_file.ops); if (err) { LOG_ERROR("Failed to init tarerofs"); goto exit; @@ -655,15 +682,16 @@ int LibErofs::extract_tar(photon::fs::IFile *source, bool meta_only, bool first_ } /* write mapfile */ - err = erofs_write_map_file(_target, blksize, cfg.mp_fp); + err = erofs_write_map_file(target_file.file, blksize, cfg.mp_fp); if (err) { LOG_ERROR("Failed to write mapfile."); goto exit; } exit: - err = erofs_close_sbi(&sbi, &erofs_cache); + err = erofs_close_sbi(&sbi, target_file.cache); erofs_close_tar(&erofstar); std::fclose(cfg.mp_fp); + delete target_file.cache; return err; } From 2ccf35e280644c7e03ba440bf1c97716668f95cc Mon Sep 17 00:00:00 2001 From: Hongzhen Luo Date: Sun, 11 Aug 2024 15:45:50 +0800 Subject: [PATCH 2/2] [EROFS] Introduce prefetch feature To support dynamic prefetching, EROFS provides the following interfaces: - ErofsFileSystem: - open - stat - opendir - ErofsFile: - fstat - fiemap - ErofsDir: - get - next - closedir - rewinddir - seekdir - telldir Signed-off-by: Hongzhen Luo --- src/overlaybd/tar/erofs/liberofs.cpp | 469 +++++++++++++++++++++++++++ src/overlaybd/tar/erofs/liberofs.h | 4 + src/prefetch.cpp | 13 +- src/test/trace_test.cpp | 86 +++++ src/tools/comm_func.cpp | 13 + src/tools/comm_func.h | 3 + 6 files changed, 587 insertions(+), 1 deletion(-) diff --git a/src/overlaybd/tar/erofs/liberofs.cpp b/src/overlaybd/tar/erofs/liberofs.cpp index 9bd1674d..c77e7b51 100644 --- a/src/overlaybd/tar/erofs/liberofs.cpp +++ b/src/overlaybd/tar/erofs/liberofs.cpp @@ -6,11 +6,13 @@ #include "erofs/block_list.h" #include "erofs/inode.h" #include "erofs/config.h" +#include "erofs/dir.h" #include "../../lsmt/file.h" #include "../../lsmt/index.h" #include #include #include +#include #define SECTOR_SIZE 512ULL #define SECTOR_BITS 9 @@ -703,3 +705,470 @@ LibErofs::LibErofs(photon::fs::IFile *target, uint64_t blksize, bool import_tar_ LibErofs::~LibErofs() { } + +class ErofsFileSystem: public photon::fs::IFileSystem { +public: + struct erofs_sb_info sbi; + struct liberofs_file target_file; + + ErofsFileSystem(photon::fs::IFile *imgfile, uint64_t blksize); + ~ErofsFileSystem(); + photon::fs::IFile* open(const char *pathname, int flags); + photon::fs::IFile* open(const char *pathname, int flags, mode_t mode); + photon::fs::IFile* creat(const char *pathname, mode_t mode); + int mkdir(const char *pathname, mode_t mode); + int rmdir(const char *pathname); + int symlink(const char *oldname, const char *newname); + ssize_t readlink(const char *path, char *buf, size_t bufsiz); + int link(const char *oldname, const char *newname); + int rename(const char *oldname, const char *newname); + int unlink(const char *filename); + int chmod(const char *pathname, mode_t mode); + int chown(const char *pathname, uid_t owner, gid_t group); + int lchown(const char *pathname, uid_t owner, gid_t group); + int statfs(const char *path, struct statfs *buf); + int statvfs(const char *path, struct statvfs *buf); + int stat(const char *path, struct stat *buf); + int lstat(const char *path, struct stat *buf); + int access(const char *pathname, int mode); + int truncate(const char *path, off_t length); + int utime(const char *path, const struct utimbuf *file_times); + int utimes(const char *path, const struct timeval times[2]); + int lutimes(const char *path, const struct timeval times[2]); + int mknod(const char *path, mode_t mode, dev_t dev); + int syncfs(); + photon::fs::DIR* opendir(const char *name); +}; + +class ErofsFile: public photon::fs::VirtualReadOnlyFile { +public: + ErofsFileSystem *fs; + struct erofs_inode inode; + + ErofsFile(ErofsFileSystem *fs); + photon::fs::IFileSystem *filesystem(); + int fstat(struct stat *buf); + int fiemap(struct photon::fs::fiemap *map); +}; + +class ErofsDir: public photon::fs::DIR { +public: + std::vector<::dirent> m_dirs; + ::dirent *direntp = nullptr; + long loc; + ErofsDir(std::vector<::dirent> &dirs); + ~ErofsDir(); + int closedir(); + dirent *get(); + int next(); + void rewinddir(); + void seekdir(long loc); + long telldir(); +}; + +#define EROFS_UNIMPLEMENTED_FUNC(ret_type, cls, func, ret) \ +ret_type cls::func { \ + return ret; \ +} + +// ErofsFile +ErofsFile::ErofsFile(ErofsFileSystem *fs): fs(fs) +{ + memset(&inode, 0, sizeof(struct erofs_inode)); +} +photon::fs::IFileSystem *ErofsFile::filesystem() { return fs; } + +struct liberofs_nameidata { + struct erofs_sb_info *sbi; + erofs_nid_t nid; +}; + +static int liberofs_link_path_walk(const char *name, + struct liberofs_nameidata *nd); + +static struct erofs_dirent *liberofs_find_dirent(void *data, const char *name, + unsigned int len, + unsigned int nameoff, + unsigned int maxsize) +{ + struct erofs_dirent *de = (struct erofs_dirent *)data; + const struct erofs_dirent *end = (struct erofs_dirent *)((char *)data + nameoff); + + while (de < end) { + const char *de_name; + unsigned int de_namelen; + + nameoff = le16_to_cpu(de->nameoff); + de_name = (char *)((char *)data + nameoff); + if (de + 1 >= end) + de_namelen = strnlen(de_name, maxsize - nameoff); + else + de_namelen = le16_to_cpu(de[1].nameoff - nameoff); + + if (nameoff + de_namelen > maxsize) { + LOG_ERROR("[erofs] bogus dirent"); + return (struct erofs_dirent *)ERR_PTR(-EINVAL); + } + + if (len == de_namelen && !memcmp(de_name, name, de_namelen)) + return de; + ++de; + } + return NULL; +} + +static int liberofs_namei(struct liberofs_nameidata *nd, const char *name, + unsigned int len) +{ + erofs_nid_t nid = nd->nid; + int ret; + char buf[EROFS_MAX_BLOCK_SIZE]; + struct erofs_sb_info *sbi = nd->sbi; + struct erofs_inode vi = {}; + erofs_off_t offset; + + vi.sbi = sbi; + vi.nid = nid; + ret = erofs_read_inode_from_disk(&vi); + if (ret) + return ret; + + offset = 0; + while (offset < vi.i_size) { + erofs_off_t maxsize = min_t(erofs_off_t, + vi.i_size - offset, erofs_blksiz(sbi)); + struct erofs_dirent *de = (struct erofs_dirent *)buf; + unsigned int nameoff; + + ret = erofs_pread(&vi, buf, maxsize, offset); + if (ret) + return ret; + + nameoff = le16_to_cpu(de->nameoff); + if (nameoff < sizeof(struct erofs_dirent) || + nameoff >= erofs_blksiz(sbi)) + LOG_ERRNO_RETURN(-EINVAL, -EINVAL, "[erofs] invalid nameoff"); + + de = liberofs_find_dirent(buf, name, len, nameoff, maxsize); + if (IS_ERR(de)) + return PTR_ERR(de); + + if (de) { + nd->nid = le64_to_cpu(de->nid); + return 0; + } + offset += maxsize; + } + return -ENOENT; +} + + +static int liberofs_step_into_link(struct liberofs_nameidata *nd, + struct erofs_inode *vi) +{ + char buf[PATH_MAX]; + int err; + + if (vi->i_size > PATH_MAX) + return -EINVAL; + memset(buf, 0, sizeof(buf)); + err = erofs_pread(vi, buf, vi->i_size, 0); + if (err) + return err; + return liberofs_link_path_walk(buf, nd); +} + +static int liberofs_link_path_walk(const char *name, + struct liberofs_nameidata *nd) +{ + struct erofs_inode vi; + erofs_nid_t nid; + const char *p; + int ret; + + if (*name == '/') + nd->nid = nd->sbi->root_nid; + + while (*name == '/') + name ++; + + while (*name != '\0') { + p = name; + do { + ++p; + } while (*p != '\0' && *p != '/'); + + nid = nd->nid; + ret = liberofs_namei(nd, name, p - name); + if (ret) + return ret; + vi.sbi = nd->sbi; + vi.nid = nd->nid; + ret = erofs_read_inode_from_disk(&vi); + if (ret) + return ret; + if (S_ISLNK(vi.i_mode)) { + nd->nid = nid; + ret = liberofs_step_into_link(nd, &vi); + if (ret) + return ret; + } + for (name = p; *name == '/'; ++name) + ; + } + return 0; +} + + +static int do_erofs_ilookup(const char *path, struct erofs_inode *vi) +{ + int ret; + struct liberofs_nameidata nd = {.sbi = vi->sbi}; + + nd.nid = vi->sbi->root_nid; + ret = liberofs_link_path_walk(path, &nd); + if (ret) + return ret; + vi->nid = nd.nid; + return erofs_read_inode_from_disk(vi); +} + +int ErofsFile::fstat(struct stat *buf) +{ + buf->st_mode = inode.i_mode; + buf->st_nlink = inode.i_nlink; + buf->st_size = inode.i_size; + buf->st_blocks = roundup(inode.i_size, erofs_blksiz(inode.sbi)) >> 9; + buf->st_uid = inode.i_uid; + buf->st_gid = inode.i_gid; + buf->st_ctime = inode.i_mtime; + buf->st_mtime = inode.i_mtime; + buf->st_atime = inode.i_mtime; + return 0; +} + +int ErofsFile::fiemap(struct photon::fs::fiemap *map) +{ + photon::fs::fiemap_extent *ext_buf = &map->fm_extents[0]; + struct erofs_map_blocks erofs_map; + int err; + + map->fm_mapped_extents = 0; + erofs_map.index = UINT_MAX; + erofs_map.m_la = 0; + + while (erofs_map.m_la < inode.i_size) { + err = erofs_map_blocks(&inode, &erofs_map, 0); + if (err) + LOG_ERROR_RETURN(err, err, "[erofs] Fail to map erofs blocks"); + ext_buf[map->fm_mapped_extents].fe_physical = erofs_map.m_pa; + ext_buf[map->fm_mapped_extents].fe_length = erofs_map.m_plen; + map->fm_mapped_extents += 1; + erofs_map.m_la += erofs_map.m_llen; + } + return 0; +} + +// ErofsFileSystem +EROFS_UNIMPLEMENTED_FUNC(photon::fs::IFile*, ErofsFileSystem, open(const char *pathname, int flags, mode_t mode), NULL) +EROFS_UNIMPLEMENTED_FUNC(photon::fs::IFile*, ErofsFileSystem, creat(const char *pathname, mode_t mode), NULL) +EROFS_UNIMPLEMENTED_FUNC(int, ErofsFileSystem, mkdir(const char *pathname, mode_t mode), -EROFS_UNIMPLEMENTED) +EROFS_UNIMPLEMENTED_FUNC(int, ErofsFileSystem, rmdir(const char *pathname), -EROFS_UNIMPLEMENTED) +EROFS_UNIMPLEMENTED_FUNC(int, ErofsFileSystem, symlink(const char *oldname, const char *newname), -EROFS_UNIMPLEMENTED) +EROFS_UNIMPLEMENTED_FUNC(ssize_t, ErofsFileSystem, readlink(const char *path, char *buf, size_t bufsiz), -EROFS_UNIMPLEMENTED) +EROFS_UNIMPLEMENTED_FUNC(int, ErofsFileSystem, link(const char *oldname, const char *newname), -EROFS_UNIMPLEMENTED) +EROFS_UNIMPLEMENTED_FUNC(int, ErofsFileSystem, rename(const char *oldname, const char *newname), -EROFS_UNIMPLEMENTED) +EROFS_UNIMPLEMENTED_FUNC(int, ErofsFileSystem, unlink(const char *filename), -EROFS_UNIMPLEMENTED) +EROFS_UNIMPLEMENTED_FUNC(int, ErofsFileSystem, chmod(const char *pathname, mode_t mode), -EROFS_UNIMPLEMENTED) +EROFS_UNIMPLEMENTED_FUNC(int, ErofsFileSystem, chown(const char *pathname, uid_t owner, gid_t group), -EROFS_UNIMPLEMENTED) +EROFS_UNIMPLEMENTED_FUNC(int, ErofsFileSystem, lchown(const char *pathname, uid_t owner, gid_t group), -EROFS_UNIMPLEMENTED) +EROFS_UNIMPLEMENTED_FUNC(int, ErofsFileSystem, statfs(const char *path, struct statfs *buf), -EROFS_UNIMPLEMENTED) +EROFS_UNIMPLEMENTED_FUNC(int, ErofsFileSystem, statvfs(const char *path, struct statvfs *buf), -EROFS_UNIMPLEMENTED) +EROFS_UNIMPLEMENTED_FUNC(int, ErofsFileSystem, lstat(const char *path, struct stat *buf), -EROFS_UNIMPLEMENTED) +EROFS_UNIMPLEMENTED_FUNC(int, ErofsFileSystem, access(const char *pathname, int mode), -EROFS_UNIMPLEMENTED) +EROFS_UNIMPLEMENTED_FUNC(int, ErofsFileSystem, truncate(const char *path, off_t length), -EROFS_UNIMPLEMENTED) +EROFS_UNIMPLEMENTED_FUNC(int, ErofsFileSystem, utime(const char *path, const struct utimbuf *file_times), -EROFS_UNIMPLEMENTED) +EROFS_UNIMPLEMENTED_FUNC(int, ErofsFileSystem, utimes(const char *path, const struct timeval times[2]), -EROFS_UNIMPLEMENTED) +EROFS_UNIMPLEMENTED_FUNC(int, ErofsFileSystem, lutimes(const char *path, const struct timeval times[2]), -EROFS_UNIMPLEMENTED) +EROFS_UNIMPLEMENTED_FUNC(int, ErofsFileSystem, mknod(const char *path, mode_t mode, dev_t dev), -EROFS_UNIMPLEMENTED) +EROFS_UNIMPLEMENTED_FUNC(int, ErofsFileSystem, syncfs(), -EROFS_UNIMPLEMENTED) + +ErofsFileSystem::ErofsFileSystem(photon::fs::IFile *imgfile, uint64_t blksize) +{ + target_file.ops.pread = erofs_target_pread; + target_file.ops.pwrite = erofs_target_pwrite; + target_file.ops.pread = erofs_target_pread; + target_file.ops.pwrite = erofs_target_pwrite; + target_file.ops.fsync = erofs_target_fsync; + target_file.ops.fallocate = erofs_target_fallocate; + target_file.ops.ftruncate = erofs_target_ftruncate; + target_file.ops.read = erofs_target_read; + target_file.ops.lseek = erofs_target_lseek; + target_file.file = imgfile; + target_file.cache = new ErofsCache(target_file.file, 128); + + memset(&sbi, 0, sizeof(struct erofs_sb_info)); + (void)erofs_init_sbi(&sbi, target_file.file, &target_file.ops, ilog2(blksize)); + if (erofs_read_superblock(&sbi)) + LOG_ERROR("[erofs] Fail to read_super_block"); +} + +ErofsFileSystem::~ErofsFileSystem() +{ + delete target_file.cache; +} + +int ErofsFileSystem::stat(const char *path, struct stat *buf) +{ + struct erofs_inode vi; + int err; + + vi.sbi = &sbi; + err = do_erofs_ilookup(path, &vi); + if (err) + LOG_ERRNO_RETURN(err, err, "[erofs] Fail to lookup inode"); + buf->st_mode = vi.i_mode; + buf->st_nlink = vi.i_nlink; + buf->st_size = vi.i_size; + buf->st_blocks = roundup(vi.i_size, erofs_blksiz(vi.sbi)) >> 9; + buf->st_uid = vi.i_uid; + buf->st_gid = vi.i_gid; + buf->st_ctime = vi.i_mtime; + buf->st_mtime = vi.i_mtime; + buf->st_atime = vi.i_mtime; + return 0; +} + +photon::fs::IFile* ErofsFileSystem::open(const char *pathname, int flags) +{ + ErofsFile *file = new ErofsFile(this); + int err; + + file->inode.sbi = &sbi; + err = do_erofs_ilookup(pathname, &file->inode); + if (err) { + delete file; + LOG_ERROR_RETURN(-err, nullptr, "[erofs] Fail to lookup inode by path"); + } + return file; +} + +struct liberofs_dir_context { + struct erofs_dir_context ctx; + std::vector<::dirent> *dirs; +}; + +static int liberofs_readdir(struct erofs_dir_context *ctx) +{ + struct liberofs_dir_context *libctx = reinterpret_cast(ctx); + std::vector<::dirent> *dirs = libctx->dirs; + struct dirent tmpdir; + + if (ctx->dot_dotdot) + return 0; + + tmpdir.d_ino = (ino_t) ctx->de_nid; + tmpdir.d_off = 0; + tmpdir.d_reclen = sizeof(struct erofs_dirent); + if (ctx->de_namelen > sizeof(tmpdir.d_name)) + LOG_ERROR_RETURN(-EINVAL, -EINVAL, "[erofs] Invalid name length"); + memset(tmpdir.d_name, 0, sizeof(tmpdir.d_name)); + memcpy(tmpdir.d_name, ctx->dname, ctx->de_namelen); + dirs->emplace_back(tmpdir); + return 0; +} + +static int do_erofs_readdir(struct erofs_sb_info *sbi, const char *path, std::vector<::dirent> *dirs) +{ + struct liberofs_dir_context ctx; + struct erofs_inode vi; + int err; + + vi.sbi = sbi; + err = do_erofs_ilookup(path, &vi); + if (err) + LOG_ERRNO_RETURN(err, err, "[erofs] Fail to lookup inode"); + ctx.ctx.dir = &vi; + ctx.ctx.cb = liberofs_readdir; + ctx.dirs = dirs; + + return erofs_iterate_dir(&ctx.ctx, false); +} + +photon::fs::DIR* ErofsFileSystem::opendir(const char *name) +{ + std::vector<::dirent> dirs; + + auto ret = do_erofs_readdir(&sbi, name, &dirs); + if (ret) { + errno = -ret; + return nullptr; + } + return new ErofsDir(dirs); +} + + +// ErofsDir +ErofsDir::ErofsDir(std::vector<::dirent> &dirs) : loc(0) { + m_dirs = std::move(dirs); + next(); +} + +ErofsDir::~ErofsDir() { + closedir(); +} + +int ErofsDir::closedir() { + if (!m_dirs.empty()) { + m_dirs.clear(); + } + return 0; +} + +dirent *ErofsDir::get() { + return direntp; +} + +int ErofsDir::next() { + if (!m_dirs.empty()) { + if (loc < (long) m_dirs.size()) { + direntp = &m_dirs[loc++]; + } else { + direntp = nullptr; + } + } + return direntp != nullptr ? 1 : 0; +} + +void ErofsDir::rewinddir() { + loc = 0; + next(); +} + +void ErofsDir::seekdir(long loc){ + this->loc = loc; + next(); +} + +long ErofsDir::telldir() { + return loc; +} + +bool erofs_check_fs(const photon::fs::IFile *imgfile) +{ + u8 data[EROFS_MAX_BLOCK_SIZE]; + struct erofs_super_block *dsb; + photon::fs::IFile *file = const_cast(imgfile); + int ret; + + ret = file->pread(data, EROFS_MAX_BLOCK_SIZE, 0); + if (ret != EROFS_MAX_BLOCK_SIZE) + LOG_ERROR_RETURN(-EIO, false, "[erofs] Fail to read superblock"); + dsb = reinterpret_cast(data + EROFS_SUPER_OFFSET); + return le32_to_cpu(dsb->magic) == EROFS_SUPER_MAGIC_V1; +} + +photon::fs::IFileSystem *erofs_create_fs(photon::fs::IFile *imgfile, uint64_t blksz) +{ + return new ErofsFileSystem(imgfile, blksz); +} diff --git a/src/overlaybd/tar/erofs/liberofs.h b/src/overlaybd/tar/erofs/liberofs.h index a509c637..4805ca6f 100644 --- a/src/overlaybd/tar/erofs/liberofs.h +++ b/src/overlaybd/tar/erofs/liberofs.h @@ -15,4 +15,8 @@ class LibErofs { uint64_t blksize; bool ddtaridx; }; + +bool erofs_check_fs(const photon::fs::IFile *imgfile); +photon::fs::IFileSystem *erofs_create_fs(photon::fs::IFile *imgfile, uint64_t blksz); + #endif diff --git a/src/prefetch.cpp b/src/prefetch.cpp index 9636ca8e..9fef880e 100644 --- a/src/prefetch.cpp +++ b/src/prefetch.cpp @@ -346,6 +346,7 @@ class DynamicPrefetcher : public PrefetcherImpl { const size_t MAX_FILE_SIZE = 65536; std::string m_prefetch_list = ""; + std::string fstype = "ext4"; vector files; DynamicPrefetcher(const std::string &prefetch_list, int concurrency) : @@ -464,8 +465,13 @@ class DynamicPrefetcher : public PrefetcherImpl { int generate_trace(const IFile *imagefile) { + photon::fs::IFileSystem *fs; + + if (fstype == "erofs") + fs = create_erofs_fs(const_cast(imagefile), 4096); + else + fs = create_ext4fs(const_cast(imagefile), false, true, "/"); - auto fs = create_ext4fs(const_cast(imagefile), false, true, "/"); if (fs == nullptr) { LOG_ERROR_RETURN(0, -1, "unrecognized filesystem in dynamic prefetcher"); } @@ -494,6 +500,11 @@ class DynamicPrefetcher : public PrefetcherImpl { virtual int replay(const IFile *imagefile) override { + if (is_erofs_fs(imagefile)) + fstype = "erofs"; + else + fstype = "ext4"; + LOG_DEBUG("get fstype `", fstype); auto th = photon::thread_create11(&DynamicPrefetcher::generate_trace, this, imagefile); m_reload_thread = photon::thread_enable_join(th); return PrefetcherImpl::replay(nullptr); diff --git a/src/test/trace_test.cpp b/src/test/trace_test.cpp index 0fdd14b1..4c1cf95a 100644 --- a/src/test/trace_test.cpp +++ b/src/test/trace_test.cpp @@ -12,6 +12,7 @@ #include #include "../overlaybd/gzip/gz.h" #include "../overlaybd/tar/libtar.h" +#include "../overlaybd/tar/erofs/liberofs.h" #include "../prefetch.cpp" #include "../tools/comm_func.h" @@ -30,6 +31,16 @@ const vector> fnlist = { {"overlaybd-1.0.12/src/main.cpp", "sha256:4653edf45471096d549b2a002d2b10dafb5beb939cff3f1dfc936fb4d75c070a"} }; +const string erofs_imgs[]={"https://github.com/salvete/erofs-imgs/raw/main/alpine.img"}; +const vector> erofs_fnlist = { + {"/bin/busybox", "sha256:42de297577993675efecf295acf0260e26128458048b3081451e1ac43f611b49"}, + {"/bin/sh", "sha256:42de297577993675efecf295acf0260e26128458048b3081451e1ac43f611b49"}, + {"/lib/ld-musl-x86_64.so.1", "sha256:60d0ed88672b260b8337bf1e5b721f9ca9c877f4d901886472b8195a38ff3630"}, + {"/lib/libc.musl-x86_64.so.1", "sha256:60d0ed88672b260b8337bf1e5b721f9ca9c877f4d901886472b8195a38ff3630"}, + {"/lib/libz.so.1.3.1", "sha256:5134dcc47a23d1bfa7cd0f8046343e9268d4d3f1827dce295713d3b10ada5e0a"}, + {"/lib/libz.so.1", "sha256:5134dcc47a23d1bfa7cd0f8046343e9268d4d3f1827dce295713d3b10ada5e0a"} +}; + class MockFile : public ForwardFile_Ownership{ public: char trash_data[1<<20]; @@ -56,6 +67,16 @@ int download(const std::string &url, const std::string &out) { } return 0; } + +int download_erofs_img(const std::string &url, const std::string &out) +{ + std::string cmd = "wget -O" + out + " " + url; + auto ret = system(cmd.c_str()); + if (ret != 0) + LOG_ERRNO_RETURN(0, -1, "download failed: `", url.c_str()); + return 0; +} + TEST(trace, case0) { return; @@ -132,6 +153,71 @@ TEST(trace, case0) { delete p; } +TEST(trace, case1) { + //return; + std::string workdir = "/tmp/trace_test/"; + mkdir(workdir.c_str(), 0755); + + std::string erofs_img_path = workdir + basename(erofs_imgs[0].c_str()); + ASSERT_EQ( + 0, download_erofs_img(erofs_imgs[0], erofs_img_path.c_str())); + + auto dst = open_file(erofs_img_path.c_str(), O_RDONLY, 0644); + dst = new MockFile(dst); + DEFER(delete dst); + + auto flist = open_file((workdir+"list").c_str(), O_CREAT|O_RDWR|O_TRUNC, 0644); + for (auto it : erofs_fnlist) { + auto fn = it.first + "\n"; + flist->write(fn.c_str(), fn.size()); + } + flist->write("/etc/\n", strlen("/etc/\n")); + delete flist; + + auto fs = create_erofs_fs(dst, 4096); + ASSERT_NE(fs, nullptr); + DEFER(delete fs); + + auto p = new_dynamic_prefetcher(workdir+"list", 8); + p->replay(dst); + + for (auto fn: erofs_fnlist) { + vector> lba; + auto file = fs->open(fn.first.c_str(), O_RDONLY); + auto fout = open_file((workdir+"check").c_str(), O_CREAT|O_RDWR|O_TRUNC, 0644); + LOG_INFO("file_name: `", fn.first.c_str()); + ASSERT_NE(file, nullptr); + DEFER(delete file); + struct stat buf; + file->fstat(&buf); + uint64_t size = buf.st_size; + + photon::fs::fiemap_t<8192> fie(0, size); + ASSERT_EQ(file->fiemap(&fie), 0); + LOG_INFO("check ` size: `, extents: `", fn.first, size, fie.fm_mapped_extents); + // uint64_t count = ((size+ T_BLOCKSIZE - 1) / T_BLOCKSIZE) * T_BLOCKSIZE; + for (uint32_t i = 0; i < fie.fm_mapped_extents; i++) { + LOG_INFO("get segment: ` `", fie.fm_extents[i].fe_physical, fie.fm_extents[i].fe_length); + lba.push_back(make_pair(fie.fm_extents[i].fe_physical, fie.fm_extents[i].fe_length < size ? fie.fm_extents[i].fe_length : size)); + size -= lba.back().second; + } + for (auto it : lba) { + char data[4<<20]; + LOG_INFO("write segment: ` `", it.first, it.second); + dst->pread(data, it.second, it.first); + fout->write(data, it.second); + } + fout->lseek(0, SEEK_SET); + auto sha256file = new_sha256_file(fout, true); + DEFER(delete sha256file); + LOG_INFO("verify sha256 of `", fn.first); + ASSERT_STREQ(sha256file->sha256_checksum().c_str(), fn.second.c_str()); + } + + delete p; +} + + int main(int argc, char **arg) { photon::init(); diff --git a/src/tools/comm_func.cpp b/src/tools/comm_func.cpp index fe50913e..1e6e6399 100644 --- a/src/tools/comm_func.cpp +++ b/src/tools/comm_func.cpp @@ -22,6 +22,7 @@ #include #include "../image_service.h" #include "../image_file.h" +#include "../overlaybd/tar/erofs/liberofs.h" using namespace std; @@ -78,3 +79,15 @@ photon::fs::IFileSystem *create_ext4fs(photon::fs::IFile *imgfile, bool mkfs, } return target; } + +bool is_erofs_fs(const photon::fs::IFile *imgfile) +{ + if (imgfile == nullptr) + LOG_ERRNO_RETURN(-EINVAL, false, "Imgfile is nullptr"); + return erofs_check_fs(imgfile); +} + +photon::fs::IFileSystem *create_erofs_fs(photon::fs::IFile *imgfile, uint64_t blksz) +{ + return erofs_create_fs(imgfile, blksz); +} diff --git a/src/tools/comm_func.h b/src/tools/comm_func.h index d013717c..80458ec9 100644 --- a/src/tools/comm_func.h +++ b/src/tools/comm_func.h @@ -43,3 +43,6 @@ int create_overlaybd(const std::string &srv_config, const std::string &dev_confi photon::fs::IFileSystem *create_ext4fs(photon::fs::IFile *imgfile, bool mkfs, bool enable_buffer, const char* root); + +bool is_erofs_fs(const photon::fs::IFile *imgfile); +photon::fs::IFileSystem *create_erofs_fs(photon::fs::IFile *imgfile, uint64_t blksz);