diff options
author | Pavel Tikhomirov <ptikhomirov@virtuozzo.com> | 2020-02-13 13:03:12 +0300 |
---|---|---|
committer | Andrei Vagin <avagin@gmail.com> | 2022-04-29 03:53:52 +0300 |
commit | cfed6f35e140f7863c0cb1fe99b99ef8a4d8c2a0 (patch) | |
tree | cc7ee699003987c6e7aa8f69a3757e4809bdb95f /criu/files-reg.c | |
parent | 5a0943c900caf9ba195e14e18acaa707d72bed1a (diff) |
files-reg: fix error handling of rm_parent_dirs
If unlinkat fails it means that fs is in "corrupted" state - spoiled
with non-unlinked auxiliary directories.
While on it add fixme note as this function can be racy and BUG_ON if
path contains double slashes.
Cherry-picked from Virtuozzo criu:
https://src.openvz.org/projects/OVZ/repos/criu/commits/b7b4e69fd
Changes: simplify while loop condition, remove confusing FIXME, remove
excess !count check in favour of while loop condition check
Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
Diffstat (limited to 'criu/files-reg.c')
-rw-r--r-- | criu/files-reg.c | 45 |
1 files changed, 30 insertions, 15 deletions
diff --git a/criu/files-reg.c b/criu/files-reg.c index 4560f253e..0e126a32e 100644 --- a/criu/files-reg.c +++ b/criu/files-reg.c @@ -1792,30 +1792,42 @@ out: return ret; } -static void rm_parent_dirs(int mntns_root, char *path, int count) +static int rm_parent_dirs(int mntns_root, char *path, int count) { char *p, *prev = NULL; + int ret = -1; - if (!count) - return; - - while (count > 0) { - count -= 1; + while (count-- > 0) { p = strrchr(path, '/'); - if (p) + if (p) { + /* We don't handle "//" in path */ + BUG_ON(prev && (prev - p == 1)); *p = '\0'; + } else { + /* Inconsistent path and count */ + pr_perror("Can't strrchr \"/\" in \"%s\"/\"%s\"]" + " left count=%d\n", + path, prev ? prev + 1 : "", count + 1); + goto err; + } + if (prev) *prev = '/'; + prev = p; - if (unlinkat(mntns_root, path, AT_REMOVEDIR)) + if (unlinkat(mntns_root, path, AT_REMOVEDIR)) { pr_perror("Can't remove %s AT %d", path, mntns_root); - else - pr_debug("Unlinked parent dir: %s AT %d\n", path, mntns_root); - prev = p; + goto err; + } + pr_debug("Unlinked parent dir: %s AT %d\n", path, mntns_root); } + ret = 0; +err: if (prev) *prev = '/'; + + return ret; } /* Construct parent dir name and mkdir parent/grandparents if they're not exist */ @@ -1847,6 +1859,7 @@ static int make_parent_dirs_if_need(int mntns_root, char *path) err = mkdirat(mntns_root, path, 0777); if (err && errno != EEXIST) { pr_perror("Can't create dir: %s AT %d", path, mntns_root); + /* Failing anyway -> no retcode check */ rm_parent_dirs(mntns_root, path, count); count = -1; goto out; @@ -1933,10 +1946,11 @@ out_root: if (linkat_hard(mntns_root, rpath, mntns_root, path, rfi->remap->uid, rfi->remap->gid, 0) < 0) { int errno_saved = errno; - rm_parent_dirs(mntns_root, path, *level); - errno = errno_saved; - if (errno == EEXIST) + + if (!rm_parent_dirs(mntns_root, path, *level) && errno_saved == EEXIST) { + errno = errno_saved; return 1; + } return -1; } @@ -2124,7 +2138,8 @@ ext: pr_perror("Failed to unlink the remap file"); goto err; } - rm_parent_dirs(mntns_root, rfi->path, level); + if (rm_parent_dirs(mntns_root, rfi->path, level)) + goto err; } mutex_unlock(remap_open_lock); |