nfs4state.c
字号:
goto out; } }out: return status;}static inline voidnfs4_set_lock_denied(struct file_lock *fl, struct nfsd4_lock_denied *deny){ struct nfs4_stateowner *sop = (struct nfs4_stateowner *) fl->fl_owner; unsigned int hval = lockownerid_hashval(sop->so_id); deny->ld_sop = NULL; if (nfs4_verify_lock_stateowner(sop, hval)) deny->ld_sop = sop; deny->ld_start = fl->fl_start; deny->ld_length = ~(u64)0; if (fl->fl_end != ~(u64)0) deny->ld_length = fl->fl_end - fl->fl_start + 1; deny->ld_type = NFS4_READ_LT; if (fl->fl_type != F_RDLCK) deny->ld_type = NFS4_WRITE_LT;}static struct nfs4_stateowner *find_lockstateowner(struct xdr_netobj *owner, clientid_t *clid){ struct nfs4_stateowner *local = NULL; int i; for (i = 0; i < LOCK_HASH_SIZE; i++) { list_for_each_entry(local, &lock_ownerid_hashtbl[i], so_idhash) { if (!cmp_owner_str(local, owner, clid)) continue; return local; } } return NULL;}static intfind_lockstateowner_str(unsigned int hashval, struct xdr_netobj *owner, clientid_t *clid, struct nfs4_stateowner **op) { struct nfs4_stateowner *local = NULL; list_for_each_entry(local, &lock_ownerstr_hashtbl[hashval], so_strhash) { if (!cmp_owner_str(local, owner, clid)) continue; *op = local; return(1); } *op = NULL; return 0;}/* * Alloc a lock owner structure. * Called in nfsd4_lock - therefore, OPEN and OPEN_CONFIRM (if needed) has * occured. * * strhashval = lock_ownerstr_hashval * so_seqid = lock->lk_new_lock_seqid - 1: it gets bumped in encode */static struct nfs4_stateowner *alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, struct nfs4_stateid *open_stp, struct nfsd4_lock *lock) { struct nfs4_stateowner *sop; struct nfs4_replay *rp; unsigned int idhashval; if (!(sop = alloc_stateowner(&lock->lk_new_owner))) return NULL; idhashval = lockownerid_hashval(current_ownerid); INIT_LIST_HEAD(&sop->so_idhash); INIT_LIST_HEAD(&sop->so_strhash); INIT_LIST_HEAD(&sop->so_perclient); INIT_LIST_HEAD(&sop->so_perfilestate); INIT_LIST_HEAD(&sop->so_perlockowner); INIT_LIST_HEAD(&sop->so_close_lru); /* not used */ sop->so_time = 0; list_add(&sop->so_idhash, &lock_ownerid_hashtbl[idhashval]); list_add(&sop->so_strhash, &lock_ownerstr_hashtbl[strhashval]); list_add(&sop->so_perclient, &clp->cl_perclient); list_add(&sop->so_perlockowner, &open_stp->st_perlockowner); add_perclient++; sop->so_is_open_owner = 0; sop->so_id = current_ownerid++; sop->so_client = clp; sop->so_seqid = lock->lk_new_lock_seqid - 1; sop->so_confirmed = 1; rp = &sop->so_replay; rp->rp_status = NFSERR_SERVERFAULT; rp->rp_buflen = 0; rp->rp_buf = rp->rp_ibuf; alloc_lsowner++; return sop;}struct nfs4_stateid *alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struct nfs4_stateid *open_stp){ struct nfs4_stateid *stp; unsigned int hashval = stateid_hashval(sop->so_id, fp->fi_id); if ((stp = kmalloc(sizeof(struct nfs4_stateid), GFP_KERNEL)) == NULL) goto out; INIT_LIST_HEAD(&stp->st_hash); INIT_LIST_HEAD(&stp->st_perfile); INIT_LIST_HEAD(&stp->st_perfilestate); INIT_LIST_HEAD(&stp->st_perlockowner); /* not used */ list_add(&stp->st_hash, &lockstateid_hashtbl[hashval]); list_add(&stp->st_perfile, &fp->fi_perfile); list_add_perfile++; list_add(&stp->st_perfilestate, &sop->so_perfilestate); stp->st_stateowner = sop; stp->st_file = fp; stp->st_stateid.si_boot = boot_time; stp->st_stateid.si_stateownerid = sop->so_id; stp->st_stateid.si_fileid = fp->fi_id; stp->st_stateid.si_generation = 0; stp->st_vfs_file = open_stp->st_vfs_file; /* FIXME refcount?? */ stp->st_vfs_set = open_stp->st_vfs_set; stp->st_access_bmap = open_stp->st_access_bmap; stp->st_deny_bmap = open_stp->st_deny_bmap;out: return stp;}intcheck_lock_length(u64 offset, u64 length){ return ((length == 0) || ((length != ~(u64)0) && LOFF_OVERFLOW(offset, length)));}/* * LOCK operation * * nfs4_unlock_state(); called in encode */intnfsd4_lock(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock *lock){ struct nfs4_stateowner *lock_sop = NULL, *open_sop = NULL; struct nfs4_stateid *lock_stp; struct file *filp; struct file_lock file_lock; struct file_lock *conflock; int status = 0; unsigned int strhashval; dprintk("NFSD: nfsd4_lock: start=%Ld length=%Ld\n", (long long) lock->lk_offset, (long long) lock->lk_length); if (nfs4_in_grace() && !lock->lk_reclaim) return nfserr_grace; if (!nfs4_in_grace() && lock->lk_reclaim) return nfserr_no_grace; if (check_lock_length(lock->lk_offset, lock->lk_length)) return nfserr_inval; lock->lk_stateowner = NULL; nfs4_lock_state(); if (lock->lk_is_new) { /* * Client indicates that this is a new lockowner. * Use open owner and open stateid to create lock owner and lock * stateid. */ struct nfs4_stateid *open_stp = NULL; struct nfs4_file *fp; status = nfserr_stale_clientid; if (STALE_CLIENTID(&lock->lk_new_clientid)) { printk("NFSD: nfsd4_lock: clientid is stale!\n"); goto out; } /* is the new lock seqid presented by the client zero? */ status = nfserr_bad_seqid; if (lock->v.new.lock_seqid != 0) goto out; /* validate and update open stateid and open seqid */ status = nfs4_preprocess_seqid_op(current_fh, lock->lk_new_open_seqid, &lock->lk_new_open_stateid, CHECK_FH | OPEN_STATE, &open_sop, &open_stp, &lock->v.new.clientid); if (status) { if (lock->lk_reclaim) status = nfserr_reclaim_bad; goto out; } /* create lockowner and lock stateid */ fp = open_stp->st_file; strhashval = lock_ownerstr_hashval(fp->fi_inode, open_sop->so_client->cl_clientid.cl_id, lock->v.new.owner); /* * If we already have this lock owner, the client is in * error (or our bookeeping is wrong!) * for asking for a 'new lock'. */ status = nfserr_bad_stateid; lock_sop = find_lockstateowner(&lock->v.new.owner, &lock->v.new.clientid); if (lock_sop) goto out; status = nfserr_resource; if (!(lock->lk_stateowner = alloc_init_lock_stateowner(strhashval, open_sop->so_client, open_stp, lock))) goto out; if ((lock_stp = alloc_init_lock_stateid(lock->lk_stateowner, fp, open_stp)) == NULL) { release_stateowner(lock->lk_stateowner); goto out; } /* bump the open seqid used to create the lock */ open_sop->so_seqid++; } else { /* lock (lock owner + lock stateid) already exists */ status = nfs4_preprocess_seqid_op(current_fh, lock->lk_old_lock_seqid, &lock->lk_old_lock_stateid, CHECK_FH | LOCK_STATE, &lock->lk_stateowner, &lock_stp, NULL); if (status) goto out; } /* lock->lk_stateowner and lock_stp have been created or found */ filp = lock_stp->st_vfs_file; if ((status = fh_verify(rqstp, current_fh, S_IFREG, MAY_LOCK))) { printk("NFSD: nfsd4_lock: permission denied!\n"); goto out; } locks_init_lock(&file_lock); switch (lock->lk_type) { case NFS4_READ_LT: case NFS4_READW_LT: file_lock.fl_type = F_RDLCK; break; case NFS4_WRITE_LT: case NFS4_WRITEW_LT: file_lock.fl_type = F_WRLCK; break; default: status = nfserr_inval; goto out; } file_lock.fl_owner = (fl_owner_t) lock->lk_stateowner; file_lock.fl_pid = current->tgid; file_lock.fl_file = filp; file_lock.fl_flags = FL_POSIX; file_lock.fl_start = lock->lk_offset; if ((lock->lk_length == ~(u64)0) || LOFF_OVERFLOW(lock->lk_offset, lock->lk_length)) file_lock.fl_end = ~(u64)0; else file_lock.fl_end = lock->lk_offset + lock->lk_length - 1; nfs4_transform_lock_offset(&file_lock); /* * Try to lock the file in the VFS. * Note: locks.c uses the BKL to protect the inode's lock list. */ status = posix_lock_file(filp, &file_lock); if (file_lock.fl_ops && file_lock.fl_ops->fl_release_private) file_lock.fl_ops->fl_release_private(&file_lock); dprintk("NFSD: nfsd4_lock: posix_lock_file status %d\n",status); switch (-status) { case 0: /* success! */ update_stateid(&lock_stp->st_stateid); memcpy(&lock->lk_resp_stateid, &lock_stp->st_stateid, sizeof(stateid_t)); goto out; case (EAGAIN): goto conflicting_lock; case (EDEADLK): status = nfserr_deadlock; default: dprintk("NFSD: nfsd4_lock: posix_lock_file() failed! status %d\n",status); goto out_destroy_new_stateid; }conflicting_lock: dprintk("NFSD: nfsd4_lock: conflicting lock found!\n"); status = nfserr_denied; /* XXX There is a race here. Future patch needed to provide * an atomic posix_lock_and_test_file */ if (!(conflock = posix_test_lock(filp, &file_lock))) { status = nfserr_serverfault; goto out; } nfs4_set_lock_denied(conflock, &lock->lk_denied);out_destroy_new_stateid: if (lock->lk_is_new) { dprintk("NFSD: nfsd4_lock: destroy new stateid!\n"); /* * An error encountered after instantiation of the new * stateid has forced us to destroy it. */ if (!seqid_mutating_err(status)) open_sop->so_seqid--; release_state_owner(lock_stp, &lock->lk_stateowner, LOCK_STATE); }out: return status;}/* * LOCKT operation */intnfsd4_lockt(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lockt *lockt){ struct inode *inode; struct file file; struct file_lock file_lock; struct file_lock *conflicting_lock; unsigned int strhashval; int status; if (nfs4_in_grace()) return nfserr_grace; if (check_lock_length(lockt->lt_offset, lockt->lt_length)) return nfserr_inval; lockt->lt_stateowner = NULL; nfs4_lock_state(); status = nfserr_stale_clientid; if (STALE_CLIENTID(&lockt->lt_clientid)) { printk("NFSD: nfsd4_lockt: clientid is stale!\n"); goto out; } if ((status = fh_verify(rqstp, current_fh, S_IFREG, 0))) { printk("NFSD: nfsd4_lockt: fh_verify() failed!\n"); if (status == nfserr_symlink) status = nfserr_inval; goto out; } inode = current_fh->fh_dentry->d_inode; locks_init_lock(&file_lock); switch (lockt->lt_type) { case NFS4_READ_LT: case NFS4_READW_LT: file_lock.fl_type = F_RDLCK; break; case NFS4_WRITE_LT: case NFS4_WRITEW_LT: file_lock.fl_type = F_WRLCK; break; default: printk("NFSD: nfs4_lockt: bad lock type!\n"); status = nfserr_inval; goto out; } strhashval = lock_ownerstr_hashval(inode, lockt->lt_clientid.cl_id, lockt->lt_owner); find_lockstateowner_str(strhashval, &lockt->lt_owner, &lockt->lt_clientid, &lockt->lt_stateowner); if (lockt->lt_stateowner) file_lock.fl_owner = (fl_owner_t)lockt->lt_stateowner; file_lock.fl_pid = current->tgid; file_lock.fl_flags = FL_POSIX; file_lock.fl_start = lockt->lt_offset; if ((lockt->lt_length == ~(u64)0) || LOFF_OVERFLOW(lockt->lt_offset, lockt->lt_length)) file_lock.fl_end = ~(u64)0; else file_lock.fl_end = lockt->lt_offset + lockt->lt_length - 1; nfs4_transform_lock_offset(&file_lock); /* posix_test_lock uses the struct file _only_ to resolve the inode. * since LOCKT doesn't require an OPEN, and therefore a struct * file may not exist, pass posix_test_lock a struct file with * only the dentry:inode set. */ memset(&file, 0, sizeof (struct file)); file.f_dentry = current_fh->fh_dentry; status = nfs_ok; conflicting_lock = posix_test_lock(&file, &file_lock); if (conflicting_lock) { status = nfserr_denied; nfs4_set_lock_denied(conflicting_lock, &lockt->lt_denied); }out: nfs4_unlock_state(); return status;}intnfsd4_locku(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_locku *locku){ struct nfs4_stateid *stp; struct file *filp = NULL; struct file_lock file_lock; int status; dprintk("NFSD: nfsd4_locku: start=%Ld length=%Ld\n", (long long) locku->lu_offset, (long long) locku->lu_length); if (check_lock_length(locku->lu_offset, locku->lu_length)) return nfserr_inval; locku->lu_stateowner = NULL; nfs4_lock_state(); if ((status = nfs4_preprocess_seqid_op(current_fh, locku->lu_seqid, &locku->lu_stateid, CHECK_FH | LOCK_STATE, &locku->lu_stateowner, &stp, NULL))) goto out; filp = stp->st_vfs_file; BUG_ON(!filp); locks_init_lock(&file_lock); file_lock.fl_type = F_UNLCK; file_lock.fl_owner = (fl_owner_t) locku->lu_stateowner; file_lock.fl_pid = current->tgid; file_lock.fl_file = filp; file_lock.fl_flags = FL_POSIX; file_lock.fl_start = locku->lu_offset; if ((locku->lu_length == ~(u64)0) || LOFF_OVERFLOW(locku->lu_offset, locku->lu_length)) file_lock.fl_end = ~(u64)0; else file_lock.fl_end = locku->lu_offset + locku->lu_length - 1; nfs4_transform_lock_offset(&file_lock); /* * Try to unlock the file in the VFS. */ status = posix_lock_file(filp, &file_lock); if (file_lock.fl_ops && file_lock.fl_ops->fl_release_private) file_lock.fl_ops->fl_release_private(&file_lock); if (status) { printk("NFSD: nfs4_locku: posix_lock_file failed!\n"); goto out_nfserr; } /* * OK, unlock succeeded; the only thing left to do is update the stateid. */ update_stateid(&stp->st_stateid); memcpy(&locku->lu_stateid, &stp->st_stateid, sizeof(stateid_t));out: return status;out_nfserr: status = nfserrno(status); goto out;}/* * returns * 1: locks held by lockowner * 0: no locks held by lockowner */static intcheck_for
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -