diff options
Diffstat (limited to 'fs/nfs/nfs4proc.c')
-rw-r--r-- | fs/nfs/nfs4proc.c | 129 |
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 | ||
2191 | static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen) | 2191 | struct nfs4_cached_acl { |
2192 | int cached; | ||
2193 | size_t len; | ||
2194 | char data[]; | ||
2195 | }; | ||
2196 | |||
2197 | static 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 | |||
2207 | static void nfs4_zap_acl_attr(struct inode *inode) | ||
2208 | { | ||
2209 | nfs4_set_cached_acl(inode, NULL); | ||
2210 | } | ||
2211 | |||
2212 | static 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); | ||
2230 | out_len: | ||
2231 | ret = acl->len; | ||
2232 | out: | ||
2233 | spin_unlock(&inode->i_lock); | ||
2234 | return ret; | ||
2235 | } | ||
2236 | |||
2237 | static 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; | ||
2254 | out: | ||
2255 | nfs4_set_cached_acl(inode, acl); | ||
2256 | } | ||
2257 | |||
2258 | static 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; | ||
2305 | out_free: | ||
2306 | if (localpage) | ||
2307 | __free_page(localpage); | ||
2216 | return ret; | 2308 | return ret; |
2217 | } | 2309 | } |
2218 | 2310 | ||
2311 | static 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 | |||
2219 | static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen) | 2327 | static 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 | /* |