[PATCH] cifs: Fix caching problem
authorSteve French <smfrench@austin.rr.com>
Fri, 29 Apr 2005 05:41:08 +0000 (22:41 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Fri, 29 Apr 2005 05:41:08 +0000 (22:41 -0700)
pointed out by Dave Stahl and Vince Negri in which cifs can update the
last modify time on a server modified file without invalidating the
local cached data due to an intervening readdir.

Signed-off-by: Steve French (sfrench@us.ibm.com)
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
fs/cifs/AUTHORS
fs/cifs/CHANGES
fs/cifs/cifsfs.h
fs/cifs/cifssmb.c
fs/cifs/readdir.c

index acce36e..72fdc10 100644 (file)
@@ -4,13 +4,16 @@ Steve French (sfrench@samba.org)
 
 The author wishes to express his appreciation and thanks to:
 Andrew Tridgell (Samba team) for his early suggestions about smb/cifs VFS
-improvements. Thanks to IBM for allowing me the time and test resources to pursue
-this project. Jim McDonough from IBM (and the Samba Team) for his help.
-The IBM Linux JFS team for explaining many esoteric Linux filesystem features.
+improvements. Thanks to IBM for allowing me time and test resources to pursue
+this project, to Jim McDonough from IBM (and the Samba Team) for his help, to
+the IBM Linux JFS team for explaining many esoteric Linux filesystem features.
+Jeremy Allison of the Samba team has done invaluable work in adding the server
+side of the original CIFS Unix extensions and reviewing and implementing
+portions of the newer CIFS POSIX extensions into the Samba 3 file server. Thank
 Dave Boutcher of IBM Rochester (author of the OS/400 smb/cifs filesystem client)
-for proving years ago that a very good smb/cifs client could be done on a Unix like 
-operating system.  Volker Lendecke, Andrew Tridgell, Urban Widmark, John Newbigi
-and others for their work on the Linux smbfs module over the years. Thanks to
+for proving years ago that very good smb/cifs clients could be done on Unix-like
+operating systems.  Volker Lendecke, Andrew Tridgell, Urban Widmark, Joh
+Newbigin and others for their work on the Linux smbfs module.  Thanks to
 the other members of the Storage Network Industry Association CIFS Technical
 Workgroup for their work specifying this highly complex protocol and finally
 thanks to the Samba team for their technical advice and encouragement.
@@ -24,9 +27,11 @@ Shobhit Dayal
 Sergey Vlasov
 Richard Hughes
 Yury Umanets
-Mark Hamzy
+Mark Hamzy (for some of the early cifs IPv6 work)
 Domen Puncer
-Jesper Juhl
+Jesper Juhl (in particular for lots of whitespace/formatting cleanup)
+Vince Negri and Dave Stahl (for finding an important caching bug)
+Adrian Bunk (kcalloc cleanups)
 
 Test case and Bug Report contributors
 -------------------------------------
@@ -36,7 +41,8 @@ Rene Scharfe, Martin Josefsson, Alexander Wild, Anthony Liguori,
 Lars Muller, Urban Widmark, Massimiliano Ferrero, Howard Owen,
 Olaf Kirch, Kieron Briggs, Nick Millington and others. Also special
 mention to the Stanford Checker (SWAT) which pointed out many minor
-bugs in error paths.
+bugs in error paths.  Valuable suggestions also have come from Al Viro
+and Dave Miller.
 
 And thanks to the IBM LTC and Power test teams and SuSE testers for
 finding multiple bugs during excellent stress test runs.
index be3a575..0414eb3 100644 (file)
@@ -1,3 +1,11 @@
+Version 1.33
+------------
+Fix caching problem, in which readdir of directory containing a file
+which was cached could cause the file's time stamp to be updated
+without invalidating the readahead data (so we could get stale
+file data on the client for that file even as the server copy changed).
+
+
 Version 1.32
 ------------
 Fix oops in ls when Transact2 FindFirst (or FindNext) returns more than one
index e0e46f4..8f74279 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *   fs/cifs/cifsfs.h
  *
- *   Copyright (c) International Business Machines  Corp., 2002
+ *   Copyright (c) International Business Machines  Corp., 2002, 2005
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *
  *   This library is free software; you can redistribute it and/or modify
@@ -96,5 +96,5 @@ extern ssize_t        cifs_getxattr(struct dentry *, const char *, void *, size_t);
 extern ssize_t cifs_listxattr(struct dentry *, char *, size_t);
 extern int cifs_ioctl (struct inode * inode, struct file * filep,
                       unsigned int command, unsigned long arg);
-#define CIFS_VERSION   "1.32"
+#define CIFS_VERSION   "1.33"
 #endif                         /* _CIFSFS_H */
index f7c4914..b102ddb 100644 (file)
@@ -464,7 +464,7 @@ CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)
        rc = SendReceive(xid, tcon->ses, smb_buffer, smb_buffer_response,
                         &length, 0);
        if (rc)
