Skip to content
Snippets Groups Projects
nfs4proc.c 161 KiB
Newer Older
  • Learn to ignore specific revisions
  • Linus Torvalds's avatar
    Linus Torvalds committed
    static int do_vfs_lock(struct file *file, struct file_lock *fl)
    {
    	int res = 0;
    	switch (fl->fl_flags & (FL_POSIX|FL_FLOCK)) {
    		case FL_POSIX:
    			res = posix_lock_file_wait(file, fl);
    			break;
    		case FL_FLOCK:
    			res = flock_lock_file_wait(file, fl);
    			break;
    		default:
    			BUG();
    	}
    	return res;
    }
    
    
    	struct nfs_locku_args arg;
    	struct nfs_locku_res res;
    
    	struct nfs4_lock_state *lsp;
    	struct nfs_open_context *ctx;
    
    	struct file_lock fl;
    	const struct nfs_server *server;
    
    static struct nfs4_unlockdata *nfs4_alloc_unlockdata(struct file_lock *fl,
    		struct nfs_open_context *ctx,
    		struct nfs4_lock_state *lsp,
    		struct nfs_seqid *seqid)
    {
    	struct nfs4_unlockdata *p;
    	struct inode *inode = lsp->ls_state->inode;
    
    
    	p = kzalloc(sizeof(*p), GFP_NOFS);
    
    	if (p == NULL)
    		return NULL;
    	p->arg.fh = NFS_FH(inode);
    	p->arg.fl = &p->fl;
    	p->arg.seqid = seqid;
    
    	p->arg.stateid = &lsp->ls_stateid;
    	p->lsp = lsp;
    	atomic_inc(&lsp->ls_count);
    	/* Ensure we don't close file until we're done freeing locks! */
    	p->ctx = get_nfs_open_context(ctx);
    	memcpy(&p->fl, fl, sizeof(p->fl));
    	p->server = NFS_SERVER(inode);
    	return p;
    }
    
    
    static void nfs4_locku_release_calldata(void *data)
    
    	struct nfs4_unlockdata *calldata = data;
    
    	nfs_free_seqid(calldata->arg.seqid);
    
    	nfs4_put_lock_state(calldata->lsp);
    	put_nfs_open_context(calldata->ctx);
    	kfree(calldata);
    
    static void nfs4_locku_done(struct rpc_task *task, void *data)
    
    	struct nfs4_unlockdata *calldata = data;
    
    	if (!nfs4_sequence_done(task, &calldata->res.seq_res))
    		return;
    
    	switch (task->tk_status) {
    		case 0:
    			memcpy(calldata->lsp->ls_stateid.data,
    
    					calldata->res.stateid.data,
    
    					sizeof(calldata->lsp->ls_stateid.data));
    
    			renew_lease(calldata->server, calldata->timestamp);
    
    		case -NFS4ERR_BAD_STATEID:
    		case -NFS4ERR_OLD_STATEID:
    
    		case -NFS4ERR_STALE_STATEID:
    		case -NFS4ERR_EXPIRED:
    			break;
    		default:
    
    			if (nfs4_async_handle_error(task, calldata->server, NULL) == -EAGAIN)
    
    						 calldata->server->nfs_client);
    
    static void nfs4_locku_prepare(struct rpc_task *task, void *data)
    
    	struct nfs4_unlockdata *calldata = data;
    
    	if (nfs_wait_on_sequence(calldata->arg.seqid, task) != 0)
    
    		return;
    	if ((calldata->lsp->ls_flags & NFS_LOCK_INITIALIZED) == 0) {
    
    		/* Note: exit _without_ running nfs4_locku_done */
    		task->tk_action = NULL;
    
    	if (nfs4_setup_sequence(calldata->server,
    
    				&calldata->arg.seq_args,
    				&calldata->res.seq_res, 1, task))
    		return;
    
    	rpc_call_start(task);
    
    static const struct rpc_call_ops nfs4_locku_ops = {
    
    	.rpc_call_prepare = nfs4_locku_prepare,
    
    	.rpc_call_done = nfs4_locku_done,
    
    	.rpc_release = nfs4_locku_release_calldata,
    
    static struct rpc_task *nfs4_do_unlck(struct file_lock *fl,
    		struct nfs_open_context *ctx,
    		struct nfs4_lock_state *lsp,
    		struct nfs_seqid *seqid)
    {
    	struct nfs4_unlockdata *data;
    
    	struct rpc_message msg = {
    		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCKU],
    		.rpc_cred = ctx->cred,
    	};
    
    	struct rpc_task_setup task_setup_data = {
    		.rpc_client = NFS_CLIENT(lsp->ls_state->inode),
    
    		.callback_ops = &nfs4_locku_ops,
    
    		.flags = RPC_TASK_ASYNC,
    	};
    
    	/* Ensure this is an unlock - when canceling a lock, the
    	 * canceled lock is passed in, and it won't be an unlock.
    	 */
    	fl->fl_type = F_UNLCK;
    
    
    	data = nfs4_alloc_unlockdata(fl, ctx, lsp, seqid);
    	if (data == NULL) {
    		nfs_free_seqid(seqid);
    		return ERR_PTR(-ENOMEM);
    	}
    
    
    	msg.rpc_argp = &data->arg;
    	msg.rpc_resp = &data->res;
    
    	task_setup_data.callback_data = data;
    	return rpc_run_task(&task_setup_data);
    
    static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request)
    {
    
    	struct nfs_inode *nfsi = NFS_I(state->inode);
    
    	struct nfs_seqid *seqid;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	struct nfs4_lock_state *lsp;
    
    	struct rpc_task *task;
    	int status = 0;
    
    	unsigned char fl_flags = request->fl_flags;
    
    	status = nfs4_set_lock_state(state, request);
    
    	/* Unlock _before_ we do the RPC call */
    	request->fl_flags |= FL_EXISTS;
    
    	down_read(&nfsi->rwsem);
    	if (do_vfs_lock(request->fl_file, request) == -ENOENT) {
    		up_read(&nfsi->rwsem);
    
    		goto out;
    	/* Is this a delegated lock? */
    	if (test_bit(NFS_DELEGATED_STATE, &state->flags))
    		goto out;
    
    	lsp = request->fl_u.nfs4_fl.owner;
    
    	seqid = nfs_alloc_seqid(&lsp->ls_seqid, GFP_KERNEL);
    
    	if (seqid == NULL)
    
    	task = nfs4_do_unlck(request, nfs_file_open_context(request->fl_file), lsp, seqid);
    
    	status = PTR_ERR(task);
    	if (IS_ERR(task))
    
    	status = nfs4_wait_for_completion_rpc_task(task);
    
    	rpc_put_task(task);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return status;
    }
    
    
    struct nfs4_lockdata {
    	struct nfs_lock_args arg;
    	struct nfs_lock_res res;
    	struct nfs4_lock_state *lsp;
    	struct nfs_open_context *ctx;
    	struct file_lock fl;
    
    	struct nfs_server *server;
    
    };
    
    static struct nfs4_lockdata *nfs4_alloc_lockdata(struct file_lock *fl,
    
    		struct nfs_open_context *ctx, struct nfs4_lock_state *lsp,
    		gfp_t gfp_mask)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	struct nfs4_lockdata *p;
    	struct inode *inode = lsp->ls_state->inode;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	struct nfs_server *server = NFS_SERVER(inode);
    
    	p = kzalloc(sizeof(*p), gfp_mask);
    
    	if (p == NULL)
    		return NULL;
    
    	p->arg.fh = NFS_FH(inode);
    	p->arg.fl = &p->fl;
    
    	p->arg.open_seqid = nfs_alloc_seqid(&lsp->ls_state->owner->so_seqid, gfp_mask);
    
    	if (p->arg.open_seqid == NULL)
    		goto out_free;
    
    	p->arg.lock_seqid = nfs_alloc_seqid(&lsp->ls_seqid, gfp_mask);
    
    	if (p->arg.lock_seqid == NULL)
    
    	p->arg.lock_stateid = &lsp->ls_stateid;
    
    	p->arg.lock_owner.clientid = server->nfs_client->cl_clientid;
    
    	p->arg.lock_owner.id = lsp->ls_id.id;
    
    	p->arg.lock_owner.s_dev = server->s_dev;
    
    	p->res.lock_seqid = p->arg.lock_seqid;
    
    	p->server = server;
    
    	atomic_inc(&lsp->ls_count);
    	p->ctx = get_nfs_open_context(ctx);
    	memcpy(&p->fl, fl, sizeof(p->fl));
    	return p;
    
    out_free_seqid:
    	nfs_free_seqid(p->arg.open_seqid);
    
    out_free:
    	kfree(p);
    	return NULL;
    }
    
    static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
    {
    	struct nfs4_lockdata *data = calldata;
    	struct nfs4_state *state = data->lsp->ls_state;
    
    	dprintk("%s: begin!\n", __func__);
    
    	if (nfs_wait_on_sequence(data->arg.lock_seqid, task) != 0)
    		return;
    
    	/* Do we need to do an open_to_lock_owner? */
    	if (!(data->arg.lock_seqid->sequence->flags & NFS_SEQID_CONFIRMED)) {
    
    		if (nfs_wait_on_sequence(data->arg.open_seqid, task) != 0)
    			return;
    
    		data->arg.open_stateid = &state->stateid;
    		data->arg.new_lock_owner = 1;
    
    		data->res.open_seqid = data->arg.open_seqid;
    
    	} else
    		data->arg.new_lock_owner = 0;
    
    	if (nfs4_setup_sequence(data->server,
    				&data->arg.seq_args,
    
    				&data->res.seq_res, 1, task))
    		return;
    
    	rpc_call_start(task);
    
    	dprintk("%s: done!, ret = %d\n", __func__, data->rpc_status);
    
    static void nfs4_recover_lock_prepare(struct rpc_task *task, void *calldata)
    {
    	rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
    	nfs4_lock_prepare(task, calldata);
    }
    
    
    static void nfs4_lock_done(struct rpc_task *task, void *calldata)
    {
    	struct nfs4_lockdata *data = calldata;
    
    
    	dprintk("%s: begin!\n", __func__);
    
    	if (!nfs4_sequence_done(task, &data->res.seq_res))
    		return;
    
    	data->rpc_status = task->tk_status;
    	if (data->arg.new_lock_owner != 0) {
    		if (data->rpc_status == 0)
    			nfs_confirm_seqid(&data->lsp->ls_seqid, 0);
    		else
    			goto out;
    	}
    	if (data->rpc_status == 0) {
    		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);
    
    	dprintk("%s: done, ret = %d!\n", __func__, data->rpc_status);
    
    }
    
    static void nfs4_lock_release(void *calldata)
    {
    	struct nfs4_lockdata *data = calldata;
    
    
    	dprintk("%s: begin!\n", __func__);
    
    	nfs_free_seqid(data->arg.open_seqid);
    
    	if (data->cancelled != 0) {
    		struct rpc_task *task;
    		task = nfs4_do_unlck(&data->fl, data->ctx, data->lsp,
    				data->arg.lock_seqid);
    		if (!IS_ERR(task))
    
    		dprintk("%s: cancelling lock!\n", __func__);
    
    	} else
    		nfs_free_seqid(data->arg.lock_seqid);
    	nfs4_put_lock_state(data->lsp);
    	put_nfs_open_context(data->ctx);
    	kfree(data);
    
    	dprintk("%s: done!\n", __func__);
    
    }
    
    static const struct rpc_call_ops nfs4_lock_ops = {
    	.rpc_call_prepare = nfs4_lock_prepare,
    	.rpc_call_done = nfs4_lock_done,
    	.rpc_release = nfs4_lock_release,
    };
    
    
    static const struct rpc_call_ops nfs4_recover_lock_ops = {
    	.rpc_call_prepare = nfs4_recover_lock_prepare,
    	.rpc_call_done = nfs4_lock_done,
    	.rpc_release = nfs4_lock_release,
    };
    
    
    static void nfs4_handle_setlk_error(struct nfs_server *server, struct nfs4_lock_state *lsp, int new_lock_owner, int error)
    {
    	switch (error) {
    	case -NFS4ERR_ADMIN_REVOKED:
    	case -NFS4ERR_BAD_STATEID:
    
    		lsp->ls_seqid.flags &= ~NFS_SEQID_CONFIRMED;
    
    		if (new_lock_owner != 0 ||
    		   (lsp->ls_flags & NFS_LOCK_INITIALIZED) != 0)
    
    			nfs4_schedule_stateid_recovery(server, lsp->ls_state);
    
    		break;
    	case -NFS4ERR_STALE_STATEID:
    		lsp->ls_seqid.flags &= ~NFS_SEQID_CONFIRMED;
    
    	case -NFS4ERR_EXPIRED:
    		nfs4_schedule_lease_recovery(server->nfs_client);
    
    static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *fl, int recovery_type)
    
    {
    	struct nfs4_lockdata *data;
    	struct rpc_task *task;
    
    	struct rpc_message msg = {
    		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCK],
    		.rpc_cred = state->owner->so_cred,
    	};
    
    	struct rpc_task_setup task_setup_data = {
    		.rpc_client = NFS_CLIENT(state->inode),
    
    		.callback_ops = &nfs4_lock_ops,
    
    		.flags = RPC_TASK_ASYNC,
    	};
    
    	dprintk("%s: begin!\n", __func__);
    
    	data = nfs4_alloc_lockdata(fl, nfs_file_open_context(fl->fl_file),
    
    			fl->fl_u.nfs4_fl.owner,
    			recovery_type == NFS_LOCK_NEW ? GFP_KERNEL : GFP_NOFS);
    
    	if (data == NULL)
    		return -ENOMEM;
    	if (IS_SETLKW(cmd))
    		data->arg.block = 1;
    
    	if (recovery_type > NFS_LOCK_NEW) {
    		if (recovery_type == NFS_LOCK_RECLAIM)
    
    			data->arg.reclaim = NFS_LOCK_RECLAIM;
    
    		task_setup_data.callback_ops = &nfs4_recover_lock_ops;
    	}
    
    	msg.rpc_argp = &data->arg;
    	msg.rpc_resp = &data->res;
    
    	task_setup_data.callback_data = data;
    	task = rpc_run_task(&task_setup_data);
    
    		return PTR_ERR(task);
    	ret = nfs4_wait_for_completion_rpc_task(task);
    	if (ret == 0) {
    		ret = data->rpc_status;
    
    		if (ret)
    			nfs4_handle_setlk_error(data->server, data->lsp,
    					data->arg.new_lock_owner, ret);
    
    	rpc_put_task(task);
    
    	dprintk("%s: done, ret = %d!\n", __func__, ret);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request)
    {
    
    	struct nfs_server *server = NFS_SERVER(state->inode);
    	struct nfs4_exception exception = { };
    	int err;
    
    	do {
    
    		/* Cache the lock if possible... */
    		if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0)
    			return 0;
    
    		err = _nfs4_do_setlk(state, F_SETLK, request, NFS_LOCK_RECLAIM);
    
    		if (err != -NFS4ERR_DELAY)
    
    			break;
    		nfs4_handle_exception(server, err, &exception);
    	} while (exception.retry);
    	return err;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request)
    {
    
    	struct nfs_server *server = NFS_SERVER(state->inode);
    	struct nfs4_exception exception = { };
    	int err;
    
    
    	err = nfs4_set_lock_state(state, request);
    	if (err != 0)
    		return err;
    
    		if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0)
    			return 0;
    
    		err = _nfs4_do_setlk(state, F_SETLK, request, NFS_LOCK_EXPIRED);
    
    		switch (err) {
    		default:
    			goto out;
    		case -NFS4ERR_GRACE:
    		case -NFS4ERR_DELAY:
    			nfs4_handle_exception(server, err, &exception);
    			err = 0;
    		}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
    {
    
    	struct nfs_inode *nfsi = NFS_I(state->inode);
    
    	unsigned char fl_flags = request->fl_flags;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	if ((fl_flags & FL_POSIX) &&
    			!test_bit(NFS_STATE_POSIX_LOCKS, &state->flags))
    		goto out;
    
    	/* Is this a delegated open? */
    	status = nfs4_set_lock_state(state, request);
    	if (status != 0)
    		goto out;
    
    	request->fl_flags |= FL_ACCESS;
    	status = do_vfs_lock(request->fl_file, request);
    	if (status < 0)
    		goto out;
    
    	if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
    		/* Yes: cache locks! */
    		/* ...but avoid races with delegation recall... */
    
    		request->fl_flags = fl_flags & ~FL_SLEEP;
    		status = do_vfs_lock(request->fl_file, request);
    		goto out_unlock;
    
    	status = _nfs4_do_setlk(state, cmd, request, NFS_LOCK_NEW);
    
    	/* Note: we always want to sleep here! */
    
    	request->fl_flags = fl_flags | FL_SLEEP;
    
    	if (do_vfs_lock(request->fl_file, request) < 0)
    
    		printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __func__);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return status;
    }
    
    static int nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
    {
    	struct nfs4_exception exception = { };
    	int err;
    
    	do {
    
    		err = _nfs4_proc_setlk(state, cmd, request);
    		if (err == -NFS4ERR_DENIED)
    			err = -EAGAIN;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		err = nfs4_handle_exception(NFS_SERVER(state->inode),
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	} while (exception.retry);
    	return err;
    }
    
    static int
    nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request)
    {
    	struct nfs_open_context *ctx;
    	struct nfs4_state *state;
    	unsigned long timeout = NFS4_LOCK_MINTIMEOUT;
    	int status;
    
    	/* verify open state */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	state = ctx->state;
    
    	if (request->fl_start < 0 || request->fl_end < 0)
    		return -EINVAL;
    
    
    	if (IS_GETLK(cmd)) {
    		if (state != NULL)
    			return nfs4_proc_getlk(state, F_GETLK, request);
    		return 0;
    	}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	if (!(IS_SETLK(cmd) || IS_SETLKW(cmd)))
    		return -EINVAL;
    
    
    	if (request->fl_type == F_UNLCK) {
    		if (state != NULL)
    			return nfs4_proc_unlck(state, cmd, request);
    		return 0;
    	}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	do {
    		status = nfs4_proc_setlk(state, cmd, request);
    		if ((status != -EAGAIN) || IS_SETLK(cmd))
    			break;
    		timeout = nfs4_set_lock_task_retry(timeout);
    		status = -ERESTARTSYS;
    		if (signalled())
    			break;
    	} while(status < 0);
    	return status;
    }
    
    
    int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl)
    {
    	struct nfs_server *server = NFS_SERVER(state->inode);
    	struct nfs4_exception exception = { };
    	int err;
    
    	err = nfs4_set_lock_state(state, fl);
    	if (err != 0)
    		goto out;
    	do {
    
    		err = _nfs4_do_setlk(state, F_SETLK, fl, NFS_LOCK_NEW);
    
    		switch (err) {
    			default:
    				printk(KERN_ERR "%s: unhandled error %d.\n",
    						__func__, err);
    			case 0:
    
    				goto out;
    			case -NFS4ERR_EXPIRED:
    			case -NFS4ERR_STALE_CLIENTID:
    
    				nfs4_schedule_lease_recovery(server->nfs_client);
    				goto out;
    
    			case -NFS4ERR_BADSESSION:
    			case -NFS4ERR_BADSLOT:
    			case -NFS4ERR_BAD_HIGH_SLOT:
    			case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
    			case -NFS4ERR_DEADSESSION:
    
    				nfs4_schedule_session_recovery(server->nfs_client->cl_session);
    
    			case -ERESTARTSYS:
    				/*
    				 * The show must go on: exit, but mark the
    				 * stateid as needing recovery.
    				 */
    			case -NFS4ERR_ADMIN_REVOKED:
    			case -NFS4ERR_BAD_STATEID:
    			case -NFS4ERR_OPENMODE:
    
    				nfs4_schedule_stateid_recovery(server, state);
    
    			case -EKEYEXPIRED:
    				/*
    				 * User RPCSEC_GSS context has expired.
    				 * We cannot recover this stateid now, so
    				 * skip it and allow recovery thread to
    				 * proceed.
    				 */
    				err = 0;
    				goto out;
    
    			case -ENOMEM:
    			case -NFS4ERR_DENIED:
    				/* kill_proc(fl->fl_pid, SIGLOST, 1); */
    				err = 0;
    				goto out;
    
    		err = nfs4_handle_exception(server, err, &exception);
    	} while (exception.retry);
    out:
    	return err;
    }
    
    static void nfs4_release_lockowner_release(void *calldata)
    {
    	kfree(calldata);
    }
    
    const struct rpc_call_ops nfs4_release_lockowner_ops = {
    	.rpc_release = nfs4_release_lockowner_release,
    };
    
    void nfs4_release_lockowner(const struct nfs4_lock_state *lsp)
    {
    	struct nfs_server *server = lsp->ls_state->owner->so_server;
    	struct nfs_release_lockowner_args *args;
    	struct rpc_message msg = {
    		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RELEASE_LOCKOWNER],
    	};
    
    	if (server->nfs_client->cl_mvops->minor_version != 0)
    		return;
    	args = kmalloc(sizeof(*args), GFP_NOFS);
    	if (!args)
    		return;
    	args->lock_owner.clientid = server->nfs_client->cl_clientid;
    	args->lock_owner.id = lsp->ls_id.id;
    
    	args->lock_owner.s_dev = server->s_dev;
    
    	msg.rpc_argp = args;
    	rpc_call_async(server->client, &msg, 0, &nfs4_release_lockowner_ops, args);
    }
    
    
    #define XATTR_NAME_NFSV4_ACL "system.nfs4_acl"
    
    
    static int nfs4_xattr_set_nfs4_acl(struct dentry *dentry, const char *key,
    				   const void *buf, size_t buflen,
    				   int flags, int type)
    
    	if (strcmp(key, "") != 0)
    		return -EINVAL;
    
    	return nfs4_proc_set_acl(dentry->d_inode, buf, buflen);
    
    static int nfs4_xattr_get_nfs4_acl(struct dentry *dentry, const char *key,
    				   void *buf, size_t buflen, int type)
    
    	if (strcmp(key, "") != 0)
    		return -EINVAL;
    
    	return nfs4_proc_get_acl(dentry->d_inode, buf, buflen);
    
    static size_t nfs4_xattr_list_nfs4_acl(struct dentry *dentry, char *list,
    				       size_t list_len, const char *name,
    				       size_t name_len, int type)
    
    	size_t len = sizeof(XATTR_NAME_NFSV4_ACL);
    
    	if (!nfs4_server_supports_acls(NFS_SERVER(dentry->d_inode)))
    		return 0;
    
    
    	if (list && len <= list_len)
    		memcpy(list, XATTR_NAME_NFSV4_ACL, len);
    
    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)))
    		return;
    
    	fattr->valid |= NFS_ATTR_FATTR_TYPE | NFS_ATTR_FATTR_MODE |
    		NFS_ATTR_FATTR_NLINK;
    	fattr->mode = S_IFDIR | S_IRUGO | S_IXUGO;
    	fattr->nlink = 2;
    }
    
    
    int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
    
    		struct nfs4_fs_locations *fs_locations, struct page *page)
    
    {
    	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),
    
    		.page = page,
    		.bitmask = bitmask,
    	};
    
    	struct nfs4_fs_locations_res res = {
    		.fs_locations = fs_locations,
    	};
    
    	struct rpc_message msg = {
    		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FS_LOCATIONS],
    		.rpc_argp = &args,
    
    		.rpc_resp = &res,
    
    	dprintk("%s: start\n", __func__);
    
    	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);
    
    static int _nfs4_proc_secinfo(struct inode *dir, const struct qstr *name, struct nfs4_secinfo_flavors *flavors)
    {
    	int status;
    	struct nfs4_secinfo_arg args = {
    		.dir_fh = NFS_FH(dir),
    		.name   = name,
    	};
    	struct nfs4_secinfo_res res = {
    		.flavors     = flavors,
    	};
    	struct rpc_message msg = {
    		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SECINFO],
    		.rpc_argp = &args,
    		.rpc_resp = &res,
    	};
    
    	dprintk("NFS call  secinfo %s\n", name->name);
    	status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &msg, &args.seq_args, &res.seq_res, 0);
    	dprintk("NFS reply  secinfo: %d\n", status);
    	return status;
    }
    
    int nfs4_proc_secinfo(struct inode *dir, const struct qstr *name, struct nfs4_secinfo_flavors *flavors)
    {
    	struct nfs4_exception exception = { };
    	int err;
    	do {
    		err = nfs4_handle_exception(NFS_SERVER(dir),
    				_nfs4_proc_secinfo(dir, name, flavors),
    				&exception);
    	} while (exception.retry);
    	return err;
    }
    
    
    #ifdef CONFIG_NFS_V4_1
    
    /*
     * Check the exchange flags returned by the server for invalid flags, having
     * both PNFS and NON_PNFS flags set, and not having one of NON_PNFS, PNFS, or
     * DS flags set.
     */
    static int nfs4_check_cl_exchange_flags(u32 flags)
    {
    	if (flags & ~EXCHGID4_FLAG_MASK_R)
    		goto out_inval;
    	if ((flags & EXCHGID4_FLAG_USE_PNFS_MDS) &&
    	    (flags & EXCHGID4_FLAG_USE_NON_PNFS))
    		goto out_inval;
    	if (!(flags & (EXCHGID4_FLAG_MASK_PNFS)))
    		goto out_inval;
    	return NFS_OK;
    out_inval:
    	return -NFS4ERR_INVAL;
    }
    
    
    /*
     * nfs4_proc_exchange_id()
     *
     * Since the clientid has expired, all compounds using sessions
     * associated with the stale clientid will be returning
     * NFS4ERR_BADSESSION in the sequence operation, and will therefore
     * be in some phase of session reset.
     */
    
    int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
    
    {
    	nfs4_verifier verifier;
    	struct nfs41_exchange_id_args args = {
    		.client = clp,
    
    		.flags = EXCHGID4_FLAG_SUPP_MOVED_REFER,
    
    	};
    	struct nfs41_exchange_id_res res = {
    		.client = clp,
    	};
    	int status;
    	struct rpc_message msg = {
    		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_EXCHANGE_ID],
    		.rpc_argp = &args,
    		.rpc_resp = &res,
    		.rpc_cred = cred,
    	};
    	__be32 *p;
    
    	dprintk("--> %s\n", __func__);
    	BUG_ON(clp == NULL);
    
    	p = (u32 *)verifier.data;
    	*p++ = htonl((u32)clp->cl_boot_time.tv_sec);
    	*p = htonl((u32)clp->cl_boot_time.tv_nsec);
    	args.verifier = &verifier;
    
    
    	args.id_len = scnprintf(args.id, sizeof(args.id),
    				"%s/%s.%s/%u",
    				clp->cl_ipaddr,
    				init_utsname()->nodename,
    				init_utsname()->domainname,
    				clp->cl_rpcclient->cl_auth->au_flavor);
    
    	status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
    
    	if (!status)
    		status = nfs4_check_cl_exchange_flags(clp->cl_exchange_flags);
    
    	dprintk("<-- %s status= %d\n", __func__, status);
    	return status;
    }
    
    
    Andy Adamson's avatar
    Andy Adamson committed
    struct nfs4_get_lease_time_data {
    	struct nfs4_get_lease_time_args *args;
    	struct nfs4_get_lease_time_res *res;
    	struct nfs_client *clp;
    };
    
    static void nfs4_get_lease_time_prepare(struct rpc_task *task,
    					void *calldata)
    {
    	int ret;
    	struct nfs4_get_lease_time_data *data =
    			(struct nfs4_get_lease_time_data *)calldata;
    
    	dprintk("--> %s\n", __func__);
    
    	rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
    
    Andy Adamson's avatar
    Andy Adamson committed
    	/* just setup sequence, do not trigger session recovery
    	   since we're invoked within one */
    	ret = nfs41_setup_sequence(data->clp->cl_session,
    
    				   &data->args->la_seq_args,
    				   &data->res->lr_seq_res, 0, task);
    
    Andy Adamson's avatar
    Andy Adamson committed
    
    	BUG_ON(ret == -EAGAIN);
    	rpc_call_start(task);
    	dprintk("<-- %s\n", __func__);
    }
    
    /*
     * Called from nfs4_state_manager thread for session setup, so don't recover
     * from sequence operation or clientid errors.
     */
    static void nfs4_get_lease_time_done(struct rpc_task *task, void *calldata)
    {
    	struct nfs4_get_lease_time_data *data =
    			(struct nfs4_get_lease_time_data *)calldata;
    
    	dprintk("--> %s\n", __func__);
    
    	if (!nfs41_sequence_done(task, &data->res->lr_seq_res))
    		return;
    
    Andy Adamson's avatar
    Andy Adamson committed
    	switch (task->tk_status) {
    	case -NFS4ERR_DELAY:
    	case -NFS4ERR_GRACE:
    		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:
    
    Andy Adamson's avatar
    Andy Adamson committed
    		return;
    	}
    	dprintk("<-- %s\n", __func__);
    }
    
    struct rpc_call_ops nfs4_get_lease_time_ops = {
    	.rpc_call_prepare = nfs4_get_lease_time_prepare,
    	.rpc_call_done = nfs4_get_lease_time_done,
    };
    
    int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo)
    {
    	struct rpc_task *task;
    	struct nfs4_get_lease_time_args args;
    	struct nfs4_get_lease_time_res res = {
    		.lr_fsinfo = fsinfo,
    	};
    	struct nfs4_get_lease_time_data data = {
    		.args = &args,
    		.res = &res,
    		.clp = clp,
    	};
    	struct rpc_message msg = {
    		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GET_LEASE_TIME],
    		.rpc_argp = &args,
    		.rpc_resp = &res,
    	};
    	struct rpc_task_setup task_setup = {
    		.rpc_client = clp->cl_rpcclient,
    		.rpc_message = &msg,
    		.callback_ops = &nfs4_get_lease_time_ops,
    
    		.callback_data = &data,
    		.flags = RPC_TASK_TIMEOUT,
    
    Andy Adamson's avatar
    Andy Adamson committed
    	};
    	int status;
    
    	dprintk("--> %s\n", __func__);
    	task = rpc_run_task(&task_setup);
    
    	if (IS_ERR(task))
    		status = PTR_ERR(task);
    	else {
    		status = task->tk_status;
    		rpc_put_task(task);
    	}
    	dprintk("<-- %s return %d\n", __func__, status);
    
    	return status;
    }
    
    
    static int nfs4_reset_slot_table(struct nfs4_slot_table *tbl, u32 max_reqs,
    				 int ivalue)
    
    	struct nfs4_slot *new = NULL;
    
    	dprintk("--> %s: max_reqs=%u, tbl->max_slots %d\n", __func__,
    		max_reqs, tbl->max_slots);
    
    	/* Does the newly negotiated max_reqs match the existing slot table? */
    	if (max_reqs != tbl->max_slots) {
    		ret = -ENOMEM;
    		new = kmalloc(max_reqs * sizeof(struct nfs4_slot),
    
    		if (!new)
    			goto out;
    		ret = 0;
    		kfree(tbl->slots);
    
    	}
    	spin_lock(&tbl->slot_tbl_lock);
    
    	if (new) {
    		tbl->slots = new;
    		tbl->max_slots = max_reqs;
    	}
    	for (i = 0; i < tbl->max_slots; ++i)
    
    		tbl->slots[i].seq_nr = ivalue;
    
    	spin_unlock(&tbl->slot_tbl_lock);
    	dprintk("%s: tbl=%p slots=%p max_slots=%d\n", __func__,
    		tbl, tbl->slots, tbl->max_slots);
    out:
    	dprintk("<-- %s: return %d\n", __func__, ret);
    	return ret;
    }
    
    
    /*
     * Reset the forechannel and backchannel slot tables
     */
    static int nfs4_reset_slot_tables(struct nfs4_session *session)
    {
    	int status;
    
    	status = nfs4_reset_slot_table(&session->fc_slot_table,
    
    			session->fc_attrs.max_reqs, 1);
    
    	if (status)
    		return status;
    
    	status = nfs4_reset_slot_table(&session->bc_slot_table,
    
    			session->bc_attrs.max_reqs, 0);
    
    /* Destroy the slot table */
    static void nfs4_destroy_slot_tables(struct nfs4_session *session)
    {
    	if (session->fc_slot_table.slots != NULL) {
    		kfree(session->fc_slot_table.slots);
    		session->fc_slot_table.slots = NULL;
    	}
    	if (session->bc_slot_table.slots != NULL) {
    		kfree(session->bc_slot_table.slots);
    		session->bc_slot_table.slots = NULL;
    	}
    	return;
    }
    
    
    /*
     * Initialize slot table
     */
    
    static int nfs4_init_slot_table(struct nfs4_slot_table *tbl,
    		int max_slots, int ivalue)
    
    {
    	struct nfs4_slot *slot;
    	int ret = -ENOMEM;
    
    	BUG_ON(max_slots > NFS4_MAX_SLOT_TABLE);
    
    
    	dprintk("--> %s: max_reqs=%u\n", __func__, max_slots);
    
    	slot = kcalloc(max_slots, sizeof(struct nfs4_slot), GFP_NOFS);
    
    	if (!slot)
    		goto out;
    	ret = 0;
    
    	spin_lock(&tbl->slot_tbl_lock);
    	tbl->max_slots = max_slots;
    	tbl->slots = slot;
    	tbl->highest_used_slotid = -1;  /* no slot is currently used */
    	spin_unlock(&tbl->slot_tbl_lock);