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 <[email protected]>
This commit is contained in:
Nathan Patrizi
2026-01-02 22:47:12 +00:00
committed by Adrien Destugues
parent 8323a6694e
commit 6f8c212378
@@ -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);