summaryrefslogtreecommitdiffstats
path: root/fs/nfs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-07-22 01:26:01 +0200
committerLinus Torvalds <torvalds@linux-foundation.org>2017-07-22 01:26:01 +0200
commit505d5c11192960a3f0639d1d9e05dffeddd4e874 (patch)
tree4b9f151bda0f7c146107851bb9053fc1ec4c6461 /fs/nfs
parentMerge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/g... (diff)
parentNFS/filelayout: Fix racy setting of fl->dsaddr in filelayout_check_deviceid() (diff)
downloadlinux-505d5c11192960a3f0639d1d9e05dffeddd4e874.tar.xz
linux-505d5c11192960a3f0639d1d9e05dffeddd4e874.zip
Merge tag 'nfs-for-4.13-2' of git://git.linux-nfs.org/projects/anna/linux-nfs
Pull NFS client bugfixes from Anna Schumaker: "Stable bugfix: - Fix error reporting regression Bugfixes: - Fix setting filelayout ds address race - Fix subtle access bug when using ACLs - Fix setting mnt3_counts array size - Fix a couple of pNFS commit races" * tag 'nfs-for-4.13-2' of git://git.linux-nfs.org/projects/anna/linux-nfs: NFS/filelayout: Fix racy setting of fl->dsaddr in filelayout_check_deviceid() NFS: Be more careful about mapping file permissions NFS: Store the raw NFS access mask in the inode's access cache NFSv3: Convert nfs3_proc_access() to use nfs_access_set_mask() NFS: Refactor NFS access to kernel access mask calculation net/sunrpc/xprt_sock: fix regression in connection error reporting. nfs: count correct array for mnt3_counts array size Revert commit 722f0b891198 ("pNFS: Don't send COMMITs to the DSes if...") pNFS/flexfiles: Handle expired layout segments in ff_layout_initiate_commit() NFS: Fix another COMMIT race in pNFS NFS: Fix a COMMIT race in pNFS mount: copy the port field into the cloned nfs_server structure. NFS: Don't run wake_up_bit() when nobody is waiting... nfs: add export operations
Diffstat (limited to 'fs/nfs')
-rw-r--r--fs/nfs/client.c1
-rw-r--r--fs/nfs/dir.c47
-rw-r--r--fs/nfs/filelayout/filelayout.c13
-rw-r--r--fs/nfs/flexfilelayout/flexfilelayout.c4
-rw-r--r--fs/nfs/mount_clnt.c2
-rw-r--r--fs/nfs/nfs3proc.c11
-rw-r--r--fs/nfs/pnfs_nfs.c24
7 files changed, 69 insertions, 33 deletions
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index ee5ddbd36088..efebe6cf4378 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -820,6 +820,7 @@ void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *sour
target->caps = source->caps;
target->options = source->options;
target->auth_info = source->auth_info;
+ target->port = source->port;
}
EXPORT_SYMBOL_GPL(nfs_server_copy_userdata);
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 5ac484fe0dee..3522b1249019 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -2372,16 +2372,40 @@ void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set)
}
EXPORT_SYMBOL_GPL(nfs_access_add_cache);
+#define NFS_MAY_READ (NFS4_ACCESS_READ)
+#define NFS_MAY_WRITE (NFS4_ACCESS_MODIFY | \
+ NFS4_ACCESS_EXTEND | \
+ NFS4_ACCESS_DELETE)
+#define NFS_FILE_MAY_WRITE (NFS4_ACCESS_MODIFY | \
+ NFS4_ACCESS_EXTEND)
+#define NFS_DIR_MAY_WRITE NFS_MAY_WRITE
+#define NFS_MAY_LOOKUP (NFS4_ACCESS_LOOKUP)
+#define NFS_MAY_EXECUTE (NFS4_ACCESS_EXECUTE)
+static int
+nfs_access_calc_mask(u32 access_result, umode_t umode)
+{
+ int mask = 0;
+
+ if (access_result & NFS_MAY_READ)
+ mask |= MAY_READ;
+ if (S_ISDIR(umode)) {
+ if ((access_result & NFS_DIR_MAY_WRITE) == NFS_DIR_MAY_WRITE)
+ mask |= MAY_WRITE;
+ if ((access_result & NFS_MAY_LOOKUP) == NFS_MAY_LOOKUP)
+ mask |= MAY_EXEC;
+ } else if (S_ISREG(umode)) {
+ if ((access_result & NFS_FILE_MAY_WRITE) == NFS_FILE_MAY_WRITE)
+ mask |= MAY_WRITE;
+ if ((access_result & NFS_MAY_EXECUTE) == NFS_MAY_EXECUTE)
+ mask |= MAY_EXEC;
+ } else if (access_result & NFS_MAY_WRITE)
+ mask |= MAY_WRITE;
+ return mask;
+}
+
void nfs_access_set_mask(struct nfs_access_entry *entry, u32 access_result)
{
- entry->mask = 0;
- if (access_result & NFS4_ACCESS_READ)
- entry->mask |= MAY_READ;
- if (access_result &
- (NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND | NFS4_ACCESS_DELETE))
- entry->mask |= MAY_WRITE;
- if (access_result & (NFS4_ACCESS_LOOKUP|NFS4_ACCESS_EXECUTE))
- entry->mask |= MAY_EXEC;
+ entry->mask = access_result;
}
EXPORT_SYMBOL_GPL(nfs_access_set_mask);
@@ -2389,6 +2413,7 @@ static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask)
{
struct nfs_access_entry cache;
bool may_block = (mask & MAY_NOT_BLOCK) == 0;
+ int cache_mask;
int status;
trace_nfs_access_enter(inode);
@@ -2404,7 +2429,8 @@ static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask)
goto out;
/* Be clever: ask server to check for all possible rights */
- cache.mask = MAY_EXEC | MAY_WRITE | MAY_READ;
+ cache.mask = NFS_MAY_LOOKUP | NFS_MAY_EXECUTE
+ | NFS_MAY_WRITE | NFS_MAY_READ;
cache.cred = cred;
cache.jiffies = jiffies;
status = NFS_PROTO(inode)->access(inode, &cache);
@@ -2418,7 +2444,8 @@ static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask)
}
nfs_access_add_cache(inode, &cache);
out_cached:
- if ((mask & ~cache.mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) != 0)
+ cache_mask = nfs_access_calc_mask(cache.mask, inode->i_mode);
+ if ((mask & ~cache_mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) != 0)
status = -EACCES;
out:
trace_nfs_access_exit(inode, status);
diff --git a/fs/nfs/filelayout/filelayout.c b/fs/nfs/filelayout/filelayout.c
index 080fc6b278bd..44c638b7876c 100644
--- a/fs/nfs/filelayout/filelayout.c
+++ b/fs/nfs/filelayout/filelayout.c
@@ -542,6 +542,10 @@ filelayout_check_deviceid(struct pnfs_layout_hdr *lo,
struct nfs4_file_layout_dsaddr *dsaddr;
int status = -EINVAL;
+ /* Is the deviceid already set? If so, we're good. */
+ if (fl->dsaddr != NULL)
+ return 0;
+
/* find and reference the deviceid */
d = nfs4_find_get_deviceid(NFS_SERVER(lo->plh_inode), &fl->deviceid,
lo->plh_lc_cred, gfp_flags);
@@ -553,8 +557,6 @@ filelayout_check_deviceid(struct pnfs_layout_hdr *lo,
if (filelayout_test_devid_unavailable(&dsaddr->id_node))
goto out_put;
- fl->dsaddr = dsaddr;
-
if (fl->first_stripe_index >= dsaddr->stripe_count) {
dprintk("%s Bad first_stripe_index %u\n",
__func__, fl->first_stripe_index);
@@ -570,6 +572,13 @@ filelayout_check_deviceid(struct pnfs_layout_hdr *lo,
goto out_put;
}
status = 0;
+
+ /*
+ * Atomic compare and xchange to ensure we don't scribble
+ * over a non-NULL pointer.
+ */
+ if (cmpxchg(&fl->dsaddr, NULL, dsaddr) != NULL)
+ goto out_put;
out:
return status;
out_put:
diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c
index 1f2ac3dd0fe5..b0fa83a60754 100644
--- a/fs/nfs/flexfilelayout/flexfilelayout.c
+++ b/fs/nfs/flexfilelayout/flexfilelayout.c
@@ -1842,6 +1842,10 @@ static int ff_layout_initiate_commit(struct nfs_commit_data *data, int how)
int vers, ret;
struct nfs_fh *fh;
+ if (!lseg || !(pnfs_is_valid_lseg(lseg) ||
+ test_bit(NFS_LSEG_LAYOUTRETURN, &lseg->pls_flags)))
+ goto out_err;
+
idx = calc_ds_index_from_commit(lseg, data->ds_commit_index);
ds = nfs4_ff_layout_prepare_ds(lseg, idx, true);
if (!ds)
diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c
index 3efe946672be..60bad882c123 100644
--- a/fs/nfs/mount_clnt.c
+++ b/fs/nfs/mount_clnt.c
@@ -512,7 +512,7 @@ static const struct rpc_version mnt_version1 = {
.counts = mnt_counts,
};
-static unsigned int mnt3_counts[ARRAY_SIZE(mnt_procedures)];
+static unsigned int mnt3_counts[ARRAY_SIZE(mnt3_procedures)];
static const struct rpc_version mnt_version3 = {
.number = 3,
.nrprocs = ARRAY_SIZE(mnt3_procedures),
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
index df4a7d3ab915..d1e87ec0df84 100644
--- a/fs/nfs/nfs3proc.c
+++ b/fs/nfs/nfs3proc.c
@@ -220,15 +220,8 @@ static int nfs3_proc_access(struct inode *inode, struct nfs_access_entry *entry)
status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
nfs_refresh_inode(inode, res.fattr);
- if (status == 0) {
- entry->mask = 0;
- if (res.access & NFS3_ACCESS_READ)
- entry->mask |= MAY_READ;
- if (res.access & (NFS3_ACCESS_MODIFY | NFS3_ACCESS_EXTEND | NFS3_ACCESS_DELETE))
- entry->mask |= MAY_WRITE;
- if (res.access & (NFS3_ACCESS_LOOKUP|NFS3_ACCESS_EXECUTE))
- entry->mask |= MAY_EXEC;
- }
+ if (status == 0)
+ nfs_access_set_mask(entry, res.access);
nfs_free_fattr(res.fattr);
out:
dprintk("NFS reply access: %d\n", status);
diff --git a/fs/nfs/pnfs_nfs.c b/fs/nfs/pnfs_nfs.c
index d40755a0984b..25f28fa64c57 100644
--- a/fs/nfs/pnfs_nfs.c
+++ b/fs/nfs/pnfs_nfs.c
@@ -159,13 +159,18 @@ void pnfs_generic_recover_commit_reqs(struct list_head *dst,
{
struct pnfs_commit_bucket *b;
struct pnfs_layout_segment *freeme;
+ int nwritten;
int i;
lockdep_assert_held(&cinfo->inode->i_lock);
restart:
for (i = 0, b = cinfo->ds->buckets; i < cinfo->ds->nbuckets; i++, b++) {
- if (pnfs_generic_transfer_commit_list(&b->written, dst,
- cinfo, 0)) {
+ nwritten = pnfs_generic_transfer_commit_list(&b->written,
+ dst, cinfo, 0);
+ if (!nwritten)
+ continue;
+ cinfo->ds->nwritten -= nwritten;
+ if (list_empty(&b->written)) {
freeme = b->wlseg;
b->wlseg = NULL;
spin_unlock(&cinfo->inode->i_lock);
@@ -174,7 +179,6 @@ restart:
goto restart;
}
}
- cinfo->ds->nwritten = 0;
}
EXPORT_SYMBOL_GPL(pnfs_generic_recover_commit_reqs);
@@ -183,6 +187,7 @@ static void pnfs_generic_retry_commit(struct nfs_commit_info *cinfo, int idx)
struct pnfs_ds_commit_info *fl_cinfo = cinfo->ds;
struct pnfs_commit_bucket *bucket;
struct pnfs_layout_segment *freeme;
+ struct list_head *pos;
LIST_HEAD(pages);
int i;
@@ -193,6 +198,8 @@ static void pnfs_generic_retry_commit(struct nfs_commit_info *cinfo, int idx)
continue;
freeme = bucket->clseg;
bucket->clseg = NULL;
+ list_for_each(pos, &bucket->committing)
+ cinfo->ds->ncommitting--;
list_splice_init(&bucket->committing, &pages);
spin_unlock(&cinfo->inode->i_lock);
nfs_retry_commit(&pages, freeme, cinfo, i);
@@ -217,13 +224,6 @@ pnfs_generic_alloc_ds_commits(struct nfs_commit_info *cinfo,
for (i = 0; i < fl_cinfo->nbuckets; i++, bucket++) {
if (list_empty(&bucket->committing))
continue;
- /*
- * If the layout segment is invalid, then let
- * pnfs_generic_retry_commit() clean up the bucket.
- */
- if (bucket->clseg && !pnfs_is_valid_lseg(bucket->clseg) &&
- !test_bit(NFS_LSEG_LAYOUTRETURN, &bucket->clseg->pls_flags))
- break;
data = nfs_commitdata_alloc(false);
if (!data)
break;
@@ -243,9 +243,12 @@ void pnfs_fetch_commit_bucket_list(struct list_head *pages,
struct nfs_commit_info *cinfo)
{
struct pnfs_commit_bucket *bucket;
+ struct list_head *pos;
bucket = &cinfo->ds->buckets[data->ds_commit_index];
spin_lock(&cinfo->inode->i_lock);
+ list_for_each(pos, &bucket->committing)
+ cinfo->ds->ncommitting--;
list_splice_init(&bucket->committing, pages);
data->lseg = bucket->clseg;
bucket->clseg = NULL;
@@ -330,7 +333,6 @@ pnfs_generic_commit_pagelist(struct inode *inode, struct list_head *mds_pages,
}
}
out:
- cinfo->ds->ncommitting = 0;
return PNFS_ATTEMPTED;
}
EXPORT_SYMBOL_GPL(pnfs_generic_commit_pagelist);