-               cFYI(1, (" Tree disconnect failed %d", rc));
+               cFYI(1, ("Tree disconnect failed %d", rc));
 
        if (smb_buffer)
                cifs_small_buf_release(smb_buffer);
index 7ca876b..39170cf 100644 (file)
@@ -3,7 +3,7 @@
  *
  *   Directory search handling
  * 
- *   Copyright (C) International Business Machines  Corp., 2004
+ *   Copyright (C) International Business Machines  Corp., 2004, 2005
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *
  *   This library is free software; you can redistribute it and/or modify
@@ -65,14 +65,14 @@ static int construct_dentry(struct qstr *qstring, struct file *file,
        struct cifsTconInfo *pTcon;
        int rc = 0;
 
-       cFYI(1, ("For %s ", qstring->name));
+       cFYI(1, ("For %s", qstring->name));
        cifs_sb = CIFS_SB(file->f_dentry->d_sb);
        pTcon = cifs_sb->tcon;
 
        qstring->hash = full_name_hash(qstring->name, qstring->len);
        tmp_dentry = d_lookup(file->f_dentry, qstring);
        if (tmp_dentry) {
-               cFYI(0, (" existing dentry with inode 0x%p", tmp_dentry->d_inode));
+               cFYI(0, ("existing dentry with inode 0x%p", tmp_dentry->d_inode));
                *ptmp_inode = tmp_dentry->d_inode;
 /* BB overwrite old name? i.e. tmp_dentry->d_name and tmp_dentry->d_name.len??*/
                if(*ptmp_inode == NULL) {
@@ -105,8 +105,11 @@ static int construct_dentry(struct qstr *qstring, struct file *file,
 }
 
 static void fill_in_inode(struct inode *tmp_inode,
-       FILE_DIRECTORY_INFO *pfindData, int *pobject_type)
+       FILE_DIRECTORY_INFO *pfindData, int *pobject_type, int isNewInode)
 {
+       loff_t local_size;
+       struct timespec local_mtime;
+
        struct cifsInodeInfo *cifsInfo = CIFS_I(tmp_inode);
        struct cifs_sb_info *cifs_sb = CIFS_SB(tmp_inode->i_sb);
        __u32 attr = le32_to_cpu(pfindData->ExtFileAttributes);
@@ -116,6 +119,10 @@ static void fill_in_inode(struct inode *tmp_inode,
        cifsInfo->cifsAttrs = attr;
        cifsInfo->time = jiffies;
 
+       /* save mtime and size */
+       local_mtime = tmp_inode->i_mtime;
+       local_size  = tmp_inode->i_size;
+
        /* Linux can not store file creation time unfortunately so ignore it */
        tmp_inode->i_atime =
            cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastAccessTime));
@@ -134,7 +141,6 @@ static void fill_in_inode(struct inode *tmp_inode,
                tmp_inode->i_mode = cifs_sb->mnt_file_mode;
        }
 
-       cFYI(0,("CIFS FFIRST: Attributes came in as 0x%x",attr));
        if (attr & ATTR_DIRECTORY) {
                *pobject_type = DT_DIR;
                /* override default perms since we do not lock dirs */
@@ -175,30 +181,46 @@ static void fill_in_inode(struct inode *tmp_inode,
              (unsigned long)tmp_inode->i_size, tmp_inode->i_blocks,
              tmp_inode->i_blksize));
        if (S_ISREG(tmp_inode->i_mode)) {
-               cFYI(1, (" File inode "));
+               cFYI(1, ("File inode"));
                tmp_inode->i_op = &cifs_file_inode_ops;
                if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO)
                        tmp_inode->i_fop = &cifs_file_direct_ops;
                else
                        tmp_inode->i_fop = &cifs_file_ops;
                tmp_inode->i_data.a_ops = &cifs_addr_ops;
+
+               if(isNewInode)
+                       return; /* No sense invalidating pages for new inode since we
+                                          have not started caching readahead file data yet */
+
+               if (timespec_equal(&tmp_inode->i_mtime, &local_mtime) &&
+                       (local_size == tmp_inode->i_size)) {
+                       cFYI(1, ("inode exists but unchanged"));
+               } else {
+                       /* file may have changed on server */
+                       cFYI(1, ("invalidate inode, readdir detected change"));
+                       invalidate_remote_inode(tmp_inode);
+               }
        } else if (S_ISDIR(tmp_inode->i_mode)) {
-               cFYI(1, (" Directory inode"));
+               cFYI(1, ("Directory inode"));
                tmp_inode->i_op = &cifs_dir_inode_ops;
                tmp_inode->i_fop = &cifs_dir_ops;
        } else if (S_ISLNK(tmp_inode->i_mode)) {
-               cFYI(1, (" Symbolic Link inode "));
+               cFYI(1, ("Symbolic Link inode"));
                tmp_inode->i_op = &cifs_symlink_inode_ops;
        } else {
-               cFYI(1, (" Init special inode "));
+               cFYI(1, ("Init special inode"));
                init_special_inode(tmp_inode, tmp_inode->i_mode,
                                   tmp_inode->i_rdev);
        }
 }
 
 static void unix_fill_in_inode(struct inode *tmp_inode,
-       FILE_UNIX_INFO *pfindData, int *pobject_type)
+       FILE_UNIX_INFO *pfindData, int *pobject_type, int isNewInode)
 {
+       loff_t local_size;
+       struct timespec local_mtime;
+
        struct cifsInodeInfo *cifsInfo = CIFS_I(tmp_inode);
        struct cifs_sb_info *cifs_sb = CIFS_SB(tmp_inode->i_sb);
 
@@ -208,6 +230,10 @@ static void unix_fill_in_inode(struct inode *tmp_inode,
        cifsInfo->time = jiffies;
        atomic_inc(&cifsInfo->inUse);
 
+       /* save mtime and size */
+       local_mtime = tmp_inode->i_mtime;
+       local_size  = tmp_inode->i_size;
+
        tmp_inode->i_atime =
            cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastAccessTime));
        tmp_inode->i_mtime =
