|
@@ -105,9 +105,6 @@ nfs4_label_init_security(struct inode *dir, struct dentry *dentry,
|
|
|
if (nfs_server_capable(dir, NFS_CAP_SECURITY_LABEL) == 0)
|
|
|
return NULL;
|
|
|
|
|
|
- if (NFS_SERVER(dir)->nfs_client->cl_minorversion < 2)
|
|
|
- return NULL;
|
|
|
-
|
|
|
err = security_dentry_init_security(dentry, sattr->ia_mode,
|
|
|
&dentry->d_name, (void **)&label->label, &label->len);
|
|
|
if (err == 0)
|
|
@@ -384,6 +381,14 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc
|
|
|
case -NFS4ERR_STALE_CLIENTID:
|
|
|
nfs4_schedule_lease_recovery(clp);
|
|
|
goto wait_on_recovery;
|
|
|
+ case -NFS4ERR_MOVED:
|
|
|
+ ret = nfs4_schedule_migration_recovery(server);
|
|
|
+ if (ret < 0)
|
|
|
+ break;
|
|
|
+ goto wait_on_recovery;
|
|
|
+ case -NFS4ERR_LEASE_MOVED:
|
|
|
+ nfs4_schedule_lease_moved_recovery(clp);
|
|
|
+ goto wait_on_recovery;
|
|
|
#if defined(CONFIG_NFS_V4_1)
|
|
|
case -NFS4ERR_BADSESSION:
|
|
|
case -NFS4ERR_BADSLOT:
|
|
@@ -431,6 +436,8 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc
|
|
|
return nfs4_map_errors(ret);
|
|
|
wait_on_recovery:
|
|
|
ret = nfs4_wait_clnt_recover(clp);
|
|
|
+ if (test_bit(NFS_MIG_FAILED, &server->mig_status))
|
|
|
+ return -EIO;
|
|
|
if (ret == 0)
|
|
|
exception->retry = 1;
|
|
|
return ret;
|
|
@@ -1318,31 +1325,24 @@ _nfs4_opendata_reclaim_to_nfs4_state(struct nfs4_opendata *data)
|
|
|
int ret;
|
|
|
|
|
|
if (!data->rpc_done) {
|
|
|
- ret = data->rpc_status;
|
|
|
- goto err;
|
|
|
+ if (data->rpc_status) {
|
|
|
+ ret = data->rpc_status;
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+ /* cached opens have already been processed */
|
|
|
+ goto update;
|
|
|
}
|
|
|
|
|
|
- ret = -ESTALE;
|
|
|
- if (!(data->f_attr.valid & NFS_ATTR_FATTR_TYPE) ||
|
|
|
- !(data->f_attr.valid & NFS_ATTR_FATTR_FILEID) ||
|
|
|
- !(data->f_attr.valid & NFS_ATTR_FATTR_CHANGE))
|
|
|
- goto err;
|
|
|
-
|
|
|
- ret = -ENOMEM;
|
|
|
- state = nfs4_get_open_state(inode, data->owner);
|
|
|
- if (state == NULL)
|
|
|
- goto err;
|
|
|
-
|
|
|
ret = nfs_refresh_inode(inode, &data->f_attr);
|
|
|
if (ret)
|
|
|
goto err;
|
|
|
|
|
|
- nfs_setsecurity(inode, &data->f_attr, data->f_label);
|
|
|
-
|
|
|
if (data->o_res.delegation_type != 0)
|
|
|
nfs4_opendata_check_deleg(data, state);
|
|
|
+update:
|
|
|
update_open_stateid(state, &data->o_res.stateid, NULL,
|
|
|
data->o_arg.fmode);
|
|
|
+ atomic_inc(&state->count);
|
|
|
|
|
|
return state;
|
|
|
err:
|
|
@@ -1575,6 +1575,12 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct
|
|
|
/* Don't recall a delegation if it was lost */
|
|
|
nfs4_schedule_lease_recovery(server->nfs_client);
|
|
|
return -EAGAIN;
|
|
|
+ case -NFS4ERR_MOVED:
|
|
|
+ nfs4_schedule_migration_recovery(server);
|
|
|
+ return -EAGAIN;
|
|
|
+ case -NFS4ERR_LEASE_MOVED:
|
|
|
+ nfs4_schedule_lease_moved_recovery(server->nfs_client);
|
|
|
+ return -EAGAIN;
|
|
|
case -NFS4ERR_DELEG_REVOKED:
|
|
|
case -NFS4ERR_ADMIN_REVOKED:
|
|
|
case -NFS4ERR_BAD_STATEID:
|
|
@@ -2697,6 +2703,10 @@ static void nfs4_close_context(struct nfs_open_context *ctx, int is_sync)
|
|
|
nfs4_close_state(ctx->state, ctx->mode);
|
|
|
}
|
|
|
|
|
|
+#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_CHANGE_SECURITY_LABEL - 1UL)
|
|
|
+
|
|
|
static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle)
|
|
|
{
|
|
|
struct nfs4_server_caps_arg args = {
|
|
@@ -2712,12 +2722,25 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
|
|
|
|
|
|
status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
|
|
|
if (status == 0) {
|
|
|
+ /* Sanity check the server answers */
|
|
|
+ switch (server->nfs_client->cl_minorversion) {
|
|
|
+ case 0:
|
|
|
+ res.attr_bitmask[1] &= FATTR4_WORD1_NFS40_MASK;
|
|
|
+ res.attr_bitmask[2] = 0;
|
|
|
+ break;
|
|
|
+ case 1:
|
|
|
+ res.attr_bitmask[2] &= FATTR4_WORD2_NFS41_MASK;
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ res.attr_bitmask[2] &= FATTR4_WORD2_NFS42_MASK;
|
|
|
+ }
|
|
|
memcpy(server->attr_bitmask, res.attr_bitmask, sizeof(server->attr_bitmask));
|
|
|
server->caps &= ~(NFS_CAP_ACLS|NFS_CAP_HARDLINKS|
|
|
|
NFS_CAP_SYMLINKS|NFS_CAP_FILEID|
|
|
|
NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER|
|
|
|
NFS_CAP_OWNER_GROUP|NFS_CAP_ATIME|
|
|
|
- NFS_CAP_CTIME|NFS_CAP_MTIME);
|
|
|
+ NFS_CAP_CTIME|NFS_CAP_MTIME|
|
|
|
+ NFS_CAP_SECURITY_LABEL);
|
|
|
if (res.attr_bitmask[0] & FATTR4_WORD0_ACL)
|
|
|
server->caps |= NFS_CAP_ACLS;
|
|
|
if (res.has_links != 0)
|
|
@@ -2746,14 +2769,12 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
|
|
|
#endif
|
|
|
memcpy(server->attr_bitmask_nl, res.attr_bitmask,
|
|
|
sizeof(server->attr_bitmask));
|
|
|
+ server->attr_bitmask_nl[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
|
|
|
|
|
|
- if (server->caps & NFS_CAP_SECURITY_LABEL) {
|
|
|
- server->attr_bitmask_nl[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
|
|
|
- res.attr_bitmask[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
|
|
|
- }
|
|
|
memcpy(server->cache_consistency_bitmask, res.attr_bitmask, sizeof(server->cache_consistency_bitmask));
|
|
|
server->cache_consistency_bitmask[0] &= FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE;
|
|
|
server->cache_consistency_bitmask[1] &= FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY;
|
|
|
+ server->cache_consistency_bitmask[2] = 0;
|
|
|
server->acl_bitmask = res.acl_bitmask;
|
|
|
server->fh_expire_type = res.fh_expire_type;
|
|
|
}
|
|
@@ -2864,11 +2885,24 @@ static int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
|
|
|
int status = -EPERM;
|
|
|
size_t i;
|
|
|
|
|
|
- for (i = 0; i < ARRAY_SIZE(flav_array); i++) {
|
|
|
- status = nfs4_lookup_root_sec(server, fhandle, info, flav_array[i]);
|
|
|
- if (status == -NFS4ERR_WRONGSEC || status == -EACCES)
|
|
|
- continue;
|
|
|
- break;
|
|
|
+ if (server->auth_info.flavor_len > 0) {
|
|
|
+ /* try each flavor specified by user */
|
|
|
+ for (i = 0; i < server->auth_info.flavor_len; i++) {
|
|
|
+ status = nfs4_lookup_root_sec(server, fhandle, info,
|
|
|
+ server->auth_info.flavors[i]);
|
|
|
+ if (status == -NFS4ERR_WRONGSEC || status == -EACCES)
|
|
|
+ continue;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ /* no flavors specified by user, try default list */
|
|
|
+ for (i = 0; i < ARRAY_SIZE(flav_array); i++) {
|
|
|
+ status = nfs4_lookup_root_sec(server, fhandle, info,
|
|
|
+ flav_array[i]);
|
|
|
+ if (status == -NFS4ERR_WRONGSEC || status == -EACCES)
|
|
|
+ continue;
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -2910,9 +2944,6 @@ int nfs4_proc_get_rootfh(struct nfs_server *server, struct nfs_fh *fhandle,
|
|
|
status = nfs4_lookup_root(server, fhandle, info);
|
|
|
if (status != -NFS4ERR_WRONGSEC)
|
|
|
break;
|
|
|
- /* Did user force a 'sec=' mount option? */
|
|
|
- if (server->flags & NFS_MOUNT_SECFLAVOUR)
|
|
|
- break;
|
|
|
default:
|
|
|
status = nfs4_do_find_root_sec(server, fhandle, info);
|
|
|
}
|
|
@@ -2981,11 +3012,16 @@ static int nfs4_get_referral(struct rpc_clnt *client, struct inode *dir,
|
|
|
status = nfs4_proc_fs_locations(client, dir, name, locations, page);
|
|
|
if (status != 0)
|
|
|
goto out;
|
|
|
- /* Make sure server returned a different fsid for the referral */
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If the fsid didn't change, this is a migration event, not a
|
|
|
+ * referral. Cause us to drop into the exception handler, which
|
|
|
+ * will kick off migration recovery.
|
|
|
+ */
|
|
|
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);
|
|
|
- status = -EIO;
|
|
|
+ status = -NFS4ERR_MOVED;
|
|
|
goto out;
|
|
|
}
|
|
|
/* Fixup attributes for the nfs_lookup() call to nfs_fhget() */
|
|
@@ -3165,9 +3201,6 @@ static int nfs4_proc_lookup_common(struct rpc_clnt **clnt, struct inode *dir,
|
|
|
err = -EPERM;
|
|
|
if (client != *clnt)
|
|
|
goto out;
|
|
|
- /* No security negotiation if the user specified 'sec=' */
|
|
|
- if (NFS_SERVER(dir)->flags & NFS_MOUNT_SECFLAVOUR)
|
|
|
- goto out;
|
|
|
client = nfs4_create_sec_client(client, dir, name);
|
|
|
if (IS_ERR(client))
|
|
|
return PTR_ERR(client);
|
|
@@ -4221,7 +4254,13 @@ static void nfs4_renew_done(struct rpc_task *task, void *calldata)
|
|
|
unsigned long timestamp = data->timestamp;
|
|
|
|
|
|
trace_nfs4_renew_async(clp, task->tk_status);
|
|
|
- if (task->tk_status < 0) {
|
|
|
+ switch (task->tk_status) {
|
|
|
+ case 0:
|
|
|
+ break;
|
|
|
+ case -NFS4ERR_LEASE_MOVED:
|
|
|
+ nfs4_schedule_lease_moved_recovery(clp);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
/* Unless we're shutting down, schedule state recovery! */
|
|
|
if (test_bit(NFS_CS_RENEWD, &clp->cl_res_state) == 0)
|
|
|
return;
|
|
@@ -4575,7 +4614,7 @@ static int _nfs4_get_security_label(struct inode *inode, void *buf,
|
|
|
struct nfs4_label label = {0, 0, buflen, buf};
|
|
|
|
|
|
u32 bitmask[3] = { 0, 0, FATTR4_WORD2_SECURITY_LABEL };
|
|
|
- struct nfs4_getattr_arg args = {
|
|
|
+ struct nfs4_getattr_arg arg = {
|
|
|
.fh = NFS_FH(inode),
|
|
|
.bitmask = bitmask,
|
|
|
};
|
|
@@ -4586,14 +4625,14 @@ static int _nfs4_get_security_label(struct inode *inode, void *buf,
|
|
|
};
|
|
|
struct rpc_message msg = {
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETATTR],
|
|
|
- .rpc_argp = &args,
|
|
|
+ .rpc_argp = &arg,
|
|
|
.rpc_resp = &res,
|
|
|
};
|
|
|
int ret;
|
|
|
|
|
|
nfs_fattr_init(&fattr);
|
|
|
|
|
|
- ret = rpc_call_sync(server->client, &msg, 0);
|
|
|
+ ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 0);
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
if (!(fattr.valid & NFS_ATTR_FATTR_V4_SECURITY_LABEL))
|
|
@@ -4630,7 +4669,7 @@ static int _nfs4_do_set_security_label(struct inode *inode,
|
|
|
struct iattr sattr = {0};
|
|
|
struct nfs_server *server = NFS_SERVER(inode);
|
|
|
const u32 bitmask[3] = { 0, 0, FATTR4_WORD2_SECURITY_LABEL };
|
|
|
- struct nfs_setattrargs args = {
|
|
|
+ struct nfs_setattrargs arg = {
|
|
|
.fh = NFS_FH(inode),
|
|
|
.iap = &sattr,
|
|
|
.server = server,
|
|
@@ -4644,14 +4683,14 @@ static int _nfs4_do_set_security_label(struct inode *inode,
|
|
|
};
|
|
|
struct rpc_message msg = {
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETATTR],
|
|
|
- .rpc_argp = &args,
|
|
|
+ .rpc_argp = &arg,
|
|
|
.rpc_resp = &res,
|
|
|
};
|
|
|
int status;
|
|
|
|
|
|
- nfs4_stateid_copy(&args.stateid, &zero_stateid);
|
|
|
+ nfs4_stateid_copy(&arg.stateid, &zero_stateid);
|
|
|
|
|
|
- status = rpc_call_sync(server->client, &msg, 0);
|
|
|
+ status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
|
|
|
if (status)
|
|
|
dprintk("%s failed: %d\n", __func__, status);
|
|
|
|
|
@@ -4735,17 +4774,24 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
|
|
|
if (state == NULL)
|
|
|
break;
|
|
|
if (nfs4_schedule_stateid_recovery(server, state) < 0)
|
|
|
- goto stateid_invalid;
|
|
|
+ goto recovery_failed;
|
|
|
goto wait_on_recovery;
|
|
|
case -NFS4ERR_EXPIRED:
|
|
|
if (state != NULL) {
|
|
|
if (nfs4_schedule_stateid_recovery(server, state) < 0)
|
|
|
- goto stateid_invalid;
|
|
|
+ goto recovery_failed;
|
|
|
}
|
|
|
case -NFS4ERR_STALE_STATEID:
|
|
|
case -NFS4ERR_STALE_CLIENTID:
|
|
|
nfs4_schedule_lease_recovery(clp);
|
|
|
goto wait_on_recovery;
|
|
|
+ case -NFS4ERR_MOVED:
|
|
|
+ if (nfs4_schedule_migration_recovery(server) < 0)
|
|
|
+ goto recovery_failed;
|
|
|
+ goto wait_on_recovery;
|
|
|
+ case -NFS4ERR_LEASE_MOVED:
|
|
|
+ nfs4_schedule_lease_moved_recovery(clp);
|
|
|
+ goto wait_on_recovery;
|
|
|
#if defined(CONFIG_NFS_V4_1)
|
|
|
case -NFS4ERR_BADSESSION:
|
|
|
case -NFS4ERR_BADSLOT:
|
|
@@ -4757,29 +4803,28 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
|
|
|
dprintk("%s ERROR %d, Reset session\n", __func__,
|
|
|
task->tk_status);
|
|
|
nfs4_schedule_session_recovery(clp->cl_session, task->tk_status);
|
|
|
- task->tk_status = 0;
|
|
|
- return -EAGAIN;
|
|
|
+ goto restart_call;
|
|
|
#endif /* CONFIG_NFS_V4_1 */
|
|
|
case -NFS4ERR_DELAY:
|
|
|
nfs_inc_server_stats(server, NFSIOS_DELAY);
|
|
|
case -NFS4ERR_GRACE:
|
|
|
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;
|
|
|
+ goto restart_call;
|
|
|
}
|
|
|
task->tk_status = nfs4_map_errors(task->tk_status);
|
|
|
return 0;
|
|
|
-stateid_invalid:
|
|
|
+recovery_failed:
|
|
|
task->tk_status = -EIO;
|
|
|
return 0;
|
|
|
wait_on_recovery:
|
|
|
rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL);
|
|
|
if (test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) == 0)
|
|
|
rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task);
|
|
|
+ if (test_bit(NFS_MIG_FAILED, &server->mig_status))
|
|
|
+ goto recovery_failed;
|
|
|
+restart_call:
|
|
|
task->tk_status = 0;
|
|
|
return -EAGAIN;
|
|
|
}
|
|
@@ -5106,6 +5151,7 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock
|
|
|
status = 0;
|
|
|
}
|
|
|
request->fl_ops->fl_release_private(request);
|
|
|
+ request->fl_ops = NULL;
|
|
|
out:
|
|
|
return status;
|
|
|
}
|
|
@@ -5779,6 +5825,7 @@ struct nfs_release_lockowner_data {
|
|
|
struct nfs_release_lockowner_args args;
|
|
|
struct nfs4_sequence_args seq_args;
|
|
|
struct nfs4_sequence_res seq_res;
|
|
|
+ unsigned long timestamp;
|
|
|
};
|
|
|
|
|
|
static void nfs4_release_lockowner_prepare(struct rpc_task *task, void *calldata)
|
|
@@ -5786,12 +5833,27 @@ static void nfs4_release_lockowner_prepare(struct rpc_task *task, void *calldata
|
|
|
struct nfs_release_lockowner_data *data = calldata;
|
|
|
nfs40_setup_sequence(data->server,
|
|
|
&data->seq_args, &data->seq_res, task);
|
|
|
+ data->timestamp = jiffies;
|
|
|
}
|
|
|
|
|
|
static void nfs4_release_lockowner_done(struct rpc_task *task, void *calldata)
|
|
|
{
|
|
|
struct nfs_release_lockowner_data *data = calldata;
|
|
|
+ struct nfs_server *server = data->server;
|
|
|
+
|
|
|
nfs40_sequence_done(task, &data->seq_res);
|
|
|
+
|
|
|
+ switch (task->tk_status) {
|
|
|
+ case 0:
|
|
|
+ renew_lease(server, data->timestamp);
|
|
|
+ break;
|
|
|
+ case -NFS4ERR_STALE_CLIENTID:
|
|
|
+ case -NFS4ERR_EXPIRED:
|
|
|
+ case -NFS4ERR_LEASE_MOVED:
|
|
|
+ case -NFS4ERR_DELAY:
|
|
|
+ if (nfs4_async_handle_error(task, server, NULL) == -EAGAIN)
|
|
|
+ rpc_restart_call_prepare(task);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
static void nfs4_release_lockowner_release(void *calldata)
|
|
@@ -5990,6 +6052,283 @@ int nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir,
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * This operation also signals the server that this client is
|
|
|
+ * performing migration recovery. The server can stop returning
|
|
|
+ * NFS4ERR_LEASE_MOVED to this client. A RENEW operation is
|
|
|
+ * appended to this compound to identify the client ID which is
|
|
|
+ * performing recovery.
|
|
|
+ */
|
|
|
+static int _nfs40_proc_get_locations(struct inode *inode,
|
|
|
+ struct nfs4_fs_locations *locations,
|
|
|
+ struct page *page, struct rpc_cred *cred)
|
|
|
+{
|
|
|
+ struct nfs_server *server = NFS_SERVER(inode);
|
|
|
+ struct rpc_clnt *clnt = server->client;
|
|
|
+ u32 bitmask[2] = {
|
|
|
+ [0] = FATTR4_WORD0_FSID | FATTR4_WORD0_FS_LOCATIONS,
|
|
|
+ };
|
|
|
+ struct nfs4_fs_locations_arg args = {
|
|
|
+ .clientid = server->nfs_client->cl_clientid,
|
|
|
+ .fh = NFS_FH(inode),
|
|
|
+ .page = page,
|
|
|
+ .bitmask = bitmask,
|
|
|
+ .migration = 1, /* skip LOOKUP */
|
|
|
+ .renew = 1, /* append RENEW */
|
|
|
+ };
|
|
|
+ struct nfs4_fs_locations_res res = {
|
|
|
+ .fs_locations = locations,
|
|
|
+ .migration = 1,
|
|
|
+ .renew = 1,
|
|
|
+ };
|
|
|
+ struct rpc_message msg = {
|
|
|
+ .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FS_LOCATIONS],
|
|
|
+ .rpc_argp = &args,
|
|
|
+ .rpc_resp = &res,
|
|
|
+ .rpc_cred = cred,
|
|
|
+ };
|
|
|
+ unsigned long now = jiffies;
|
|
|
+ int status;
|
|
|
+
|
|
|
+ nfs_fattr_init(&locations->fattr);
|
|
|
+ locations->server = server;
|
|
|
+ locations->nlocations = 0;
|
|
|
+
|
|
|
+ nfs4_init_sequence(&args.seq_args, &res.seq_res, 0);
|
|
|
+ nfs4_set_sequence_privileged(&args.seq_args);
|
|
|
+ status = nfs4_call_sync_sequence(clnt, server, &msg,
|
|
|
+ &args.seq_args, &res.seq_res);
|
|
|
+ if (status)
|
|
|
+ return status;
|
|
|
+
|
|
|
+ renew_lease(server, now);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+#ifdef CONFIG_NFS_V4_1
|
|
|
+
|
|
|
+/*
|
|
|
+ * This operation also signals the server that this client is
|
|
|
+ * performing migration recovery. The server can stop asserting
|
|
|
+ * SEQ4_STATUS_LEASE_MOVED for this client. The client ID
|
|
|
+ * performing this operation is identified in the SEQUENCE
|
|
|
+ * operation in this compound.
|
|
|
+ *
|
|
|
+ * When the client supports GETATTR(fs_locations_info), it can
|
|
|
+ * be plumbed in here.
|
|
|
+ */
|
|
|
+static int _nfs41_proc_get_locations(struct inode *inode,
|
|
|
+ struct nfs4_fs_locations *locations,
|
|
|
+ struct page *page, struct rpc_cred *cred)
|
|
|
+{
|
|
|
+ struct nfs_server *server = NFS_SERVER(inode);
|
|
|
+ struct rpc_clnt *clnt = server->client;
|
|
|
+ u32 bitmask[2] = {
|
|
|
+ [0] = FATTR4_WORD0_FSID | FATTR4_WORD0_FS_LOCATIONS,
|
|
|
+ };
|
|
|
+ struct nfs4_fs_locations_arg args = {
|
|
|
+ .fh = NFS_FH(inode),
|
|
|
+ .page = page,
|
|
|
+ .bitmask = bitmask,
|
|
|
+ .migration = 1, /* skip LOOKUP */
|
|
|
+ };
|
|
|
+ struct nfs4_fs_locations_res res = {
|
|
|
+ .fs_locations = locations,
|
|
|
+ .migration = 1,
|
|
|
+ };
|
|
|
+ struct rpc_message msg = {
|
|
|
+ .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FS_LOCATIONS],
|
|
|
+ .rpc_argp = &args,
|
|
|
+ .rpc_resp = &res,
|
|
|
+ .rpc_cred = cred,
|
|
|
+ };
|
|
|
+ int status;
|
|
|
+
|
|
|
+ nfs_fattr_init(&locations->fattr);
|
|
|
+ locations->server = server;
|
|
|
+ locations->nlocations = 0;
|
|
|
+
|
|
|
+ nfs4_init_sequence(&args.seq_args, &res.seq_res, 0);
|
|
|
+ nfs4_set_sequence_privileged(&args.seq_args);
|
|
|
+ status = nfs4_call_sync_sequence(clnt, server, &msg,
|
|
|
+ &args.seq_args, &res.seq_res);
|
|
|
+ if (status == NFS4_OK &&
|
|
|
+ res.seq_res.sr_status_flags & SEQ4_STATUS_LEASE_MOVED)
|
|
|
+ status = -NFS4ERR_LEASE_MOVED;
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+#endif /* CONFIG_NFS_V4_1 */
|
|
|
+
|
|
|
+/**
|
|
|
+ * nfs4_proc_get_locations - discover locations for a migrated FSID
|
|
|
+ * @inode: inode on FSID that is migrating
|
|
|
+ * @locations: result of query
|
|
|
+ * @page: buffer
|
|
|
+ * @cred: credential to use for this operation
|
|
|
+ *
|
|
|
+ * Returns NFS4_OK on success, a negative NFS4ERR status code if the
|
|
|
+ * operation failed, or a negative errno if a local error occurred.
|
|
|
+ *
|
|
|
+ * On success, "locations" is filled in, but if the server has
|
|
|
+ * no locations information, NFS_ATTR_FATTR_V4_LOCATIONS is not
|
|
|
+ * asserted.
|
|
|
+ *
|
|
|
+ * -NFS4ERR_LEASE_MOVED is returned if the server still has leases
|
|
|
+ * from this client that require migration recovery.
|
|
|
+ */
|
|
|
+int nfs4_proc_get_locations(struct inode *inode,
|
|
|
+ struct nfs4_fs_locations *locations,
|
|
|
+ struct page *page, struct rpc_cred *cred)
|
|
|
+{
|
|
|
+ struct nfs_server *server = NFS_SERVER(inode);
|
|
|
+ struct nfs_client *clp = server->nfs_client;
|
|
|
+ const struct nfs4_mig_recovery_ops *ops =
|
|
|
+ clp->cl_mvops->mig_recovery_ops;
|
|
|
+ struct nfs4_exception exception = { };
|
|
|
+ int status;
|
|
|
+
|
|
|
+ dprintk("%s: FSID %llx:%llx on \"%s\"\n", __func__,
|
|
|
+ (unsigned long long)server->fsid.major,
|
|
|
+ (unsigned long long)server->fsid.minor,
|
|
|
+ clp->cl_hostname);
|
|
|
+ nfs_display_fhandle(NFS_FH(inode), __func__);
|
|
|
+
|
|
|
+ do {
|
|
|
+ status = ops->get_locations(inode, locations, page, cred);
|
|
|
+ if (status != -NFS4ERR_DELAY)
|
|
|
+ break;
|
|
|
+ nfs4_handle_exception(server, status, &exception);
|
|
|
+ } while (exception.retry);
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * This operation also signals the server that this client is
|
|
|
+ * performing "lease moved" recovery. The server can stop
|
|
|
+ * returning NFS4ERR_LEASE_MOVED to this client. A RENEW operation
|
|
|
+ * is appended to this compound to identify the client ID which is
|
|
|
+ * performing recovery.
|
|
|
+ */
|
|
|
+static int _nfs40_proc_fsid_present(struct inode *inode, struct rpc_cred *cred)
|
|
|
+{
|
|
|
+ struct nfs_server *server = NFS_SERVER(inode);
|
|
|
+ struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
|
|
|
+ struct rpc_clnt *clnt = server->client;
|
|
|
+ struct nfs4_fsid_present_arg args = {
|
|
|
+ .fh = NFS_FH(inode),
|
|
|
+ .clientid = clp->cl_clientid,
|
|
|
+ .renew = 1, /* append RENEW */
|
|
|
+ };
|
|
|
+ struct nfs4_fsid_present_res res = {
|
|
|
+ .renew = 1,
|
|
|
+ };
|
|
|
+ struct rpc_message msg = {
|
|
|
+ .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FSID_PRESENT],
|
|
|
+ .rpc_argp = &args,
|
|
|
+ .rpc_resp = &res,
|
|
|
+ .rpc_cred = cred,
|
|
|
+ };
|
|
|
+ unsigned long now = jiffies;
|
|
|
+ int status;
|
|
|
+
|
|
|
+ res.fh = nfs_alloc_fhandle();
|
|
|
+ if (res.fh == NULL)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ nfs4_init_sequence(&args.seq_args, &res.seq_res, 0);
|
|
|
+ nfs4_set_sequence_privileged(&args.seq_args);
|
|
|
+ status = nfs4_call_sync_sequence(clnt, server, &msg,
|
|
|
+ &args.seq_args, &res.seq_res);
|
|
|
+ nfs_free_fhandle(res.fh);
|
|
|
+ if (status)
|
|
|
+ return status;
|
|
|
+
|
|
|
+ do_renew_lease(clp, now);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+#ifdef CONFIG_NFS_V4_1
|
|
|
+
|
|
|
+/*
|
|
|
+ * This operation also signals the server that this client is
|
|
|
+ * performing "lease moved" recovery. The server can stop asserting
|
|
|
+ * SEQ4_STATUS_LEASE_MOVED for this client. The client ID performing
|
|
|
+ * this operation is identified in the SEQUENCE operation in this
|
|
|
+ * compound.
|
|
|
+ */
|
|
|
+static int _nfs41_proc_fsid_present(struct inode *inode, struct rpc_cred *cred)
|
|
|
+{
|
|
|
+ struct nfs_server *server = NFS_SERVER(inode);
|
|
|
+ struct rpc_clnt *clnt = server->client;
|
|
|
+ struct nfs4_fsid_present_arg args = {
|
|
|
+ .fh = NFS_FH(inode),
|
|
|
+ };
|
|
|
+ struct nfs4_fsid_present_res res = {
|
|
|
+ };
|
|
|
+ struct rpc_message msg = {
|
|
|
+ .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FSID_PRESENT],
|
|
|
+ .rpc_argp = &args,
|
|
|
+ .rpc_resp = &res,
|
|
|
+ .rpc_cred = cred,
|
|
|
+ };
|
|
|
+ int status;
|
|
|
+
|
|
|
+ res.fh = nfs_alloc_fhandle();
|
|
|
+ if (res.fh == NULL)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ nfs4_init_sequence(&args.seq_args, &res.seq_res, 0);
|
|
|
+ nfs4_set_sequence_privileged(&args.seq_args);
|
|
|
+ status = nfs4_call_sync_sequence(clnt, server, &msg,
|
|
|
+ &args.seq_args, &res.seq_res);
|
|
|
+ nfs_free_fhandle(res.fh);
|
|
|
+ if (status == NFS4_OK &&
|
|
|
+ res.seq_res.sr_status_flags & SEQ4_STATUS_LEASE_MOVED)
|
|
|
+ status = -NFS4ERR_LEASE_MOVED;
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+#endif /* CONFIG_NFS_V4_1 */
|
|
|
+
|
|
|
+/**
|
|
|
+ * nfs4_proc_fsid_present - Is this FSID present or absent on server?
|
|
|
+ * @inode: inode on FSID to check
|
|
|
+ * @cred: credential to use for this operation
|
|
|
+ *
|
|
|
+ * Server indicates whether the FSID is present, moved, or not
|
|
|
+ * recognized. This operation is necessary to clear a LEASE_MOVED
|
|
|
+ * condition for this client ID.
|
|
|
+ *
|
|
|
+ * Returns NFS4_OK if the FSID is present on this server,
|
|
|
+ * -NFS4ERR_MOVED if the FSID is no longer present, a negative
|
|
|
+ * NFS4ERR code if some error occurred on the server, or a
|
|
|
+ * negative errno if a local failure occurred.
|
|
|
+ */
|
|
|
+int nfs4_proc_fsid_present(struct inode *inode, struct rpc_cred *cred)
|
|
|
+{
|
|
|
+ struct nfs_server *server = NFS_SERVER(inode);
|
|
|
+ struct nfs_client *clp = server->nfs_client;
|
|
|
+ const struct nfs4_mig_recovery_ops *ops =
|
|
|
+ clp->cl_mvops->mig_recovery_ops;
|
|
|
+ struct nfs4_exception exception = { };
|
|
|
+ int status;
|
|
|
+
|
|
|
+ dprintk("%s: FSID %llx:%llx on \"%s\"\n", __func__,
|
|
|
+ (unsigned long long)server->fsid.major,
|
|
|
+ (unsigned long long)server->fsid.minor,
|
|
|
+ clp->cl_hostname);
|
|
|
+ nfs_display_fhandle(NFS_FH(inode), __func__);
|
|
|
+
|
|
|
+ do {
|
|
|
+ status = ops->fsid_present(inode, cred);
|
|
|
+ if (status != -NFS4ERR_DELAY)
|
|
|
+ break;
|
|
|
+ nfs4_handle_exception(server, status, &exception);
|
|
|
+ } while (exception.retry);
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* If 'use_integrity' is true and the state managment nfs_client
|
|
|
* cl_rpcclient is using krb5i/p, use the integrity protected cl_rpcclient
|
|
@@ -6276,8 +6615,14 @@ static int _nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred,
|
|
|
struct nfs41_exchange_id_args args = {
|
|
|
.verifier = &verifier,
|
|
|
.client = clp,
|
|
|
+#ifdef CONFIG_NFS_V4_1_MIGRATION
|
|
|
.flags = EXCHGID4_FLAG_SUPP_MOVED_REFER |
|
|
|
- EXCHGID4_FLAG_BIND_PRINC_STATEID,
|
|
|
+ EXCHGID4_FLAG_BIND_PRINC_STATEID |
|
|
|
+ EXCHGID4_FLAG_SUPP_MOVED_MIGR,
|
|
|
+#else
|
|
|
+ .flags = EXCHGID4_FLAG_SUPP_MOVED_REFER |
|
|
|
+ EXCHGID4_FLAG_BIND_PRINC_STATEID,
|
|
|
+#endif
|
|
|
};
|
|
|
struct nfs41_exchange_id_res res = {
|
|
|
0
|
|
@@ -7616,6 +7961,9 @@ nfs41_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
+ if (!nfs_auth_info_match(&server->auth_info, flavor))
|
|
|
+ flavor = RPC_AUTH_MAXFLAVOR;
|
|
|
+
|
|
|
if (flavor != RPC_AUTH_MAXFLAVOR) {
|
|
|
err = nfs4_lookup_root_sec(server, fhandle,
|
|
|
info, flavor);
|
|
@@ -7887,6 +8235,18 @@ static const struct nfs4_state_maintenance_ops nfs41_state_renewal_ops = {
|
|
|
};
|
|
|
#endif
|
|
|
|
|
|
+static const struct nfs4_mig_recovery_ops nfs40_mig_recovery_ops = {
|
|
|
+ .get_locations = _nfs40_proc_get_locations,
|
|
|
+ .fsid_present = _nfs40_proc_fsid_present,
|
|
|
+};
|
|
|
+
|
|
|
+#if defined(CONFIG_NFS_V4_1)
|
|
|
+static const struct nfs4_mig_recovery_ops nfs41_mig_recovery_ops = {
|
|
|
+ .get_locations = _nfs41_proc_get_locations,
|
|
|
+ .fsid_present = _nfs41_proc_fsid_present,
|
|
|
+};
|
|
|
+#endif /* CONFIG_NFS_V4_1 */
|
|
|
+
|
|
|
static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = {
|
|
|
.minor_version = 0,
|
|
|
.init_caps = NFS_CAP_READDIRPLUS
|
|
@@ -7902,6 +8262,7 @@ static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = {
|
|
|
.reboot_recovery_ops = &nfs40_reboot_recovery_ops,
|
|
|
.nograce_recovery_ops = &nfs40_nograce_recovery_ops,
|
|
|
.state_renewal_ops = &nfs40_state_renewal_ops,
|
|
|
+ .mig_recovery_ops = &nfs40_mig_recovery_ops,
|
|
|
};
|
|
|
|
|
|
#if defined(CONFIG_NFS_V4_1)
|
|
@@ -7922,6 +8283,7 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
|
|
|
.reboot_recovery_ops = &nfs41_reboot_recovery_ops,
|
|
|
.nograce_recovery_ops = &nfs41_nograce_recovery_ops,
|
|
|
.state_renewal_ops = &nfs41_state_renewal_ops,
|
|
|
+ .mig_recovery_ops = &nfs41_mig_recovery_ops,
|
|
|
};
|
|
|
#endif
|
|
|
|