Make sure we properly reference count the struct files that a lock
depends on, and release them when the lock stateid is released.

This fixes a major leak of struct files when using locking over nfsv4.

Cc: stable@kernel.org
Reported-by: Rick Koshi <nfs-bug-report@more-right-rudder.com>
Tested-by: Ivo Přikryl <prikryl@eurosat.cz>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
This commit is contained in:
J. Bruce Fields 2011-03-02 18:01:35 -05:00
Родитель 529d7b2a7f
Коммит 0997b17360
1 изменённых файлов: 21 добавлений и 11 удалений

Просмотреть файл

@ -397,6 +397,9 @@ static void unhash_generic_stateid(struct nfs4_stateid *stp)
static void free_generic_stateid(struct nfs4_stateid *stp) static void free_generic_stateid(struct nfs4_stateid *stp)
{ {
int oflag = nfs4_access_bmap_to_omode(stp);
nfs4_file_put_access(stp->st_file, oflag);
put_nfs4_file(stp->st_file); put_nfs4_file(stp->st_file);
kmem_cache_free(stateid_slab, stp); kmem_cache_free(stateid_slab, stp);
} }
@ -448,11 +451,8 @@ release_stateid_lockowners(struct nfs4_stateid *open_stp)
static void release_open_stateid(struct nfs4_stateid *stp) static void release_open_stateid(struct nfs4_stateid *stp)
{ {
int oflag = nfs4_access_bmap_to_omode(stp);
unhash_generic_stateid(stp); unhash_generic_stateid(stp);
release_stateid_lockowners(stp); release_stateid_lockowners(stp);
nfs4_file_put_access(stp->st_file, oflag);
free_generic_stateid(stp); free_generic_stateid(stp);
} }
@ -3734,6 +3734,7 @@ alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struc
stp->st_stateid.si_stateownerid = sop->so_id; stp->st_stateid.si_stateownerid = sop->so_id;
stp->st_stateid.si_fileid = fp->fi_id; stp->st_stateid.si_fileid = fp->fi_id;
stp->st_stateid.si_generation = 0; stp->st_stateid.si_generation = 0;
stp->st_access_bmap = 0;
stp->st_deny_bmap = open_stp->st_deny_bmap; stp->st_deny_bmap = open_stp->st_deny_bmap;
stp->st_openstp = open_stp; stp->st_openstp = open_stp;
@ -3748,6 +3749,17 @@ check_lock_length(u64 offset, u64 length)
LOFF_OVERFLOW(offset, length))); LOFF_OVERFLOW(offset, length)));
} }
static void get_lock_access(struct nfs4_stateid *lock_stp, u32 access)
{
struct nfs4_file *fp = lock_stp->st_file;
int oflag = nfs4_access_to_omode(access);
if (test_bit(access, &lock_stp->st_access_bmap))
return;
nfs4_file_get_access(fp, oflag);
__set_bit(access, &lock_stp->st_access_bmap);
}
/* /*
* LOCK operation * LOCK operation
*/ */
@ -3845,18 +3857,16 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
switch (lock->lk_type) { switch (lock->lk_type) {
case NFS4_READ_LT: case NFS4_READ_LT:
case NFS4_READW_LT: case NFS4_READW_LT:
if (find_readable_file(lock_stp->st_file)) { filp = find_readable_file(lock_stp->st_file);
nfs4_get_vfs_file(rqstp, fp, &cstate->current_fh, NFS4_SHARE_ACCESS_READ); if (filp)
filp = find_readable_file(lock_stp->st_file); get_lock_access(lock_stp, NFS4_SHARE_ACCESS_READ);
}
file_lock.fl_type = F_RDLCK; file_lock.fl_type = F_RDLCK;
break; break;
case NFS4_WRITE_LT: case NFS4_WRITE_LT:
case NFS4_WRITEW_LT: case NFS4_WRITEW_LT:
if (find_writeable_file(lock_stp->st_file)) { filp = find_writeable_file(lock_stp->st_file);
nfs4_get_vfs_file(rqstp, fp, &cstate->current_fh, NFS4_SHARE_ACCESS_WRITE); if (filp)
filp = find_writeable_file(lock_stp->st_file); get_lock_access(lock_stp, NFS4_SHARE_ACCESS_WRITE);
}
file_lock.fl_type = F_WRLCK; file_lock.fl_type = F_WRLCK;
break; break;
default: default: