From f26f1f71aec208466f55ba6a2e7d20e4455a0bd0 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Wed, 17 Jun 2020 15:00:07 +0200 Subject: [PATCH 1/2] mkdir: if the destination exists atomically swap them if the destination already exists as it could not be properly cleaned up, attempt to atomically swap the two directories and free the old one. Closes: https://github.com/containers/fuse-overlayfs/issues/213 Signed-off-by: Giuseppe Scrivano --- main.c | 150 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 84 insertions(+), 66 deletions(-) diff --git a/main.c b/main.c index 15eb416..a59d4e2 100644 --- a/main.c +++ b/main.c @@ -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); @@ -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 @@ -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) @@ -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) { @@ -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; @@ -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; @@ -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; From d68235ff9bf9f5f746f508bfb0675cd269811686 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Wed, 17 Jun 2020 15:57:22 +0200 Subject: [PATCH 2/2] configure.ac: tag 1.1.0 Signed-off-by: Giuseppe Scrivano --- NEWS | 9 +++++++++ configure.ac | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 4d90494..55622f0 100644 --- a/NEWS +++ b/NEWS @@ -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. diff --git a/configure.ac b/configure.ac index 18066f5..aff464e 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ([2.69]) -AC_INIT([fuse-overlayfs], [1.0.0], [giuseppe@scrivano.org]) +AC_INIT([fuse-overlayfs], [1.1.0], [giuseppe@scrivano.org]) AC_CONFIG_SRCDIR([main.c]) AC_CONFIG_HEADERS([config.h])