summaryrefslogtreecommitdiffstats
path: root/fs/nfs/nfs4proc.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfs/nfs4proc.c')
-rw-r--r--fs/nfs/nfs4proc.c547
1 files changed, 429 insertions, 118 deletions
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 9bf41eab3e4..079614deca3 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -46,6 +46,7 @@
#include <linux/nfs4.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_page.h>
+#include <linux/nfs_mount.h>
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/module.h>
@@ -79,7 +80,10 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
struct nfs_fattr *fattr, struct iattr *sattr,
struct nfs4_state *state);
-
+#ifdef CONFIG_NFS_V4_1
+static int nfs41_test_stateid(struct nfs_server *, struct nfs4_state *);
+static int nfs41_free_stateid(struct nfs_server *, struct nfs4_state *);
+#endif
/* Prevent leaks of NFSv4 errors into userland */
static int nfs4_map_errors(int err)
{
@@ -266,9 +270,11 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc
break;
nfs4_schedule_stateid_recovery(server, state);
goto wait_on_recovery;
+ case -NFS4ERR_EXPIRED:
+ if (state != NULL)
+ nfs4_schedule_stateid_recovery(server, state);
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_STALE_CLIENTID:
- case -NFS4ERR_EXPIRED:
nfs4_schedule_lease_recovery(clp);
goto wait_on_recovery;
#if defined(CONFIG_NFS_V4_1)
@@ -299,6 +305,7 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc
ret = nfs4_delay(server->client, &exception->timeout);
if (ret != 0)
break;
+ case -NFS4ERR_RETRY_UNCACHED_REP:
case -NFS4ERR_OLD_STATEID:
exception->retry = 1;
break;
@@ -443,8 +450,8 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *
if (res->sr_status == 1)
res->sr_status = NFS_OK;
- /* -ERESTARTSYS can result in skipping nfs41_sequence_setup */
- if (!res->sr_slot)
+ /* don't increment the sequence number if the task wasn't sent */
+ if (!RPC_WAS_SENT(task))
goto out;
/* Check the SEQUENCE operation status */
@@ -759,8 +766,8 @@ struct nfs4_opendata {
struct nfs_open_confirmres c_res;
struct nfs_fattr f_attr;
struct nfs_fattr dir_attr;
- struct path path;
struct dentry *dir;
+ struct dentry *dentry;
struct nfs4_state_owner *owner;
struct nfs4_state *state;
struct iattr attrs;
@@ -782,12 +789,12 @@ static void nfs4_init_opendata_res(struct nfs4_opendata *p)
nfs_fattr_init(&p->dir_attr);
}
-static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path,
+static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
struct nfs4_state_owner *sp, fmode_t fmode, int flags,
const struct iattr *attrs,
gfp_t gfp_mask)
{
- struct dentry *parent = dget_parent(path->dentry);
+ struct dentry *parent = dget_parent(dentry);
struct inode *dir = parent->d_inode;
struct nfs_server *server = NFS_SERVER(dir);
struct nfs4_opendata *p;
@@ -798,8 +805,8 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path,
p->o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid, gfp_mask);
if (p->o_arg.seqid == NULL)
goto err_free;
- path_get(path);
- p->path = *path;
+ nfs_sb_active(dentry->d_sb);
+ p->dentry = dget(dentry);
p->dir = parent;
p->owner = sp;
atomic_inc(&sp->so_count);
@@ -808,7 +815,7 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path,
p->o_arg.fmode = fmode & (FMODE_READ|FMODE_WRITE);
p->o_arg.clientid = server->nfs_client->cl_clientid;
p->o_arg.id = sp->so_owner_id.id;
- p->o_arg.name = &p->path.dentry->d_name;
+ p->o_arg.name = &dentry->d_name;
p->o_arg.server = server;
p->o_arg.bitmask = server->attr_bitmask;
p->o_arg.claim = NFS4_OPEN_CLAIM_NULL;
@@ -838,13 +845,15 @@ static void nfs4_opendata_free(struct kref *kref)
{
struct nfs4_opendata *p = container_of(kref,
struct nfs4_opendata, kref);
+ struct super_block *sb = p->dentry->d_sb;
nfs_free_seqid(p->o_arg.seqid);
if (p->state != NULL)
nfs4_put_open_state(p->state);
nfs4_put_state_owner(p->owner);
dput(p->dir);
- path_put(&p->path);
+ dput(p->dentry);
+ nfs_sb_deactive(sb);
kfree(p);
}
@@ -1126,7 +1135,7 @@ static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context
{
struct nfs4_opendata *opendata;
- opendata = nfs4_opendata_alloc(&ctx->path, state->owner, 0, 0, NULL, GFP_NOFS);
+ opendata = nfs4_opendata_alloc(ctx->dentry, state->owner, 0, 0, NULL, GFP_NOFS);
if (opendata == NULL)
return ERR_PTR(-ENOMEM);
opendata->state = state;
@@ -1150,7 +1159,7 @@ static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, fmode_t fmod
newstate = nfs4_opendata_to_nfs4_state(opendata);
if (IS_ERR(newstate))
return PTR_ERR(newstate);
- nfs4_close_state(&opendata->path, newstate, fmode);
+ nfs4_close_state(newstate, fmode);
*res = newstate;
return 0;
}
@@ -1348,7 +1357,7 @@ static void nfs4_open_confirm_release(void *calldata)
goto out_free;
state = nfs4_opendata_to_nfs4_state(data);
if (!IS_ERR(state))
- nfs4_close_state(&data->path, state, data->o_arg.fmode);
+ nfs4_close_state(state, data->o_arg.fmode);
out_free:
nfs4_opendata_put(data);
}
@@ -1493,7 +1502,7 @@ static void nfs4_open_release(void *calldata)
goto out_free;
state = nfs4_opendata_to_nfs4_state(data);
if (!IS_ERR(state))
- nfs4_close_state(&data->path, state, data->o_arg.fmode);
+ nfs4_close_state(state, data->o_arg.fmode);
out_free:
nfs4_opendata_put(data);
}
@@ -1644,7 +1653,7 @@ static int _nfs4_open_expired(struct nfs_open_context *ctx, struct nfs4_state *s
return PTR_ERR(opendata);
ret = nfs4_open_recover(opendata, state);
if (ret == -ESTALE)
- d_drop(ctx->path.dentry);
+ d_drop(ctx->dentry);
nfs4_opendata_put(opendata);
return ret;
}
@@ -1683,6 +1692,20 @@ static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *sta
return ret;
}
+#if defined(CONFIG_NFS_V4_1)
+static int nfs41_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state)
+{
+ int status;
+ struct nfs_server *server = NFS_SERVER(state->inode);
+
+ status = nfs41_test_stateid(server, state);
+ if (status == NFS_OK)
+ return 0;
+ nfs41_free_stateid(server, state);
+ return nfs4_open_expired(sp, state);
+}
+#endif
+
/*
* on an EXCLUSIVE create, the server should send back a bitmask with FATTR4-*
* fields corresponding to attributes that were used to store the verifier.
@@ -1702,7 +1725,7 @@ static inline void nfs4_exclusive_attrset(struct nfs4_opendata *opendata, struct
/*
* Returns a referenced nfs4_state
*/
-static int _nfs4_do_open(struct inode *dir, struct path *path, fmode_t fmode, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res)
+static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, fmode_t fmode, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res)
{
struct nfs4_state_owner *sp;
struct nfs4_state *state = NULL;
@@ -1719,15 +1742,15 @@ static int _nfs4_do_open(struct inode *dir, struct path *path, fmode_t fmode, in
status = nfs4_recover_expired_lease(server);
if (status != 0)
goto err_put_state_owner;
- if (path->dentry->d_inode != NULL)
- nfs4_return_incompatible_delegation(path->dentry->d_inode, fmode);
+ if (dentry->d_inode != NULL)
+ nfs4_return_incompatible_delegation(dentry->d_inode, fmode);
status = -ENOMEM;
- opendata = nfs4_opendata_alloc(path, sp, fmode, flags, sattr, GFP_KERNEL);
+ opendata = nfs4_opendata_alloc(dentry, sp, fmode, flags, sattr, GFP_KERNEL);
if (opendata == NULL)
goto err_put_state_owner;
- if (path->dentry->d_inode != NULL)
- opendata->state = nfs4_get_open_state(path->dentry->d_inode, sp);
+ if (dentry->d_inode != NULL)
+ opendata->state = nfs4_get_open_state(dentry->d_inode, sp);
status = _nfs4_proc_open(opendata);
if (status != 0)
@@ -1765,14 +1788,14 @@ out_err:
}
-static struct nfs4_state *nfs4_do_open(struct inode *dir, struct path *path, fmode_t fmode, int flags, struct iattr *sattr, struct rpc_cred *cred)
+static struct nfs4_state *nfs4_do_open(struct inode *dir, struct dentry *dentry, fmode_t fmode, int flags, struct iattr *sattr, struct rpc_cred *cred)
{
struct nfs4_exception exception = { };
struct nfs4_state *res;
int status;
do {
- status = _nfs4_do_open(dir, path, fmode, flags, sattr, cred, &res);
+ status = _nfs4_do_open(dir, dentry, fmode, flags, sattr, cred, &res);
if (status == 0)
break;
/* NOTE: BAD_SEQID means the server and client disagree about the
@@ -1869,7 +1892,6 @@ static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
}
struct nfs4_closedata {
- struct path path;
struct inode *inode;
struct nfs4_state *state;
struct nfs_closeargs arg;
@@ -1884,13 +1906,14 @@ static void nfs4_free_closedata(void *data)
{
struct nfs4_closedata *calldata = data;
struct nfs4_state_owner *sp = calldata->state->owner;
+ struct super_block *sb = calldata->state->inode->i_sb;
if (calldata->roc)
pnfs_roc_release(calldata->state->inode);
nfs4_put_open_state(calldata->state);
nfs_free_seqid(calldata->arg.seqid);
nfs4_put_state_owner(sp);
- path_put(&calldata->path);
+ nfs_sb_deactive(sb);
kfree(calldata);
}
@@ -2010,7 +2033,7 @@ static const struct rpc_call_ops nfs4_close_ops = {
*
* NOTE: Caller must be holding the sp->so_owner semaphore!
*/
-int nfs4_do_close(struct path *path, struct nfs4_state *state, gfp_t gfp_mask, int wait, bool roc)
+int nfs4_do_close(struct nfs4_state *state, gfp_t gfp_mask, int wait, bool roc)
{
struct nfs_server *server = NFS_SERVER(state->inode);
struct nfs4_closedata *calldata;
@@ -2046,8 +2069,7 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, gfp_t gfp_mask, i
calldata->res.seqid = calldata->arg.seqid;
calldata->res.server = server;
calldata->roc = roc;
- path_get(path);
- calldata->path = *path;
+ nfs_sb_active(calldata->inode->i_sb);
msg.rpc_argp = &calldata->arg;
msg.rpc_resp = &calldata->res;
@@ -2076,7 +2098,7 @@ nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx, int open_flags
struct nfs4_state *state;
/* Protect against concurrent sillydeletes */
- state = nfs4_do_open(dir, &ctx->path, ctx->mode, open_flags, attr, ctx->cred);
+ state = nfs4_do_open(dir, ctx->dentry, ctx->mode, open_flags, attr, ctx->cred);
if (IS_ERR(state))
return ERR_CAST(state);
ctx->state = state;
@@ -2088,9 +2110,9 @@ static void nfs4_close_context(struct nfs_open_context *ctx, int is_sync)
if (ctx->state == NULL)
return;
if (is_sync)
- nfs4_close_sync(&ctx->path, ctx->state, ctx->mode);
+ nfs4_close_sync(ctx->state, ctx->mode);
else
- nfs4_close_state(&ctx->path, ctx->state, ctx->mode);
+ nfs4_close_state(ctx->state, ctx->mode);
}
static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle)
@@ -2185,9 +2207,14 @@ static int nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs4_exception exception = { };
int err;
do {
- err = nfs4_handle_exception(server,
- _nfs4_lookup_root(server, fhandle, info),
- &exception);
+ err = _nfs4_lookup_root(server, fhandle, info);
+ switch (err) {
+ case 0:
+ case -NFS4ERR_WRONGSEC:
+ break;
+ default:
+ err = nfs4_handle_exception(server, err, &exception);
+ }
} while (exception.retry);
return err;
}
@@ -2208,25 +2235,48 @@ out:
return ret;
}
-/*
- * get the file handle for the "/" directory on the server
- */
-static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
+static int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fsinfo *info)
{
int i, len, status = 0;
- rpc_authflavor_t flav_array[NFS_MAX_SECFLAVORS + 2];
+ rpc_authflavor_t flav_array[NFS_MAX_SECFLAVORS];
- flav_array[0] = RPC_AUTH_UNIX;
- len = gss_mech_list_pseudoflavors(&flav_array[1]);
- flav_array[1+len] = RPC_AUTH_NULL;
- len += 2;
+ len = gss_mech_list_pseudoflavors(&flav_array[0]);
+ flav_array[len] = RPC_AUTH_NULL;
+ len += 1;
for (i = 0; i < len; i++) {
status = nfs4_lookup_root_sec(server, fhandle, info, flav_array[i]);
- if (status != -EPERM)
- break;
+ if (status == -NFS4ERR_WRONGSEC || status == -EACCES)
+ continue;
+ break;
}
+ /*
+ * -EACCESS could mean that the user doesn't have correct permissions
+ * to access the mount. It could also mean that we tried to mount
+ * with a gss auth flavor, but rpc.gssd isn't running. Either way,
+ * existing mount programs don't handle -EACCES very well so it should
+ * be mapped to -EPERM instead.
+ */
+ if (status == -EACCES)
+ status = -EPERM;
+ return status;
+}
+
+/*
+ * get the file handle for the "/" directory on the server
+ */
+static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
+ struct nfs_fsinfo *info)
+{
+ int minor_version = server->nfs_client->cl_minorversion;
+ int status = nfs4_lookup_root(server, fhandle, info);
+ if ((status == -NFS4ERR_WRONGSEC) && !(server->flags & NFS_MOUNT_SECFLAVOUR))
+ /*
+ * A status of -NFS4ERR_WRONGSEC will be mapped to -EPERM
+ * by nfs4_map_errors() as this function exits.
+ */
+ status = nfs_v4_minor_ops[minor_version]->find_root_sec(server, fhandle, info);
if (status == 0)
status = nfs4_server_capabilities(server, fhandle);
if (status == 0)
@@ -2234,12 +2284,14 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
return nfs4_map_errors(status);
}
+static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr);
/*
* Get locations and (maybe) other attributes of a referral.
* Note that we'll actually follow the referral later when
* we detect fsid mismatch in inode revalidation
*/
-static int nfs4_get_referral(struct inode *dir, const struct qstr *name, struct nfs_fattr *fattr, struct nfs_fh *fhandle)
+static int nfs4_get_referral(struct inode *dir, const struct qstr *name,
+ struct nfs_fattr *fattr, struct nfs_fh *fhandle)
{
int status = -ENOMEM;
struct page *page = NULL;
@@ -2257,15 +2309,16 @@ static int nfs4_get_referral(struct inode *dir, const struct qstr *name, struct
goto out;
/* Make sure server returned a different fsid for the referral */
if (nfs_fsid_equal(&NFS_SERVER(dir)->fsid, &locations->fattr.fsid)) {
- dprintk("%s: server did not return a different fsid for a referral at %s\n", __func__, name->name);
+ dprintk("%s: server did not return a different fsid for"
+ " a referral at %s\n", __func__, name->name);
status = -EIO;
goto out;
}
+ /* Fixup attributes for the nfs_lookup() call to nfs_fhget() */
+ nfs_fixup_referral_attributes(&locations->fattr);
+ /* replace the lookup nfs_fattr with the locations nfs_fattr */
memcpy(fattr, &locations->fattr, sizeof(struct nfs_fattr));
- fattr->valid |= NFS_ATTR_FATTR_V4_REFERRAL;
- if (!fattr->mode)
- fattr->mode = S_IFDIR;
memset(fhandle, 0, sizeof(struct nfs_fh));
out:
if (page)
@@ -2332,6 +2385,9 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
struct nfs4_state *state = NULL;
int status;
+ if (pnfs_ld_layoutret_on_setattr(inode))
+ pnfs_return_layout(inode);
+
nfs_fattr_init(fattr);
/* Search for an existing open(O_WRITE) file */
@@ -2579,10 +2635,7 @@ static int
nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
int flags, struct nfs_open_context *ctx)
{
- struct path my_path = {
- .dentry = dentry,
- };
- struct path *path = &my_path;
+ struct dentry *de = dentry;
struct nfs4_state *state;
struct rpc_cred *cred = NULL;
fmode_t fmode = 0;
@@ -2590,11 +2643,11 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
if (ctx != NULL) {
cred = ctx->cred;
- path = &ctx->path;
+ de = ctx->dentry;
fmode = ctx->mode;
}
sattr->ia_mode &= ~current_umask();
- state = nfs4_do_open(dir, path, fmode, flags, sattr, cred);
+ state = nfs4_do_open(dir, de, fmode, flags, sattr, cred);
d_drop(dentry);
if (IS_ERR(state)) {
status = PTR_ERR(state);
@@ -2605,7 +2658,7 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
if (ctx != NULL)
ctx->state = state;
else
- nfs4_close_sync(path, state, fmode);
+ nfs4_close_sync(state, fmode);
out:
return status;
}
@@ -3146,6 +3199,11 @@ static int nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
return err;
}
+void __nfs4_read_done_cb(struct nfs_read_data *data)
+{
+ nfs_invalidate_atime(data->inode);
+}
+
static int nfs4_read_done_cb(struct rpc_task *task, struct nfs_read_data *data)
{
struct nfs_server *server = NFS_SERVER(data->inode);
@@ -3155,7 +3213,7 @@ static int nfs4_read_done_cb(struct rpc_task *task, struct nfs_read_data *data)
return -EAGAIN;
}
- nfs_invalidate_atime(data->inode);
+ __nfs4_read_done_cb(data);
if (task->tk_status > 0)
renew_lease(server, data->timestamp);
return 0;
@@ -3169,7 +3227,8 @@ static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data)
if (!nfs4_sequence_done(task, &data->res.seq_res))
return -EAGAIN;
- return data->read_done_cb(task, data);
+ return data->read_done_cb ? data->read_done_cb(task, data) :
+ nfs4_read_done_cb(task, data);
}
static void nfs4_proc_read_setup(struct nfs_read_data *data, struct rpc_message *msg)
@@ -3214,7 +3273,8 @@ static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data)
{
if (!nfs4_sequence_done(task, &data->res.seq_res))
return -EAGAIN;
- return data->write_done_cb(task, data);
+ return data->write_done_cb ? data->write_done_cb(task, data) :
+ nfs4_write_done_cb(task, data);
}
/* Reset the the nfs_write_data to send the write to the MDS. */
@@ -3641,9 +3701,11 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
break;
nfs4_schedule_stateid_recovery(server, state);
goto wait_on_recovery;
+ case -NFS4ERR_EXPIRED:
+ if (state != NULL)
+ nfs4_schedule_stateid_recovery(server, state);
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_STALE_CLIENTID:
- case -NFS4ERR_EXPIRED:
nfs4_schedule_lease_recovery(clp);
goto wait_on_recovery;
#if defined(CONFIG_NFS_V4_1)
@@ -3667,6 +3729,7 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
rpc_delay(task, NFS4_POLL_RETRY_MAX);
task->tk_status = 0;
return -EAGAIN;
+ case -NFS4ERR_RETRY_UNCACHED_REP:
case -NFS4ERR_OLD_STATEID:
task->tk_status = 0;
return -EAGAIN;
@@ -3723,21 +3786,20 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,
sizeof(setclientid.sc_uaddr), "%s.%u.%u",
clp->cl_ipaddr, port >> 8, port & 255);
- status = rpc_call_sync(clp->cl_rpcclient, &msg, 0);
+ status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
if (status != -NFS4ERR_CLID_INUSE)
break;
- if (signalled())
+ if (loop != 0) {
+ ++clp->cl_id_uniquifier;
break;
- if (loop++ & 1)
- ssleep(clp->cl_lease_time / HZ + 1);
- else
- if (++clp->cl_id_uniquifier == 0)
- break;
+ }
+ ++loop;
+ ssleep(clp->cl_lease_time / HZ + 1);
}
return status;
}
-static int _nfs4_proc_setclientid_confirm(struct nfs_client *clp,
+int nfs4_proc_setclientid_confirm(struct nfs_client *clp,
struct nfs4_setclientid_res *arg,
struct rpc_cred *cred)
{
@@ -3752,7 +3814,7 @@ static int _nfs4_proc_setclientid_confirm(struct nfs_client *clp,
int status;
now = jiffies;
- status = rpc_call_sync(clp->cl_rpcclient, &msg, 0);
+ status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
if (status == 0) {
spin_lock(&clp->cl_lock);
clp->cl_lease_time = fsinfo.lease_time * HZ;
@@ -3762,26 +3824,6 @@ static int _nfs4_proc_setclientid_confirm(struct nfs_client *clp,
return status;
}
-int nfs4_proc_setclientid_confirm(struct nfs_client *clp,
- struct nfs4_setclientid_res *arg,
- struct rpc_cred *cred)
-{
- long timeout = 0;
- int err;
- do {
- err = _nfs4_proc_setclientid_confirm(clp, arg, cred);
- switch (err) {
- case 0:
- return err;
- case -NFS4ERR_RESOURCE:
- /* The IBM lawyers misread another document! */
- case -NFS4ERR_DELAY:
- err = nfs4_delay(clp->cl_rpcclient, &timeout);
- }
- } while (err == 0);
- return err;
-}
-
struct nfs4_delegreturndata {
struct nfs4_delegreturnargs args;
struct nfs4_delegreturnres res;
@@ -4268,7 +4310,7 @@ static void nfs4_lock_done(struct rpc_task *task, void *calldata)
memcpy(data->lsp->ls_stateid.data, data->res.stateid.data,
sizeof(data->lsp->ls_stateid.data));
data->lsp->ls_flags |= NFS_LOCK_INITIALIZED;
- renew_lease(NFS_SERVER(data->ctx->path.dentry->d_inode), data->timestamp);
+ renew_lease(NFS_SERVER(data->ctx->dentry->d_inode), data->timestamp);
}
out:
dprintk("%s: done, ret = %d!\n", __func__, data->rpc_status);
@@ -4417,6 +4459,20 @@ out:
return err;
}
+#if defined(CONFIG_NFS_V4_1)
+static int nfs41_lock_expired(struct nfs4_state *state, struct file_lock *request)
+{
+ int status;
+ struct nfs_server *server = NFS_SERVER(state->inode);
+
+ status = nfs41_test_stateid(server, state);
+ if (status == NFS_OK)
+ return 0;
+ nfs41_free_stateid(server, state);
+ return nfs4_lock_expired(state, request);
+}
+#endif
+
static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
{
struct nfs_inode *nfsi = NFS_I(state->inode);
@@ -4534,6 +4590,7 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl)
case -ESTALE:
goto out;
case -NFS4ERR_EXPIRED:
+ nfs4_schedule_stateid_recovery(server, state);
case -NFS4ERR_STALE_CLIENTID:
case -NFS4ERR_STALE_STATEID:
nfs4_schedule_lease_recovery(server->nfs_client);
@@ -4643,11 +4700,15 @@ static size_t nfs4_xattr_list_nfs4_acl(struct dentry *dentry, char *list,
return len;
}
+/*
+ * nfs_fhget will use either the mounted_on_fileid or the fileid
+ */
static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr)
{
- if (!((fattr->valid & NFS_ATTR_FATTR_FILEID) &&
- (fattr->valid & NFS_ATTR_FATTR_FSID) &&
- (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)))
+ if (!(((fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID) ||
+ (fattr->valid & NFS_ATTR_FATTR_FILEID)) &&
+ (fattr->valid & NFS_ATTR_FATTR_FSID) &&
+ (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)))
return;
fattr->valid |= NFS_ATTR_FATTR_TYPE | NFS_ATTR_FATTR_MODE |
@@ -4662,7 +4723,6 @@ int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
struct nfs_server *server = NFS_SERVER(dir);
u32 bitmask[2] = {
[0] = FATTR4_WORD0_FSID | FATTR4_WORD0_FS_LOCATIONS,
- [1] = FATTR4_WORD1_MOUNTED_ON_FILEID,
};
struct nfs4_fs_locations_arg args = {
.dir_fh = NFS_FH(dir),
@@ -4681,11 +4741,18 @@ int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
int status;
dprintk("%s: start\n", __func__);
+
+ /* Ask for the fileid of the absent filesystem if mounted_on_fileid
+ * is not supported */
+ if (NFS_SERVER(dir)->attr_bitmask[1] & FATTR4_WORD1_MOUNTED_ON_FILEID)
+ bitmask[1] |= FATTR4_WORD1_MOUNTED_ON_FILEID;
+ else
+ bitmask[0] |= FATTR4_WORD0_FILEID;
+
nfs_fattr_init(&fs_locations->fattr);
fs_locations->server = server;
fs_locations->nlocations = 0;
status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
- nfs_fixup_referral_attributes(&fs_locations->fattr);
dprintk("%s: returned status = %d\n", __func__, status);
return status;
}
@@ -4744,6 +4811,16 @@ out_inval:
return -NFS4ERR_INVAL;
}
+static bool
+nfs41_same_server_scope(struct server_scope *a, struct server_scope *b)
+{
+ if (a->server_scope_sz == b->server_scope_sz &&
+ memcmp(a->server_scope, b->server_scope, a->server_scope_sz) == 0)
+ return true;
+
+ return false;
+}
+
/*
* nfs4_proc_exchange_id()
*
@@ -4786,9 +4863,31 @@ int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
init_utsname()->domainname,
clp->cl_rpcclient->cl_auth->au_flavor);
- status = rpc_call_sync(clp->cl_rpcclient, &msg, 0);
+ res.server_scope = kzalloc(sizeof(struct server_scope), GFP_KERNEL);
+ if (unlikely(!res.server_scope))
+ return -ENOMEM;
+
+ status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
if (!status)
status = nfs4_check_cl_exchange_flags(clp->cl_exchange_flags);
+
+ if (!status) {
+ if (clp->server_scope &&
+ !nfs41_same_server_scope(clp->server_scope,
+ res.server_scope)) {
+ dprintk("%s: server_scope mismatch detected\n",
+ __func__);
+ set_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH, &clp->cl_state);
+ kfree(clp->server_scope);
+ clp->server_scope = NULL;
+ }
+
+ if (!clp->server_scope)
+ clp->server_scope = res.server_scope;
+ else
+ kfree(res.server_scope);
+ }
+
dprintk("<-- %s status= %d\n", __func__, status);
return status;
}
@@ -4837,6 +4936,8 @@ static void nfs4_get_lease_time_done(struct rpc_task *task, void *calldata)
dprintk("%s Retry: tk_status %d\n", __func__, task->tk_status);
rpc_delay(task, NFS4_POLL_RETRY_MIN);
task->tk_status = 0;
+ /* fall through */
+ case -NFS4ERR_RETRY_UNCACHED_REP:
nfs_restart_rpc(task, data->clp);
return;
}
@@ -4869,7 +4970,8 @@ int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo)
.rpc_client = clp->cl_rpcclient,
.rpc_message = &msg,
.callback_ops = &nfs4_get_lease_time_ops,
- .callback_data = &data
+ .callback_data = &data,
+ .flags = RPC_TASK_TIMEOUT,
};
int status;
@@ -5071,7 +5173,6 @@ static void nfs4_init_channel_attrs(struct nfs41_create_session_args *args)
if (mxresp_sz == 0)
mxresp_sz = NFS_MAX_FILE_IO_SIZE;
/* Fore channel attributes */
- args->fc_attrs.headerpadsz = 0;
args->fc_attrs.max_rqst_sz = mxrqst_sz;
args->fc_attrs.max_resp_sz = mxresp_sz;
args->fc_attrs.max_ops = NFS4_MAX_OPS;
@@ -5084,7 +5185,6 @@ static void nfs4_init_channel_attrs(struct nfs41_create_session_args *args)
args->fc_attrs.max_ops, args->fc_attrs.max_reqs);
/* Back channel attributes */
- args->bc_attrs.headerpadsz = 0;
args->bc_attrs.max_rqst_sz = PAGE_SIZE;
args->bc_attrs.max_resp_sz = PAGE_SIZE;
args->bc_attrs.max_resp_sz_cached = 0;
@@ -5104,8 +5204,6 @@ static int nfs4_verify_fore_channel_attrs(struct nfs41_create_session_args *args
struct nfs4_channel_attrs *sent = &args->fc_attrs;
struct nfs4_channel_attrs *rcvd = &session->fc_attrs;
- if (rcvd->headerpadsz > sent->headerpadsz)
- return -EINVAL;
if (rcvd->max_resp_sz > sent->max_resp_sz)
return -EINVAL;
/*
@@ -5171,7 +5269,7 @@ static int _nfs4_proc_create_session(struct nfs_client *clp)
nfs4_init_channel_attrs(&args);
args.flags = (SESSION4_PERSIST | SESSION4_BACK_CHAN);
- status = rpc_call_sync(session->clp->cl_rpcclient, &msg, 0);
+ status = rpc_call_sync(session->clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
if (!status)
/* Verify the session's negotiated channel_attrs values */
@@ -5194,20 +5292,10 @@ int nfs4_proc_create_session(struct nfs_client *clp)
int status;
unsigned *ptr;
struct nfs4_session *session = clp->cl_session;
- long timeout = 0;
- int err;
dprintk("--> %s clp=%p session=%p\n", __func__, clp, session);
- do {
- status = _nfs4_proc_create_session(clp);
- if (status == -NFS4ERR_DELAY) {
- err = nfs4_delay(clp->cl_rpcclient, &timeout);
- if (err)
- status = err;
- }
- } while (status == -NFS4ERR_DELAY);
-
+ status = _nfs4_proc_create_session(clp);
if (status)
goto out;
@@ -5248,7 +5336,7 @@ int nfs4_proc_destroy_session(struct nfs4_session *session)
msg.rpc_argp = session;
msg.rpc_resp = NULL;
msg.rpc_cred = NULL;
- status = rpc_call_sync(session->clp->cl_rpcclient, &msg, 0);
+ status = rpc_call_sync(session->clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
if (status)
printk(KERN_WARNING
@@ -5481,6 +5569,8 @@ static int nfs41_reclaim_complete_handle_errors(struct rpc_task *task, struct nf
break;
case -NFS4ERR_DELAY:
rpc_delay(task, NFS4_POLL_RETRY_MAX);
+ /* fall through */
+ case -NFS4ERR_RETRY_UNCACHED_REP:
return -EAGAIN;
default:
nfs4_schedule_lease_recovery(clp);
@@ -5662,6 +5752,88 @@ int nfs4_proc_layoutget(struct nfs4_layoutget *lgp)
return status;
}
+static void
+nfs4_layoutreturn_prepare(struct rpc_task *task, void *calldata)
+{
+ struct nfs4_layoutreturn *lrp = calldata;
+
+ dprintk("--> %s\n", __func__);
+ if (nfs41_setup_sequence(lrp->clp->cl_session, &lrp->args.seq_args,
+ &lrp->res.seq_res, 0, task))
+ return;
+ rpc_call_start(task);
+}
+
+static void nfs4_layoutreturn_done(struct rpc_task *task, void *calldata)
+{
+ struct nfs4_layoutreturn *lrp = calldata;
+ struct nfs_server *server;
+ struct pnfs_layout_hdr *lo = lrp->args.layout;
+
+ dprintk("--> %s\n", __func__);
+
+ if (!nfs4_sequence_done(task, &lrp->res.seq_res))
+ return;
+
+ server = NFS_SERVER(lrp->args.inode);
+ if (nfs4_async_handle_error(task, server, NULL) == -EAGAIN) {
+ nfs_restart_rpc(task, lrp->clp);
+ return;
+ }
+ spin_lock(&lo->plh_inode->i_lock);
+ if (task->tk_status == 0) {
+ if (lrp->res.lrs_present) {
+ pnfs_set_layout_stateid(lo, &lrp->res.stateid, true);
+ } else
+ BUG_ON(!list_empty(&lo->plh_segs));
+ }
+ lo->plh_block_lgets--;
+ spin_unlock(&lo->plh_inode->i_lock);
+ dprintk("<-- %s\n", __func__);
+}
+
+static void nfs4_layoutreturn_release(void *calldata)
+{
+ struct nfs4_layoutreturn *lrp = calldata;
+
+ dprintk("--> %s\n", __func__);
+ put_layout_hdr(lrp->args.layout);
+ kfree(calldata);
+ dprintk("<-- %s\n", __func__);
+}
+
+static const struct rpc_call_ops nfs4_layoutreturn_call_ops = {
+ .rpc_call_prepare = nfs4_layoutreturn_prepare,
+ .rpc_call_done = nfs4_layoutreturn_done,
+ .rpc_release = nfs4_layoutreturn_release,
+};
+
+int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp)
+{
+ struct rpc_task *task;
+ struct rpc_message msg = {
+ .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTRETURN],
+ .rpc_argp = &lrp->args,
+ .rpc_resp = &lrp->res,
+ };
+ struct rpc_task_setup task_setup_data = {
+ .rpc_client = lrp->clp->cl_rpcclient,
+ .rpc_message = &msg,
+ .callback_ops = &nfs4_layoutreturn_call_ops,
+ .callback_data = lrp,
+ };
+ int status;
+
+ dprintk("--> %s\n", __func__);
+ task = rpc_run_task(&task_setup_data);
+ if (IS_ERR(task))
+ return PTR_ERR(task);
+ status = task->tk_status;
+ dprintk("<-- %s status=%d\n", __func__, status);
+ rpc_put_task(task);
+ return status;
+}
+
static int
_nfs4_proc_getdeviceinfo(struct nfs_server *server, struct pnfs_device *pdev)
{
@@ -5793,6 +5965,143 @@ out:
rpc_put_task(task);
return status;
}
+
+static int
+_nfs41_proc_secinfo_no_name(struct nfs_server *server, struct nfs_fh *fhandle,
+ struct nfs_fsinfo *info, struct nfs4_secinfo_flavors *flavors)
+{
+ struct nfs41_secinfo_no_name_args args = {
+ .style = SECINFO_STYLE_CURRENT_FH,
+ };
+ struct nfs4_secinfo_res res = {
+ .flavors = flavors,
+ };
+ struct rpc_message msg = {
+ .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SECINFO_NO_NAME],
+ .rpc_argp = &args,
+ .rpc_resp = &res,
+ };
+ return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
+}
+
+static int
+nfs41_proc_secinfo_no_name(struct nfs_server *server, struct nfs_fh *fhandle,
+ struct nfs_fsinfo *info, struct nfs4_secinfo_flavors *flavors)
+{
+ struct nfs4_exception exception = { };
+ int err;
+ do {
+ err = _nfs41_proc_secinfo_no_name(server, fhandle, info, flavors);
+ switch (err) {
+ case 0:
+ case -NFS4ERR_WRONGSEC:
+ case -NFS4ERR_NOTSUPP:
+ break;
+ default:
+ err = nfs4_handle_exception(server, err, &exception);
+ }
+ } while (exception.retry);
+ return err;
+}
+
+static int
+nfs41_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
+ struct nfs_fsinfo *info)
+{
+ int err;
+ struct page *page;
+ rpc_authflavor_t flavor;
+ struct nfs4_secinfo_flavors *flavors;
+
+ page = alloc_page(GFP_KERNEL);
+ if (!page) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ flavors = page_address(page);
+ err = nfs41_proc_secinfo_no_name(server, fhandle, info, flavors);
+
+ /*
+ * Fall back on "guess and check" method if
+ * the server doesn't support SECINFO_NO_NAME
+ */
+ if (err == -NFS4ERR_WRONGSEC || err == -NFS4ERR_NOTSUPP) {
+ err = nfs4_find_root_sec(server, fhandle, info);
+ goto out_freepage;
+ }
+ if (err)
+ goto out_freepage;
+
+ flavor = nfs_find_best_sec(flavors);
+ if (err == 0)
+ err = nfs4_lookup_root_sec(server, fhandle, info, flavor);
+
+out_freepage:
+ put_page(page);
+ if (err == -EACCES)
+ return -EPERM;
+out:
+ return err;
+}
+static int _nfs41_test_stateid(struct nfs_server *server, struct nfs4_state *state)
+{
+ int status;
+ struct nfs41_test_stateid_args args = {
+ .stateid = &state->stateid,
+ };
+ struct nfs41_test_stateid_res res;
+ struct rpc_message msg = {
+ .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_TEST_STATEID],
+ .rpc_argp = &args,
+ .rpc_resp = &res,
+ };
+ args.seq_args.sa_session = res.seq_res.sr_session = NULL;
+ status = nfs4_call_sync_sequence(server->client, server, &msg, &args.seq_args, &res.seq_res, 0, 1);
+ return status;
+}
+
+static int nfs41_test_stateid(struct nfs_server *server, struct nfs4_state *state)
+{
+ struct nfs4_exception exception = { };
+ int err;
+ do {
+ err = nfs4_handle_exception(server,
+ _nfs41_test_stateid(server, state),
+ &exception);
+ } while (exception.retry);
+ return err;
+}
+
+static int _nfs4_free_stateid(struct nfs_server *server, struct nfs4_state *state)
+{
+ int status;
+ struct nfs41_free_stateid_args args = {
+ .stateid = &state->stateid,
+ };
+ struct nfs41_free_stateid_res res;
+ struct rpc_message msg = {
+ .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FREE_STATEID],
+ .rpc_argp = &args,
+ .rpc_resp = &res,
+ };
+
+ args.seq_args.sa_session = res.seq_res.sr_session = NULL;
+ status = nfs4_call_sync_sequence(server->client, server, &msg, &args.seq_args, &res.seq_res, 0, 1);
+ return status;
+}
+
+static int nfs41_free_stateid(struct nfs_server *server, struct nfs4_state *state)
+{
+ struct nfs4_exception exception = { };
+ int err;
+ do {
+ err = nfs4_handle_exception(server,
+ _nfs4_free_stateid(server, state),
+ &exception);
+ } while (exception.retry);
+ return err;
+}
#endif /* CONFIG_NFS_V4_1 */
struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = {
@@ -5829,8 +6138,8 @@ struct nfs4_state_recovery_ops nfs40_nograce_recovery_ops = {
struct nfs4_state_recovery_ops nfs41_nograce_recovery_ops = {
.owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE,
.state_flag_bit = NFS_STATE_RECLAIM_NOGRACE,
- .recover_open = nfs4_open_expired,
- .recover_lock = nfs4_lock_expired,
+ .recover_open = nfs41_open_expired,
+ .recover_lock = nfs41_lock_expired,
.establish_clid = nfs41_init_clientid,
.get_clid_cred = nfs4_get_exchange_id_cred,
};
@@ -5854,6 +6163,7 @@ static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = {
.minor_version = 0,
.call_sync = _nfs4_call_sync,
.validate_stateid = nfs4_validate_delegation_stateid,
+ .find_root_sec = nfs4_find_root_sec,
.reboot_recovery_ops = &nfs40_reboot_recovery_ops,
.nograce_recovery_ops = &nfs40_nograce_recovery_ops,
.state_renewal_ops = &nfs40_state_renewal_ops,
@@ -5864,6 +6174,7 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
.minor_version = 1,
.call_sync = _nfs4_call_sync_session,
.validate_stateid = nfs41_validate_delegation_stateid,
+ .find_root_sec = nfs41_find_root_sec,
.reboot_recovery_ops = &nfs41_reboot_recovery_ops,
.nograce_recovery_ops = &nfs41_nograce_recovery_ops,
.state_renewal_ops = &nfs41_state_renewal_ops,