cifs: fix potential races in cifs_revalidate_mapping
authorJeff Layton <jlayton@poochiereds.net>
Wed, 30 Apr 2014 13:31:47 +0000 (09:31 -0400)
committerSteve French <smfrench@gmail.com>
Wed, 21 May 2014 17:18:05 +0000 (10:18 -0700)
The handling of the CIFS_INO_INVALID_MAPPING flag is racy. It's possible
for two tasks to attempt to revalidate the mapping at the same time. The
first sees that CIFS_INO_INVALID_MAPPING is set. It clears the flag and
then calls invalidate_inode_pages2 to start shooting down the pagecache.

While that's going on, another task checks the flag and sees that it's
clear. It then ends up trusting the pagecache to satisfy a read when it
shouldn't.

Fix this by adding a bitlock to ensure that the clearing of the flag is
atomic with respect to the actual cache invalidation. Also, move the
other existing users of cifs_invalidate_mapping to use a new
cifs_zap_mapping() function that just sets the INVALID_MAPPING bit and
then uses the standard codepath to handle the invalidation.

Signed-off-by: Jeff Layton <jlayton@poochiereds.net>
Signed-off-by: Steve French <smfrench@gmail.com>
fs/cifs/cifsfs.h
fs/cifs/cifsglob.h
fs/cifs/file.c
fs/cifs/inode.c

index 63d5127..1bbe97c 100644 (file)
@@ -76,6 +76,7 @@ extern int cifs_revalidate_file(struct file *filp);
 extern int cifs_revalidate_dentry(struct dentry *);
 extern int cifs_invalidate_mapping(struct inode *inode);
 extern int cifs_revalidate_mapping(struct inode *inode);
+extern int cifs_zap_mapping(struct inode *inode);
 extern int cifs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
 extern int cifs_setattr(struct dentry *, struct iattr *);
 
index 69da55b..630e0f4 100644 (file)
@@ -1118,6 +1118,7 @@ struct cifsInodeInfo {
 #define CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2 (2) /* Downgrade oplock to L2 */
 #define CIFS_INO_DELETE_PENDING                  (3) /* delete pending on server */
 #define CIFS_INO_INVALID_MAPPING         (4) /* pagecache is invalid */
+#define CIFS_INO_LOCK                    (5) /* lock bit for synchronization */
        unsigned long flags;
        spinlock_t writers_lock;
        unsigned int writers;           /* Number of writers on this inode */
diff --cc fs/cifs/file.c
Simple merge
diff --cc fs/cifs/inode.c
Simple merge