Skip to content
GitLab
Projects
Groups
Topics
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
hpc
gekkofs
Compare revisions
0916c55578dc76a119c3125424cb1d176f4943c3 to ec54bd3c03ccce3729e31cf7b57b9ea05e411e3d
Commits on Source (2)
refactor resolve path
· 0606fad7
Julius Athenstaedt
authored
Mar 11, 2024
0606fad7
add BUILD flags and register new resolve fn
· ec54bd3c
Julius Athenstaedt
authored
Mar 25, 2024
ec54bd3c
Hide whitespace changes
Inline
Side-by-side
CMake/gkfs-options.cmake
View file @
ec54bd3c
...
...
@@ -227,6 +227,14 @@ gkfs_define_option(
DEFAULT_VALUE OFF
)
# use old resolve function
gkfs_define_option
(
GKFS_USE_OLD_PATH_RESOLVE
HELP_TEXT
"Use the old implementation of the resolve function"
DEFAULT_VALUE OFF
)
cmake_dependent_option
(
GKFS_INSTALL_TESTS
"Install GekkoFS self tests"
OFF
"GKFS_BUILD_TESTS"
OFF
)
...
...
@@ -258,6 +266,14 @@ gkfs_define_option(
DESCRIPTION
"Compile with support for rename ops (experimental)"
)
## external link support
gkfs_define_option
(
GKFS_FOLLOW_EXTERNAL_SYMLINKS
HELP_TEXT
"Enable support for following external links for resolving the path"
DEFAULT_VALUE OFF
DESCRIPTION
"Compile with lstat usage in path resolve"
)
################################################################################
# Options and variables that control how GekkoFS behaves internally
...
...
CMakeLists.txt
View file @
ec54bd3c
...
...
@@ -259,6 +259,14 @@ if (GKFS_SYMLINK_SUPPORT)
add_definitions
(
-DHAS_SYMLINKS
)
endif
()
if
(
GKFS_USE_OLD_PATH_RESOLVE
)
add_definitions
(
-DGKFS_USE_OLD_PATH_RESOLVE
)
endif
()
if
(
GKFS_FOLLOW_EXTERNAL_SYMLINKS
)
add_definitions
(
-DGKFS_FOLLOW_EXTERNAL_SYMLINKS
)
endif
()
if
(
GKFS_RENAME_SUPPORT
)
# Rename depends on symlink support
add_definitions
(
-DHAS_SYMLINKS
)
...
...
include/client/path.hpp
View file @
ec54bd3c
...
...
@@ -36,7 +36,14 @@ unsigned int
match_components
(
const
std
::
string
&
path
,
unsigned
int
&
path_components
,
const
std
::
vector
<
std
::
string
>&
components
);
bool
/// @resolve_last_link is used only for the old implementation: GKFS_USE_OLD_PATH_RESOLVE
std
::
pair
<
bool
,
std
::
string
>
resolve
(
const
std
::
string
&
path
,
bool
resolve_last_link
=
true
);
std
::
pair
<
bool
,
std
::
string
>
resolve_new
(
const
std
::
string
&
path
);
[[
deprecated
(
"Use GKFS_USE_OLD_PATH_RESOLVE to use old implementation"
)]]
bool
resolve
(
const
std
::
string
&
path
,
std
::
string
&
resolved
,
bool
resolve_last_link
=
true
);
...
...
src/client/path.cpp
View file @
ec54bd3c
...
...
@@ -34,10 +34,11 @@
#include
<common/path_util.hpp>
#include
<stack>
#include
<utility>
#include
<vector>
#include
<string>
#include
<cassert>
#include
<climits>
extern
"C"
{
#include
<sys/stat.h>
...
...
@@ -95,6 +96,90 @@ match_components(const string& path, unsigned int& path_components,
return
matched
;
}
string
follow_symlinks
(
const
string
&
path
)
{
struct
stat
st
{};
if
(
lstat
(
path
.
c_str
(),
&
st
)
<
0
)
{
LOG
(
DEBUG
,
"path
\"
{}
\"
does not exist"
,
path
);
return
path
;
}
if
(
S_ISLNK
(
st
.
st_mode
))
{
auto
link_resolved
=
::
unique_ptr
<
char
[]
>
(
new
char
[
PATH_MAX
]);
if
(
realpath
(
path
.
c_str
(),
link_resolved
.
get
())
==
nullptr
)
{
LOG
(
ERROR
,
"Failed to get realpath for link
\"
{}
\"
. "
"Error: {}"
,
path
,
::
strerror
(
errno
));
return
path
;
}
// substituute resolved with new link path
return
link_resolved
.
get
();
}
return
path
;
}
pair
<
bool
,
string
>
resolve
(
const
string
&
path
,
bool
resolve_last_link
)
{
#ifdef GKFS_USE_OLD_PATH_RESOLVE
string
resolved
;
bool
is_in_path
=
resolve
(
path
,
resolved
,
resolve_last_link
);
return
make_pair
(
is_in_path
,
resolved
);
#else
return
resolve_new
(
path
);
#endif
}
pair
<
bool
,
string
>
resolve_new
(
const
string
&
path
)
{
const
string
&
mountdir
=
CTX
->
mountdir
();
LOG
(
DEBUG
,
"path:
\"
{}
\"
, mountdir:
\"
{}
\"
"
,
path
,
mountdir
);
string
resolved
=
""
;
stack
<
size_t
>
last_component_pos
;
for
(
size_t
start
=
0
;
start
<
path
.
size
();
start
++
)
{
size_t
end
=
path
.
find
(
path
::
separator
,
start
);
size_t
comp_size
=
end
-
start
;
if
(
comp_size
==
1
&&
path
.
at
(
start
)
==
path
::
separator
)
{
// should I use same while loop as in the original here?
continue
;
}
if
(
comp_size
==
1
&&
path
.
at
(
start
)
==
'.'
)
{
// component is '.', we skip it
continue
;
}
if
(
comp_size
==
2
&&
path
.
at
(
start
)
==
'.'
&&
path
.
at
(
start
+
1
)
==
'.'
)
{
// component is '..', we skip it
LOG
(
DEBUG
,
"path:
\"
{}
\"
, mountdir:
\"
{}
\"
"
,
path
,
mountdir
);
resolved
.
erase
(
last_component_pos
.
top
());
last_component_pos
.
pop
();
continue
;
}
// add `/<component>` to the reresolved path
resolved
.
push_back
(
path
::
separator
);
last_component_pos
.
push
(
resolved
.
size
()
-
1
);
resolved
.
append
(
path
,
start
,
comp_size
);
#ifdef GKFS_FOLLOW_EXTERNAL_SYMLINKS
resolved
=
follow_symlinks
(
resolved
);
#endif
}
if
(
resolved
.
substr
(
0
,
mountdir
.
size
())
==
mountdir
)
{
resolved
.
erase
(
1
,
CTX
->
mountdir
().
size
());
LOG
(
DEBUG
,
"internal:
\"
{}
\"
"
,
resolved
);
return
make_pair
(
true
,
resolved
);
}
if
(
resolved
.
empty
())
{
resolved
.
push_back
(
path
::
separator
);
}
LOG
(
DEBUG
,
"external:
\"
{}
\"
"
,
resolved
);
return
make_pair
(
false
,
resolved
);
}
/** Resolve path to its canonical representation
*
* Populate `resolved` with the canonical representation of `path`.
...
...
@@ -324,4 +409,4 @@ set_cwd(const string& path, bool internal) {
CTX
->
cwd
(
path
);
}
}
// namespace gkfs::path
\ No newline at end of file
}
// namespace gkfs::path
src/client/preload_context.cpp
View file @
ec54bd3c
...
...
@@ -41,6 +41,7 @@
#include
<hermes.hpp>
#include
<cassert>
#include
<utility>
extern
"C"
{
#include
<libsyscall_intercept_hook_point.h>
...
...
@@ -234,7 +235,9 @@ PreloadContext::relativize_fd_path(int dirfd, const char* raw_path,
path
=
raw_path
;
}
if
(
gkfs
::
path
::
resolve
(
path
,
relative_path
,
resolve_last_link
))
{
std
::
pair
<
bool
,
std
::
string
>
resolved_path
=
gkfs
::
path
::
resolve
(
path
,
resolve_last_link
);
relative_path
=
resolved_path
.
second
;
if
(
resolved_path
.
first
)
{
return
RelativizeStatus
::
internal
;
}
return
RelativizeStatus
::
external
;
...
...
@@ -264,7 +267,10 @@ PreloadContext::relativize_path(const char* raw_path,
path
=
raw_path
;
}
return
gkfs
::
path
::
resolve
(
path
,
relative_path
,
resolve_last_link
);
std
::
pair
<
bool
,
std
::
string
>
resolved_path
=
gkfs
::
path
::
resolve
(
path
,
resolve_last_link
);
relative_path
=
resolved_path
.
second
;
return
resolved_path
.
first
;
}
const
std
::
shared_ptr
<
gkfs
::
filemap
::
OpenFileMap
>&
...
...
tests/unit/CMakeLists.txt
View file @
ec54bd3c
...
...
@@ -49,7 +49,7 @@ endif ()
add_subdirectory
(
helpers
)
# create a convenience library with Catch2's main
# create a convenience library with Catch2's main
# to speed up test compilation
add_library
(
catch2_main STATIC
)
target_sources
(
catch2_main PRIVATE catch_main.cpp
)
...
...
@@ -63,6 +63,7 @@ add_executable(tests)
target_sources
(
tests
PRIVATE
${
CMAKE_CURRENT_LIST_DIR
}
/test_utils_arithmetic.cpp
${
CMAKE_CURRENT_LIST_DIR
}
/test_path.cpp
${
CMAKE_CURRENT_LIST_DIR
}
/test_helpers.cpp
)
if
(
GKFS_TESTS_GUIDED_DISTRIBUTION
)
...
...
@@ -76,6 +77,7 @@ target_link_libraries(tests
helpers
arithmetic
distributor
gkfs_intercept
)
# Catch2's contrib folder includes some helper functions
...
...
tests/unit/test_path.cpp
0 → 100644
View file @
ec54bd3c
/*
Copyright 2018-2024, Barcelona Supercomputing Center (BSC), Spain
Copyright 2015-2024, Johannes Gutenberg Universitaet Mainz, Germany
This software was partially supported by the
EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu).
This software was partially supported by the
ADA-FS project under the SPPEXA project funded by the DFG.
This file is part of GekkoFS.
GekkoFS is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
GekkoFS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GekkoFS. If not, see <https://www.gnu.org/licenses/>.
SPDX-License-Identifier: GPL-3.0-or-later
*/
#include
<catch2/catch.hpp>
#include
<client/path.hpp>
#include
<client/preload_context.hpp>
// mock CTX
class
text_context
{
std
::
string
mntdir
=
"/tmp/gkfs_mount"
;
const
std
::
string
&
mountdir
(){
return
mntdir
;
}
};
#define CTX text_context
SCENARIO
(
" resolve fn should handle empty path "
,
"[test_path][empty]"
)
{
GIVEN
(
" a mount path "
)
{
std
::
string
mntpath
=
"/tmp/gkfs_mount"
;
WHEN
(
" resolve with empty path "
)
{
THEN
(
" / should be returned "
)
{
REQUIRE
(
gkfs
::
path
::
resolve_new
(
""
).
second
==
"/"
);
}
}
}
}
// TODO check pair.first is true
SCENARIO
(
" resolve fn should handle internal paths "
,
"[test_path][external paths]"
)
{
GIVEN
(
" a mount path "
)
{
std
::
string
mntpath
=
"/tmp/gkfs_mount"
;
WHEN
(
" resolve with relative path "
)
{
THEN
(
" iwas "
)
{
REQUIRE
(
gkfs
::
path
::
resolve_new
(
"/home/foo/../../tmp/./gkfs_mount/bar/./"
).
second
==
"/bar/"
);
}
THEN
(
" iwas "
)
{
REQUIRE
(
gkfs
::
path
::
resolve_new
(
"/tmp/../tmp/gkfs_mount/bar/./"
).
second
==
"/bar/"
);
}
THEN
(
" iwas "
)
{
REQUIRE
(
gkfs
::
path
::
resolve_new
(
"/tmp/./gkfs_mount/bar/./"
).
second
==
"/bar/"
);
}
THEN
(
" iwas "
)
{
REQUIRE
(
gkfs
::
path
::
resolve_new
(
"/tmp/gkfs_mount/bar/./"
).
second
==
"/bar/"
);
}
THEN
(
" iwas "
)
{
REQUIRE
(
gkfs
::
path
::
resolve_new
(
"/home/foo/../.././tmp/gkfs_mount/"
).
second
==
"/"
);
}
}
}
}
// TODO check pair.first is false
SCENARIO
(
" resolve fn should handle external paths "
,
"[test_path][external paths]"
)
{
GIVEN
(
" a mount path "
)
{
std
::
string
mntpath
=
"/tmp/gkfs_mount"
;
WHEN
(
" resolve with relative path "
)
{
THEN
(
" iwas "
)
{
REQUIRE
(
gkfs
::
path
::
resolve_new
(
"/home/foo/../ar/."
).
second
==
"/home/bar"
);
}
THEN
(
" iwas "
)
{
REQUIRE
(
gkfs
::
path
::
resolve_new
(
"/home/foo/../bar/./"
).
second
==
"/home/bar/"
);
}
THEN
(
" iwas "
)
{
REQUIRE
(
gkfs
::
path
::
resolve_new
(
"/home/foo/../bar/../"
).
second
==
"/home/"
);
}
THEN
(
" iwas "
)
{
REQUIRE
(
gkfs
::
path
::
resolve_new
(
"/home/foo/../../"
).
second
==
"/"
);
}
THEN
(
" iwas "
)
{
REQUIRE
(
gkfs
::
path
::
resolve_new
(
"/home/foo/./bar/../"
).
second
==
"/home/foo/"
);
}
THEN
(
" iwas "
)
{
REQUIRE
(
gkfs
::
path
::
resolve_new
(
"/home/./../tmp/"
).
second
==
"/tmp/"
);
}
}
}
}