Merge tag 'nfs-for-5.9-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs
Pull NFS client updates from Trond Myklebust: "Stable fixes: - pNFS: Don't return layout segments that are being used for I/O - pNFS: Don't move layout segments off the active list when being used for I/O Features: - NFS: Add support for user xattrs through the NFSv4.2 protocol - NFS: Allow applications to speed up readdir+statx() using AT_STATX_DONT_SYNC - NFSv4.0 allow nconnect for v4.0 Bugfixes and cleanups: - nfs: ensure correct writeback errors are returned on close() - nfs: nfs_file_write() should check for writeback errors - nfs: Fix getxattr kernel panic and memory overflow - NFS: Fix the pNFS/flexfiles mirrored read failover code - SUNRPC: dont update timeout value on connection reset - freezer: Add unsafe versions of freezable_schedule_timeout_interruptible for NFS - sunrpc: destroy rpc_inode_cachep after unregister_filesystem" * tag 'nfs-for-5.9-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: (32 commits) NFS: Fix flexfiles read failover fs: nfs: delete repeated words in comments rpc_pipefs: convert comma to semicolon nfs: Fix getxattr kernel panic and memory overflow NFS: Don't return layout segments that are in use NFS: Don't move layouts to plh_return_segs list while in use NFS: Add layout segment info to pnfs read/write/commit tracepoints NFS: Add tracepoints for layouterror and layoutstats. NFS: Report the stateid + status in trace_nfs4_layoutreturn_on_close() SUNRPC dont update timeout value on connection reset nfs: nfs_file_write() should check for writeback errors nfs: ensure correct writeback errors are returned on close() NFSv4.2: xattr cache: get rid of cache discard work queue NFS: remove redundant initialization of variable result NFSv4.0 allow nconnect for v4.0 freezer: Add unsafe versions of freezable_schedule_timeout_interruptible for NFS sunrpc: destroy rpc_inode_cachep after unregister_filesystem NFSv4.2: add client side xattr caching. NFSv4.2: hook in the user extended attribute handlers NFSv4.2: add the extended attribute proc functions. ...
This commit is contained in:
@@ -66,6 +66,7 @@
|
||||
#include "nfs4idmap.h"
|
||||
#include "nfs4session.h"
|
||||
#include "fscache.h"
|
||||
#include "nfs42.h"
|
||||
|
||||
#include "nfs4trace.h"
|
||||
|
||||
@@ -256,6 +257,7 @@ const u32 nfs4_fsinfo_bitmap[3] = { FATTR4_WORD0_MAXFILESIZE
|
||||
| FATTR4_WORD1_FS_LAYOUT_TYPES,
|
||||
FATTR4_WORD2_LAYOUT_BLKSIZE
|
||||
| FATTR4_WORD2_CLONE_BLKSIZE
|
||||
| FATTR4_WORD2_XATTR_SUPPORT
|
||||
};
|
||||
|
||||
const u32 nfs4_fs_locations_bitmap[3] = {
|
||||
@@ -1173,37 +1175,49 @@ nfs4_dec_nlink_locked(struct inode *inode)
|
||||
}
|
||||
|
||||
static void
|
||||
update_changeattr_locked(struct inode *dir, struct nfs4_change_info *cinfo,
|
||||
nfs4_update_changeattr_locked(struct inode *inode,
|
||||
struct nfs4_change_info *cinfo,
|
||||
unsigned long timestamp, unsigned long cache_validity)
|
||||
{
|
||||
struct nfs_inode *nfsi = NFS_I(dir);
|
||||
struct nfs_inode *nfsi = NFS_I(inode);
|
||||
|
||||
nfsi->cache_validity |= NFS_INO_INVALID_CTIME
|
||||
| NFS_INO_INVALID_MTIME
|
||||
| NFS_INO_INVALID_DATA
|
||||
| cache_validity;
|
||||
if (cinfo->atomic && cinfo->before == inode_peek_iversion_raw(dir)) {
|
||||
|
||||
if (cinfo->atomic && cinfo->before == inode_peek_iversion_raw(inode)) {
|
||||
nfsi->cache_validity &= ~NFS_INO_REVAL_PAGECACHE;
|
||||
nfsi->attrtimeo_timestamp = jiffies;
|
||||
} else {
|
||||
nfs_force_lookup_revalidate(dir);
|
||||
if (cinfo->before != inode_peek_iversion_raw(dir))
|
||||
if (S_ISDIR(inode->i_mode)) {
|
||||
nfsi->cache_validity |= NFS_INO_INVALID_DATA;
|
||||
nfs_force_lookup_revalidate(inode);
|
||||
} else {
|
||||
if (!NFS_PROTO(inode)->have_delegation(inode,
|
||||
FMODE_READ))
|
||||
nfsi->cache_validity |= NFS_INO_REVAL_PAGECACHE;
|
||||
}
|
||||
|
||||
if (cinfo->before != inode_peek_iversion_raw(inode))
|
||||
nfsi->cache_validity |= NFS_INO_INVALID_ACCESS |
|
||||
NFS_INO_INVALID_ACL;
|
||||
NFS_INO_INVALID_ACL |
|
||||
NFS_INO_INVALID_XATTR;
|
||||
}
|
||||
inode_set_iversion_raw(dir, cinfo->after);
|
||||
inode_set_iversion_raw(inode, cinfo->after);
|
||||
nfsi->read_cache_jiffies = timestamp;
|
||||
nfsi->attr_gencount = nfs_inc_attr_generation_counter();
|
||||
nfsi->cache_validity &= ~NFS_INO_INVALID_CHANGE;
|
||||
nfs_fscache_invalidate(dir);
|
||||
|
||||
if (nfsi->cache_validity & NFS_INO_INVALID_DATA)
|
||||
nfs_fscache_invalidate(inode);
|
||||
}
|
||||
|
||||
static void
|
||||
update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo,
|
||||
void
|
||||
nfs4_update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo,
|
||||
unsigned long timestamp, unsigned long cache_validity)
|
||||
{
|
||||
spin_lock(&dir->i_lock);
|
||||
update_changeattr_locked(dir, cinfo, timestamp, cache_validity);
|
||||
nfs4_update_changeattr_locked(dir, cinfo, timestamp, cache_validity);
|
||||
spin_unlock(&dir->i_lock);
|
||||
}
|
||||
|
||||
@@ -1356,6 +1370,12 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
|
||||
NFS4_ACCESS_MODIFY |
|
||||
NFS4_ACCESS_EXTEND |
|
||||
NFS4_ACCESS_EXECUTE;
|
||||
#ifdef CONFIG_NFS_V4_2
|
||||
if (server->caps & NFS_CAP_XATTR)
|
||||
p->o_arg.access |= NFS4_ACCESS_XAREAD |
|
||||
NFS4_ACCESS_XAWRITE |
|
||||
NFS4_ACCESS_XALIST;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
p->o_arg.clientid = server->nfs_client->cl_clientid;
|
||||
@@ -2653,8 +2673,9 @@ static int _nfs4_proc_open(struct nfs4_opendata *data,
|
||||
data->file_created = true;
|
||||
if (data->file_created ||
|
||||
inode_peek_iversion_raw(dir) != o_res->cinfo.after)
|
||||
update_changeattr(dir, &o_res->cinfo,
|
||||
o_res->f_attr->time_start, 0);
|
||||
nfs4_update_changeattr(dir, &o_res->cinfo,
|
||||
o_res->f_attr->time_start,
|
||||
NFS_INO_INVALID_DATA);
|
||||
}
|
||||
if ((o_res->rflags & NFS4_OPEN_RESULT_LOCKTYPE_POSIX) == 0)
|
||||
server->caps &= ~NFS_CAP_POSIX_LOCK;
|
||||
@@ -3756,7 +3777,7 @@ static void nfs4_close_context(struct nfs_open_context *ctx, int is_sync)
|
||||
|
||||
#define FATTR4_WORD1_NFS40_MASK (2*FATTR4_WORD1_MOUNTED_ON_FILEID - 1UL)
|
||||
#define FATTR4_WORD2_NFS41_MASK (2*FATTR4_WORD2_SUPPATTR_EXCLCREAT - 1UL)
|
||||
#define FATTR4_WORD2_NFS42_MASK (2*FATTR4_WORD2_MODE_UMASK - 1UL)
|
||||
#define FATTR4_WORD2_NFS42_MASK (2*FATTR4_WORD2_XATTR_SUPPORT - 1UL)
|
||||
|
||||
static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle)
|
||||
{
|
||||
@@ -4540,7 +4561,8 @@ _nfs4_proc_remove(struct inode *dir, const struct qstr *name, u32 ftype)
|
||||
status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 1);
|
||||
if (status == 0) {
|
||||
spin_lock(&dir->i_lock);
|
||||
update_changeattr_locked(dir, &res.cinfo, timestamp, 0);
|
||||
nfs4_update_changeattr_locked(dir, &res.cinfo, timestamp,
|
||||
NFS_INO_INVALID_DATA);
|
||||
/* Removing a directory decrements nlink in the parent */
|
||||
if (ftype == NF4DIR && dir->i_nlink > 2)
|
||||
nfs4_dec_nlink_locked(dir);
|
||||
@@ -4624,8 +4646,9 @@ static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir)
|
||||
&data->timeout) == -EAGAIN)
|
||||
return 0;
|
||||
if (task->tk_status == 0)
|
||||
update_changeattr(dir, &res->cinfo,
|
||||
res->dir_attr->time_start, 0);
|
||||
nfs4_update_changeattr(dir, &res->cinfo,
|
||||
res->dir_attr->time_start,
|
||||
NFS_INO_INVALID_DATA);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -4669,16 +4692,18 @@ static int nfs4_proc_rename_done(struct rpc_task *task, struct inode *old_dir,
|
||||
if (task->tk_status == 0) {
|
||||
if (new_dir != old_dir) {
|
||||
/* Note: If we moved a directory, nlink will change */
|
||||
update_changeattr(old_dir, &res->old_cinfo,
|
||||
nfs4_update_changeattr(old_dir, &res->old_cinfo,
|
||||
res->old_fattr->time_start,
|
||||
NFS_INO_INVALID_OTHER);
|
||||
update_changeattr(new_dir, &res->new_cinfo,
|
||||
NFS_INO_INVALID_OTHER |
|
||||
NFS_INO_INVALID_DATA);
|
||||
nfs4_update_changeattr(new_dir, &res->new_cinfo,
|
||||
res->new_fattr->time_start,
|
||||
NFS_INO_INVALID_OTHER);
|
||||
NFS_INO_INVALID_OTHER |
|
||||
NFS_INO_INVALID_DATA);
|
||||
} else
|
||||
update_changeattr(old_dir, &res->old_cinfo,
|
||||
nfs4_update_changeattr(old_dir, &res->old_cinfo,
|
||||
res->old_fattr->time_start,
|
||||
0);
|
||||
NFS_INO_INVALID_DATA);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -4719,7 +4744,8 @@ static int _nfs4_proc_link(struct inode *inode, struct inode *dir, const struct
|
||||
|
||||
status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
|
||||
if (!status) {
|
||||
update_changeattr(dir, &res.cinfo, res.fattr->time_start, 0);
|
||||
nfs4_update_changeattr(dir, &res.cinfo, res.fattr->time_start,
|
||||
NFS_INO_INVALID_DATA);
|
||||
status = nfs_post_op_update_inode(inode, res.fattr);
|
||||
if (!status)
|
||||
nfs_setsecurity(inode, res.fattr, res.label);
|
||||
@@ -4797,8 +4823,9 @@ static int nfs4_do_create(struct inode *dir, struct dentry *dentry, struct nfs4_
|
||||
&data->arg.seq_args, &data->res.seq_res, 1);
|
||||
if (status == 0) {
|
||||
spin_lock(&dir->i_lock);
|
||||
update_changeattr_locked(dir, &data->res.dir_cinfo,
|
||||
data->res.fattr->time_start, 0);
|
||||
nfs4_update_changeattr_locked(dir, &data->res.dir_cinfo,
|
||||
data->res.fattr->time_start,
|
||||
NFS_INO_INVALID_DATA);
|
||||
/* Creating a directory bumps nlink in the parent */
|
||||
if (data->arg.ftype == NF4DIR)
|
||||
nfs4_inc_nlink_locked(dir);
|
||||
@@ -5531,7 +5558,7 @@ static inline int nfs4_server_supports_acls(struct nfs_server *server)
|
||||
*/
|
||||
#define NFS4ACL_MAXPAGES DIV_ROUND_UP(XATTR_SIZE_MAX, PAGE_SIZE)
|
||||
|
||||
static int buf_to_pages_noslab(const void *buf, size_t buflen,
|
||||
int nfs4_buf_to_pages_noslab(const void *buf, size_t buflen,
|
||||
struct page **pages)
|
||||
{
|
||||
struct page *newpage, **spages;
|
||||
@@ -5773,7 +5800,7 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
|
||||
return -EOPNOTSUPP;
|
||||
if (npages > ARRAY_SIZE(pages))
|
||||
return -ERANGE;
|
||||
i = buf_to_pages_noslab(buf, buflen, arg.acl_pages);
|
||||
i = nfs4_buf_to_pages_noslab(buf, buflen, arg.acl_pages);
|
||||
if (i < 0)
|
||||
return i;
|
||||
nfs4_inode_make_writeable(inode);
|
||||
@@ -5845,8 +5872,6 @@ static int _nfs4_get_security_label(struct inode *inode, void *buf,
|
||||
return ret;
|
||||
if (!(fattr.valid & NFS_ATTR_FATTR_V4_SECURITY_LABEL))
|
||||
return -ENOENT;
|
||||
if (buflen < label.len)
|
||||
return -ERANGE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -7430,6 +7455,133 @@ nfs4_listxattr_nfs4_label(struct inode *inode, char *list, size_t list_len)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NFS_V4_2
|
||||
static int nfs4_xattr_set_nfs4_user(const struct xattr_handler *handler,
|
||||
struct dentry *unused, struct inode *inode,
|
||||
const char *key, const void *buf,
|
||||
size_t buflen, int flags)
|
||||
{
|
||||
struct nfs_access_entry cache;
|
||||
int ret;
|
||||
|
||||
if (!nfs_server_capable(inode, NFS_CAP_XATTR))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/*
|
||||
* There is no mapping from the MAY_* flags to the NFS_ACCESS_XA*
|
||||
* flags right now. Handling of xattr operations use the normal
|
||||
* file read/write permissions.
|
||||
*
|
||||
* Just in case the server has other ideas (which RFC 8276 allows),
|
||||
* do a cached access check for the XA* flags to possibly avoid
|
||||
* doing an RPC and getting EACCES back.
|
||||
*/
|
||||
if (!nfs_access_get_cached(inode, current_cred(), &cache, true)) {
|
||||
if (!(cache.mask & NFS_ACCESS_XAWRITE))
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
if (buf == NULL) {
|
||||
ret = nfs42_proc_removexattr(inode, key);
|
||||
if (!ret)
|
||||
nfs4_xattr_cache_remove(inode, key);
|
||||
} else {
|
||||
ret = nfs42_proc_setxattr(inode, key, buf, buflen, flags);
|
||||
if (!ret)
|
||||
nfs4_xattr_cache_add(inode, key, buf, NULL, buflen);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nfs4_xattr_get_nfs4_user(const struct xattr_handler *handler,
|
||||
struct dentry *unused, struct inode *inode,
|
||||
const char *key, void *buf, size_t buflen)
|
||||
{
|
||||
struct nfs_access_entry cache;
|
||||
ssize_t ret;
|
||||
|
||||
if (!nfs_server_capable(inode, NFS_CAP_XATTR))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!nfs_access_get_cached(inode, current_cred(), &cache, true)) {
|
||||
if (!(cache.mask & NFS_ACCESS_XAREAD))
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
ret = nfs_revalidate_inode(NFS_SERVER(inode), inode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = nfs4_xattr_cache_get(inode, key, buf, buflen);
|
||||
if (ret >= 0 || (ret < 0 && ret != -ENOENT))
|
||||
return ret;
|
||||
|
||||
ret = nfs42_proc_getxattr(inode, key, buf, buflen);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
nfs4_listxattr_nfs4_user(struct inode *inode, char *list, size_t list_len)
|
||||
{
|
||||
u64 cookie;
|
||||
bool eof;
|
||||
ssize_t ret, size;
|
||||
char *buf;
|
||||
size_t buflen;
|
||||
struct nfs_access_entry cache;
|
||||
|
||||
if (!nfs_server_capable(inode, NFS_CAP_XATTR))
|
||||
return 0;
|
||||
|
||||
if (!nfs_access_get_cached(inode, current_cred(), &cache, true)) {
|
||||
if (!(cache.mask & NFS_ACCESS_XALIST))
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = nfs_revalidate_inode(NFS_SERVER(inode), inode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = nfs4_xattr_cache_list(inode, list, list_len);
|
||||
if (ret >= 0 || (ret < 0 && ret != -ENOENT))
|
||||
return ret;
|
||||
|
||||
cookie = 0;
|
||||
eof = false;
|
||||
buflen = list_len ? list_len : XATTR_LIST_MAX;
|
||||
buf = list_len ? list : NULL;
|
||||
size = 0;
|
||||
|
||||
while (!eof) {
|
||||
ret = nfs42_proc_listxattrs(inode, buf, buflen,
|
||||
&cookie, &eof);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (list_len) {
|
||||
buf += ret;
|
||||
buflen -= ret;
|
||||
}
|
||||
size += ret;
|
||||
}
|
||||
|
||||
if (list_len)
|
||||
nfs4_xattr_cache_set_list(inode, list, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static ssize_t
|
||||
nfs4_listxattr_nfs4_user(struct inode *inode, char *list, size_t list_len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_NFS_V4_2 */
|
||||
|
||||
/*
|
||||
* nfs_fhget will use either the mounted_on_fileid or the fileid
|
||||
*/
|
||||
@@ -10035,7 +10187,7 @@ const struct nfs4_minor_version_ops *nfs_v4_minor_ops[] = {
|
||||
|
||||
static ssize_t nfs4_listxattr(struct dentry *dentry, char *list, size_t size)
|
||||
{
|
||||
ssize_t error, error2;
|
||||
ssize_t error, error2, error3;
|
||||
|
||||
error = generic_listxattr(dentry, list, size);
|
||||
if (error < 0)
|
||||
@@ -10048,7 +10200,17 @@ static ssize_t nfs4_listxattr(struct dentry *dentry, char *list, size_t size)
|
||||
error2 = nfs4_listxattr_nfs4_label(d_inode(dentry), list, size);
|
||||
if (error2 < 0)
|
||||
return error2;
|
||||
return error + error2;
|
||||
|
||||
if (list) {
|
||||
list += error2;
|
||||
size -= error2;
|
||||
}
|
||||
|
||||
error3 = nfs4_listxattr_nfs4_user(d_inode(dentry), list, size);
|
||||
if (error3 < 0)
|
||||
return error3;
|
||||
|
||||
return error + error2 + error3;
|
||||
}
|
||||
|
||||
static const struct inode_operations nfs4_dir_inode_operations = {
|
||||
@@ -10136,10 +10298,21 @@ static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = {
|
||||
.set = nfs4_xattr_set_nfs4_acl,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_NFS_V4_2
|
||||
static const struct xattr_handler nfs4_xattr_nfs4_user_handler = {
|
||||
.prefix = XATTR_USER_PREFIX,
|
||||
.get = nfs4_xattr_get_nfs4_user,
|
||||
.set = nfs4_xattr_set_nfs4_user,
|
||||
};
|
||||
#endif
|
||||
|
||||
const struct xattr_handler *nfs4_xattr_handlers[] = {
|
||||
&nfs4_xattr_nfs4_acl_handler,
|
||||
#ifdef CONFIG_NFS_V4_SECURITY_LABEL
|
||||
&nfs4_xattr_nfs4_label_handler,
|
||||
#endif
|
||||
#ifdef CONFIG_NFS_V4_2
|
||||
&nfs4_xattr_nfs4_user_handler,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
Reference in New Issue
Block a user