diff options
author | Andreas Kling <awesomekling@gmail.com> | 2020-01-03 03:53:06 +0100 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2020-01-03 03:57:10 +0100 |
commit | 3be1c7b5147066812488942f7b982a63fe40885f (patch) | |
tree | 56e5fba0aa0d600f4c33874316fe1fd3f5cd2f5c | |
parent | 8cc5fa55988b84a57bc2afabda8a5f79dbbdf06f (diff) | |
download | serenity-3be1c7b5147066812488942f7b982a63fe40885f.zip |
Kernel: Fix awkward bug where "touch /foo/bar/baz" could create "/baz"
To accomodate file creation, path resolution optionally returns the
last valid parent directory seen while traversing the path.
Clients will then interpret "ENOENT, but I have a parent for you" as
meaning that the file doesn't exist, but its immediate parent directory
does. The client then goes ahead and creates a new file.
In the case of "/foo/bar/baz" where there is no "/foo", it would fail
with ENOENT and "/" as the last seen parent directory, causing e.g the
open() syscall to create "/baz".
Covered by test_io.
-rw-r--r-- | Kernel/FileSystem/VirtualFileSystem.cpp | 13 |
1 files changed, 12 insertions, 1 deletions
diff --git a/Kernel/FileSystem/VirtualFileSystem.cpp b/Kernel/FileSystem/VirtualFileSystem.cpp index 64317dec64..5539032e69 100644 --- a/Kernel/FileSystem/VirtualFileSystem.cpp +++ b/Kernel/FileSystem/VirtualFileSystem.cpp @@ -692,8 +692,19 @@ KResultOr<NonnullRefPtr<Custody>> VFS::resolve_path(StringView path, Custody& ba auto& current_parent = custody_chain.last(); crumb_id = crumb_inode->lookup(part); - if (!crumb_id.is_valid()) + if (!crumb_id.is_valid()) { + if (i != parts.size() - 1) { + // We didn't find the filename we were looking for, + // and we didn't even reach the last path part. + // (ENOENT with non-null parent_custody) signals to caller that + // we found the immediate parent of the file, but the file itself + // does not exist yet. + // Since this is not the immediate parent, clear parent_custody. + if (parent_custody) + *parent_custody = nullptr; + } return KResult(-ENOENT); + } if (auto mount = find_mount_for_host(crumb_id)) crumb_id = mount->guest(); if (inode_was_root_at_head_of_loop && crumb_id.is_root_inode() && !is_vfs_root(crumb_id) && part == "..") { |