summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Gruenbacher <agruenba@redhat.com>2016-03-24 09:38:37 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2016-03-31 00:30:15 -0400
commitb8a7a3a6674725d7ca0ff6e322f6c1cab6e6a11d (patch)
tree5f442f636d5a2c2a95ae53a9bb067b320108974b
parent8861964f4c7caecbacc89bcb6b513b40cf097a02 (diff)
posix_acl: Inode acl caching fixes
When get_acl() is called for an inode whose ACL is not cached yet, the get_acl inode operation is called to fetch the ACL from the filesystem. The inode operation is responsible for updating the cached acl with set_cached_acl(). This is done without locking at the VFS level, so another task can call set_cached_acl() or forget_cached_acl() before the get_acl inode operation gets to calling set_cached_acl(), and then get_acl's call to set_cached_acl() results in caching an outdate ACL. Prevent this from happening by setting the cached ACL pointer to a task-specific sentinel value before calling the get_acl inode operation. Move the responsibility for updating the cached ACL from the get_acl inode operations to get_acl(). There, only set the cached ACL if the sentinel value hasn't changed. The sentinel values are chosen to have odd values. Likewise, the value of ACL_NOT_CACHED is odd. In contrast, ACL object pointers always have an even value (ACLs are aligned in memory). This allows to distinguish uncached ACLs values from ACL objects. In addition, switch from guarding inode->i_acl and inode->i_default_acl upates by the inode->i_lock spinlock to using xchg() and cmpxchg(). Filesystems that do not want ACLs returned from their get_acl inode operations to be cached must call forget_cached_acl() to prevent the VFS from doing so. (Patch written by Al Viro and Andreas Gruenbacher.) Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--fs/9p/acl.c2
-rw-r--r--fs/btrfs/acl.c3
-rw-r--r--fs/ceph/acl.c2
-rw-r--r--fs/ext2/acl.c3
-rw-r--r--fs/ext4/acl.c3
-rw-r--r--fs/f2fs/acl.c3
-rw-r--r--fs/hfsplus/posix_acl.c3
-rw-r--r--fs/inode.c4
-rw-r--r--fs/jffs2/acl.c2
-rw-r--r--fs/jfs/acl.c2
-rw-r--r--fs/namei.c2
-rw-r--r--fs/nfs/nfs3acl.c43
-rw-r--r--fs/ocfs2/dlmglue.c3
-rw-r--r--fs/posix_acl.c103
-rw-r--r--fs/reiserfs/xattr_acl.c6
-rw-r--r--fs/xfs/xfs_acl.c20
-rw-r--r--include/linux/fs.h12
17 files changed, 138 insertions, 78 deletions
diff --git a/fs/9p/acl.c b/fs/9p/acl.c
index 9da967f38387..2d94e94b6b59 100644
--- a/fs/9p/acl.c
+++ b/fs/9p/acl.c
@@ -93,7 +93,7 @@ static struct posix_acl *v9fs_get_cached_acl(struct inode *inode, int type)
93 * instantiating the inode (v9fs_inode_from_fid) 93 * instantiating the inode (v9fs_inode_from_fid)
94 */ 94 */
95 acl = get_cached_acl(inode, type); 95 acl = get_cached_acl(inode, type);
96 BUG_ON(acl == ACL_NOT_CACHED); 96 BUG_ON(is_uncached_acl(acl));
97 return acl; 97 return acl;
98} 98}
99 99
diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c
index 6d263bb1621c..67a607709d4f 100644
--- a/fs/btrfs/acl.c
+++ b/fs/btrfs/acl.c
@@ -63,9 +63,6 @@ struct posix_acl *btrfs_get_acl(struct inode *inode, int type)
63 } 63 }
64 kfree(value); 64 kfree(value);
65 65
66 if (!IS_ERR(acl))
67 set_cached_acl(inode, type, acl);
68
69 return acl; 66 return acl;
70} 67}
71 68
diff --git a/fs/ceph/acl.c b/fs/ceph/acl.c
index f19708487e2f..5457f216e2e5 100644
--- a/fs/ceph/acl.c
+++ b/fs/ceph/acl.c
@@ -37,6 +37,8 @@ static inline void ceph_set_cached_acl(struct inode *inode,
37 spin_lock(&ci->i_ceph_lock); 37 spin_lock(&ci->i_ceph_lock);
38 if (__ceph_caps_issued_mask(ci, CEPH_CAP_XATTR_SHARED, 0)) 38 if (__ceph_caps_issued_mask(ci, CEPH_CAP_XATTR_SHARED, 0))
39 set_cached_acl(inode, type, acl); 39 set_cached_acl(inode, type, acl);
40 else
41 forget_cached_acl(inode, type);
40 spin_unlock(&ci->i_ceph_lock); 42 spin_unlock(&ci->i_ceph_lock);
41} 43}
42 44
diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c
index 27695e6f4e46..42f1d1814083 100644
--- a/fs/ext2/acl.c
+++ b/fs/ext2/acl.c
@@ -172,9 +172,6 @@ ext2_get_acl(struct inode *inode, int type)
172 acl = ERR_PTR(retval); 172 acl = ERR_PTR(retval);
173 kfree(value); 173 kfree(value);
174 174
175 if (!IS_ERR(acl))
176 set_cached_acl(inode, type, acl);
177
178 return acl; 175 return acl;
179} 176}
180 177
diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c
index 69b1e73026a5..c6601a476c02 100644
--- a/fs/ext4/acl.c
+++ b/fs/ext4/acl.c
@@ -172,9 +172,6 @@ ext4_get_acl(struct inode *inode, int type)
172 acl = ERR_PTR(retval); 172 acl = ERR_PTR(retval);
173 kfree(value); 173 kfree(value);
174 174
175 if (!IS_ERR(acl))
176 set_cached_acl(inode, type, acl);
177
178 return acl; 175 return acl;
179} 176}
180 177
diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c
index c8f25f7241f0..6f1fdda977b3 100644
--- a/fs/f2fs/acl.c
+++ b/fs/f2fs/acl.c
@@ -190,9 +190,6 @@ static struct posix_acl *__f2fs_get_acl(struct inode *inode, int type,
190 acl = ERR_PTR(retval); 190 acl = ERR_PTR(retval);
191 kfree(value); 191 kfree(value);
192 192
193 if (!IS_ERR(acl))
194 set_cached_acl(inode, type, acl);
195
196 return acl; 193 return acl;
197} 194}
198 195
diff --git a/fs/hfsplus/posix_acl.c b/fs/hfsplus/posix_acl.c
index afb33eda6d7d..ab7ea2506b4d 100644
--- a/fs/hfsplus/posix_acl.c
+++ b/fs/hfsplus/posix_acl.c
@@ -48,9 +48,6 @@ struct posix_acl *hfsplus_get_posix_acl(struct inode *inode, int type)
48 48
49 hfsplus_destroy_attr_entry((hfsplus_attr_entry *)value); 49 hfsplus_destroy_attr_entry((hfsplus_attr_entry *)value);
50 50
51 if (!IS_ERR(acl))
52 set_cached_acl(inode, type, acl);
53
54 return acl; 51 return acl;
55} 52}
56 53
diff --git a/fs/inode.c b/fs/inode.c
index 69b8b526c194..4202aac99464 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -238,9 +238,9 @@ void __destroy_inode(struct inode *inode)
238 } 238 }
239 239
240#ifdef CONFIG_FS_POSIX_ACL 240#ifdef CONFIG_FS_POSIX_ACL
241 if (inode->i_acl && inode->i_acl != ACL_NOT_CACHED) 241 if (inode->i_acl && !is_uncached_acl(inode->i_acl))
242 posix_acl_release(inode->i_acl); 242 posix_acl_release(inode->i_acl);
243 if (inode->i_default_acl && inode->i_default_acl != ACL_NOT_CACHED) 243 if (inode->i_default_acl && !is_uncached_acl(inode->i_default_acl))
244 posix_acl_release(inode->i_default_acl); 244 posix_acl_release(inode->i_default_acl);
245#endif 245#endif
246 this_cpu_dec(nr_inodes); 246 this_cpu_dec(nr_inodes);
diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c
index 2f7a3c090489..bc2693d56298 100644
--- a/fs/jffs2/acl.c
+++ b/fs/jffs2/acl.c
@@ -203,8 +203,6 @@ struct posix_acl *jffs2_get_acl(struct inode *inode, int type)
203 acl = ERR_PTR(rc); 203 acl = ERR_PTR(rc);
204 } 204 }
205 kfree(value); 205 kfree(value);
206 if (!IS_ERR(acl))
207 set_cached_acl(inode, type, acl);
208 return acl; 206 return acl;
209} 207}
210 208
diff --git a/fs/jfs/acl.c b/fs/jfs/acl.c
index ab4882801b24..21fa92ba2c19 100644
--- a/fs/jfs/acl.c
+++ b/fs/jfs/acl.c
@@ -63,8 +63,6 @@ struct posix_acl *jfs_get_acl(struct inode *inode, int type)
63 acl = posix_acl_from_xattr(&init_user_ns, value, size); 63 acl = posix_acl_from_xattr(&init_user_ns, value, size);
64 } 64 }
65 kfree(value); 65 kfree(value);
66 if (!IS_ERR(acl))
67 set_cached_acl(inode, type, acl);
68 return acl; 66 return acl;
69} 67}
70 68
diff --git a/fs/namei.c b/fs/namei.c
index 794f81dce766..3498d53de26f 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -265,7 +265,7 @@ static int check_acl(struct inode *inode, int mask)
265 if (!acl) 265 if (!acl)
266 return -EAGAIN; 266 return -EAGAIN;
267 /* no ->get_acl() calls in RCU mode... */ 267 /* no ->get_acl() calls in RCU mode... */
268 if (acl == ACL_NOT_CACHED) 268 if (is_uncached_acl(acl))
269 return -ECHILD; 269 return -ECHILD;
270 return posix_acl_permission(inode, acl, mask & ~MAY_NOT_BLOCK); 270 return posix_acl_permission(inode, acl, mask & ~MAY_NOT_BLOCK);
271 } 271 }
diff --git a/fs/nfs/nfs3acl.c b/fs/nfs/nfs3acl.c
index 17c0fa1eccfa..720d92f5abfb 100644
--- a/fs/nfs/nfs3acl.c
+++ b/fs/nfs/nfs3acl.c
@@ -11,6 +11,38 @@
11 11
12#define NFSDBG_FACILITY NFSDBG_PROC 12#define NFSDBG_FACILITY NFSDBG_PROC
13 13
14/*
15 * nfs3_prepare_get_acl, nfs3_complete_get_acl, nfs3_abort_get_acl: Helpers for
16 * caching get_acl results in a race-free way. See fs/posix_acl.c:get_acl()
17 * for explanations.
18 */
19static void nfs3_prepare_get_acl(struct posix_acl **p)
20{
21 struct posix_acl *sentinel = uncached_acl_sentinel(current);
22
23 if (cmpxchg(p, ACL_NOT_CACHED, sentinel) != ACL_NOT_CACHED) {
24 /* Not the first reader or sentinel already in place. */
25 }
26}
27
28static void nfs3_complete_get_acl(struct posix_acl **p, struct posix_acl *acl)
29{
30 struct posix_acl *sentinel = uncached_acl_sentinel(current);
31
32 /* Only cache the ACL if our sentinel is still in place. */
33 posix_acl_dup(acl);
34 if (cmpxchg(p, sentinel, acl) != sentinel)
35 posix_acl_release(acl);
36}
37
38static void nfs3_abort_get_acl(struct posix_acl **p)
39{
40 struct posix_acl *sentinel = uncached_acl_sentinel(current);
41
42 /* Remove our sentinel upon failure. */
43 cmpxchg(p, sentinel, ACL_NOT_CACHED);
44}
45
14struct posix_acl *nfs3_get_acl(struct inode *inode, int type) 46struct posix_acl *nfs3_get_acl(struct inode *inode, int type)
15{ 47{
16 struct nfs_server *server = NFS_SERVER(inode); 48 struct nfs_server *server = NFS_SERVER(inode);
@@ -55,6 +87,11 @@ struct posix_acl *nfs3_get_acl(struct inode *inode, int type)
55 if (res.fattr == NULL) 87 if (res.fattr == NULL)
56 return ERR_PTR(-ENOMEM); 88 return ERR_PTR(-ENOMEM);
57 89
90 if (args.mask & NFS_ACL)
91 nfs3_prepare_get_acl(&inode->i_acl);
92 if (args.mask & NFS_DFACL)
93 nfs3_prepare_get_acl(&inode->i_default_acl);
94
58 status = rpc_call_sync(server->client_acl, &msg, 0); 95 status = rpc_call_sync(server->client_acl, &msg, 0);
59 dprintk("NFS reply getacl: %d\n", status); 96 dprintk("NFS reply getacl: %d\n", status);
60 97
@@ -89,12 +126,12 @@ struct posix_acl *nfs3_get_acl(struct inode *inode, int type)
89 } 126 }
90 127
91 if (res.mask & NFS_ACL) 128 if (res.mask & NFS_ACL)
92 set_cached_acl(inode, ACL_TYPE_ACCESS, res.acl_access); 129 nfs3_complete_get_acl(&inode->i_acl, res.acl_access);
93 else 130 else
94 forget_cached_acl(inode, ACL_TYPE_ACCESS); 131 forget_cached_acl(inode, ACL_TYPE_ACCESS);
95 132
96 if (res.mask & NFS_DFACL) 133 if (res.mask & NFS_DFACL)
97 set_cached_acl(inode, ACL_TYPE_DEFAULT, res.acl_default); 134 nfs3_complete_get_acl(&inode->i_default_acl, res.acl_default);
98 else 135 else
99 forget_cached_acl(inode, ACL_TYPE_DEFAULT); 136 forget_cached_acl(inode, ACL_TYPE_DEFAULT);
100 137
@@ -108,6 +145,8 @@ struct posix_acl *nfs3_get_acl(struct inode *inode, int type)
108 } 145 }
109 146
110getout: 147getout:
148 nfs3_abort_get_acl(&inode->i_acl);
149 nfs3_abort_get_acl(&inode->i_default_acl);
111 posix_acl_release(res.acl_access); 150 posix_acl_release(res.acl_access);
112 posix_acl_release(res.acl_default); 151 posix_acl_release(res.acl_default);
113 nfs_free_fattr(res.fattr); 152 nfs_free_fattr(res.fattr);
diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c
index 474e57f834e6..1eaa9100c889 100644
--- a/fs/ocfs2/dlmglue.c
+++ b/fs/ocfs2/dlmglue.c
@@ -54,6 +54,7 @@
54#include "uptodate.h" 54#include "uptodate.h"
55#include "quota.h" 55#include "quota.h"
56#include "refcounttree.h" 56#include "refcounttree.h"
57#include "acl.h"
57 58
58#include "buffer_head_io.h" 59#include "buffer_head_io.h"
59 60
@@ -3623,6 +3624,8 @@ static int ocfs2_data_convert_worker(struct ocfs2_lock_res *lockres,
3623 filemap_fdatawait(mapping); 3624 filemap_fdatawait(mapping);
3624 } 3625 }
3625 3626
3627 forget_all_cached_acls(inode);
3628
3626out: 3629out:
3627 return UNBLOCK_CONTINUE; 3630 return UNBLOCK_CONTINUE;
3628} 3631}
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index 711dd5170376..bc6736d60786 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -37,14 +37,18 @@ EXPORT_SYMBOL(acl_by_type);
37struct posix_acl *get_cached_acl(struct inode *inode, int type) 37struct posix_acl *get_cached_acl(struct inode *inode, int type)
38{ 38{
39 struct posix_acl **p = acl_by_type(inode, type); 39 struct posix_acl **p = acl_by_type(inode, type);
40 struct posix_acl *acl = ACCESS_ONCE(*p); 40 struct posix_acl *acl;
41 if (acl) { 41
42 spin_lock(&inode->i_lock); 42 for (;;) {
43 acl = *p; 43 rcu_read_lock();
44 if (acl != ACL_NOT_CACHED) 44 acl = rcu_dereference(*p);
45 acl = posix_acl_dup(acl); 45 if (!acl || is_uncached_acl(acl) ||
46 spin_unlock(&inode->i_lock); 46 atomic_inc_not_zero(&acl->a_refcount))
47 break;
48 rcu_read_unlock();
49 cpu_relax();
47 } 50 }
51 rcu_read_unlock();
48 return acl; 52 return acl;
49} 53}
50EXPORT_SYMBOL(get_cached_acl); 54EXPORT_SYMBOL(get_cached_acl);
@@ -59,58 +63,72 @@ void set_cached_acl(struct inode *inode, int type, struct posix_acl *acl)
59{ 63{
60 struct posix_acl **p = acl_by_type(inode, type); 64 struct posix_acl **p = acl_by_type(inode, type);
61 struct posix_acl *old; 65 struct posix_acl *old;
62 spin_lock(&inode->i_lock); 66
63 old = *p; 67 old = xchg(p, posix_acl_dup(acl));
64 rcu_assign_pointer(*p, posix_acl_dup(acl)); 68 if (!is_uncached_acl(old))
65 spin_unlock(&inode->i_lock);
66 if (old != ACL_NOT_CACHED)
67 posix_acl_release(old); 69 posix_acl_release(old);
68} 70}
69EXPORT_SYMBOL(set_cached_acl); 71EXPORT_SYMBOL(set_cached_acl);
70 72
71void forget_cached_acl(struct inode *inode, int type) 73static void __forget_cached_acl(struct posix_acl **p)
72{ 74{
73 struct posix_acl **p = acl_by_type(inode, type);
74 struct posix_acl *old; 75 struct posix_acl *old;
75 spin_lock(&inode->i_lock); 76
76 old = *p; 77 old = xchg(p, ACL_NOT_CACHED);
77 *p = ACL_NOT_CACHED; 78 if (!is_uncached_acl(old))
78 spin_unlock(&inode->i_lock);
79 if (old != ACL_NOT_CACHED)
80 posix_acl_release(old); 79 posix_acl_release(old);
81} 80}
81
82void forget_cached_acl(struct inode *inode, int type)
83{
84 __forget_cached_acl(acl_by_type(inode, type));
85}
82EXPORT_SYMBOL(forget_cached_acl); 86EXPORT_SYMBOL(forget_cached_acl);
83 87
84void forget_all_cached_acls(struct inode *inode) 88void forget_all_cached_acls(struct inode *inode)
85{ 89{
86 struct posix_acl *old_access, *old_default; 90 __forget_cached_acl(&inode->i_acl);
87 spin_lock(&inode->i_lock); 91 __forget_cached_acl(&inode->i_default_acl);
88 old_access = inode->i_acl;
89 old_default = inode->i_default_acl;
90 inode->i_acl = inode->i_default_acl = ACL_NOT_CACHED;
91 spin_unlock(&inode->i_lock);
92 if (old_access != ACL_NOT_CACHED)
93 posix_acl_release(old_access);
94 if (old_default != ACL_NOT_CACHED)
95 posix_acl_release(old_default);
96} 92}
97EXPORT_SYMBOL(forget_all_cached_acls); 93EXPORT_SYMBOL(forget_all_cached_acls);
98 94
99struct posix_acl *get_acl(struct inode *inode, int type) 95struct posix_acl *get_acl(struct inode *inode, int type)
100{ 96{
97 void *sentinel;
98 struct posix_acl **p;
101 struct posix_acl *acl; 99 struct posix_acl *acl;
102 100
101 /*
102 * The sentinel is used to detect when another operation like
103 * set_cached_acl() or forget_cached_acl() races with get_acl().
104 * It is guaranteed that is_uncached_acl(sentinel) is true.
105 */
106
103 acl = get_cached_acl(inode, type); 107 acl = get_cached_acl(inode, type);
104 if (acl != ACL_NOT_CACHED) 108 if (!is_uncached_acl(acl))
105 return acl; 109 return acl;
106 110
107 if (!IS_POSIXACL(inode)) 111 if (!IS_POSIXACL(inode))
108 return NULL; 112 return NULL;
109 113
114 sentinel = uncached_acl_sentinel(current);
115 p = acl_by_type(inode, type);
116
110 /* 117 /*
111 * A filesystem can force a ACL callback by just never filling the 118 * If the ACL isn't being read yet, set our sentinel. Otherwise, the
112 * ACL cache. But normally you'd fill the cache either at inode 119 * current value of the ACL will not be ACL_NOT_CACHED and so our own
113 * instantiation time, or on the first ->get_acl call. 120 * sentinel will not be set; another task will update the cache. We
121 * could wait for that other task to complete its job, but it's easier
122 * to just call ->get_acl to fetch the ACL ourself. (This is going to
123 * be an unlikely race.)
124 */
125 if (cmpxchg(p, ACL_NOT_CACHED, sentinel) != ACL_NOT_CACHED)
126 /* fall through */ ;
127
128 /*
129 * Normally, the ACL returned by ->get_acl will be cached.
130 * A filesystem can prevent that by calling
131 * forget_cached_acl(inode, type) in ->get_acl.
114 * 132 *
115 * If the filesystem doesn't have a get_acl() function at all, we'll 133 * If the filesystem doesn't have a get_acl() function at all, we'll
116 * just create the negative cache entry. 134 * just create the negative cache entry.
@@ -119,7 +137,24 @@ struct posix_acl *get_acl(struct inode *inode, int type)
119 set_cached_acl(inode, type, NULL); 137 set_cached_acl(inode, type, NULL);
120 return NULL; 138 return NULL;
121 } 139 }
122 return inode->i_op->get_acl(inode, type); 140 acl = inode->i_op->get_acl(inode, type);
141
142 if (IS_ERR(acl)) {
143 /*
144 * Remove our sentinel so that we don't block future attempts
145 * to cache the ACL.
146 */
147 cmpxchg(p, sentinel, ACL_NOT_CACHED);
148 return acl;
149 }
150
151 /*
152 * Cache the result, but only if our sentinel is still in place.
153 */
154 posix_acl_dup(acl);
155 if (unlikely(cmpxchg(p, sentinel, acl) != sentinel))
156 posix_acl_release(acl);
157 return acl;
123} 158}
124EXPORT_SYMBOL(get_acl); 159EXPORT_SYMBOL(get_acl);
125 160
diff --git a/fs/reiserfs/xattr_acl.c b/fs/reiserfs/xattr_acl.c
index ec74bbedc873..dbed42f755e0 100644
--- a/fs/reiserfs/xattr_acl.c
+++ b/fs/reiserfs/xattr_acl.c
@@ -197,10 +197,8 @@ struct posix_acl *reiserfs_get_acl(struct inode *inode, int type)
197 197
198 size = reiserfs_xattr_get(inode, name, NULL, 0); 198 size = reiserfs_xattr_get(inode, name, NULL, 0);
199 if (size < 0) { 199 if (size < 0) {
200 if (size == -ENODATA || size == -ENOSYS) { 200 if (size == -ENODATA || size == -ENOSYS)
201 set_cached_acl(inode, type, NULL);
202 return NULL; 201 return NULL;
203 }
204 return ERR_PTR(size); 202 return ERR_PTR(size);
205 } 203 }
206 204
@@ -220,8 +218,6 @@ struct posix_acl *reiserfs_get_acl(struct inode *inode, int type)
220 } else { 218 } else {
221 acl = reiserfs_posix_acl_from_disk(value, retval); 219 acl = reiserfs_posix_acl_from_disk(value, retval);
222 } 220 }
223 if (!IS_ERR(acl))
224 set_cached_acl(inode, type, acl);
225 221
226 kfree(value); 222 kfree(value);
227 return acl; 223 return acl;
diff --git a/fs/xfs/xfs_acl.c b/fs/xfs/xfs_acl.c
index 2d5df1f23bbc..b6e527b8eccb 100644
--- a/fs/xfs/xfs_acl.c
+++ b/fs/xfs/xfs_acl.c
@@ -158,22 +158,14 @@ xfs_get_acl(struct inode *inode, int type)
158 if (error) { 158 if (error) {
159 /* 159 /*
160 * If the attribute doesn't exist make sure we have a negative 160 * If the attribute doesn't exist make sure we have a negative
161 * cache entry, for any other error assume it is transient and 161 * cache entry, for any other error assume it is transient.
162 * leave the cache entry as ACL_NOT_CACHED.
163 */ 162 */
164 if (error == -ENOATTR) 163 if (error != -ENOATTR)
165 goto out_update_cache; 164 acl = ERR_PTR(error);
166 acl = ERR_PTR(error); 165 } else {
167 goto out; 166 acl = xfs_acl_from_disk(xfs_acl, len,
167 XFS_ACL_MAX_ENTRIES(ip->i_mount));
168 } 168 }
169
170 acl = xfs_acl_from_disk(xfs_acl, len, XFS_ACL_MAX_ENTRIES(ip->i_mount));
171 if (IS_ERR(acl))
172 goto out;
173
174out_update_cache:
175 set_cached_acl(inode, type, acl);
176out:
177 kmem_free(xfs_acl); 169 kmem_free(xfs_acl);
178 return acl; 170 return acl;
179} 171}
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 14a97194b34b..329ed372d708 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -577,6 +577,18 @@ static inline void mapping_allow_writable(struct address_space *mapping)
577struct posix_acl; 577struct posix_acl;
578#define ACL_NOT_CACHED ((void *)(-1)) 578#define ACL_NOT_CACHED ((void *)(-1))
579 579
580static inline struct posix_acl *
581uncached_acl_sentinel(struct task_struct *task)
582{
583 return (void *)task + 1;
584}
585
586static inline bool
587is_uncached_acl(struct posix_acl *acl)
588{
589 return (long)acl & 1;
590}
591
580#define IOP_FASTPERM 0x0001 592#define IOP_FASTPERM 0x0001
581#define IOP_LOOKUP 0x0002 593#define IOP_LOOKUP 0x0002
582#define IOP_NOFOLLOW 0x0004 594#define IOP_NOFOLLOW 0x0004