mirror of
https://github.com/containers/fuse-overlayfs.git
synced 2025-08-03 18:05:58 -04:00
Refactor and fix rename code
- Create source whiteout only when needed - Fix missing source whiteout when destination is whiteout - Try the EXCHANGE trick also when NOREPLACE is set Signed-off-by: Tuupertunut <tuupertunut@outlook.com>
This commit is contained in:
parent
878cb0ccad
commit
6a0de4a5b0
71
main.c
71
main.c
@ -4538,11 +4538,15 @@ ovl_rename_direct (fuse_req_t req, fuse_ino_t parent, const char *name,
|
|||||||
if (node == NULL)
|
if (node == NULL)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
/* If NOREPLACE flag is given, check if we should throw an error now.
|
||||||
|
If not, just remove the flag as it might cause problems with replacing whiteouts later. */
|
||||||
if (flags & RENAME_NOREPLACE && destnode && !destnode->whiteout)
|
if (flags & RENAME_NOREPLACE && destnode && !destnode->whiteout)
|
||||||
{
|
{
|
||||||
errno = EEXIST;
|
errno = EEXIST;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
flags = flags & ~RENAME_NOREPLACE;
|
||||||
|
|
||||||
if (destnode)
|
if (destnode)
|
||||||
{
|
{
|
||||||
@ -4597,17 +4601,6 @@ ovl_rename_direct (fuse_req_t req, fuse_ino_t parent, const char *name,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the destnode is a whiteout, first attempt to EXCHANGE the source and the destination,
|
|
||||||
so that with one operation we get both the rename and the whiteout created. */
|
|
||||||
if (destnode_is_whiteout)
|
|
||||||
{
|
|
||||||
ret = direct_renameat2 (srcfd, name, destfd, newname, flags|RENAME_EXCHANGE);
|
|
||||||
if (ret == 0)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
/* If it fails for any reason, fallback to the more articulated method. */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the node is a directory we must ensure there is no whiteout at the
|
/* If the node is a directory we must ensure there is no whiteout at the
|
||||||
destination, otherwise the renameat2 will fail. Create a .wh.$NAME style
|
destination, otherwise the renameat2 will fail. Create a .wh.$NAME style
|
||||||
whiteout file until the renameat2 is completed. */
|
whiteout file until the renameat2 is completed. */
|
||||||
@ -4619,39 +4612,51 @@ ovl_rename_direct (fuse_req_t req, fuse_ino_t parent, const char *name,
|
|||||||
unlinkat (destfd, newname, 0);
|
unlinkat (destfd, newname, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Try to create the whiteout atomically, if it fails do the
|
bool src_needs_whiteout = (node->last_layer != get_upper_layer (lo));
|
||||||
rename+mknod separately. */
|
|
||||||
if (! can_mknod)
|
if (src_needs_whiteout)
|
||||||
{
|
{
|
||||||
ret = -1;
|
/* Trying to atomically both rename and create the whiteout.
|
||||||
errno = EPERM;
|
If destination is a whiteout, we can EXCHANGE source and destination and reuse the old whiteout.
|
||||||
|
If not, we can try to atomically create one with the WHITEOUT flag. */
|
||||||
|
if (destnode_is_whiteout)
|
||||||
|
ret = direct_renameat2 (srcfd, name, destfd, newname, flags|RENAME_EXCHANGE);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (! can_mknod)
|
||||||
|
{
|
||||||
|
ret = -1;
|
||||||
|
errno = EPERM;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret = direct_renameat2 (srcfd, name, destfd, newname, flags|RENAME_WHITEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If atomic whiteout creation failed, fall back to separate rename and whiteout creation. */
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
ret = direct_renameat2 (srcfd, name, destfd, newname, flags);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
ret = create_whiteout (lo, pnode, name, false, true);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ret = direct_renameat2 (srcfd, name, destfd,
|
ret = direct_renameat2 (srcfd, name, destfd, newname, flags);
|
||||||
newname, flags|RENAME_WHITEOUT);
|
|
||||||
}
|
|
||||||
/* If the destination is a whiteout, just overwrite it. */
|
|
||||||
if (ret < 0 && errno == EEXIST)
|
|
||||||
ret = direct_renameat2 (srcfd, name, destfd, newname, flags & ~RENAME_NOREPLACE);
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
|
||||||
ret = direct_renameat2 (srcfd, name, destfd,
|
|
||||||
newname, flags);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
ret = create_whiteout (lo, pnode, name, false, true);
|
|
||||||
if (ret < 0)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
pnode->loaded = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pnode->loaded = 0;
|
||||||
|
|
||||||
|
/* If the destination was .wh. style whiteout, it was not replaced automatically, so delete it. */
|
||||||
if (delete_whiteout (lo, destfd, NULL, newname) < 0)
|
if (delete_whiteout (lo, destfd, NULL, newname) < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
done:
|
|
||||||
hash_delete (pnode->children, node);
|
hash_delete (pnode->children, node);
|
||||||
|
|
||||||
free (node->name);
|
free (node->name);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user