Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 22b4e63e authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull NFS client bugfixes from Trond Myklebust:

 - Final (hopefully) fix for the range checking code in NFSv4 getacl.
   This should fix the Oopses being seen when the acl size is close to
   PAGE_SIZE.
 - Fix a regression with the legacy binary mount code
 - Fix a regression in the readdir cookieverf initialisation
 - Fix an RPC over UDP regression
 - Ensure that we report all errors in the NFSv4 open code
 - Ensure that fsync() reports all relevant synchronisation errors.

* tag 'nfs-for-3.6-4' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
  NFS: fsync() must exit with an error if page writeback failed
  SUNRPC: Fix a UDP transport regression
  NFS: return error from decode_getfh in decode open
  NFSv4: Fix buffer overflow checking in __nfs4_get_acl_uncached
  NFSv4: Fix range checking in __nfs4_get_acl_uncached and __nfs4_proc_set_acl
  NFS: Fix a problem with the legacy binary mount code
  NFS: Fix the initialisation of the readdir 'cookieverf' array
parents 0bd1189e 7b281ee0
Loading
Loading
Loading
Loading
+3 −1
Original line number Original line Diff line number Diff line
@@ -287,10 +287,12 @@ nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
	struct inode *inode = file->f_path.dentry->d_inode;
	struct inode *inode = file->f_path.dentry->d_inode;


	ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
	ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
	if (ret != 0)
		goto out;
	mutex_lock(&inode->i_mutex);
	mutex_lock(&inode->i_mutex);
	ret = nfs_file_fsync_commit(file, start, end, datasync);
	ret = nfs_file_fsync_commit(file, start, end, datasync);
	mutex_unlock(&inode->i_mutex);
	mutex_unlock(&inode->i_mutex);

out:
	return ret;
	return ret;
}
}


+1 −1
Original line number Original line Diff line number Diff line
@@ -154,7 +154,7 @@ static void nfs_zap_caches_locked(struct inode *inode)
	nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
	nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
	nfsi->attrtimeo_timestamp = jiffies;
	nfsi->attrtimeo_timestamp = jiffies;


	memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
	memset(NFS_I(inode)->cookieverf, 0, sizeof(NFS_I(inode)->cookieverf));
	if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))
	if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))
		nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE;
		nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE;
	else
	else
+1 −1
Original line number Original line Diff line number Diff line
@@ -643,7 +643,7 @@ nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
		  u64 cookie, struct page **pages, unsigned int count, int plus)
		  u64 cookie, struct page **pages, unsigned int count, int plus)
{
{
	struct inode		*dir = dentry->d_inode;
	struct inode		*dir = dentry->d_inode;
	__be32			*verf = NFS_COOKIEVERF(dir);
	__be32			*verf = NFS_I(dir)->cookieverf;
	struct nfs3_readdirargs	arg = {
	struct nfs3_readdirargs	arg = {
		.fh		= NFS_FH(dir),
		.fh		= NFS_FH(dir),
		.cookie		= cookie,
		.cookie		= cookie,
+3 −1
Original line number Original line Diff line number Diff line
@@ -96,13 +96,15 @@ nfs4_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
	struct inode *inode = file->f_path.dentry->d_inode;
	struct inode *inode = file->f_path.dentry->d_inode;


	ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
	ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
	if (ret != 0)
		goto out;
	mutex_lock(&inode->i_mutex);
	mutex_lock(&inode->i_mutex);
	ret = nfs_file_fsync_commit(file, start, end, datasync);
	ret = nfs_file_fsync_commit(file, start, end, datasync);
	if (!ret && !datasync)
	if (!ret && !datasync)
		/* application has asked for meta-data sync */
		/* application has asked for meta-data sync */
		ret = pnfs_layoutcommit_inode(inode, true);
		ret = pnfs_layoutcommit_inode(inode, true);
	mutex_unlock(&inode->i_mutex);
	mutex_unlock(&inode->i_mutex);

out:
	return ret;
	return ret;
}
}


+25 −30
Original line number Original line Diff line number Diff line
@@ -3215,11 +3215,11 @@ static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
			dentry->d_parent->d_name.name,
			dentry->d_parent->d_name.name,
			dentry->d_name.name,
			dentry->d_name.name,
			(unsigned long long)cookie);
			(unsigned long long)cookie);
	nfs4_setup_readdir(cookie, NFS_COOKIEVERF(dir), dentry, &args);
	nfs4_setup_readdir(cookie, NFS_I(dir)->cookieverf, dentry, &args);
	res.pgbase = args.pgbase;
	res.pgbase = args.pgbase;
	status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &msg, &args.seq_args, &res.seq_res, 0);
	status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &msg, &args.seq_args, &res.seq_res, 0);
	if (status >= 0) {
	if (status >= 0) {
		memcpy(NFS_COOKIEVERF(dir), res.verifier.data, NFS4_VERIFIER_SIZE);
		memcpy(NFS_I(dir)->cookieverf, res.verifier.data, NFS4_VERIFIER_SIZE);
		status += args.pgbase;
		status += args.pgbase;
	}
	}


@@ -3653,11 +3653,11 @@ static inline int nfs4_server_supports_acls(struct nfs_server *server)
		&& (server->acl_bitmask & ACL4_SUPPORT_DENY_ACL);
		&& (server->acl_bitmask & ACL4_SUPPORT_DENY_ACL);
}
}


/* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_CACHE_SIZE, and that
/* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_SIZE, and that
 * it's OK to put sizeof(void) * (XATTR_SIZE_MAX/PAGE_CACHE_SIZE) bytes on
 * it's OK to put sizeof(void) * (XATTR_SIZE_MAX/PAGE_SIZE) bytes on
 * the stack.
 * the stack.
 */
 */
