static int _nfs4_proc_open(struct nfs4_opendata *data);
static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *);
-static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry);
static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception);
static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs_client *clp);
-static int _nfs4_do_access(struct inode *inode, struct rpc_cred *cred, int openflags);
+static int _nfs4_proc_lookup(struct inode *dir, const struct qstr *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr);
+static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr);
/* Prevent leaks of NFSv4 errors into userland */
int nfs4_map_errors(int err)
*p++ = xdr_one; /* bitmap length */
*p++ = htonl(FATTR4_WORD0_FILEID); /* bitmap */
*p++ = htonl(8); /* attribute buffer length */
- p = xdr_encode_hyper(p, dentry->d_inode->i_ino);
+ p = xdr_encode_hyper(p, NFS_FILEID(dentry->d_inode));
}
*p++ = xdr_one; /* next */
*p++ = xdr_one; /* bitmap length */
*p++ = htonl(FATTR4_WORD0_FILEID); /* bitmap */
*p++ = htonl(8); /* attribute buffer length */
- p = xdr_encode_hyper(p, dentry->d_parent->d_inode->i_ino);
+ p = xdr_encode_hyper(p, NFS_FILEID(dentry->d_parent->d_inode));
readdir->pgbase = (char *)p - (char *)start;
readdir->count -= readdir->pgbase;
spin_lock(&dir->i_lock);
nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA;
- if (cinfo->before == nfsi->change_attr && cinfo->atomic)
- nfsi->change_attr = cinfo->after;
+ if (!cinfo->atomic || cinfo->before != nfsi->change_attr)
+ nfsi->cache_change_attribute = jiffies;
+ nfsi->change_attr = cinfo->after;
spin_unlock(&dir->i_lock);
}
switch (mode & (FMODE_READ|FMODE_WRITE|O_EXCL)) {
case FMODE_READ:
ret |= test_bit(NFS_O_RDONLY_STATE, &state->flags) != 0;
- ret |= test_bit(NFS_O_RDWR_STATE, &state->flags) != 0;
break;
case FMODE_WRITE:
ret |= test_bit(NFS_O_WRONLY_STATE, &state->flags) != 0;
- ret |= test_bit(NFS_O_RDWR_STATE, &state->flags) != 0;
break;
case FMODE_READ|FMODE_WRITE:
ret |= test_bit(NFS_O_RDWR_STATE, &state->flags) != 0;
memcpy(stateid.data, delegation->stateid.data, sizeof(stateid.data));
rcu_read_unlock();
lock_kernel();
- ret = _nfs4_do_access(state->inode, state->owner->so_cred, open_mode);
+ ret = nfs_may_open(state->inode, state->owner->so_cred, open_mode);
unlock_kernel();
if (ret != 0)
goto out;
return ERR_PTR(-ENOENT);
}
+static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context *ctx, struct nfs4_state *state)
+{
+ struct nfs4_opendata *opendata;
+
+ opendata = nfs4_opendata_alloc(&ctx->path, state->owner, 0, NULL);
+ if (opendata == NULL)
+ return ERR_PTR(-ENOMEM);
+ opendata->state = state;
+ atomic_inc(&state->count);
+ return opendata;
+}
+
static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, mode_t openflags, struct nfs4_state **res)
{
struct nfs4_state *newstate;
int delegation_type = 0;
int status;
- opendata = nfs4_opendata_alloc(&ctx->path, state->owner, 0, NULL);
- if (opendata == NULL)
- return -ENOMEM;
+ opendata = nfs4_open_recoverdata_alloc(ctx, state);
+ if (IS_ERR(opendata))
+ return PTR_ERR(opendata);
opendata->o_arg.claim = NFS4_OPEN_CLAIM_PREVIOUS;
opendata->o_arg.fh = NFS_FH(state->inode);
- nfs_copy_fh(&opendata->o_res.fh, opendata->o_arg.fh);
rcu_read_lock();
delegation = rcu_dereference(NFS_I(state->inode)->delegation);
if (delegation != NULL && (delegation->flags & NFS_DELEGATION_NEED_RECLAIM) != 0)
- delegation_type = delegation->flags;
+ delegation_type = delegation->type;
rcu_read_unlock();
opendata->o_arg.u.delegation_type = delegation_type;
status = nfs4_open_recover(opendata, state);
static int _nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid)
{
- struct nfs4_state_owner *sp = state->owner;
struct nfs4_opendata *opendata;
int ret;
- opendata = nfs4_opendata_alloc(&ctx->path, sp, 0, NULL);
- if (opendata == NULL)
- return -ENOMEM;
+ opendata = nfs4_open_recoverdata_alloc(ctx, state);
+ if (IS_ERR(opendata))
+ return PTR_ERR(opendata);
opendata->o_arg.claim = NFS4_OPEN_CLAIM_DELEGATE_CUR;
memcpy(opendata->o_arg.u.delegation.data, stateid->data,
sizeof(opendata->o_arg.u.delegation.data));
/* Update sequence id. */
data->o_arg.id = sp->so_owner_id.id;
data->o_arg.clientid = sp->so_client->cl_clientid;
- if (data->o_arg.claim == NFS4_OPEN_CLAIM_PREVIOUS)
+ if (data->o_arg.claim == NFS4_OPEN_CLAIM_PREVIOUS) {
msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR];
+ nfs_copy_fh(&data->o_res.fh, data->o_arg.fh);
+ }
data->timestamp = jiffies;
rpc_call_setup(task, &msg, 0);
return;
if (status != 0 || !data->rpc_done)
return status;
+ if (o_res->fh.size == 0)
+ _nfs4_proc_lookup(dir, o_arg->name, &o_res->fh, o_res->f_attr);
+
if (o_arg->open_flags & O_CREAT) {
update_changeattr(dir, &o_res->cinfo);
nfs_post_op_update_inode(dir, o_res->dir_attr);
return status;
}
if (!(o_res->f_attr->valid & NFS_ATTR_FATTR))
- return server->nfs_client->rpc_ops->getattr(server, &o_res->fh, o_res->f_attr);
+ _nfs4_proc_getattr(server, &o_res->fh, o_res->f_attr);
return 0;
}
-static int _nfs4_do_access(struct inode *inode, struct rpc_cred *cred, int openflags)
-{
- struct nfs_access_entry cache;
- int mask = 0;
- int status;
-
- if (openflags & FMODE_READ)
- mask |= MAY_READ;
- if (openflags & FMODE_WRITE)
- mask |= MAY_WRITE;
- if (openflags & FMODE_EXEC)
- mask |= MAY_EXEC;
- status = nfs_access_get_cached(inode, cred, &cache);
- if (status == 0)
- goto out;
-
- /* Be clever: ask server to check for all possible rights */
- cache.mask = MAY_EXEC | MAY_WRITE | MAY_READ;
- cache.cred = cred;
- cache.jiffies = jiffies;
- status = _nfs4_proc_access(inode, &cache);
- if (status != 0)
- return status;
- nfs_access_add_cache(inode, &cache);
-out:
- if ((cache.mask & mask) == mask)
- return 0;
- return -EACCES;
-}
-
static int nfs4_recover_expired_lease(struct nfs_server *server)
{
struct nfs_client *clp = server->nfs_client;
struct nfs4_opendata *opendata;
int ret;
- opendata = nfs4_opendata_alloc(&ctx->path, state->owner, 0, NULL);
- if (opendata == NULL)
- return -ENOMEM;
+ opendata = nfs4_open_recoverdata_alloc(ctx, state);
+ if (IS_ERR(opendata))
+ return PTR_ERR(opendata);
ret = nfs4_open_recover(opendata, state);
if (ret == -ESTALE) {
/* Invalidate the state owner so we don't ever use it again */
nfs_increment_open_seqid(task->tk_status, calldata->arg.seqid);
switch (task->tk_status) {
case 0:
- nfs_set_open_stateid(state, &calldata->res.stateid, calldata->arg.open_flags);
+ nfs_set_open_stateid(state, &calldata->res.stateid, 0);
renew_lease(server, calldata->timestamp);
break;
case -NFS4ERR_STALE_STATEID:
.rpc_cred = state->owner->so_cred,
};
int clear_rd, clear_wr, clear_rdwr;
- int mode;
if (nfs_wait_on_sequence(calldata->arg.seqid, task) != 0)
return;
- mode = FMODE_READ|FMODE_WRITE;
clear_rd = clear_wr = clear_rdwr = 0;
spin_lock(&state->owner->so_lock);
/* Calculate the change in open mode */
if (state->n_rdwr == 0) {
if (state->n_rdonly == 0) {
- mode &= ~FMODE_READ;
clear_rd |= test_and_clear_bit(NFS_O_RDONLY_STATE, &state->flags);
clear_rdwr |= test_and_clear_bit(NFS_O_RDWR_STATE, &state->flags);
}
if (state->n_wronly == 0) {
- mode &= ~FMODE_WRITE;
clear_wr |= test_and_clear_bit(NFS_O_WRONLY_STATE, &state->flags);
clear_rdwr |= test_and_clear_bit(NFS_O_RDWR_STATE, &state->flags);
}
return;
}
nfs_fattr_init(calldata->res.fattr);
- if (mode != 0)
+ if (test_bit(NFS_O_RDONLY_STATE, &state->flags) != 0) {
+ msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE];
+ calldata->arg.open_flags = FMODE_READ;
+ } else if (test_bit(NFS_O_WRONLY_STATE, &state->flags) != 0) {
msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE];
- calldata->arg.open_flags = mode;
+ calldata->arg.open_flags = FMODE_WRITE;
+ }
calldata->timestamp = jiffies;
rpc_call_setup(task, &msg, 0);
}
/* If the open_intent is for execute, we have an extra check to make */
if (nd->intent.open.flags & FMODE_EXEC) {
- ret = _nfs4_do_access(state->inode,
+ ret = nfs_may_open(state->inode,
state->owner->so_cred,
nd->intent.open.flags);
if (ret < 0)
filp = lookup_instantiate_filp(nd, path->dentry, NULL);
if (!IS_ERR(filp)) {
struct nfs_open_context *ctx;
- ctx = (struct nfs_open_context *)filp->private_data;
+ ctx = nfs_file_open_context(filp);
ctx->state = state;
return 0;
}
state = nfs4_do_open(dir, &path, nd->intent.open.flags, &attr, cred);
put_rpccred(cred);
if (IS_ERR(state)) {
- if (PTR_ERR(state) == -ENOENT)
+ if (PTR_ERR(state) == -ENOENT) {
d_add(dentry, NULL);
+ nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
+ }
return (struct dentry *)state;
}
res = d_add_unique(dentry, igrab(state->inode));
if (res != NULL)
- dentry = res;
+ path.dentry = res;
+ nfs_set_verifier(path.dentry, nfs_save_change_attribute(dir));
nfs4_intent_set_file(nd, &path, state);
return res;
}
}
}
if (state->inode == dentry->d_inode) {
+ nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
nfs4_intent_set_file(nd, &path, state);
return 1;
}
* 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, 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;
return status;
}
-static int _nfs4_proc_lookupfh(struct nfs_server *server, struct nfs_fh *dirfh,
- struct qstr *name, struct nfs_fh *fhandle,
+static int _nfs4_proc_lookupfh(struct nfs_server *server, const struct nfs_fh *dirfh,
+ const struct qstr *name, struct nfs_fh *fhandle,
struct nfs_fattr *fattr)
{
int status;
return err;
}
-static int _nfs4_proc_lookup(struct inode *dir, struct qstr *name,
+static int _nfs4_proc_lookup(struct inode *dir, const struct qstr *name,
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
int status;
static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry)
{
+ struct nfs_server *server = NFS_SERVER(inode);
+ struct nfs_fattr fattr;
struct nfs4_accessargs args = {
.fh = NFS_FH(inode),
+ .bitmask = server->attr_bitmask,
+ };
+ struct nfs4_accessres res = {
+ .server = server,
+ .fattr = &fattr,
};
- struct nfs4_accessres res = { 0 };
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ACCESS],
.rpc_argp = &args,
if (mode & MAY_EXEC)
args.access |= NFS4_ACCESS_EXECUTE;
}
+ nfs_fattr_init(&fattr);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
if (!status) {
entry->mask = 0;
entry->mask |= MAY_WRITE;
if (res.access & (NFS4_ACCESS_LOOKUP|NFS4_ACCESS_EXECUTE))
entry->mask |= MAY_EXEC;
+ nfs_refresh_inode(inode, &fattr);
}
return status;
}
}
state = nfs4_do_open(dir, &path, flags, sattr, cred);
put_rpccred(cred);
+ d_drop(dentry);
if (IS_ERR(state)) {
status = PTR_ERR(state);
goto out;
}
- d_instantiate(dentry, igrab(state->inode));
+ d_add(dentry, igrab(state->inode));
+ nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
if (flags & O_EXCL) {
struct nfs_fattr fattr;
status = nfs4_do_setattr(state->inode, &fattr, sattr, state);
static int _nfs4_proc_remove(struct inode *dir, struct qstr *name)
{
struct nfs_server *server = NFS_SERVER(dir);
- struct nfs4_remove_arg args = {
+ struct nfs_removeargs args = {
.fh = NFS_FH(dir),
- .name = name,
+ .name.len = name->len,
+ .name.name = name->name,
.bitmask = server->attr_bitmask,
};
- struct nfs_fattr dir_attr;
- struct nfs4_remove_res res = {
+ struct nfs_removeres res = {
.server = server,
- .dir_attr = &dir_attr,
};
struct rpc_message msg = {
- .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE],
- .rpc_argp = &args,
- .rpc_resp = &res,
+ .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE],
+ .rpc_argp = &args,
+ .rpc_resp = &res,
};
int status;
- nfs_fattr_init(res.dir_attr);
+ nfs_fattr_init(&res.dir_attr);
status = rpc_call_sync(server->client, &msg, 0);
if (status == 0) {
update_changeattr(dir, &res.cinfo);
- nfs_post_op_update_inode(dir, res.dir_attr);
+ nfs_post_op_update_inode(dir, &res.dir_attr);
}
return status;
}
return err;
}
-struct unlink_desc {
- struct nfs4_remove_arg args;
- struct nfs4_remove_res res;
- struct nfs_fattr dir_attr;
-};
-
-static int nfs4_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir,
- struct qstr *name)
+static void nfs4_proc_unlink_setup(struct rpc_message *msg, struct inode *dir)
{
- struct nfs_server *server = NFS_SERVER(dir->d_inode);
- struct unlink_desc *up;
+ struct nfs_server *server = NFS_SERVER(dir);
+ struct nfs_removeargs *args = msg->rpc_argp;
+ struct nfs_removeres *res = msg->rpc_resp;
- up = kmalloc(sizeof(*up), GFP_KERNEL);
- if (!up)
- return -ENOMEM;
-
- up->args.fh = NFS_FH(dir->d_inode);
- up->args.name = name;
- up->args.bitmask = server->attr_bitmask;
- up->res.server = server;
- up->res.dir_attr = &up->dir_attr;
-
+ args->bitmask = server->attr_bitmask;
+ res->server = server;
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE];
- msg->rpc_argp = &up->args;
- msg->rpc_resp = &up->res;
- return 0;
}
-static int nfs4_proc_unlink_done(struct dentry *dir, struct rpc_task *task)
+static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir)
{
- struct rpc_message *msg = &task->tk_msg;
- struct unlink_desc *up;
-
- if (msg->rpc_resp != NULL) {
- up = container_of(msg->rpc_resp, struct unlink_desc, res);
- update_changeattr(dir->d_inode, &up->res.cinfo);
- nfs_post_op_update_inode(dir->d_inode, up->res.dir_attr);
- kfree(up);
- msg->rpc_resp = NULL;
- msg->rpc_argp = NULL;
- }
- return 0;
+ struct nfs_removeres *res = task->tk_msg.rpc_resp;
+
+ if (nfs4_async_handle_error(task, res->server) == -EAGAIN)
+ return 0;
+ update_changeattr(dir, &res->cinfo);
+ nfs_post_op_update_inode(dir, &res->dir_attr);
+ return 1;
}
static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
if (status == 0)
memcpy(NFS_COOKIEVERF(dir), res.verifier.data, NFS4_VERIFIER_SIZE);
+
+ nfs_invalidate_atime(dir);
+
dprintk("%s: returns %d\n", __FUNCTION__, status);
return status;
}
rpc_restart_call(task);
return -EAGAIN;
}
+
+ nfs_invalidate_atime(data->inode);
if (task->tk_status > 0)
renew_lease(server, data->timestamp);
return 0;
}
if (task->tk_status >= 0) {
renew_lease(NFS_SERVER(inode), data->timestamp);
- nfs_post_op_update_inode(inode, data->res.fattr);
+ nfs_post_op_update_inode_force_wcc(inode, data->res.fattr);
}
return 0;
}
rpc_restart_call(task);
return -EAGAIN;
}
- if (task->tk_status >= 0)
- nfs_post_op_update_inode(inode, data->res.fattr);
+ nfs_refresh_inode(inode, data->res.fattr);
return 0;
}
if (status == 0) {
status = data->rpc_status;
if (status == 0)
- nfs_post_op_update_inode(inode, &data->fattr);
+ nfs_refresh_inode(inode, &data->fattr);
}
rpc_put_task(task);
return status;
status = -ENOMEM;
if (seqid == NULL)
goto out;
- task = nfs4_do_unlck(request, request->fl_file->private_data, lsp, seqid);
+ task = nfs4_do_unlck(request, nfs_file_open_context(request->fl_file), lsp, seqid);
status = PTR_ERR(task);
if (IS_ERR(task))
goto out;
int ret;
dprintk("%s: begin!\n", __FUNCTION__);
- data = nfs4_alloc_lockdata(fl, fl->fl_file->private_data,
+ data = nfs4_alloc_lockdata(fl, nfs_file_open_context(fl->fl_file),
fl->fl_u.nfs4_fl.owner);
if (data == NULL)
return -ENOMEM;
int status;
/* verify open state */
- ctx = (struct nfs_open_context *)filp->private_data;
+ ctx = nfs_file_open_context(filp);
state = ctx->state;
if (request->fl_start < 0 || request->fl_end < 0)
return len;
}
-int nfs4_proc_fs_locations(struct inode *dir, struct qstr *name,
+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);