📄 nfs4proc.c
字号:
nfs_post_op_update_inode(dir, o_res->dir_attr); } else nfs_refresh_inode(dir, o_res->dir_attr); if(o_res->rflags & NFS4_OPEN_RESULT_CONFIRM) { status = _nfs4_proc_open_confirm(data); if (status != 0) return status; } if (!(o_res->f_attr->valid & NFS_ATTR_FATTR)) _nfs4_proc_getattr(server, &o_res->fh, o_res->f_attr); return 0;}static int nfs4_recover_expired_lease(struct nfs_server *server){ struct nfs_client *clp = server->nfs_client; int ret; for (;;) { ret = nfs4_wait_clnt_recover(server->client, clp); if (ret != 0) return ret; if (!test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) break; nfs4_schedule_state_recovery(clp); } return 0;}/* * OPEN_EXPIRED: * reclaim state on the server after a network partition. * Assumes caller holds the appropriate lock */static int _nfs4_open_expired(struct nfs_open_context *ctx, struct nfs4_state *state){ struct nfs4_opendata *opendata; int ret; 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 */ nfs4_drop_state_owner(state->owner); d_drop(ctx->path.dentry); } nfs4_opendata_put(opendata); return ret;}static inline int nfs4_do_open_expired(struct nfs_open_context *ctx, struct nfs4_state *state){ struct nfs_server *server = NFS_SERVER(state->inode); struct nfs4_exception exception = { }; int err; do { err = _nfs4_open_expired(ctx, state); if (err == -NFS4ERR_DELAY) nfs4_handle_exception(server, err, &exception); } while (exception.retry); return err;}static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state){ struct nfs_open_context *ctx; int ret; ctx = nfs4_state_find_open_context(state); if (IS_ERR(ctx)) return PTR_ERR(ctx); ret = nfs4_do_open_expired(ctx, state); put_nfs_open_context(ctx); return ret;}/* * on an EXCLUSIVE create, the server should send back a bitmask with FATTR4-* * fields corresponding to attributes that were used to store the verifier. * Make sure we clobber those fields in the later setattr call */static inline void nfs4_exclusive_attrset(struct nfs4_opendata *opendata, struct iattr *sattr){ if ((opendata->o_res.attrset[1] & FATTR4_WORD1_TIME_ACCESS) && !(sattr->ia_valid & ATTR_ATIME_SET)) sattr->ia_valid |= ATTR_ATIME; if ((opendata->o_res.attrset[1] & FATTR4_WORD1_TIME_MODIFY) && !(sattr->ia_valid & ATTR_MTIME_SET)) sattr->ia_valid |= ATTR_MTIME;}/* * Returns a referenced nfs4_state */static int _nfs4_do_open(struct inode *dir, struct path *path, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res){ struct nfs4_state_owner *sp; struct nfs4_state *state = NULL; struct nfs_server *server = NFS_SERVER(dir); struct nfs_client *clp = server->nfs_client; struct nfs4_opendata *opendata; int status; /* Protect against reboot recovery conflicts */ status = -ENOMEM; if (!(sp = nfs4_get_state_owner(server, cred))) { dprintk("nfs4_do_open: nfs4_get_state_owner failed!\n"); goto out_err; } 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, flags & (FMODE_READ|FMODE_WRITE)); down_read(&clp->cl_sem); status = -ENOMEM; opendata = nfs4_opendata_alloc(path, sp, flags, sattr); if (opendata == NULL) goto err_release_rwsem; if (path->dentry->d_inode != NULL) opendata->state = nfs4_get_open_state(path->dentry->d_inode, sp); status = _nfs4_proc_open(opendata); if (status != 0) goto err_opendata_put; if (opendata->o_arg.open_flags & O_EXCL) nfs4_exclusive_attrset(opendata, sattr); state = nfs4_opendata_to_nfs4_state(opendata); status = PTR_ERR(state); if (IS_ERR(state)) goto err_opendata_put; nfs4_opendata_put(opendata); nfs4_put_state_owner(sp); up_read(&clp->cl_sem); *res = state; return 0;err_opendata_put: nfs4_opendata_put(opendata);err_release_rwsem: up_read(&clp->cl_sem);err_put_state_owner: nfs4_put_state_owner(sp);out_err: *res = NULL; return status;}static struct nfs4_state *nfs4_do_open(struct inode *dir, struct path *path, 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, flags, sattr, cred, &res); if (status == 0) break; /* NOTE: BAD_SEQID means the server and client disagree about the * book-keeping w.r.t. state-changing operations * (OPEN/CLOSE/LOCK/LOCKU...) * It is actually a sign of a bug on the client or on the server. * * If we receive a BAD_SEQID error in the particular case of * doing an OPEN, we assume that nfs_increment_open_seqid() will * have unhashed the old state_owner for us, and that we can * therefore safely retry using a new one. We should still warn * the user though... */ if (status == -NFS4ERR_BAD_SEQID) { printk(KERN_WARNING "NFS: v4 server %s " " returned a bad sequence-id error!\n", NFS_SERVER(dir)->nfs_client->cl_hostname); exception.retry = 1; continue; } /* * BAD_STATEID on OPEN means that the server cancelled our * state before it received the OPEN_CONFIRM. * Recover by retrying the request as per the discussion * on Page 181 of RFC3530. */ if (status == -NFS4ERR_BAD_STATEID) { exception.retry = 1; continue; } if (status == -EAGAIN) { /* We must have found a delegation */ exception.retry = 1; continue; } res = ERR_PTR(nfs4_handle_exception(NFS_SERVER(dir), status, &exception)); } while (exception.retry); return res;}static int _nfs4_do_setattr(struct inode *inode, struct nfs_fattr *fattr, struct iattr *sattr, struct nfs4_state *state){ struct nfs_server *server = NFS_SERVER(inode); struct nfs_setattrargs arg = { .fh = NFS_FH(inode), .iap = sattr, .server = server, .bitmask = server->attr_bitmask, }; struct nfs_setattrres res = { .fattr = fattr, .server = server, }; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETATTR], .rpc_argp = &arg, .rpc_resp = &res, }; unsigned long timestamp = jiffies; int status; nfs_fattr_init(fattr); if (nfs4_copy_delegation_stateid(&arg.stateid, inode)) { /* Use that stateid */ } else if (state != NULL) { msg.rpc_cred = state->owner->so_cred; nfs4_copy_stateid(&arg.stateid, state, current->files); } else memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid)); status = rpc_call_sync(server->client, &msg, 0); if (status == 0 && state != NULL) renew_lease(server, timestamp); return status;}static int nfs4_do_setattr(struct inode *inode, struct nfs_fattr *fattr, struct iattr *sattr, struct nfs4_state *state){ struct nfs_server *server = NFS_SERVER(inode); struct nfs4_exception exception = { }; int err; do { err = nfs4_handle_exception(server, _nfs4_do_setattr(inode, fattr, sattr, state), &exception); } while (exception.retry); return err;}struct nfs4_closedata { struct path path; struct inode *inode; struct nfs4_state *state; struct nfs_closeargs arg; struct nfs_closeres res; struct nfs_fattr fattr; unsigned long timestamp;};static void nfs4_free_closedata(void *data){ struct nfs4_closedata *calldata = data; struct nfs4_state_owner *sp = calldata->state->owner; nfs4_put_open_state(calldata->state); nfs_free_seqid(calldata->arg.seqid); nfs4_put_state_owner(sp); dput(calldata->path.dentry); mntput(calldata->path.mnt); kfree(calldata);}static void nfs4_close_done(struct rpc_task *task, void *data){ struct nfs4_closedata *calldata = data; struct nfs4_state *state = calldata->state; struct nfs_server *server = NFS_SERVER(calldata->inode); if (RPC_ASSASSINATED(task)) return; /* hmm. we are done with the inode, and in the process of freeing * the state_owner. we keep this around to process errors */ nfs_increment_open_seqid(task->tk_status, calldata->arg.seqid); switch (task->tk_status) { case 0: nfs_set_open_stateid(state, &calldata->res.stateid, 0); renew_lease(server, calldata->timestamp); break; case -NFS4ERR_STALE_STATEID: case -NFS4ERR_EXPIRED: break; default: if (nfs4_async_handle_error(task, server) == -EAGAIN) { rpc_restart_call(task); return; } } nfs_refresh_inode(calldata->inode, calldata->res.fattr);}static void nfs4_close_prepare(struct rpc_task *task, void *data){ struct nfs4_closedata *calldata = data; struct nfs4_state *state = calldata->state; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE], .rpc_argp = &calldata->arg, .rpc_resp = &calldata->res, .rpc_cred = state->owner->so_cred, }; int clear_rd, clear_wr, clear_rdwr; if (nfs_wait_on_sequence(calldata->arg.seqid, task) != 0) return; 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) { 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) { clear_wr |= test_and_clear_bit(NFS_O_WRONLY_STATE, &state->flags); clear_rdwr |= test_and_clear_bit(NFS_O_RDWR_STATE, &state->flags); } } spin_unlock(&state->owner->so_lock); if (!clear_rd && !clear_wr && !clear_rdwr) { /* Note: exit _without_ calling nfs4_close_done */ task->tk_action = NULL; return; } nfs_fattr_init(calldata->res.fattr); 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 = FMODE_WRITE; } calldata->timestamp = jiffies; rpc_call_setup(task, &msg, 0);}static const struct rpc_call_ops nfs4_close_ops = { .rpc_call_prepare = nfs4_close_prepare, .rpc_call_done = nfs4_close_done, .rpc_release = nfs4_free_closedata,};/* * It is possible for data to be read/written from a mem-mapped file * after the sys_close call (which hits the vfs layer as a flush). * This means that we can't safely call nfsv4 close on a file until * the inode is cleared. This in turn means that we are not good * NFSv4 citizens - we do not indicate to the server to update the file's * share state even when we are done with one of the three share * stateid's in the inode. * * NOTE: Caller must be holding the sp->so_owner semaphore! */int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait){ struct nfs_server *server = NFS_SERVER(state->inode); struct nfs4_closedata *calldata; struct nfs4_state_owner *sp = state->owner; struct rpc_task *task; int status = -ENOMEM; calldata = kmalloc(sizeof(*calldata), GFP_KERNEL); if (calldata == NULL) goto out; calldata->inode = state->inode; calldata->state = state; calldata->arg.fh = NFS_FH(state->inode); calldata->arg.stateid = &state->open_stateid; /* Serialization for the sequence id */ calldata->arg.seqid = nfs_alloc_seqid(&state->owner->so_seqid); if (calldata->arg.seqid == NULL) goto out_free_calldata; calldata->arg.bitmask = server->attr_bitmask; calldata->res.fattr = &calldata->fattr; calldata->res.server = server; calldata->path.mnt = mntget(path->mnt); calldata->path.dentry = dget(path->dentry); task = rpc_run_task(server->client, RPC_TASK_ASYNC, &nfs4_close_ops, calldata); if (IS_ERR(task)) return PTR_ERR(task); status = 0; if (wait) status = rpc_wait_for_completion_task(task); rpc_put_task(task); return status;out_free_calldata: kfree(calldata);out: nfs4_put_open_state(state); nfs4_put_state_owner(sp); return status;}static int nfs4_intent_set_file(struct nameidata *nd, struct path *path, struct nfs4_state *state){ struct file *filp; int ret; /* If the open_intent is for execute, we have an extra check to make */ if (nd->intent.open.flags & FMODE_EXEC) { ret = nfs_may_open(state->inode, state->owner->so_cred, nd->intent.open.flags); if (ret < 0) goto out_close; } filp = lookup_instantiate_filp(nd, path->dentry, NULL); if (!IS_ERR(filp)) { struct nfs_open_context *ctx; ctx = nfs_file_open_context(filp); ctx->state = state; return 0; } ret = PTR_ERR(filp);out_close: nfs4_close_sync(path, state, nd->intent.open.flags); return ret;}struct dentry *nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd){ struct dentry *parent; struct path path = { .mnt = nd->mnt, .dentry = dentry, }; struct iattr attr; struct rpc_cred *cred; struct nfs4_state *state; struct dentry *res; if (nd->flags & LOOKUP_CREATE) { attr.ia_mode = nd->intent.open.create_mode; attr.ia_valid = ATTR_MODE; if (!IS_POSIXACL(dir)) attr.ia_mode &= ~current->fs->umask; } else { attr.ia_valid = 0; BUG_ON(nd->intent.open.flags & O_CREAT); } cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0); if (IS_ERR(cred)) return (struct dentry *)cred; parent = dentry->d_parent; /* Protect against concurrent sillydeletes */ nfs_block_sillyrename(parent);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -