@ -162,6 +162,28 @@ static struct list_head ownerstr_hashtbl[OWNER_HASH_SIZE];
static struct list_head file_hashtbl [ FILE_HASH_SIZE ] ;
static struct list_head stateid_hashtbl [ STATEID_HASH_SIZE ] ;
static inline void nfs4_file_get_access ( struct nfs4_file * fp , int oflag )
{
BUG_ON ( ! ( fp - > fi_fds [ oflag ] | | fp - > fi_fds [ O_RDWR ] ) ) ;
atomic_inc ( & fp - > fi_access [ oflag ] ) ;
}
static inline void nfs4_file_put_fd ( struct nfs4_file * fp , int oflag )
{
if ( fp - > fi_fds [ oflag ] ) {
fput ( fp - > fi_fds [ oflag ] ) ;
fp - > fi_fds [ oflag ] = NULL ;
}
}
static inline void nfs4_file_put_access ( struct nfs4_file * fp , int oflag )
{
if ( atomic_dec_and_test ( & fp - > fi_access [ oflag ] ) ) {
nfs4_file_put_fd ( fp , O_RDWR ) ;
nfs4_file_put_fd ( fp , oflag ) ;
}
}
static struct nfs4_delegation *
alloc_init_deleg ( struct nfs4_client * clp , struct nfs4_stateid * stp , struct svc_fh * current_fh , u32 type )
{
@ -191,9 +213,8 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f
dp - > dl_client = clp ;
get_nfs4_file ( fp ) ;
dp - > dl_file = fp ;
nfs4_file_get_access ( fp , O_RDONLY ) ;
dp - > dl_flock = NULL ;
get_file ( stp - > st_vfs_file ) ;
dp - > dl_vfs_file = stp - > st_vfs_file ;
dp - > dl_type = type ;
dp - > dl_ident = cb - > cb_ident ;
dp - > dl_stateid . si_boot = boot_time ;
@ -228,15 +249,12 @@ nfs4_put_delegation(struct nfs4_delegation *dp)
static void
nfs4_close_delegation ( struct nfs4_delegation * dp )
{
struct file * filp = dp - > dl_vfs_ file ;
struct file * filp = find_readable_file ( dp - > dl_file ) ;
dprintk ( " NFSD: close_delegation dp %p \n " , dp ) ;
dp - > dl_vfs_file = NULL ;
/* The following nfsd_close may not actually close the file,
* but we want to remove the lease in any case . */
if ( dp - > dl_flock )
vfs_setlease ( filp , F_UNLCK , & dp - > dl_flock ) ;
nfsd_close ( filp ) ;
nfs4_file_put_access ( dp - > dl_file , O_RDONLY ) ;
}
/* Called under the state lock. */
@ -308,8 +326,12 @@ static void free_generic_stateid(struct nfs4_stateid *stp)
static void release_lock_stateid ( struct nfs4_stateid * stp )
{
struct file * file ;
unhash_generic_stateid ( stp ) ;
locks_remove_posix ( stp - > st_vfs_file , ( fl_owner_t ) stp - > st_stateowner ) ;
file = find_any_file ( stp - > st_file ) ;
if ( file )
locks_remove_posix ( file , ( fl_owner_t ) stp - > st_stateowner ) ;
free_generic_stateid ( stp ) ;
}
@ -347,11 +369,85 @@ release_stateid_lockowners(struct nfs4_stateid *open_stp)
}
}
/*
* We store the NONE , READ , WRITE , and BOTH bits separately in the
* st_ { access , deny } _bmap field of the stateid , in order to track not
* only what share bits are currently in force , but also what
* combinations of share bits previous opens have used . This allows us
* to enforce the recommendation of rfc 3530 14.2 .19 that the server
* return an error if the client attempt to downgrade to a combination
* of share bits not explicable by closing some of its previous opens .
*
* XXX : This enforcement is actually incomplete , since we don ' t keep
* track of access / deny bit combinations ; so , e . g . , we allow :
*
* OPEN allow read , deny write
* OPEN allow both , deny none
* DOWNGRADE allow read , deny none
*
* which we should reject .
*/
static void
set_access ( unsigned int * access , unsigned long bmap ) {
int i ;
* access = 0 ;
for ( i = 1 ; i < 4 ; i + + ) {
if ( test_bit ( i , & bmap ) )
* access | = i ;
}
}
static void
set_deny ( unsigned int * deny , unsigned long bmap ) {
int i ;
* deny = 0 ;
for ( i = 0 ; i < 4 ; i + + ) {
if ( test_bit ( i , & bmap ) )
* deny | = i ;
}
}
static int
test_share ( struct nfs4_stateid * stp , struct nfsd4_open * open ) {
unsigned int access , deny ;
set_access ( & access , stp - > st_access_bmap ) ;
set_deny ( & deny , stp - > st_deny_bmap ) ;
if ( ( access & open - > op_share_deny ) | | ( deny & open - > op_share_access ) )
return 0 ;
return 1 ;
}
static int nfs4_access_to_omode ( u32 access )
{
switch ( access ) {
case NFS4_SHARE_ACCESS_READ :
return O_RDONLY ;
case NFS4_SHARE_ACCESS_WRITE :
return O_WRONLY ;
case NFS4_SHARE_ACCESS_BOTH :
return O_RDWR ;
}
BUG ( ) ;
}
static int nfs4_access_bmap_to_omode ( struct nfs4_stateid * stp )
{
unsigned int access ;
set_access ( & access , stp - > st_access_bmap ) ;
return nfs4_access_to_omode ( access ) ;
}
static void release_open_stateid ( struct nfs4_stateid * stp )
{
int oflag = nfs4_access_bmap_to_omode ( stp ) ;
unhash_generic_stateid ( stp ) ;
release_stateid_lockowners ( stp ) ;
nfsd_close ( stp - > st_vfs_file ) ;
nfs4_file_put_access ( stp - > st_file , oflag ) ;
free_generic_stateid ( stp ) ;
}
@ -1763,6 +1859,8 @@ alloc_init_file(struct inode *ino)
fp - > fi_inode = igrab ( ino ) ;
fp - > fi_id = current_fileid + + ;
fp - > fi_had_conflict = false ;
memset ( fp - > fi_fds , 0 , sizeof ( fp - > fi_fds ) ) ;
memset ( fp - > fi_access , 0 , sizeof ( fp - > fi_access ) ) ;
spin_lock ( & recall_lock ) ;
list_add ( & fp - > fi_hash , & file_hashtbl [ hashval ] ) ;
spin_unlock ( & recall_lock ) ;
@ -1973,57 +2071,6 @@ static inline int deny_valid(u32 x)
return x < = NFS4_SHARE_DENY_BOTH ;
}
/*
* We store the NONE , READ , WRITE , and BOTH bits separately in the
* st_ { access , deny } _bmap field of the stateid , in order to track not
* only what share bits are currently in force , but also what
* combinations of share bits previous opens have used . This allows us
* to enforce the recommendation of rfc 3530 14.2 .19 that the server
* return an error if the client attempt to downgrade to a combination
* of share bits not explicable by closing some of its previous opens .
*
* XXX : This enforcement is actually incomplete , since we don ' t keep
* track of access / deny bit combinations ; so , e . g . , we allow :
*
* OPEN allow read , deny write
* OPEN allow both , deny none
* DOWNGRADE allow read , deny none
*
* which we should reject .
*/
static void
set_access ( unsigned int * access , unsigned long bmap ) {
int i ;
* access = 0 ;
for ( i = 1 ; i < 4 ; i + + ) {
if ( test_bit ( i , & bmap ) )
* access | = i ;
}
}
static void
set_deny ( unsigned int * deny , unsigned long bmap ) {
int i ;
* deny = 0 ;
for ( i = 0 ; i < 4 ; i + + ) {
if ( test_bit ( i , & bmap ) )
* deny | = i ;
}
}
static int
test_share ( struct nfs4_stateid * stp , struct nfsd4_open * open ) {
unsigned int access , deny ;
set_access ( & access , stp - > st_access_bmap ) ;
set_deny ( & deny , stp - > st_deny_bmap ) ;
if ( ( access & open - > op_share_deny ) | | ( deny & open - > op_share_access ) )
return 0 ;
return 1 ;
}
/*
* Called to check deny when READ with all zero stateid or
* WRITE with all zero or all one stateid
@ -2055,14 +2102,12 @@ out:
}
static inline void
nfs4_file_downgrade ( struct file * fil p , unsigned int share_access )
nfs4_file_downgrade ( struct nfs4_ file * fp , unsigned int share_access )
{
if ( share_access & NFS4_SHARE_ACCESS_WRITE ) {
drop_file_write_access ( filp ) ;
spin_lock ( & filp - > f_lock ) ;
filp - > f_mode = ( filp - > f_mode | FMODE_READ ) & ~ FMODE_WRITE ;
spin_unlock ( & filp - > f_lock ) ;
}
if ( share_access & NFS4_SHARE_ACCESS_WRITE )
nfs4_file_put_access ( fp , O_WRONLY ) ;
if ( share_access & NFS4_SHARE_ACCESS_READ )
nfs4_file_put_access ( fp , O_RDONLY ) ;
}
/*
@ -2328,32 +2373,42 @@ static inline int nfs4_access_to_access(u32 nfs4_access)
return flags ;
}
static __be32 nfs4_get_vfs_file ( struct svc_rqst * rqstp , struct nfs4_file
* fp , struct svc_fh * cur_fh , u32 nfs4_access )
{
__be32 status ;
int oflag = nfs4_access_to_omode ( nfs4_access ) ;
int access = nfs4_access_to_access ( nfs4_access ) ;
if ( ! fp - > fi_fds [ oflag ] ) {
status = nfsd_open ( rqstp , cur_fh , S_IFREG , access ,
& fp - > fi_fds [ oflag ] ) ;
if ( status = = nfserr_dropit )
status = nfserr_jukebox ;
if ( status )
return status ;
}
nfs4_file_get_access ( fp , oflag ) ;
return nfs_ok ;
}
static __be32
nfs4_new_open ( struct svc_rqst * rqstp , struct nfs4_stateid * * stpp ,
struct nfs4_delegation * dp ,
struct svc_fh * cur_fh , struct nfsd4_open * open )
struct nfs4_file * fp , struct svc_fh * cur_fh ,
struct nfsd4_open * open )
{
struct nfs4_stateid * stp ;
__be32 status ;
stp = nfs4_alloc_stateid ( ) ;
if ( stp = = NULL )
return nfserr_resource ;
if ( dp ) {
get_file ( dp - > dl_vfs_file ) ;
stp - > st_vfs_file = dp - > dl_vfs_file ;
} else {
__be32 status ;
int access = nfs4_access_to_access ( open - > op_share_access ) ;
status = nfsd_open ( rqstp , cur_fh , S_IFREG , access ,
& stp - > st_vfs_file ) ;
if ( status ) {
if ( status = = nfserr_dropit )
status = nfserr_jukebox ;
kmem_cache_free ( stateid_slab , stp ) ;
return status ;
}
status = nfs4_get_vfs_file ( rqstp , fp , cur_fh , open - > op_share_access ) ;
if ( status ) {
kmem_cache_free ( stateid_slab , stp ) ;
return status ;
}
* stpp = stp ;
return 0 ;
@ -2375,36 +2430,29 @@ nfsd4_truncate(struct svc_rqst *rqstp, struct svc_fh *fh,
}
static __be32
nfs4_upgrade_open ( struct svc_rqst * rqstp , struct svc_fh * cur_fh , struct nfs4_stateid * stp , struct nfsd4_open * open )
nfs4_upgrade_open ( struct svc_rqst * rqstp , struct nfs4_file * fp , struct svc_fh * cur_fh , struct nfs4_stateid * stp , struct nfsd4_open * open )
{
struct file * filp = stp - > st_vfs_file ;
struct inode * inode = filp - > f_path . dentry - > d_inode ;
unsigned int share_access , new_writer ;
u32 op_share_access ;
u32 op_share_access , new_access ;
__be32 status ;
set_access ( & share_access , stp - > st_access_bmap ) ;
new_writer = ( ~ share_access ) & open - > op_share_access
& NFS4_SHARE_ACCESS_WRITE ;
if ( new_writer ) {
int err = get_write_access ( inode ) ;
if ( err )
return nfserrno ( err ) ;
err = mnt_want_write ( cur_fh - > fh_export - > ex_path . mnt ) ;
if ( err )
return nfserrno ( err ) ;
file_take_write ( filp ) ;
set_access ( & new_access , stp - > st_access_bmap ) ;
new_access = ( ~ new_access ) & open - > op_share_access & ~ NFS4_SHARE_WANT_MASK ;
if ( new_access ) {
status = nfs4_get_vfs_file ( rqstp , fp , cur_fh , new_access ) ;
if ( status )
return status ;
}
status = nfsd4_truncate ( rqstp , cur_fh , open ) ;
if ( status ) {
if ( new_writer )
put_write_access ( inode ) ;
if ( new_access ) {
int oflag = nfs4_access_to_omode ( new_access ) ;
nfs4_file_put_access ( fp , oflag ) ;
}
return status ;
}
/* remember the open */
op_share_access = open - > op_share_access & ~ NFS4_SHARE_WANT_MASK ;
filp - > f_mode | = op_share_access ;
__set_bit ( op_share_access , & stp - > st_access_bmap ) ;
__set_bit ( open - > op_share_deny , & stp - > st_deny_bmap ) ;
@ -2468,13 +2516,14 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta
fl . fl_type = flag = = NFS4_OPEN_DELEGATE_READ ? F_RDLCK : F_WRLCK ;
fl . fl_end = OFFSET_MAX ;
fl . fl_owner = ( fl_owner_t ) dp ;
fl . fl_file = stp - > st_vfs_file ;
fl . fl_file = find_readable_file ( stp - > st_file ) ;
BUG_ON ( ! fl . fl_file ) ;
fl . fl_pid = current - > tgid ;
/* vfs_setlease checks to see if delegation should be handed out.
* the lock_manager callbacks fl_mylease and fl_change are used
*/
if ( ( status = vfs_setlease ( stp - > st_vfs _file, fl . fl_type , & flp ) ) ) {
if ( ( status = vfs_setlease ( fl . fl _file, fl . fl_type , & flp ) ) ) {
dprintk ( " NFSD: setlease failed [%d], no delegation \n " , status ) ;
unhash_delegation ( dp ) ;
flag = NFS4_OPEN_DELEGATE_NONE ;
@ -2538,13 +2587,12 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
*/
if ( stp ) {
/* Stateid was found, this is an OPEN upgrade */
status = nfs4_upgrade_open ( rqstp , current_fh , stp , open ) ;
status = nfs4_upgrade_open ( rqstp , fp , current_fh , stp , open ) ;
if ( status )
goto out ;
update_stateid ( & stp - > st_stateid ) ;
} else {
/* Stateid was not found, this is a new OPEN */
status = nfs4_new_open ( rqstp , & stp , dp , current_fh , open ) ;
status = nfs4_new_open ( rqstp , & stp , fp , current_fh , open ) ;
if ( status )
goto out ;
init_stateid ( stp , fp , open ) ;
@ -2746,7 +2794,7 @@ search_close_lru(u32 st_id, int flags)
static inline int
nfs4_check_fh ( struct svc_fh * fhp , struct nfs4_stateid * stp )
{
return fhp - > fh_dentry - > d_inode ! = stp - > st_vfs_ file - > f_path . dentry - > d _inode ;
return fhp - > fh_dentry - > d_inode ! = stp - > st_file - > fi _inode ;
}
static int
@ -2894,7 +2942,8 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
goto out ;
renew_client ( dp - > dl_client ) ;
if ( filpp )
* filpp = dp - > dl_vfs_file ;
* filpp = find_readable_file ( dp - > dl_file ) ;
BUG_ON ( ! * filpp ) ;
} else { /* open or lock stateid */
stp = find_stateid ( stateid , flags ) ;
if ( ! stp )
@ -2911,8 +2960,13 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
if ( status )
goto out ;
renew_client ( stp - > st_stateowner - > so_client ) ;
if ( filpp )
* filpp = stp - > st_vfs_file ;
if ( filpp ) {
if ( flags & RD_STATE )
* filpp = find_readable_file ( stp - > st_file ) ;
else
* filpp = find_writeable_file ( stp - > st_file ) ;
BUG_ON ( ! * filpp ) ; /* assured by check_openmode */
}
}
status = nfs_ok ;
out :
@ -3148,8 +3202,7 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp,
goto out ;
}
set_access ( & share_access , stp - > st_access_bmap ) ;
nfs4_file_downgrade ( stp - > st_vfs_file ,
share_access & ~ od - > od_share_access ) ;
nfs4_file_downgrade ( stp - > st_file , share_access & ~ od - > od_share_access ) ;
reset_union_bmap_access ( od - > od_share_access , & stp - > st_access_bmap ) ;
reset_union_bmap_deny ( od - > od_share_deny , & stp - > st_deny_bmap ) ;
@ -3468,7 +3521,6 @@ 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_fileid = fp - > fi_id ;
stp - > st_stateid . si_generation = 0 ;
stp - > st_vfs_file = open_stp - > st_vfs_file ; /* FIXME refcount?? */
stp - > st_deny_bmap = open_stp - > st_deny_bmap ;
stp - > st_openstp = open_stp ;
@ -3568,7 +3620,6 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
lock_sop = lock - > lk_replay_owner ;
}
/* lock->lk_replay_owner and lock_stp have been created or found */
filp = lock_stp - > st_vfs_file ;
status = nfserr_grace ;
if ( locks_in_grace ( ) & & ! lock - > lk_reclaim )
@ -3581,11 +3632,13 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
switch ( lock - > lk_type ) {
case NFS4_READ_LT :
case NFS4_READW_LT :
filp = find_readable_file ( lock_stp - > st_file ) ;
file_lock . fl_type = F_RDLCK ;
cmd = F_SETLK ;
break ;
case NFS4_WRITE_LT :
case NFS4_WRITEW_LT :
filp = find_writeable_file ( lock_stp - > st_file ) ;
file_lock . fl_type = F_WRLCK ;
cmd = F_SETLK ;
break ;
@ -3593,6 +3646,10 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfserr_inval ;
goto out ;
}
if ( ! filp ) {
status = nfserr_openmode ;
goto out ;
}
file_lock . fl_owner = ( fl_owner_t ) lock_sop ;
file_lock . fl_pid = current - > tgid ;
file_lock . fl_file = filp ;
@ -3761,7 +3818,11 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
& locku - > lu_stateowner , & stp , NULL ) ) )
goto out ;
filp = stp - > st_vfs_file ;
filp = find_any_file ( stp - > st_file ) ;
if ( ! filp ) {
status = nfserr_lock_range ;
goto out ;
}
BUG_ON ( ! filp ) ;
locks_init_lock ( & file_lock ) ;
file_lock . fl_type = F_UNLCK ;
@ -3808,10 +3869,10 @@ out_nfserr:
* 0 : no locks held by lockowner
*/
static int
check_for_locks ( struct file * filp , struct nfs4_stateowner * lowner )
check_for_locks ( struct nfs4_ file * filp , struct nfs4_stateowner * lowner )
{
struct file_lock * * flpp ;
struct inode * inode = filp - > f_path . dentry - > d _inode ;
struct inode * inode = filp - > fi _inode ;
int status = 0 ;
lock_kernel ( ) ;
@ -3862,7 +3923,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
continue ;
list_for_each_entry ( stp , & sop - > so_stateids ,
st_perstateowner ) {
if ( check_for_locks ( stp - > st_vfs_ file , sop ) )
if ( check_for_locks ( stp - > st_file , sop ) )
goto out ;
/* Note: so_perclient unused for lockowners,
* so it ' s OK to fool with here . */