From d91e1d51c1aa7ed883e667d82ab83e5f394a9d51 Mon Sep 17 00:00:00 2001 From: Colin Date: Thu, 29 Aug 2024 11:17:35 +0000 Subject: [PATCH] bunpen: handle intermediary symlinks when binding --- pkgs/additional/bunpen/restrict/namespace.ha | 31 ++++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/pkgs/additional/bunpen/restrict/namespace.ha b/pkgs/additional/bunpen/restrict/namespace.ha index 371e9df3a..5c3cba9ff 100644 --- a/pkgs/additional/bunpen/restrict/namespace.ha +++ b/pkgs/additional/bunpen/restrict/namespace.ha @@ -105,7 +105,7 @@ fn isolate_paths(paths: []path::buffer) void = { // - [x] ancestors of `p` are all ordinary directories in the old fs: // corresponding directories will be created in the new fs. // mountpoints are treated as directories for this case. -// - [ ] ancestors of `p` are symlinks, such that `p != realpath(p)`. +// - [x] ancestors of `p` are symlinks, such that `p != realpath(p)`. // corresponding symlinks will be created in the new fs, as well as // exactly as many underlying directories necessary to bind `p`. // - [x] `p` itself is a symlink in the old fs, rather than a file/directory. @@ -130,7 +130,7 @@ fn bind_leaf(old_fs: *fs::fs, new_fs: *fs::fs, user_path: *path::buffer) void = log::printfln("[namespace] not binding external path {} (of {})", cur_strpath, path_str); return; }; - if (len(comp) >= 1 && strings::sub(comp, 0, 1) == "/") { + if (path::abs(comp)) { // dirfd doesn't do well will absolute paths. comp = strings::sub(comp, 1, strings::end); }; @@ -142,10 +142,13 @@ fn bind_leaf(old_fs: *fs::fs, new_fs: *fs::fs, user_path: *path::buffer) void = yield other; }; - match (bind_component(old_fs, new_fs, cur_strpath)) { + match (bind_component(old_fs, new_fs, cur_strpath, path::iterrem(&it))) { case let e: fs::error => log::printfln("[namespace] unable to copy intermediate path {} of {}: {}", cur_strpath, path_str, fs::strerror(e)); return; + case let e: path::error => + log::printfln("[namespace] unable to copy intermediate path {} of {}: {}", cur_strpath, path_str, path::strerror(e)); + return; case void => void; }; }; @@ -171,7 +174,7 @@ fn bind_leaf(old_fs: *fs::fs, new_fs: *fs::fs, user_path: *path::buffer) void = )); }; -fn bind_component(old_fs: *fs::fs, new_fs: *fs::fs, strpath: str) (void | fs::error) = { +fn bind_component(old_fs: *fs::fs, new_fs: *fs::fs, strpath: str, remaining: str) (void | fs::error | path::error) = { match (fs::stat(new_fs, strpath)) { case let e: fs::error => void; case let other: fs::filestat => return; // already created @@ -185,11 +188,27 @@ fn bind_component(old_fs: *fs::fs, new_fs: *fs::fs, strpath: str) (void | fs::er let linktext = fs::readlink(old_fs, strpath)?; log::printfln("[namespace/bind] ln new/{} -> {}", strpath, linktext); fs::symlink(new_fs, linktext, strpath)?; - // TODO: lots of edge cases with symlinks i need to flesh out here. + if (remaining != "") { + // bind the real path (or, the "more real" path, in case there are + // multiple layers of symlink). + let target_path: path::buffer = if (path::abs(linktext)) { + // foo/bar/baz/fnord with bar -> /target => `/target/baz/fnord` + yield path::init(linktext, remaining)?; + } else { + // foo/bar/baz/fnord with foo -> target => `foo/target/bar/baz` + yield path::init(strpath, "..", linktext, remaining)?; + }; + bind_leaf(old_fs, new_fs, &target_path); + }; } else if (fs::isdir(st.mode)) { log::printfln("[namespace/bind] mkdir new/{}", strpath); fs::mkdir(new_fs, strpath, st.mode)?; - } else { + } else { // file-like + if (remaining != "") { + log::printfln("[namespace/bind] ignoring file where a non-terminal was expected: {}", strpath); + return fs::wrongtype; + }; + // TODO: tune options (optional parameter; default is fs::flag::TRUNC) log::printfln("[namespace/bind] touch new/{}", strpath); fs::create(new_fs, strpath, st.mode)?;