Loading src/client/gkfs_metadata.cpp +21 −2 Original line number Diff line number Diff line Loading @@ -202,8 +202,13 @@ test_lock_file(const std::string& path) { * @param flags * @return 0 on success, -1 on failure */ namespace { constexpr unsigned int max_symlink_depth = 40; int gkfs_open(const std::string& path, mode_t mode, int flags) { gkfs_open_impl(const std::string& path, mode_t mode, int flags, unsigned int symlink_depth) { LOG(DEBUG, "{}() called with path: \"{}\", mode: {}, flags: {}", __func__, path, mode, flags); Loading Loading @@ -292,7 +297,14 @@ gkfs_open(const std::string& path, mode_t mode, int flags) { errno = ELOOP; return -1; } return gkfs_open(md.target_path(), mode, flags); if(symlink_depth >= max_symlink_depth) { LOG(WARNING, "{}() symbolic link traversal exceeded {} links for '{}'", __func__, max_symlink_depth, path); errno = ELOOP; return -1; } return gkfs_open_impl(md.target_path(), mode, flags, symlink_depth + 1); } if(gkfs::config::metadata::rename_support) { if(md.blocks() == -1) { Loading Loading @@ -376,6 +388,13 @@ gkfs_open(const std::string& path, mode_t mode, int flags) { return fd; } } // namespace int gkfs_open(const std::string& path, mode_t mode, int flags) { return gkfs_open_impl(path, mode, flags, 0); } /** * Wrapper function for file/directory creation * errno may be set Loading tests/integration/directories/test_symlinks.py +17 −0 Original line number Diff line number Diff line import stat import os import errno import pytest Loading Loading @@ -44,3 +45,19 @@ def test_symlink_type(client_fixture, request, gkfs_daemon): assert found_link, "Symlink entry not found in readdir" assert found_target, "Target entry not found in readdir" @pytest.mark.parametrize("client_fixture", ["gkfs_client", "gkfs_clientLibc"]) def test_symlink_cycle_returns_eloop(client_fixture, request, gkfs_daemon): """Opening a cyclic symlink must fail with ELOOP, not recurse indefinitely.""" gkfs_client = request.getfixturevalue(client_fixture) mountdir = gkfs_daemon.mountdir link_a = mountdir / "cycle_a" link_b = mountdir / "cycle_b" assert gkfs_client.symlink(str(link_b), str(link_a)).retval == 0 assert gkfs_client.symlink(str(link_a), str(link_b)).retval == 0 ret = gkfs_client.open(link_a, os.O_RDONLY) assert ret.retval == -1 assert ret.errno == errno.ELOOP Loading
src/client/gkfs_metadata.cpp +21 −2 Original line number Diff line number Diff line Loading @@ -202,8 +202,13 @@ test_lock_file(const std::string& path) { * @param flags * @return 0 on success, -1 on failure */ namespace { constexpr unsigned int max_symlink_depth = 40; int gkfs_open(const std::string& path, mode_t mode, int flags) { gkfs_open_impl(const std::string& path, mode_t mode, int flags, unsigned int symlink_depth) { LOG(DEBUG, "{}() called with path: \"{}\", mode: {}, flags: {}", __func__, path, mode, flags); Loading Loading @@ -292,7 +297,14 @@ gkfs_open(const std::string& path, mode_t mode, int flags) { errno = ELOOP; return -1; } return gkfs_open(md.target_path(), mode, flags); if(symlink_depth >= max_symlink_depth) { LOG(WARNING, "{}() symbolic link traversal exceeded {} links for '{}'", __func__, max_symlink_depth, path); errno = ELOOP; return -1; } return gkfs_open_impl(md.target_path(), mode, flags, symlink_depth + 1); } if(gkfs::config::metadata::rename_support) { if(md.blocks() == -1) { Loading Loading @@ -376,6 +388,13 @@ gkfs_open(const std::string& path, mode_t mode, int flags) { return fd; } } // namespace int gkfs_open(const std::string& path, mode_t mode, int flags) { return gkfs_open_impl(path, mode, flags, 0); } /** * Wrapper function for file/directory creation * errno may be set Loading
tests/integration/directories/test_symlinks.py +17 −0 Original line number Diff line number Diff line import stat import os import errno import pytest Loading Loading @@ -44,3 +45,19 @@ def test_symlink_type(client_fixture, request, gkfs_daemon): assert found_link, "Symlink entry not found in readdir" assert found_target, "Target entry not found in readdir" @pytest.mark.parametrize("client_fixture", ["gkfs_client", "gkfs_clientLibc"]) def test_symlink_cycle_returns_eloop(client_fixture, request, gkfs_daemon): """Opening a cyclic symlink must fail with ELOOP, not recurse indefinitely.""" gkfs_client = request.getfixturevalue(client_fixture) mountdir = gkfs_daemon.mountdir link_a = mountdir / "cycle_a" link_b = mountdir / "cycle_b" assert gkfs_client.symlink(str(link_b), str(link_a)).retval == 0 assert gkfs_client.symlink(str(link_a), str(link_b)).retval == 0 ret = gkfs_client.open(link_a, os.O_RDONLY) assert ret.retval == -1 assert ret.errno == errno.ELOOP