2
0
mirror of https://github.com/checkpoint-restore/criu synced 2025-08-30 22:05:36 +00:00

util: add get_relative_path helper

This is a smart way of getting relative paths:

1) Always returns relative path, no unexpected starting '/';
2) Detects subpath even if path formats are different, only real directory
and file names matter;
3) No path modiffication/allocation, returns shifted pointer to the
orignal path.

We have many places where we need to cut subpath from path. Different code
blocks doing this job spread widely across the codebase for instance see:
cut_root_for_bind and root_path_from_parent. But those implementations rely on
the fact that subpath's and path's formats are the same.

When we modify or concatenate paths we can accidentally get strange
path formats, paths given by user can have strange format, and the job
to manually maintain all paths in "simple" format everywhere is too
hard. So let's just add a tool to compare "strange" paths.

E.g.:

get_relative_path("./a////.///./b//././c", "///./a/b") == "c"

Note: ".." in path is not supported, and we just can't support it right
without full filesystem tree information to resolve paths like
"../../a", so we just treat ".." as a directory name which should work
in simple cases.

Cherry-picked from Virtuozzo criu:
https://src.openvz.org/projects/OVZ/repos/criu/commits/73a771348

Changes: add other useful robust path comparison helpers is_sub_path and
is_same_path based on get_relative_path, fix clang-format.

Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
This commit is contained in:
Pavel Tikhomirov
2020-05-14 13:50:20 +03:00
committed by Andrei Vagin
parent 261b7a8fd4
commit 97bd9511ca
2 changed files with 76 additions and 0 deletions

View File

@@ -241,6 +241,10 @@ static inline bool issubpath(const char *path, const char *sub_path)
return strstartswith2(path, sub_path, &end) && (end == '/' || end == '\0');
}
extern char *get_relative_path(char *path, char *sub_path);
extern bool is_sub_path(char *path, char *sub_path);
extern bool is_same_path(char *path1, char *path2);
int strip_deleted(char *path, int len);
int cut_path_ending(char *path, char *sub_path);

View File

@@ -1815,3 +1815,75 @@ void util_init()
clock_gettime(CLOCK_MONOTONIC, &tp);
criu_run_id = ((uint64_t)getpid() << 32) + tp.tv_sec + tp.tv_nsec;
}
/*
* This function cuts sub_path from the path.
* 1) It asumes all relative paths given are relative to "/":
* /a/b/c is the same as a/b/c
* 2) It can handle paths with multiple consequent slashes:
* ///a///b///c is the same as /a/b/c
* 3) It always returns relative path, with no leading slash:
* get_relative_path("/a/b/c", "/") would be "a/b/c"
* get_relative_path("/a/b/c", "/a/b") would be "c"
* get_relative_path("/", "/") would be ""
* 4) It can handle paths with single dots:
* get_relative_path("./a/b", "a/") would be "b"
* 5) Note ".." in paths are not supported and handled as normal directory name
*/
char *get_relative_path(char *path, char *sub_path)
{
bool skip_slashes = true;
while (1) {
if ((*path == '/' || *path == '\0') && (*sub_path == '/' || *sub_path == '\0'))
skip_slashes = true;
if (skip_slashes) {
while (*path == '/' || (path[0] == '.' && (path[1] == '/' || path[1] == '\0')))
path++;
while (*sub_path == '/' || (sub_path[0] == '.' && (sub_path[1] == '/' || sub_path[1] == '\0')))
sub_path++;
}
if (*sub_path == '\0') {
if (skip_slashes)
return path;
return NULL;
}
skip_slashes = false;
if (*path == '\0')
return NULL;
if (*path != *sub_path)
return NULL;
path++;
sub_path++;
}
/* will never get here */
return NULL;
}
bool is_sub_path(char *path, char *sub_path)
{
char *rel_path;
rel_path = get_relative_path(path, sub_path);
if (!rel_path)
return false;
return true;
}
bool is_same_path(char *path1, char *path2)
{
char *rel_path;
rel_path = get_relative_path(path1, path2);
if (!rel_path || *rel_path != '\0')
return false;
return true;
}