• David Howells's avatar
    afs: Fix missing dentry data version updating · 9dd0b82e
    David Howells authored
    In the in-kernel afs filesystem, the d_fsdata dentry field is used to hold
    the data version of the parent directory when it was created or when
    d_revalidate() last caused it to be updated.  This is compared to the
    ->invalid_before field in the directory inode, rather than the actual data
    version number, thereby allowing changes due to local edits to be ignored.
    Only if the server data version gets bumped unexpectedly (eg. by a
    competing client), do we need to revalidate stuff.
    
    However, the d_fsdata field should also be updated if an rpc op is
    performed that modifies that particular dentry.  Such ops return the
    revised data version of the directory(ies) involved, so we should use that.
    
    This is particularly problematic for rename, since a dentry from one
    directory may be moved directly into another directory (ie. mv a/x b/x).
    It would then be sporting the wrong data version - and if this is in the
    future, for the destination directory, revalidations would be missed,
    leading to foreign renames and hard-link deletion being missed.
    
    Fix this by the following means:
    
     (1) Return the data version number from operations that read the directory
         contents - if they issue the read.  This starts in afs_dir_iterate()
         and is used, ignored or passed back by its callers.
    
     (2) In afs_lookup*(), set the dentry version to the version returned by
         (1) before d_splice_alias() is called and the dentry published.
    
     (3) In afs_d_revalidate(), set the dentry version to that returned from
         (1) if an rpc call was issued.  This means that if a parallel
         procedure, such as mkdir(), modifies the directory, we won't
         accidentally use the data version from that.
    
     (4) In afs_{mkdir,create,link,symlink}(), set the new dentry's version to
         the directory data version before d_instantiate() is called.
    
     (5) In afs_{rmdir,unlink}, update the target dentry's version to the
         directory data version as soon as we've updated the directory inode.
    
     (6) In afs_rename(), we need to unhash the old dentry before we start so
         that we don't get afs_d_revalidate() reverting the version change in
         cross-directory renames.
    
         We then need to set both the old and the new dentry versions the data
         version of the new directory before we call d_move() as d_move() will
         rehash them.
    
    Fixes: 1da177e4 ("Linux-2.6.12-rc2")
    Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
    9dd0b82e
dir.c 50.6 KB