Skip to content

Commit

Permalink
Merge pull request #214 from giuseppe/mkdir-fix
Browse files Browse the repository at this point in the history
mkdir: if the destination exists atomically swap them
  • Loading branch information
rhatdan authored Jun 17, 2020
2 parents f3e4154 + d68235f commit 8b4062d
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 67 deletions.
9 changes: 9 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
* fuse-overlayfs-1.1.0

- use openat(2) when available.
- accept "ro" as mount option.
- fix set mtime for a symlink.
- fix some issues reported by static analysis.
- fix potential infinite loop on a short read.
- fix creating a directory if the destination already exists in the upper layer.

* fuse-overlayfs-1.0.0

- fix portability issue to 32 bits archs.
Expand Down
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
AC_PREREQ([2.69])
AC_INIT([fuse-overlayfs], [1.0.0], [[email protected]])
AC_INIT([fuse-overlayfs], [1.1.0], [[email protected]])
AC_CONFIG_SRCDIR([main.c])
AC_CONFIG_HEADERS([config.h])

Expand Down
150 changes: 84 additions & 66 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -823,7 +823,7 @@ drop_node_from_ino (Hash_table *inodes, struct ovl_node *node)
}

static int
direct_renameat2 (struct ovl_layer *l, int olddirfd, const char *oldpath,
direct_renameat2 (int olddirfd, const char *oldpath,
int newdirfd, const char *newpath, unsigned int flags)
{
return syscall (SYS_renameat2, olddirfd, oldpath, newdirfd, newpath, flags);
Expand Down Expand Up @@ -2393,6 +2393,67 @@ copy_xattr (int sfd, int dfd, char *buf, size_t buf_size)
return 0;
}

static int
empty_dirfd (int fd)
{
cleanup_dir DIR *dp = NULL;
struct dirent *dent;

dp = fdopendir (fd);
if (dp == NULL)
{
close (fd);
return -1;
}

for (;;)
{
int ret;

errno = 0;
dent = readdir (dp);
if (dent == NULL)
{
if (errno)
return -1;

break;
}
if (strcmp (dent->d_name, ".") == 0)
continue;
if (strcmp (dent->d_name, "..") == 0)
continue;

ret = unlinkat (dirfd (dp), dent->d_name, 0);
if (ret < 0 && errno == EISDIR)
{
ret = unlinkat (dirfd (dp), dent->d_name, AT_REMOVEDIR);
if (ret < 0 && errno == ENOTEMPTY)
{
int dfd;

dfd = safe_openat (dirfd (dp), dent->d_name, O_DIRECTORY, 0);
if (dfd < 0)
return -1;

ret = empty_dirfd (dfd);
if (ret < 0)
return -1;

ret = unlinkat (dirfd (dp), dent->d_name, AT_REMOVEDIR);
if (ret < 0)
return -1;

continue;
}
}
if (ret < 0)
return ret;
}

return 0;
}

static int create_node_directory (struct ovl_data *lo, struct ovl_node *src);

static int
Expand Down Expand Up @@ -2482,6 +2543,24 @@ create_directory (struct ovl_data *lo, int dirfd, const char *name, const struct
ret = renameat (lo->workdir_fd, wd_tmp_file_name, dirfd, name);
if (ret < 0)
{
if (errno == EEXIST)
{
int dfd = -1;

ret = direct_renameat2 (lo->workdir_fd, wd_tmp_file_name, dirfd, name, RENAME_EXCHANGE);
if (ret < 0)
goto out;

dfd = TEMP_FAILURE_RETRY (safe_openat (lo->workdir_fd, wd_tmp_file_name, O_DIRECTORY, 0));
if (dfd < 0)
return -1;

ret = empty_dirfd (dfd);
if (ret < 0)
goto out;

return unlinkat (lo->workdir_fd, wd_tmp_file_name, AT_REMOVEDIR);
}
if (errno == ENOTDIR)
unlinkat (dirfd, name, 0);
if (errno == ENOENT && parent)
Expand Down Expand Up @@ -2816,67 +2895,6 @@ update_paths (struct ovl_node *node)
return 0;
}

static int
empty_dirfd (int fd)
{
cleanup_dir DIR *dp = NULL;
struct dirent *dent;

dp = fdopendir (fd);
if (dp == NULL)
{
close (fd);
return -1;
}

for (;;)
{
int ret;

errno = 0;
dent = readdir (dp);
if (dent == NULL)
{
if (errno)
return -1;

break;
}
if (strcmp (dent->d_name, ".") == 0)
continue;
if (strcmp (dent->d_name, "..") == 0)
continue;

ret = unlinkat (dirfd (dp), dent->d_name, 0);
if (ret < 0 && errno == EISDIR)
{
ret = unlinkat (dirfd (dp), dent->d_name, AT_REMOVEDIR);
if (ret < 0 && errno == ENOTEMPTY)
{
int dfd;

dfd = safe_openat (dirfd (dp), dent->d_name, O_DIRECTORY, 0);
if (dfd < 0)
return -1;

ret = empty_dirfd (dfd);
if (ret < 0)
return -1;

ret = unlinkat (dirfd (dp), dent->d_name, AT_REMOVEDIR);
if (ret < 0)
return -1;

continue;
}
}
if (ret < 0)
return ret;
}

return 0;
}

static int
empty_dir (struct ovl_layer *l, const char *path)
{
Expand Down Expand Up @@ -4006,7 +4024,7 @@ ovl_rename_exchange (fuse_req_t req, fuse_ino_t parent, const char *name,
goto error;


ret = direct_renameat2 (node->layer, srcfd, name, destfd, newname, flags);
ret = direct_renameat2 (srcfd, name, destfd, newname, flags);
if (ret < 0)
goto error;

Expand Down Expand Up @@ -4179,7 +4197,7 @@ ovl_rename_direct (fuse_req_t req, fuse_ino_t parent, const char *name,
so that with one operation we get both the rename and the whiteout created. */
if (destnode_is_whiteout)
{
ret = direct_renameat2 (get_upper_layer (lo), srcfd, name, destfd, newname, flags|RENAME_EXCHANGE);
ret = direct_renameat2 (srcfd, name, destfd, newname, flags|RENAME_EXCHANGE);
if (ret == 0)
goto done;

Expand All @@ -4199,11 +4217,11 @@ ovl_rename_direct (fuse_req_t req, fuse_ino_t parent, const char *name,

/* Try to create the whiteout atomically, if it fails do the
rename+mknod separately. */
ret = direct_renameat2 (get_upper_layer (lo), srcfd, name, destfd,
ret = direct_renameat2 (srcfd, name, destfd,
newname, flags|RENAME_WHITEOUT);
if (ret < 0)
{
ret = direct_renameat2 (get_upper_layer (lo), srcfd, name, destfd,
ret = direct_renameat2 (srcfd, name, destfd,
newname, flags);
if (ret < 0)
goto error;
Expand Down

0 comments on commit 8b4062d

Please sign in to comment.