From 6f8c2123786f2f94a1962089629aa321408f75ea Mon Sep 17 00:00:00 2001 From: Nathan Patrizi Date: Tue, 30 Dec 2025 22:14:24 +0000 Subject: [PATCH] Kernel: Fix write overlay handling of existing directories * Write overlay will check if a directory being created already exists. This fixes an issue where creating a directory that already exists and is empty would remove and recreate it instead of returning a directory exists error. * Check if file exists when creating with O_EXCL and VFS did not catch it. * Add check to Open() to check and fail correctly if we are opening a non-directory with O_DIRECTORY. Fixes error when trying to copy/move a file over another (with cp/mv). * Correctly return file exists when creating a symlink that already exists. Fixes #19640 Change-Id: I82f65d06db0f2ba3cd8facbf7114af60d6987a95 Reviewed-on: https://review.haiku-os.org/c/haiku/+/10172 Reviewed-by: waddlesplash --- .../layers/write_overlay/write_overlay.cpp | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/add-ons/kernel/file_systems/layers/write_overlay/write_overlay.cpp b/src/add-ons/kernel/file_systems/layers/write_overlay/write_overlay.cpp index 03985e6c5d..cf861d491b 100644 --- a/src/add-ons/kernel/file_systems/layers/write_overlay/write_overlay.cpp +++ b/src/add-ons/kernel/file_systems/layers/write_overlay/write_overlay.cpp @@ -199,7 +199,7 @@ private: status_t _PopulateAttributeDirents(); status_t _CreateCommon(const char *name, int type, int perms, ino_t *newInodeNumber, OverlayInode **node, - bool attribute, type_code attributeType); + bool attribute, type_code attributeType, bool exclusive = false); recursive_lock fLock; OverlayVolume * fVolume; @@ -558,7 +558,7 @@ OverlayInode::Create(const char *name, int openMode, int perms, void **cookie, { OverlayInode *newNode = NULL; status_t result = _CreateCommon(name, attribute ? S_ATTR : S_IFREG, perms, - newInodeNumber, &newNode, attribute, attributeType); + newInodeNumber, &newNode, attribute, attributeType, (openMode & O_EXCL) != 0); if (result != B_OK) return result; @@ -573,6 +573,9 @@ OverlayInode::Open(int openMode, void **_cookie) if (!fHasStat) _PopulateStat(); + if ((openMode & O_DIRECTORY) != 0 && !S_ISDIR(fStat.st_mode)) + return B_NOT_A_DIRECTORY; + open_cookie *cookie = (open_cookie *)malloc(sizeof(open_cookie)); if (cookie == NULL) return B_NO_MEMORY; @@ -1438,9 +1441,11 @@ close_attr_dir: status_t OverlayInode::_CreateCommon(const char *name, int type, int perms, ino_t *newInodeNumber, OverlayInode **_node, bool attribute, - type_code attributeType) + type_code attributeType, bool exclusive) { RecursiveLocker locker(fLock); + status_t result; + if (!fHasStat) _PopulateStat(); @@ -1449,6 +1454,16 @@ OverlayInode::_CreateCommon(const char *name, int type, int perms, locker.Unlock(); + if (!attribute && (type == S_IFDIR || type == S_IFLNK || exclusive)) { + ino_t lookupInodeNumber; + result = Lookup(name, &lookupInodeNumber); + + if (result != B_ENTRY_NOT_FOUND) { + put_vnode(Volume(), lookupInodeNumber); + return B_FILE_EXISTS; + } + } + overlay_dirent *entry = (overlay_dirent *)malloc(sizeof(overlay_dirent)); if (entry == NULL) return B_NO_MEMORY; @@ -1474,7 +1489,7 @@ OverlayInode::_CreateCommon(const char *name, int type, int perms, return B_NO_MEMORY; } - status_t result = AddEntry(entry, attribute); + result = AddEntry(entry, attribute); if (result != B_OK) { free(entry->name); free(entry);