mirror of
https://github.com/containers/fuse-overlayfs.git
synced 2025-09-09 15:24:54 -04:00
fuse-overlayfs: allow copy of a dir over an existing in the lower layers
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
This commit is contained in:
parent
86f98b4c91
commit
b054b9a191
169
main.c
169
main.c
@ -252,6 +252,12 @@ get_upper_layer (struct ovl_data *lo)
|
|||||||
return lo->layers;
|
return lo->layers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct ovl_layer *
|
||||||
|
get_lower_layers (struct ovl_data *lo)
|
||||||
|
{
|
||||||
|
return lo->layers->next;
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
node_dirp (struct ovl_node *n)
|
node_dirp (struct ovl_node *n)
|
||||||
{
|
{
|
||||||
@ -382,10 +388,21 @@ hide_node (struct ovl_data *lo, struct ovl_node *node, bool unlink_src)
|
|||||||
|
|
||||||
if (! unlink_src)
|
if (! unlink_src)
|
||||||
{
|
{
|
||||||
if (linkat (node_dirfd (node), node->path, lo->workdir_fd, newpath, 0) < 0)
|
if (node_dirp (node))
|
||||||
{
|
{
|
||||||
free (newpath);
|
if (renameat (node_dirfd (node), node->path, lo->workdir_fd, newpath) < 0)
|
||||||
return -1;
|
{
|
||||||
|
free (newpath);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (linkat (node_dirfd (node), node->path, lo->workdir_fd, newpath, 0) < 0)
|
||||||
|
{
|
||||||
|
free (newpath);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -574,7 +591,7 @@ node_compare (const void *n1, const void *n2)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct ovl_node *
|
static struct ovl_node *
|
||||||
make_whiteout_node (const char *name)
|
make_whiteout_node (const char *path, const char *name)
|
||||||
{
|
{
|
||||||
struct ovl_node *ret = calloc (1, sizeof (*ret));
|
struct ovl_node *ret = calloc (1, sizeof (*ret));
|
||||||
if (ret == NULL)
|
if (ret == NULL)
|
||||||
@ -589,6 +606,14 @@ make_whiteout_node (const char *name)
|
|||||||
errno = ENOMEM;
|
errno = ENOMEM;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
ret->path = strdup (path);
|
||||||
|
if (ret->path == NULL)
|
||||||
|
{
|
||||||
|
free (ret->name);
|
||||||
|
free (ret);
|
||||||
|
errno = ENOMEM;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
ret->whiteout = 1;
|
ret->whiteout = 1;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -759,6 +784,7 @@ load_dir (struct ovl_data *lo, struct ovl_node *n, struct ovl_layer *layer, char
|
|||||||
struct ovl_node key;
|
struct ovl_node key;
|
||||||
const char *wh;
|
const char *wh;
|
||||||
struct ovl_node *child = NULL;
|
struct ovl_node *child = NULL;
|
||||||
|
char node_path[PATH_MAX + 1];
|
||||||
|
|
||||||
key.name = dent->d_name;
|
key.name = dent->d_name;
|
||||||
|
|
||||||
@ -779,10 +805,12 @@ load_dir (struct ovl_data *lo, struct ovl_node *n, struct ovl_layer *layer, char
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sprintf (node_path, "%s/%s", n->path, dent->d_name);
|
||||||
|
|
||||||
wh = get_whiteout_name (dent->d_name, &st);
|
wh = get_whiteout_name (dent->d_name, &st);
|
||||||
if (wh)
|
if (wh)
|
||||||
{
|
{
|
||||||
child = make_whiteout_node (wh);
|
child = make_whiteout_node (node_path, wh);
|
||||||
if (child == NULL)
|
if (child == NULL)
|
||||||
{
|
{
|
||||||
errno = ENOMEM;
|
errno = ENOMEM;
|
||||||
@ -793,9 +821,7 @@ load_dir (struct ovl_data *lo, struct ovl_node *n, struct ovl_layer *layer, char
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
bool dirp = st.st_mode & S_IFDIR;
|
bool dirp = st.st_mode & S_IFDIR;
|
||||||
char node_path[PATH_MAX + 1];
|
|
||||||
|
|
||||||
sprintf (node_path, "%s/%s", n->path, dent->d_name);
|
|
||||||
child = make_ovl_node (node_path, it, dent->d_name, 0, dirp);
|
child = make_ovl_node (node_path, it, dent->d_name, 0, dirp);
|
||||||
|
|
||||||
if (child == NULL)
|
if (child == NULL)
|
||||||
@ -942,7 +968,7 @@ do_lookup_file (struct ovl_data *lo, fuse_ino_t parent, const char *name)
|
|||||||
|
|
||||||
wh_name = get_whiteout_name (name, &st);
|
wh_name = get_whiteout_name (name, &st);
|
||||||
if (wh_name)
|
if (wh_name)
|
||||||
node = make_whiteout_node (wh_name);
|
node = make_whiteout_node (path, wh_name);
|
||||||
else
|
else
|
||||||
node = make_ovl_node (path, it, name, 0, st.st_mode & S_IFDIR);
|
node = make_ovl_node (path, it, name, 0, st.st_mode & S_IFDIR);
|
||||||
if (node == NULL)
|
if (node == NULL)
|
||||||
@ -1081,6 +1107,68 @@ out_errno:
|
|||||||
fuse_reply_err (req, errno);
|
fuse_reply_err (req, errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
create_missing_whiteouts (struct ovl_data *lo, struct ovl_node *node, struct ovl_node *from)
|
||||||
|
{
|
||||||
|
struct ovl_layer *l;
|
||||||
|
|
||||||
|
if (! node_dirp (node))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
node = load_dir (lo, node, node->layer, node->path, node->name);
|
||||||
|
if (node == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (l = get_lower_layers (lo); l; l = l->next)
|
||||||
|
{
|
||||||
|
DIR *dp;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = TEMP_FAILURE_RETRY (openat (l->fd, from->path, O_DIRECTORY));
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
if (errno == ENOENT)
|
||||||
|
continue;
|
||||||
|
if (errno == ENOTDIR)
|
||||||
|
break;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dp = fdopendir (fd);
|
||||||
|
if (dp)
|
||||||
|
{
|
||||||
|
struct dirent *dent;
|
||||||
|
|
||||||
|
while (dp && ((dent = readdir (dp)) != NULL))
|
||||||
|
{
|
||||||
|
struct ovl_node key;
|
||||||
|
struct ovl_node *n;
|
||||||
|
|
||||||
|
if (strcmp (dent->d_name, ".") == 0)
|
||||||
|
continue;
|
||||||
|
if (strcmp (dent->d_name, "..") == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
key.name = (char *) dent->d_name;
|
||||||
|
|
||||||
|
n = hash_lookup (node->children, &key);
|
||||||
|
if (n)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (create_whiteout (lo, node, dent->d_name) < 0)
|
||||||
|
{
|
||||||
|
closedir (dp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir (dp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ovl_do_readdir (fuse_req_t req, fuse_ino_t ino, size_t size,
|
ovl_do_readdir (fuse_req_t req, fuse_ino_t ino, size_t size,
|
||||||
off_t offset, struct fuse_file_info *fi, int plus)
|
off_t offset, struct fuse_file_info *fi, int plus)
|
||||||
@ -1574,15 +1662,22 @@ get_node_up (struct ovl_data *lo, struct ovl_node *node)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
count_dir_entries (struct ovl_node *node)
|
count_dir_entries (struct ovl_node *node, size_t *whiteouts)
|
||||||
{
|
{
|
||||||
size_t c = 0;
|
size_t c = 0;
|
||||||
struct ovl_node *it;
|
struct ovl_node *it;
|
||||||
|
|
||||||
|
if (whiteouts)
|
||||||
|
*whiteouts = 0;
|
||||||
|
|
||||||
for (it = hash_get_first (node->children); it; it = hash_get_next (node->children, it))
|
for (it = hash_get_first (node->children); it; it = hash_get_next (node->children, it))
|
||||||
{
|
{
|
||||||
if (it->whiteout)
|
if (it->whiteout)
|
||||||
continue;
|
{
|
||||||
|
if (whiteouts)
|
||||||
|
(*whiteouts)++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (strcmp (it->name, ".") == 0)
|
if (strcmp (it->name, ".") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp (it->name, "..") == 0)
|
if (strcmp (it->name, "..") == 0)
|
||||||
@ -1650,7 +1745,7 @@ do_rm (fuse_req_t req, fuse_ino_t parent, const char *name, bool dirp)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
c = count_dir_entries (node);
|
c = count_dir_entries (node, NULL);
|
||||||
if (c)
|
if (c)
|
||||||
{
|
{
|
||||||
fuse_reply_err (req, ENOTEMPTY);
|
fuse_reply_err (req, ENOTEMPTY);
|
||||||
@ -2492,24 +2587,44 @@ ovl_rename (fuse_req_t req, fuse_ino_t parent, const char *name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (destnode != NULL && !destnode->whiteout && node_dirp (destnode))
|
if (destnode != NULL && !destnode->whiteout && node_dirp (destnode))
|
||||||
{
|
{
|
||||||
errno = EISDIR;
|
size_t whiteouts = 0;
|
||||||
goto error;
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We support only the case where the destination is completely empty. */
|
||||||
|
if (whiteouts)
|
||||||
|
{
|
||||||
|
errno = EXDEV;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (create_missing_whiteouts (lo, node, destnode) < 0)
|
||||||
|
{
|
||||||
|
fuse_reply_err (req, errno);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rm = hash_lookup (destpnode->children, &key);
|
rm = hash_lookup (destpnode->children, &key);
|
||||||
if (rm)
|
if (rm)
|
||||||
{
|
{
|
||||||
|
if (!rm->whiteout && rm->ino == node->ino)
|
||||||
|
goto error;
|
||||||
if (node_dirp (node) && rm->present_lowerdir)
|
if (node_dirp (node) && rm->present_lowerdir)
|
||||||
{
|
{
|
||||||
fuse_reply_err (req, EXDEV);
|
if (create_missing_whiteouts (lo, node, rm) < 0)
|
||||||
return;
|
goto error;
|
||||||
}
|
|
||||||
if (!rm->whiteout && rm->ino == node->ino)
|
|
||||||
{
|
|
||||||
fuse_reply_err (req, 0);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hash_delete (destpnode->children, rm);
|
hash_delete (destpnode->children, rm);
|
||||||
@ -2590,7 +2705,8 @@ ovl_rename (fuse_req_t req, fuse_ino_t parent, const char *name,
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (create_whiteout (lo, pnode, name) < 0)
|
ret = create_whiteout (lo, pnode, name);
|
||||||
|
if (ret < 0)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2599,7 +2715,10 @@ ovl_rename (fuse_req_t req, fuse_ino_t parent, const char *name,
|
|||||||
free (node->name);
|
free (node->name);
|
||||||
node->name = strdup (newname);
|
node->name = strdup (newname);
|
||||||
if (node->name == NULL)
|
if (node->name == NULL)
|
||||||
goto error;
|
{
|
||||||
|
ret = -1;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
node = insert_node (destpnode, node, true);
|
node = insert_node (destpnode, node, true);
|
||||||
if (node == NULL)
|
if (node == NULL)
|
||||||
@ -2611,7 +2730,13 @@ ovl_rename (fuse_req_t req, fuse_ino_t parent, const char *name,
|
|||||||
if (delete_whiteout (lo, destfd, NULL, newname) < 0)
|
if (delete_whiteout (lo, destfd, NULL, newname) < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
ret = -1;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
saved_errno = errno;
|
saved_errno = errno;
|
||||||
if (srcfd >= 0)
|
if (srcfd >= 0)
|
||||||
close (srcfd);
|
close (srcfd);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user