Blob Blame History Raw
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