Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions src/fs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1222,13 +1222,31 @@ static int rename_with(kal_dir from, const char* a, kal_uintptr alen,
if (!okw::acceptable(a, alen) || !okw::acceptable(b, blen)) return kal_err_invalid;
if (refused_write(source_root) || refused_write(target_root)) return kal_err_permission;

// FileRenameInformation takes a directory handle and a simple leaf name.
// A name beneath the supplied root may contain several components; open
// its parent through that root before asking the object manager to rename.
struct target_parent {
kal_dir handle{};
~target_parent() { if (handle.h) kal_fs_close_dir(handle); }
} parent;
void* target_directory = target_root;
kal_uintptr leaf = 0;
for (kal_uintptr i = 0; i < blen; ++i)
if (b[i] == '/' || b[i] == '\\') leaf = i + 1;
if (leaf != 0) {
const int opened = kal_fs_open_dir(to, b, leaf - 1, &parent.handle);
if (opened != kal_ok) return opened;
target_directory = dir_handle(parent.handle);
if (refused_write(target_directory)) return kal_err_permission;
}

void* h = nullptr;
// The name is renamed, a link included, never what it leads to.
const long r = open_relative(source_root, a, alen, DELETE | SYNCHRONIZE,
okw::file_open, okw::file_open_reparse_point | okw::file_open_for_backup_intent, &h);
if (!okw::ok(r)) return okw::translate_nt(r);

okw::wide_name w(b, blen);
okw::wide_name w(b + leaf, blen - leaf);
if (!w.ok) { okw::NtClose(h); return kal_err_invalid; }

// The renaming record carries the name inline. It is obtained rather than
Expand All @@ -1243,7 +1261,7 @@ static int rename_with(kal_dir from, const char* a, kal_uintptr alen,
// field clear: the object manager refuses with a name collision and changes
// nothing, on every format that renames at all.
info->replace_if_exists = replace ? 1 : 0;
info->root_directory = target_root;
info->root_directory = target_directory;
info->file_name_length = w.string.length;
for (unsigned i = 0; i < w.string.length / 2; ++i) info->file_name[i] = w.buffer[i];

Expand Down
64 changes: 64 additions & 0 deletions tests/rename_beneath_preopen.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// A rename into a nested name beneath a supplied directory uses the target's
// parent handle and its leaf. A compiler status file exercises this shape.
#include <cstdio>
#include <openkal/fs.h>

namespace {
int failures = 0;
void check(bool ok, const char* what) {
std::printf("%s: %s\n", ok ? "ok" : "FAIL", what);
if (!ok) ++failures;
}
}

int main() {
kal_dir root{};
char name[512]{};
kal_uintptr length = 0;
check(kal_fs_preopen(0, &root, name, sizeof name, &length) == kal_ok,
"the working directory is supplied");
if (!root.h) return 1;

constexpr char directory[] = "okw-rename-nested";
constexpr char child[] = "okw-rename-nested/child";
constexpr char source[] = "okw-rename-nested/child/source";
constexpr char target[] = "okw-rename-nested/child/target";
kal_fs_remove(root, source, sizeof source - 1);
kal_fs_remove(root, target, sizeof target - 1);
kal_fs_remove(root, child, sizeof child - 1);
kal_fs_remove(root, directory, sizeof directory - 1);
check(kal_fs_mkdir(root, directory, sizeof directory - 1) == kal_ok,
"the outer directory is made");
check(kal_fs_mkdir(root, child, sizeof child - 1) == kal_ok,
"the nested directory is made");
kal_file file{};
const int created = kal_fs_open(root, source, sizeof source - 1,
KAL_OPEN_WRITE | KAL_OPEN_CREATE, &file);
check(created == kal_ok, "the source is created under the supplied root");
if (file.h) kal_fs_close_file(file);
if (created == kal_ok) {
check(kal_fs_rename_flags(root, source, sizeof source - 1,
root, target, sizeof target - 1,
KAL_RENAME_NO_REPLACE) == kal_ok,
"a nested relative destination is renamed atomically");
kal_file second{};
const int made = kal_fs_open(root, source, sizeof source - 1,
KAL_OPEN_WRITE | KAL_OPEN_CREATE, &second);
check(made == kal_ok, "a second source is created");
if (second.h) kal_fs_close_file(second);
if (made == kal_ok) {
check(kal_fs_rename_flags(root, source, sizeof source - 1,
root, target, sizeof target - 1,
KAL_RENAME_NO_REPLACE) == kal_err_exists,
"no-replace keeps the existing nested destination");
check(kal_fs_rename(root, source, sizeof source - 1,
root, target, sizeof target - 1) == kal_ok,
"replace updates the nested destination");
}
}
kal_fs_remove(root, source, sizeof source - 1);
kal_fs_remove(root, target, sizeof target - 1);
kal_fs_remove(root, child, sizeof child - 1);
kal_fs_remove(root, directory, sizeof directory - 1);
return failures ? 1 : 0;
}
Loading