@@ -265,6 +291,19 @@ static void unix_fill_in_inode(struct inode *tmp_inode,
                else
                        tmp_inode->i_fop = &cifs_file_ops;
                tmp_inode->i_data.a_ops = &cifs_addr_ops;
+
+               if(isNewInode)
+                       return; /* No sense invalidating pages for new inode since we
+                                          have not started caching readahead file data yet */
+
+               if (timespec_equal(&tmp_inode->i_mtime, &local_mtime) &&
+                       (local_size == tmp_inode->i_size)) {
+                       cFYI(1, ("inode exists but unchanged"));
+               } else {
+                       /* file may have changed on server */
+                       cFYI(1, ("invalidate inode, readdir detected change"));
+                       invalidate_remote_inode(tmp_inode);
+               }
        } else if (S_ISDIR(tmp_inode->i_mode)) {
                cFYI(1, ("Directory inode"));
                tmp_inode->i_op = &cifs_dir_inode_ops;
@@ -321,7 +360,7 @@ static int initiate_cifs_search(const int xid, struct file *file)
                return -ENOMEM;
        }
 
-       cFYI(1, ("Full path: %s start at: %lld ", full_path, file->f_pos));
+       cFYI(1, ("Full path: %s start at: %lld", full_path, file->f_pos));
 
 ffirst_retry:
        /* test for Unix extensions */
@@ -666,10 +705,15 @@ static int cifs_filldir(char *pfindEntry, struct file *file,
                insert_inode_hash(tmp_inode);
        }
 
+       /* we pass in rc below, indicating whether it is a new inode,
+          so we can figure out whether to invalidate the inode cached
+          data if the file has changed */
        if(pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX) {
-               unix_fill_in_inode(tmp_inode,(FILE_UNIX_INFO *)pfindEntry,&obj_type);
+               unix_fill_in_inode(tmp_inode,
+                                  (FILE_UNIX_INFO *)pfindEntry,&obj_type, rc);
        } else {
-               fill_in_inode(tmp_inode,(FILE_DIRECTORY_INFO *)pfindEntry,&obj_type);
+               fill_in_inode(tmp_inode,
+                             (FILE_DIRECTORY_INFO *)pfindEntry,&obj_type, rc);
        }
        
        rc = filldir(direntry,qstring.name,qstring.len,file->f_pos,tmp_inode->i_ino,obj_type);