• NeilBrown's avatar
    NFS: don't unhash dentry during unlink/rename · 3c59366c
    NeilBrown authored
    NFS unlink() (and rename over existing target) must determine if the
    file is open, and must perform a "silly rename" instead of an unlink (or
    before rename) if it is.  Otherwise the client might hold a file open
    which has been removed on the server.
    
    Consequently if it determines that the file isn't open, it must block
    any subsequent opens until the unlink/rename has been completed on the
    server.
    
    This is currently achieved by unhashing the dentry.  This forces any
    open attempt to the slow-path for lookup which will block on i_rwsem on
    the directory until the unlink/rename completes.  A future patch will
    change the VFS to only get a shared lock on i_rwsem for unlink, so this
    will no longer work.
    
    Instead we introduce an explicit interlock.  A special value is stored
    in dentry->d_fsdata while the unlink/rename is running and
    ->d_revalidate blocks while that value is present.  When ->d_revalidate
    unblocks, the dentry will be invalid.  This closes the race
    without requiring exclusion on i_rwsem.
    
    d_fsdata is already used in two different ways.
    1/ an IS_ROOT directory dentry might have a "devname" stored in
       d_fsdata.  Such a dentry doesn't have a name and so cannot be the
       target of unlink or rename.  For safety we check if an old devname
       is still stored, and remove it if it is.
    2/ a dentry with DCACHE_NFSFS_RENAMED set will have a 'struct
       nfs_unlinkdata' stored in d_fsdata.  While this is set maydelete()
       will fail, so an unlink or rename will never proceed on such
       a dentry.
    
    Neither of these can be in effect when a dentry is the target of unlink
    or rename.  So we can expect d_fsdata to be NULL, and store a special
    value ((void*)1) which is given the name NFS_FSDATA_BLOCKED to indicate
    that any lookup will be blocked.
    
    The d_count() is incremented under d_lock() when a lookup finds the
    dentry, so we check d_count() is low, and set NFS_FSDATA_BLOCKED under
    the same lock to avoid any races.
    Signed-off-by: default avatarNeilBrown <neilb@suse.de>
    Signed-off-by: default avatarTrond Myklebust <trond.myklebust@hammerspace.com>
    3c59366c
dir.c 84.3 KB