From a68c9d75b592eba68026661877c7a0aed5dabf41 Mon Sep 17 00:00:00 2001
From: Giuseppe Scrivano <gscrivan@redhat.com>
Date: Sat, 15 Feb 2020 12:33:52 +0100
Subject: [PATCH] main: force timeout 0 for ovl_link
There is an issue on RHEL 8.1 where the nlink counter is always
incremented by one, no matter what is specified in e.attr.st_nlink.
Always set timeout to 0 to force a new stat on the inode.
Closes: https://bugzilla.redhat.com/show_bug.cgi?id=1802907
Closes: https://github.com/containers/fuse-overlayfs/issues/183
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
---
main.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/main.c b/main.c
index b1bdfa4..7a6eae4 100644
--- a/main.c
+++ b/main.c
@@ -3615,235 +3615,240 @@ ovl_setattr (fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, stru
ret = truncate (path, attr->st_size);
if (ret < 0)
{
fuse_reply_err (req, errno);
return;
}
}
if (uid != -1 || gid != -1)
{
if (fd >= 0)
ret = fchown (fd, uid, gid);
else
ret = chown (path, uid, gid);
if (ret < 0)
{
fuse_reply_err (req, errno);
return;
}
}
if (do_getattr (req, &e, node, fd, path) < 0)
{
fuse_reply_err (req, errno);
return;
}
fuse_reply_attr (req, &e.attr, get_timeout (lo));
}
static int
direct_linkat (struct ovl_layer *l, const char *oldpath, const char *newpath, int flags)
{
return linkat (l->fd, oldpath, l->fd, newpath, 0);
}
static void
ovl_link (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, const char *newname)
{
cleanup_lock int l = enter_big_lock ();
struct ovl_data *lo = ovl_data (req);
struct ovl_node *node, *newparentnode, *destnode;
cleanup_free char *path = NULL;
int ret;
struct fuse_entry_param e;
char wd_tmp_file_name[32];
if (UNLIKELY (ovl_debug (req)))
fprintf (stderr, "ovl_link(ino=%" PRIu64 "s, newparent=%" PRIu64 "s, newname=%s)\n", ino, newparent, newname);
node = do_lookup_file (lo, ino, NULL);
if (node == NULL)
{
fuse_reply_err (req, ENOENT);
return;
}
node = get_node_up (lo, node);
if (node == NULL)
{
fuse_reply_err (req, errno);
return;
}
newparentnode = do_lookup_file (lo, newparent, NULL);
if (newparentnode == NULL)
{
fuse_reply_err (req, ENOENT);
return;
}
destnode = do_lookup_file (lo, newparent, newname);
if (destnode && !destnode->whiteout)
{
fuse_reply_err (req, EEXIST);
return;
}
newparentnode = get_node_up (lo, newparentnode);
if (newparentnode == NULL)
{
fuse_reply_err (req, errno);
return;
}
if (delete_whiteout (lo, -1, newparentnode, newname) < 0)
{
fuse_reply_err (req, errno);
return;
}
sprintf (wd_tmp_file_name, "%lu", get_next_wd_counter ());
ret = asprintf (&path, "%s/%s", newparentnode->path, newname);
if (ret < 0)
{
fuse_reply_err (req, errno);
return;
}
+ /*
+ There is an issue on RHEL 8.1 where the nlink counter is always
+ incremented by one, no matter what is specified in e.attr.st_nlink.
+ In this function we always set timeout to 0 to force a new stat on the inode.
+ */
ret = direct_linkat (get_upper_layer (lo), node->path, path, 0);
if (ret < 0)
{
fuse_reply_err (req, errno);
return;
}
node = make_ovl_node (lo, path, get_upper_layer (lo), newname, node->tmp_ino, node->tmp_dev, false, newparentnode, lo->fast_ino_check);
if (node == NULL)
{
fuse_reply_err (req, ENOMEM);
return;
}
if (destnode && !destnode->whiteout)
node->last_layer = get_upper_layer (lo);
node = insert_node (newparentnode, node, true);
if (node == NULL)
{
fuse_reply_err (req, ENOMEM);
return;
}
memset (&e, 0, sizeof (e));
ret = rpl_stat (req, node, -1, NULL, NULL, &e.attr);
if (ret)
{
fuse_reply_err (req, errno);
return;
}
e.ino = node_to_inode (node);
node->ino->lookups++;
- e.attr_timeout = get_timeout (lo);
+ e.attr_timeout = 0;
e.entry_timeout = get_timeout (lo);
fuse_reply_entry (req, &e);
}
static int
direct_symlinkat (struct ovl_layer *l, const char *target, const char *linkpath, uid_t uid, gid_t gid)
{
struct ovl_data *lo = l->ovl_data;
char wd_tmp_file_name[32];
int ret;
sprintf (wd_tmp_file_name, "%lu", get_next_wd_counter ());
unlinkat (lo->workdir_fd, wd_tmp_file_name, 0);
ret = symlinkat (linkpath, lo->workdir_fd, wd_tmp_file_name);
if (ret < 0)
return ret;
if (uid != lo->uid || gid != lo->gid)
{
ret = fchownat (lo->workdir_fd, wd_tmp_file_name, uid, gid, AT_SYMLINK_NOFOLLOW);
if (ret < 0)
{
unlinkat (lo->workdir_fd, wd_tmp_file_name, 0);
return ret;
}
}
ret = renameat (lo->workdir_fd, wd_tmp_file_name, get_upper_layer (lo)->fd, target);
if (ret < 0)
{
unlinkat (lo->workdir_fd, wd_tmp_file_name, 0);
return ret;
}
return 0;
}
static void
ovl_symlink (fuse_req_t req, const char *link, fuse_ino_t parent, const char *name)
{
cleanup_lock int l = enter_big_lock ();
struct ovl_data *lo = ovl_data (req);
struct ovl_node *pnode, *node;
int ret;
struct fuse_entry_param e;
const struct fuse_ctx *ctx = fuse_req_ctx (req);
char wd_tmp_file_name[32];
bool need_delete_whiteout = true;
cleanup_free char *path = NULL;
if (UNLIKELY (ovl_debug (req)))
fprintf (stderr, "ovl_symlink(link=%s, ino=%" PRIu64 "s, name=%s)\n", link, parent, name);
pnode = do_lookup_file (lo, parent, NULL);
if (pnode == NULL)
{
fuse_reply_err (req, ENOENT);
return;
}
pnode = get_node_up (lo, pnode);
if (pnode == NULL)
{
fuse_reply_err (req, errno);
return;
}
node = do_lookup_file (lo, parent, name);
if (node != NULL && !node->whiteout)
{
fuse_reply_err (req, EEXIST);
return;
}
if (pnode->loaded && node == NULL)
need_delete_whiteout = false;
ret = asprintf (&path, "%s/%s", pnode->path, name);
if (ret < 0)
{
fuse_reply_err (req, ENOMEM);
return;
}
ret = direct_symlinkat (get_upper_layer (lo), path, link, get_uid (lo, ctx->uid), get_gid (lo, ctx->gid));
if (ret < 0)
{
fuse_reply_err (req, ENOMEM);
return;
}
if (need_delete_whiteout && delete_whiteout (lo, -1, pnode, name) < 0)
{
unlinkat (lo->workdir_fd, wd_tmp_file_name, 0);
fuse_reply_err (req, errno);
return;
}
node = make_ovl_node (lo, path, get_upper_layer (lo), name, 0, 0, false, pnode, lo->fast_ino_check);
--
2.18.1