#define NFS4ACL_MAXPAGES (XATTR_SIZE_MAX >> PAGE_CACHE_SHIFT)
#define NFS4ACL_MAXPAGES DIV_ROUND_UP(XATTR_SIZE_MAX, PAGE_SIZE)


static int buf_to_pages_noslab(const void *buf, size_t buflen,
static int buf_to_pages_noslab(const void *buf, size_t buflen,
		struct page **pages, unsigned int *pgbase)
		struct page **pages, unsigned int *pgbase)
@@ -3668,7 +3668,7 @@ static int buf_to_pages_noslab(const void *buf, size_t buflen,
	spages = pages;
	spages = pages;


	do {
	do {
		len = min_t(size_t, PAGE_CACHE_SIZE, buflen);
		len = min_t(size_t, PAGE_SIZE, buflen);
		newpage = alloc_page(GFP_KERNEL);
		newpage = alloc_page(GFP_KERNEL);


		if (newpage == NULL)
		if (newpage == NULL)
@@ -3739,7 +3739,7 @@ static void nfs4_write_cached_acl(struct inode *inode, struct page **pages, size
	struct nfs4_cached_acl *acl;
	struct nfs4_cached_acl *acl;
	size_t buflen = sizeof(*acl) + acl_len;
	size_t buflen = sizeof(*acl) + acl_len;


	if (pages && buflen <= PAGE_SIZE) {
	if (buflen <= PAGE_SIZE) {
		acl = kmalloc(buflen, GFP_KERNEL);
		acl = kmalloc(buflen, GFP_KERNEL);
		if (acl == NULL)
		if (acl == NULL)
			goto out;
			goto out;
@@ -3782,17 +3782,15 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
		.rpc_argp = &args,
		.rpc_argp = &args,
		.rpc_resp = &res,
		.rpc_resp = &res,
	};
	};
	int ret = -ENOMEM, npages, i;
	unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE);
	size_t acl_len = 0;
	int ret = -ENOMEM, i;


	npages = (buflen + PAGE_SIZE - 1) >> PAGE_SHIFT;
	/* As long as we're doing a round trip to the server anyway,
	/* As long as we're doing a round trip to the server anyway,
	 * let's be prepared for a page of acl data. */
	 * let's be prepared for a page of acl data. */
	if (npages == 0)
	if (npages == 0)
		npages = 1;
		npages = 1;

	if (npages > ARRAY_SIZE(pages))
	/* Add an extra page to handle the bitmap returned */
		return -ERANGE;
	npages++;


	for (i = 0; i < npages; i++) {
	for (i = 0; i < npages; i++) {
		pages[i] = alloc_page(GFP_KERNEL);
		pages[i] = alloc_page(GFP_KERNEL);
@@ -3808,11 +3806,6 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
	args.acl_len = npages * PAGE_SIZE;
	args.acl_len = npages * PAGE_SIZE;
	args.acl_pgbase = 0;
	args.acl_pgbase = 0;


	/* Let decode_getfacl know not to fail if the ACL data is larger than
	 * the page we send as a guess */
	if (buf == NULL)
		res.acl_flags |= NFS4_ACL_LEN_REQUEST;

	dprintk("%s  buf %p buflen %zu npages %d args.acl_len %zu\n",
	dprintk("%s  buf %p buflen %zu npages %d args.acl_len %zu\n",
		__func__, buf, buflen, npages, args.acl_len);
		__func__, buf, buflen, npages, args.acl_len);
	ret = nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode),
	ret = nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode),
@@ -3820,20 +3813,19 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
	if (ret)
	if (ret)
		goto out_free;
		goto out_free;


	acl_len = res.acl_len;
	/* Handle the case where the passed-in buffer is too short */
	if (acl_len > args.acl_len)
	if (res.acl_flags & NFS4_ACL_TRUNC) {
		nfs4_write_cached_acl(inode, NULL, 0, acl_len);
		/* Did the user only issue a request for the acl length? */
	else
		if (buf == NULL)
		nfs4_write_cached_acl(inode, pages, res.acl_data_offset,
			goto out_ok;
				      acl_len);
	if (buf) {
		ret = -ERANGE;
		ret = -ERANGE;
		if (acl_len > buflen)
		goto out_free;
		goto out_free;
		_copy_from_pages(buf, pages, res.acl_data_offset,
				acl_len);
	}
	}
	ret = acl_len;
	nfs4_write_cached_acl(inode, pages, res.acl_data_offset, res.acl_len);
	if (buf)
		_copy_from_pages(buf, pages, res.acl_data_offset, res.acl_len);
out_ok:
	ret = res.acl_len;
out_free:
out_free:
	for (i = 0; i < npages; i++)
	for (i = 0; i < npages; i++)
		if (pages[i])
		if (pages[i])
@@ -3891,10 +3883,13 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
		.rpc_argp	= &arg,
		.rpc_argp	= &arg,
		.rpc_resp	= &res,
		.rpc_resp	= &res,
	};
	};
	unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE);
	int ret, i;
	int ret, i;


	if (!nfs4_server_supports_acls(server))
	if (!nfs4_server_supports_acls(server))
		return -EOPNOTSUPP;
		return -EOPNOTSUPP;
	if (npages > ARRAY_SIZE(pages))
		return -ERANGE;
	i = buf_to_pages_noslab(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
	i = buf_to_pages_noslab(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
	if (i < 0)
	if (i < 0)
		return i;
		return i;
Loading