Skip to content

Commit

Permalink
test: better test coverage, tests for symlink processing (#53)
Browse files Browse the repository at this point in the history
  • Loading branch information
maxirmx committed Sep 26, 2024
1 parent 4686255 commit 67f73d4
Show file tree
Hide file tree
Showing 11 changed files with 223 additions and 134 deletions.
6 changes: 5 additions & 1 deletion include/tebako-io-inner.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ int dwarfs_readlink(const std::string& path, std::string& link, std::string& lnk
int dwarfs_stat(const std::string& path, struct stat* buf, std::string& lnk, bool follow) noexcept;

int dwarfs_inode_access(uint32_t inode, int amode, uid_t uid, gid_t gid) noexcept;
int dwarfs_inode_relative_stat(uint32_t inode, const std::string& path, struct stat* buf, std::string& lnk, bool follow) noexcept;
int dwarfs_inode_relative_stat(uint32_t inode,
const std::string& path,
struct stat* buf,
std::string& lnk,
bool follow) noexcept;
ssize_t dwarfs_inode_read(uint32_t inode, void* buf, size_t size, off_t offset) noexcept;
int dwarfs_inode_readdir(uint32_t inode,
tebako::tebako_dirent* cache,
Expand Down
10 changes: 2 additions & 8 deletions include/tebako-mnt.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,12 @@ class sync_tebako_mount_table {
static sync_tebako_mount_table& get_tebako_mount_table(void);

bool check(const tebako_mount_point& mount_point);
bool check(const uint32_t ino, const std::string& mount_path)
{
return check(std::make_pair(ino, mount_path));
};
bool check(const uint32_t ino, const std::string& mount_path) { return check(std::make_pair(ino, mount_path)); };

void clear(void);

void erase(const tebako_mount_point& mount_point);
void erase(const uint32_t ino, const std::string& mount_path)
{
erase(std::make_pair(ino, mount_path));
};
void erase(const uint32_t ino, const std::string& mount_path) { erase(std::make_pair(ino, mount_path)); };

std::optional<std::string> get(const tebako_mount_point& mount_point);
std::optional<std::string> get(const uint32_t ino, const std::string& mount_path)
Expand Down
14 changes: 7 additions & 7 deletions src/file-io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ int tebako_openat(int nargs, int vfd, const char* path, int flags, ...)
std::filesystem::path std_path(path);
if (std_path.is_relative() && vfd != AT_FDCWD) {
ret = sync_tebako_fdtable::get_tebako_fdtable().openat(vfd, path, flags, r_path);
switch(ret) {
switch (ret) {
case DWARFS_INVALID_FD:
if (nargs == 3) {
ret = ::openat(vfd, path, flags);
Expand Down Expand Up @@ -199,23 +199,23 @@ int tebako_fstat(int vfd, struct STAT_TYPE* buf)
}

#ifdef TEBAKO_HAS_FSTATAT
int tebako_fstatat(int vfd, const char* path, struct stat* buf, int flag)
int tebako_fstatat(int vfd, const char* path, struct stat* st, int flag)
{
int ret = -1;
try {
std::filesystem::path std_path(path);
if (std_path.is_absolute() || vfd == AT_FDCWD) {
ret = (flag & AT_SYMLINK_NOFOLLOW) ? tebako_lstat(path, buf) : tebako_stat(path, buf);
ret = (flag & AT_SYMLINK_NOFOLLOW) ? tebako_lstat(path, st) : tebako_stat(path, st);
}
else {
std::string r_path;
ret = sync_tebako_fdtable::get_tebako_fdtable().fstatat(vfd, path, buf, r_path, (flag & AT_SYMLINK_NOFOLLOW) == 0);
switch(ret) {
ret = sync_tebako_fdtable::get_tebako_fdtable().fstatat(vfd, path, st, r_path, (flag & AT_SYMLINK_NOFOLLOW) == 0);
switch (ret) {
case DWARFS_INVALID_FD:
ret = is_valid_system_file_descriptor(vfd) ? ::fstatat(vfd, path, buf, flag) : DWARFS_IO_ERROR;
ret = is_valid_system_file_descriptor(vfd) ? ::fstatat(vfd, path, st, flag) : DWARFS_IO_ERROR;
break;
case DWARFS_S_LINK_OUTSIDE:
ret = TO_RB_W32_I128(fstatat)(vfd, r_path.c_str(), buf, flag);
ret = tebako_stat(r_path.c_str(), st);
break;
default:
break;
Expand Down
76 changes: 44 additions & 32 deletions src/tebako-dfs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ static int dwarfs_process_link(std::string& lnk, stdfs::path::iterator& p_iterat
lnk = new_path.generic_string();
int ret = new_path.is_relative() ? DWARFS_S_LINK_RELATIVE : DWARFS_S_LINK_ABSOLUTE;
if (ret == DWARFS_S_LINK_RELATIVE) {
p_path = new_path;
p_path = std::move(new_path);
p_iterator = p_path.begin();
}
return ret;
Expand All @@ -235,13 +235,17 @@ static int dwarfs_process_link(std::string& lnk, stdfs::path::iterator& p_iterat
// DWARFS_IO_ERROR - error [errno is set]
// DWARFS_LINK - symlink or mount point [lnk is set]

static int dwarfs_process_inode(filesystem_v2& fs, inode_view& pi, dwarfs::file_stat* st, bool follow,
std::string& lnk, stdfs::path::iterator& p_iterator, stdfs::path& p_path)
static int dwarfs_process_inode(filesystem_v2& fs,
inode_view& pi,
dwarfs::file_stat* st,
bool follow,
std::string& lnk,
stdfs::path::iterator& p_iterator,
stdfs::path& p_path)
{
int ret = DWARFS_IO_CONTINUE;
int err = fs.getattr(pi, st);
if (err == 0)
{
if (err == 0) {
// (1) It is symlink
// (2a) It is not the last element in the path
// (2b) or we should follow the last element (lstat called)
Expand All @@ -254,7 +258,7 @@ static int dwarfs_process_inode(filesystem_v2& fs, inode_view& pi, dwarfs::file_
}
// Failed to get inode attributes
// dwarfs returns (-errno)
if (err !=0 ) {
if (err != 0) {
TEBAKO_SET_LAST_ERROR(-err);
ret = DWARFS_IO_ERROR;
}
Expand All @@ -280,18 +284,21 @@ static int dwarfs_process_inode(filesystem_v2& fs, inode_view& pi, dwarfs::file_
// DWARFS_IO_ERROR - error [errno is set]
// DWARFS_LINK - symlink or mount point [lnk is set]

static int dwarfs_find_inode(uint32_t start_from, const stdfs::path& path, bool follow_last, std::string& lnk, struct stat* st) noexcept
static int dwarfs_find_inode(uint32_t start_from,
const stdfs::path& path,
bool follow_last,
std::string& lnk,
struct stat* st) noexcept
{
int ret = DWARFS_IO_CONTINUE;
dwarfs::file_stat dwarfs_st;
stdfs::path p_path{path}; // a copy of the path, mangled if symlink is found
stdfs::path p_path{path}; // a copy of the path, mangled if symlink is found

auto locked = usd.rlock();
auto p = *locked;

try {
if (p) {

LOG_PROXY(debug_logger_policy, p->lgr);
LOG_DEBUG << __func__ << " [ @inode:" << start_from << " path:" << path << " ]";

Expand All @@ -301,7 +308,7 @@ static int dwarfs_find_inode(uint32_t start_from, const stdfs::path& path, bool

if (pi) {
ret = dwarfs_process_inode(p->fs, *pi, &dwarfs_st, follow_last, lnk, p_iterator, p_path);
//(3) other stat calls (lstat, relative etc)
//(3) other stat calls (lstat, relative etc)

while (p_iterator != p_path.end() && p_iterator->string() != "" && ret == DWARFS_IO_CONTINUE) {
auto inode = pi->inode_num();
Expand Down Expand Up @@ -387,34 +394,36 @@ static int dwarfs_find_inode(uint32_t start_from, const stdfs::path& path, bool
// DWARFS_IO_ERROR - error [errno is set]
// DWARFS_LINK - symlink or mount point [lnk is set]

static int dwarfs_find_inode_abs(uint32_t start_from, const stdfs::path& path, bool follow, std::string& lnk, struct stat* st) noexcept
static int dwarfs_find_inode_abs(uint32_t start_from,
const stdfs::path& path,
bool follow,
std::string& lnk,
struct stat* st) noexcept
{
int ret = DWARFS_IO_ERROR;
try {
ret = dwarfs_find_inode(start_from, path, follow, lnk, st);
// Follow absolute links if necessary
// (indirect recursion)
if (ret == DWARFS_S_LINK_ABSOLUTE) {
if (is_tebako_path(lnk.c_str())) {
if (follow) {
// Follow absolute links if necessary
// (indirect recursion)
if (ret == DWARFS_S_LINK_ABSOLUTE) {
if (is_tebako_path(lnk.c_str())) {
if (follow) {
#ifdef RB_W32
struct STAT_TYPE _st;
ret = tebako_stat(lnk.c_str(), &_st);
st << _st;
struct STAT_TYPE _st;
ret = tebako_stat(lnk.c_str(), &_st);
st << _st;
#else
ret = tebako_stat(lnk.c_str(), st);
ret = tebako_stat(lnk.c_str(), st);
#endif
}
else
{
ret = DWARFS_IO_CONTINUE;
}
}
else
{
ret = DWARFS_S_LINK_OUTSIDE;
else {
ret = DWARFS_IO_CONTINUE;
}
}
else {
ret = DWARFS_S_LINK_OUTSIDE;
}
}
}
catch (dwarfs::system_error const& e) {
TEBAKO_SET_LAST_ERROR(e.get_errno());
Expand All @@ -425,7 +434,6 @@ static int dwarfs_find_inode_abs(uint32_t start_from, const stdfs::path& path, b
TEBAKO_SET_LAST_ERROR(ENOMEM);
}
return ret;

}

// dwarfs_find_inode_root
Expand All @@ -447,8 +455,8 @@ static int dwarfs_find_inode_root(const std::string& path, bool follow, std::str
// Normally we remove '/__tebako_memfs__/'
// However, there is also a case when it is memfs root and path isn just
// '/__tebako_memfs__'
auto adjusted_path = path.substr(path[TEBAKO_MOUNT_POINT_LENGTH] == '\0' ? TEBAKO_MOUNT_POINT_LENGTH
: TEBAKO_MOUNT_POINT_LENGTH + 1);
auto adjusted_path =
path.substr(path[TEBAKO_MOUNT_POINT_LENGTH] == '\0' ? TEBAKO_MOUNT_POINT_LENGTH : TEBAKO_MOUNT_POINT_LENGTH + 1);

return dwarfs_find_inode_abs(dwarfs_root, adjusted_path, follow, lnk, st);
}
Expand Down Expand Up @@ -663,7 +671,11 @@ int dwarfs_stat(const std::string& path, struct stat* st, std::string& lnk, bool
return dwarfs_find_inode_root(path, follow, lnk, st);
}

int dwarfs_inode_relative_stat(uint32_t inode, const std::string& path, struct stat* st, std::string& lnk, bool follow) noexcept
int dwarfs_inode_relative_stat(uint32_t inode,
const std::string& path,
struct stat* st,
std::string& lnk,
bool follow) noexcept
{
return dwarfs_find_inode_abs(inode, path, follow, lnk, st);
}
Expand Down
80 changes: 42 additions & 38 deletions src/tebako-fd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,50 +149,54 @@ int sync_tebako_fdtable::openat(int vfd, const char* path, int flags, std::strin
if (dwarfs_inode_access(stfd.st_ino, X_OK, getuid(), getgid()) == DWARFS_IO_CONTINUE) {
try {
auto fd = make_shared<tebako_fd>();
if (dwarfs_inode_relative_stat(stfd.st_ino, path, &fd->st, lnk, (flags & O_NOFOLLOW) == 0) ==
DWARFS_IO_CONTINUE) {
if (!S_ISDIR(fd->st.st_mode) && (flags & O_DIRECTORY)) {
// [ENOTDIR] ... or O_DIRECTORY was specified and the path
// argument resolves to a non - directory file.
TEBAKO_SET_LAST_ERROR(ENOTDIR);
}
else if (S_ISLNK(fd->st.st_mode) && (flags & O_NOFOLLOW)) {
// [O_NOFOLLOW] If the trailing component (i.e., basename) of
// pathname is a symbolic link, then the open fails, with
// the error ELOOP.
TEBAKO_SET_LAST_ERROR(ELOOP);
}
else {
fd->handle = new int;
if (fd->handle == NULL) {
TEBAKO_SET_LAST_ERROR(ENOMEM);
switch (dwarfs_inode_relative_stat(stfd.st_ino, path, &fd->st, lnk, (flags & O_NOFOLLOW) == 0)) {
case DWARFS_IO_CONTINUE:
if (!S_ISDIR(fd->st.st_mode) && (flags & O_DIRECTORY)) {
// [ENOTDIR] ... or O_DIRECTORY was specified and the path
// argument resolves to a non - directory file.
TEBAKO_SET_LAST_ERROR(ENOTDIR);
}
else if (S_ISLNK(fd->st.st_mode) && (flags & O_NOFOLLOW)) {
// [O_NOFOLLOW] If the trailing component (i.e., basename) of
// pathname is a symbolic link, then the open fails, with
// the error ELOOP.
TEBAKO_SET_LAST_ERROR(ELOOP);
}
else {
// get a dummy fd from the system
ret = ::dup(0);
if (ret == DWARFS_IO_ERROR) {
// [EMFILE] All file descriptors available to the process
// are currently open.
TEBAKO_SET_LAST_ERROR(EMFILE);
fd->handle = new int;
if (fd->handle == NULL) {
TEBAKO_SET_LAST_ERROR(ENOMEM);
}
else {
// construct a handle (mainly) for win32
*fd->handle = ret;
(*s_tebako_fdtable.wlock())[ret] = std::move(fd);
// get a dummy fd from the system
ret = ::dup(0);
if (ret == DWARFS_IO_ERROR) {
// [EMFILE] All file descriptors available to the process
// are currently open.
TEBAKO_SET_LAST_ERROR(EMFILE);
}
else {
// construct a handle (mainly) for win32
*fd->handle = ret;
(*s_tebako_fdtable.wlock())[ret] = std::move(fd);
}
}
}
}
}
else {
// [ENOENT] O_CREAT is not set and a component of path does
// not name an existing file, or O_CREAT is set and a component of
// the path prefix of path does not name an existing file, or path
// points to an empty string. [EROFS] The named file resides
// on a read - only file system and either O_WRONLY, O_RDWR,
// O_CREAT(if the file does not exist), or O_TRUNC is set in the
// oflag argument.
if (flags & O_CREAT)
TEBAKO_SET_LAST_ERROR(EROFS);
break;
case DWARFS_S_LINK_OUTSIDE:
ret = DWARFS_S_LINK_OUTSIDE;
break;
default:
// [ENOENT] O_CREAT is not set and a component of path does
// not name an existing file, or O_CREAT is set and a component of
// the path prefix of path does not name an existing file, or path
// points to an empty string.
// [EROFS] The named file resides
// on a read - only file system and either O_WRONLY, O_RDWR,
// O_CREAT(if the file does not exist), or O_TRUNC is set in the
// oflag argument.
TEBAKO_SET_LAST_ERROR((flags & O_CREAT) ? EROFS : ENOENT);
break;
}
}
catch (bad_alloc&) {
Expand Down
1 change: 0 additions & 1 deletion tests/tests-dir-io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ TEST_F(DirIOTests, tebako_dirfd_invalid_dirp)
}
#endif


#if defined(TEBAKO_HAS_OPENDIR) || defined(RB_W32)
TEST_F(DirIOTests, tebako_opendir_seekdir_telldir_readdir_closedir)
{
Expand Down
3 changes: 2 additions & 1 deletion tests/tests-file-ctl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ TEST_F(FileCtlTests, tebako_access_absolute_path_no_file)
EXPECT_EQ(-1, ret);
}


TEST_F(FileCtlTests, tebako_access_null)
{
int ret = tebako_access(NULL, W_OK);
Expand Down Expand Up @@ -271,6 +270,7 @@ TEST_F(FileCtlTests, tebako_open_fstat_close_relative_path_pass_through)
}

#ifdef TEBAKO_HAS_FSTATAT
#ifdef O_DIRECTORY
TEST_F(FileCtlTests, tebako_fstatat_relative_path)
{
int fd = tebako_open(2, TEBAKIZE_PATH("directory-1"), O_RDONLY | O_DIRECTORY);
Expand Down Expand Up @@ -323,6 +323,7 @@ TEST_F(FileCtlTests, tebako_fstatat_absolute_path_pass_through)
EXPECT_EQ(0, ret);
}
}
#endif // ifdef O_DIRECTORY

TEST_F(FileCtlTests, tebako_fstatat_at_fdcwd)
{
Expand Down
Loading

0 comments on commit 67f73d4

Please sign in to comment.