aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/nfs4proc.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfs/nfs4proc.c')
-rw-r--r--fs/nfs/nfs4proc.c129
1 files changed, 120 insertions, 9 deletions
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index d969dd13e7db..128d01cfea19 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -2188,9 +2188,75 @@ static void buf_to_pages(const void *buf, size_t buflen,
2188 } 2188 }
2189} 2189}
2190 2190
2191static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen) 2191struct nfs4_cached_acl {
2192 int cached;
2193 size_t len;
2194 char data[];
2195};
2196
2197static void nfs4_set_cached_acl(struct inode *inode, struct nfs4_cached_acl *acl)
2198{
2199 struct nfs_inode *nfsi = NFS_I(inode);
2200
2201 spin_lock(&inode->i_lock);
2202 kfree(nfsi->nfs4_acl);
2203 nfsi->nfs4_acl = acl;
2204 spin_unlock(&inode->i_lock);
2205}
2206
2207static void nfs4_zap_acl_attr(struct inode *inode)
2208{
2209 nfs4_set_cached_acl(inode, NULL);
2210}
2211
2212static inline ssize_t nfs4_read_cached_acl(struct inode *inode, char *buf, size_t buflen)
2213{
2214 struct nfs_inode *nfsi = NFS_I(inode);
2215 struct nfs4_cached_acl *acl;
2216 int ret = -ENOENT;
2217
2218 spin_lock(&inode->i_lock);
2219 acl = nfsi->nfs4_acl;
2220 if (acl == NULL)
2221 goto out;
2222 if (buf == NULL) /* user is just asking for length */
2223 goto out_len;
2224 if (acl->cached == 0)
2225 goto out;
2226 ret = -ERANGE; /* see getxattr(2) man page */
2227 if (acl->len > buflen)
2228 goto out;
2229 memcpy(buf, acl->data, acl->len);
2230out_len:
2231 ret = acl->len;
2232out:
2233 spin_unlock(&inode->i_lock);
2234 return ret;
2235}
2236
2237static void nfs4_write_cached_acl(struct inode *inode, const char *buf, size_t acl_len)
2238{
2239 struct nfs4_cached_acl *acl;
2240
2241 if (buf && acl_len <= PAGE_SIZE) {
2242 acl = kmalloc(sizeof(*acl) + acl_len, GFP_KERNEL);
2243 if (acl == NULL)
2244 goto out;
2245 acl->cached = 1;
2246 memcpy(acl->data, buf, acl_len);
2247 } else {
2248 acl = kmalloc(sizeof(*acl), GFP_KERNEL);
2249 if (acl == NULL)
2250 goto out;
2251 acl->cached = 0;
2252 }
2253 acl->len = acl_len;
2254out:
2255 nfs4_set_cached_acl(inode, acl);
2256}
2257
2258static inline ssize_t nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen)
2192{ 2259{
2193 struct nfs_server *server = NFS_SERVER(inode);
2194 struct page *pages[NFS4ACL_MAXPAGES]; 2260 struct page *pages[NFS4ACL_MAXPAGES];
2195 struct nfs_getaclargs args = { 2261 struct nfs_getaclargs args = {
2196 .fh = NFS_FH(inode), 2262 .fh = NFS_FH(inode),
@@ -2198,24 +2264,66 @@ static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen)
2198 .acl_len = buflen, 2264 .acl_len = buflen,
2199 }; 2265 };
2200 size_t resp_len = buflen; 2266 size_t resp_len = buflen;
2267 void *resp_buf;
2201 struct rpc_message msg = { 2268 struct rpc_message msg = {
2202 .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETACL], 2269 .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETACL],
2203 .rpc_argp = &args, 2270 .rpc_argp = &args,
2204 .rpc_resp = &resp_len, 2271 .rpc_resp = &resp_len,
2205 }; 2272 };
2273 struct page *localpage = NULL;
2206 int ret; 2274 int ret;
2207 2275
2208 if (!nfs4_server_supports_acls(server)) 2276 if (buflen < PAGE_SIZE) {
2209 return -EOPNOTSUPP; 2277 /* As long as we're doing a round trip to the server anyway,
2210 buf_to_pages(buf, buflen, args.acl_pages, &args.acl_pgbase); 2278 * let's be prepared for a page of acl data. */
2279 localpage = alloc_page(GFP_KERNEL);
2280 resp_buf = page_address(localpage);
2281 if (localpage == NULL)
2282 return -ENOMEM;
2283 args.acl_pages[0] = localpage;
2284 args.acl_pgbase = 0;
2285 args.acl_len = PAGE_SIZE;
2286 } else {
2287 resp_buf = buf;
2288 buf_to_pages(buf, buflen, args.acl_pages, &args.acl_pgbase);
2289 }
2211 ret = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); 2290 ret = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
2212 if (buflen && resp_len > buflen) 2291 if (ret)
2213 return -ERANGE; 2292 goto out_free;
2214 if (ret == 0) 2293 if (resp_len > args.acl_len)
2215 ret = resp_len; 2294 nfs4_write_cached_acl(inode, NULL, resp_len);
2295 else
2296 nfs4_write_cached_acl(inode, resp_buf, resp_len);
2297 if (buf) {
2298 ret = -ERANGE;
2299 if (resp_len > buflen)
2300 goto out_free;
2301 if (localpage)
2302 memcpy(buf, resp_buf, resp_len);
2303 }
2304 ret = resp_len;
2305out_free:
2306 if (localpage)
2307 __free_page(localpage);
2216 return ret; 2308 return ret;
2217} 2309}
2218 2310
2311static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen)
2312{
2313 struct nfs_server *server = NFS_SERVER(inode);
2314 int ret;
2315
2316 if (!nfs4_server_supports_acls(server))
2317 return -EOPNOTSUPP;
2318 ret = nfs_revalidate_inode(server, inode);
2319 if (ret < 0)
2320 return ret;
2321 ret = nfs4_read_cached_acl(inode, buf, buflen);
2322 if (ret != -ENOENT)
2323 return ret;
2324 return nfs4_get_acl_uncached(inode, buf, buflen);
2325}
2326
2219static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen) 2327static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen)
2220{ 2328{
2221 struct nfs_server *server = NFS_SERVER(inode); 2329 struct nfs_server *server = NFS_SERVER(inode);
@@ -2236,6 +2344,8 @@ static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen
2236 return -EOPNOTSUPP; 2344 return -EOPNOTSUPP;
2237 buf_to_pages(buf, buflen, arg.acl_pages, &arg.acl_pgbase); 2345 buf_to_pages(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
2238 ret = rpc_call_sync(NFS_SERVER(inode)->client, &msg, 0); 2346 ret = rpc_call_sync(NFS_SERVER(inode)->client, &msg, 0);
2347 if (ret == 0)
2348 nfs4_write_cached_acl(inode, buf, buflen);
2239 return ret; 2349 return ret;
2240} 2350}
2241 2351
@@ -2907,6 +3017,7 @@ struct nfs_rpc_ops nfs_v4_clientops = {
2907 .file_open = nfs4_proc_file_open, 3017 .file_open = nfs4_proc_file_open,
2908 .file_release = nfs4_proc_file_release, 3018 .file_release = nfs4_proc_file_release,
2909 .lock = nfs4_proc_lock, 3019 .lock = nfs4_proc_lock,
3020 .clear_acl_cache = nfs4_zap_acl_attr,
2910}; 3021};
2911 3022
2912/* 3023/*