mirror of
https://github.com/containers/fuse-overlayfs.git
synced 2025-09-10 07:44:54 -04:00
fuse-overlayfs: rename uses a .wh. style whiteout
during a rename, use a .wh. style whiteout for the destination, so that if the rename fails we don't make visible files from the lower layers. Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
This commit is contained in:
parent
e908a68156
commit
cc18f5ce18
173
main.c
173
main.c
@ -294,13 +294,13 @@ has_prefix (const char *str, const char *pref)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
create_whiteout (struct ovl_data *lo, struct ovl_node *parent, const char *name)
|
create_whiteout (struct ovl_data *lo, struct ovl_node *parent, const char *name, bool skip_mknod)
|
||||||
{
|
{
|
||||||
char whiteout_path[PATH_MAX + 10];
|
char whiteout_path[PATH_MAX + 10];
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
static bool can_mknod = true;
|
static bool can_mknod = true;
|
||||||
|
|
||||||
if (can_mknod)
|
if (!skip_mknod && can_mknod)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -338,23 +338,25 @@ delete_whiteout (struct ovl_data *lo, int dirfd, struct ovl_node *parent, const
|
|||||||
&& major (st.st_rdev) == 0
|
&& major (st.st_rdev) == 0
|
||||||
&& minor (st.st_rdev) == 0)
|
&& minor (st.st_rdev) == 0)
|
||||||
{
|
{
|
||||||
return unlinkat (dirfd, name, 0);
|
if (unlinkat (dirfd, name, 0) < 0)
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sprintf (whiteout_path, "%s/.wh.%s", parent->path, name);
|
sprintf (whiteout_path, "%s/%s", parent->path, name);
|
||||||
|
|
||||||
if (TEMP_FAILURE_RETRY (fstatat (get_upper_layer (lo)->fd, whiteout_path, &st, AT_SYMLINK_NOFOLLOW)) == 0
|
if (TEMP_FAILURE_RETRY (fstatat (get_upper_layer (lo)->fd, whiteout_path, &st, AT_SYMLINK_NOFOLLOW)) == 0
|
||||||
&& (st.st_mode & S_IFMT) == S_IFCHR
|
&& (st.st_mode & S_IFMT) == S_IFCHR
|
||||||
&& major (st.st_rdev) == 0
|
&& major (st.st_rdev) == 0
|
||||||
&& minor (st.st_rdev) == 0)
|
&& minor (st.st_rdev) == 0)
|
||||||
{
|
{
|
||||||
return unlinkat (get_upper_layer (lo)->fd, whiteout_path, 0);
|
if (unlinkat (get_upper_layer (lo)->fd, whiteout_path, 0) < 0)
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the whiteout was not found, try the .wh. alternative used when running as non-root. */
|
/* Look for the .wh. alternative as well. */
|
||||||
|
|
||||||
if (dirfd >= 0)
|
if (dirfd >= 0)
|
||||||
{
|
{
|
||||||
@ -389,27 +391,9 @@ hide_node (struct ovl_data *lo, struct ovl_node *node, bool unlink_src)
|
|||||||
|
|
||||||
/* Might be leftover from a previous run. */
|
/* Might be leftover from a previous run. */
|
||||||
unlinkat (lo->workdir_fd, newpath, 0);
|
unlinkat (lo->workdir_fd, newpath, 0);
|
||||||
|
unlinkat (lo->workdir_fd, newpath, AT_REMOVEDIR);
|
||||||
|
|
||||||
if (! unlink_src)
|
if (unlink_src)
|
||||||
{
|
|
||||||
if (node_dirp (node))
|
|
||||||
{
|
|
||||||
if (renameat (node_dirfd (node), node->path, lo->workdir_fd, newpath) < 0)
|
|
||||||
{
|
|
||||||
free (newpath);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (linkat (node_dirfd (node), node->path, lo->workdir_fd, newpath, 0) < 0)
|
|
||||||
{
|
|
||||||
free (newpath);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
/* If the atomic rename+mknod failed, then fallback into doing it in two steps. */
|
/* If the atomic rename+mknod failed, then fallback into doing it in two steps. */
|
||||||
if (syscall (SYS_renameat2, node_dirfd (node), node->path, lo->workdir_fd,
|
if (syscall (SYS_renameat2, node_dirfd (node), node->path, lo->workdir_fd,
|
||||||
@ -422,11 +406,30 @@ hide_node (struct ovl_data *lo, struct ovl_node *node, bool unlink_src)
|
|||||||
}
|
}
|
||||||
if (node->parent)
|
if (node->parent)
|
||||||
{
|
{
|
||||||
if (create_whiteout (lo, node->parent, node->name) < 0)
|
if (create_whiteout (lo, node->parent, node->name, false) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (node_dirp (node))
|
||||||
|
{
|
||||||
|
if (mkdirat (lo->workdir_fd, newpath, 0700) < 0)
|
||||||
|
{
|
||||||
|
free (newpath);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (linkat (node_dirfd (node), node->path, lo->workdir_fd, newpath, 0) < 0)
|
||||||
|
{
|
||||||
|
free (newpath);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
node->hidden_dirfd = lo->workdir_fd;
|
node->hidden_dirfd = lo->workdir_fd;
|
||||||
free (node->path);
|
free (node->path);
|
||||||
node->path = newpath;
|
node->path = newpath;
|
||||||
@ -761,7 +764,7 @@ load_dir (struct ovl_data *lo, struct ovl_node *n, struct ovl_layer *layer, char
|
|||||||
DIR *dp;
|
DIR *dp;
|
||||||
struct dirent *dent;
|
struct dirent *dent;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
struct ovl_layer *it;
|
struct ovl_layer *it, *upper_layer = get_upper_layer (lo);
|
||||||
|
|
||||||
if (!n)
|
if (!n)
|
||||||
{
|
{
|
||||||
@ -804,9 +807,19 @@ load_dir (struct ovl_data *lo, struct ovl_node *n, struct ovl_layer *layer, char
|
|||||||
child = hash_lookup (n->children, &key);
|
child = hash_lookup (n->children, &key);
|
||||||
if (child)
|
if (child)
|
||||||
{
|
{
|
||||||
if (it->low)
|
if (child->whiteout && it == upper_layer)
|
||||||
child->present_lowerdir = 1;
|
{
|
||||||
continue;
|
hash_delete (n->children, child);
|
||||||
|
node_free (child);
|
||||||
|
child = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (it->low)
|
||||||
|
child->present_lowerdir = 1;
|
||||||
|
child->ino = st.st_ino;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sprintf (node_path, "%s/%s", n->path, dent->d_name);
|
sprintf (node_path, "%s/%s", n->path, dent->d_name);
|
||||||
@ -940,6 +953,7 @@ do_lookup_file (struct ovl_data *lo, fuse_ino_t parent, const char *name)
|
|||||||
char path[PATH_MAX];
|
char path[PATH_MAX];
|
||||||
struct ovl_layer *it;
|
struct ovl_layer *it;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
struct ovl_layer *upper_layer = get_upper_layer (lo);
|
||||||
|
|
||||||
for (it = lo->layers; it; it = it->next)
|
for (it = lo->layers; it; it = it->next)
|
||||||
{
|
{
|
||||||
@ -964,10 +978,19 @@ do_lookup_file (struct ovl_data *lo, fuse_ino_t parent, const char *name)
|
|||||||
/* If we already know the node, simply update the ino. */
|
/* If we already know the node, simply update the ino. */
|
||||||
if (node)
|
if (node)
|
||||||
{
|
{
|
||||||
node->ino = st.st_ino;
|
if (node->whiteout && it == upper_layer)
|
||||||
if (it->low)
|
{
|
||||||
node->present_lowerdir = 1;
|
hash_delete (pnode->children, node);
|
||||||
continue;
|
node_free (node);
|
||||||
|
node = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
node->ino = st.st_ino;
|
||||||
|
if (it->low)
|
||||||
|
node->present_lowerdir = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wh_name = get_whiteout_name (name, &st);
|
wh_name = get_whiteout_name (name, &st);
|
||||||
@ -1185,7 +1208,7 @@ create_missing_whiteouts (struct ovl_data *lo, struct ovl_node *node, const char
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (create_whiteout (lo, node, dent->d_name) < 0)
|
if (create_whiteout (lo, node, dent->d_name, false) < 0)
|
||||||
{
|
{
|
||||||
closedir (dp);
|
closedir (dp);
|
||||||
return -1;
|
return -1;
|
||||||
@ -1573,11 +1596,6 @@ copyup (struct ovl_data *lo, struct ovl_node *node)
|
|||||||
ret = create_node_directory (lo, node->parent);
|
ret = create_node_directory (lo, node->parent);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
char whpath[PATH_MAX + 10];
|
|
||||||
sprintf (whpath, "%s/.wh.%s", node->parent->path, node->name);
|
|
||||||
if (unlinkat (get_upper_layer (lo)->fd, whpath, 0) < 0 && errno != ENOENT)
|
|
||||||
goto exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((st.st_mode & S_IFMT) == S_IFDIR)
|
if ((st.st_mode & S_IFMT) == S_IFDIR)
|
||||||
@ -1654,6 +1672,14 @@ copyup (struct ovl_data *lo, struct ovl_node *node)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto exit;
|
goto exit;
|
||||||
|
|
||||||
|
if (node->parent)
|
||||||
|
{
|
||||||
|
char whpath[PATH_MAX + 10];
|
||||||
|
sprintf (whpath, "%s/.wh.%s", node->parent->path, node->name);
|
||||||
|
if (unlinkat (get_upper_layer (lo)->fd, whpath, 0) < 0 && errno != ENOENT)
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
success:
|
success:
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
@ -2705,7 +2731,7 @@ ovl_rename_direct (fuse_req_t req, fuse_ino_t parent, const char *name,
|
|||||||
struct ovl_node key;
|
struct ovl_node key;
|
||||||
|
|
||||||
node = do_lookup_file (lo, parent, name);
|
node = do_lookup_file (lo, parent, name);
|
||||||
if (node == NULL)
|
if (node == NULL || node->whiteout)
|
||||||
{
|
{
|
||||||
fuse_reply_err (req, ENOENT);
|
fuse_reply_err (req, ENOENT);
|
||||||
return;
|
return;
|
||||||
@ -2762,41 +2788,31 @@ ovl_rename_direct (fuse_req_t req, fuse_ino_t parent, const char *name,
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (destnode != NULL && !destnode->whiteout && node_dirp (destnode))
|
|
||||||
{
|
|
||||||
size_t whiteouts = 0;
|
|
||||||
|
|
||||||
destnode = load_dir (lo, destnode, destnode->layer, destnode->path, destnode->name);
|
|
||||||
if (destnode == NULL)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
if (count_dir_entries (destnode, &whiteouts) > 0)
|
|
||||||
{
|
|
||||||
errno = ENOTEMPTY;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (whiteouts && empty_dir (lo, destnode) < 0)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
if (create_missing_whiteouts (lo, node, destnode->path) < 0)
|
|
||||||
{
|
|
||||||
fuse_reply_err (req, errno);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (destnode)
|
if (destnode)
|
||||||
{
|
{
|
||||||
|
size_t destnode_whiteouts = 0;
|
||||||
|
|
||||||
if (!destnode->whiteout && destnode->ino == node->ino)
|
if (!destnode->whiteout && destnode->ino == node->ino)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (node_dirp (node) && destnode->present_lowerdir)
|
if (!destnode->whiteout && node_dirp (destnode))
|
||||||
{
|
{
|
||||||
if (create_missing_whiteouts (lo, node, destnode->path) < 0)
|
destnode = load_dir (lo, destnode, destnode->layer, destnode->path, destnode->name);
|
||||||
|
if (destnode == NULL)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (count_dir_entries (destnode, &destnode_whiteouts) > 0)
|
||||||
|
{
|
||||||
|
errno = ENOTEMPTY;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (destnode_whiteouts && empty_dir (lo, destnode) < 0)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (node_dirp (node) && create_missing_whiteouts (lo, node, destnode->path) < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
if (destnode->lookups > 0)
|
if (destnode->lookups > 0)
|
||||||
node_free (destnode);
|
node_free (destnode);
|
||||||
else
|
else
|
||||||
@ -2822,7 +2838,16 @@ ovl_rename_direct (fuse_req_t req, fuse_ino_t parent, const char *name,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unlinkat (destfd, newname, 0);
|
/* 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
|
||||||
|
whiteout file until the renameat2 is completed. */
|
||||||
|
if (node_dirp (node))
|
||||||
|
{
|
||||||
|
ret = create_whiteout (lo, destpnode, newname, true);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error;
|
||||||
|
unlinkat (destfd, newname, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/* Try to create the whiteout atomically, if it fails do the
|
/* Try to create the whiteout atomically, if it fails do the
|
||||||
rename+mknod separately. */
|
rename+mknod separately. */
|
||||||
@ -2834,11 +2859,14 @@ ovl_rename_direct (fuse_req_t req, fuse_ino_t parent, const char *name,
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
ret = create_whiteout (lo, pnode, name);
|
ret = create_whiteout (lo, pnode, name, false);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (delete_whiteout (lo, destfd, NULL, newname) < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
hash_delete (pnode->children, node);
|
hash_delete (pnode->children, node);
|
||||||
|
|
||||||
free (node->name);
|
free (node->name);
|
||||||
@ -2855,9 +2883,6 @@ ovl_rename_direct (fuse_req_t req, fuse_ino_t parent, const char *name,
|
|||||||
if (update_paths (node) < 0)
|
if (update_paths (node) < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (delete_whiteout (lo, destfd, NULL, newname) < 0)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
@ -2964,7 +2989,7 @@ hide_all (struct ovl_data *lo, struct ovl_node *node)
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
it = nodes[i];
|
it = nodes[i];
|
||||||
ret = create_whiteout (lo, node, it->name);
|
ret = create_whiteout (lo, node, it->name, false);
|
||||||
node_free (it);
|
node_free (it);
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user