[CIFS] Fix locking problem around some cifs uses of i_size write
[powerpc.git] / fs / cifs / inode.c
index c4fa91b..24df13a 100644 (file)
@@ -90,6 +90,9 @@ int cifs_get_inode_info_unix(struct inode **pinode,
                                (*pinode)->i_ino =
                                        (unsigned long)findData.UniqueId;
                        } /* note ino incremented to unique num in new_inode */
+                       if(sb->s_flags & MS_NOATIME)
+                               (*pinode)->i_flags |= S_NOATIME | S_NOCMTIME;
+                               
                        insert_inode_hash(*pinode);
                }
 
@@ -140,10 +143,10 @@ int cifs_get_inode_info_unix(struct inode **pinode,
                inode->i_gid = le64_to_cpu(findData.Gid);
                inode->i_nlink = le64_to_cpu(findData.Nlinks);
 
-               if (is_size_safe_to_change(cifsInfo)) {
+               spin_lock(&inode->i_lock);
+               if (is_size_safe_to_change(cifsInfo, end_of_file)) {
                /* can not safely change the file size here if the
                   client is writing to it due to potential races */
-
                        i_size_write(inode, end_of_file);
 
                /* blksize needs to be multiple of two. So safer to default to
@@ -159,6 +162,7 @@ int cifs_get_inode_info_unix(struct inode **pinode,
                /* for this calculation */
                        inode->i_blocks = (512 - 1 + num_of_bytes) >> 9;
                }
+               spin_unlock(&inode->i_lock);
 
                if (num_of_bytes < end_of_file)
                        cFYI(1, ("allocation size less than end of file"));
@@ -421,6 +425,8 @@ int cifs_get_inode_info(struct inode **pinode,
                                } else /* do we need cast or hash to ino? */
                                        (*pinode)->i_ino = inode_num;
                        } /* else ino incremented to unique num in new_inode*/
+                       if(sb->s_flags & MS_NOATIME)
+                               (*pinode)->i_flags |= S_NOATIME | S_NOCMTIME;
                        insert_inode_hash(*pinode);
                }
                inode = *pinode;
@@ -491,8 +497,10 @@ int cifs_get_inode_info(struct inode **pinode,
                /* BB add code here -
                   validate if device or weird share or device type? */
                }
-               if (is_size_safe_to_change(cifsInfo)) {
-                       /* can not safely change the file size here if the
+               
+               spin_lock(&inode->i_lock);
+               if (is_size_safe_to_change(cifsInfo, le64_to_cpu(pfindData->EndOfFile))) {
+                       /* can not safely shrink the file size here if the
                           client is writing to it due to potential races */
                        i_size_write(inode,le64_to_cpu(pfindData->EndOfFile));
 
@@ -501,6 +509,7 @@ int cifs_get_inode_info(struct inode **pinode,
                        inode->i_blocks = (512 - 1 + le64_to_cpu(
                                           pfindData->AllocationSize)) >> 9;
                }
+               spin_unlock(&inode->i_lock);
 
                inode->i_nlink = le32_to_cpu(pfindData->NumberOfLinks);
 
@@ -829,8 +838,10 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
 
        if (!rc) {
                drop_nlink(inode);
+               spin_lock(&direntry->d_inode->i_lock);
                i_size_write(direntry->d_inode,0);
                clear_nlink(direntry->d_inode);
+               spin_unlock(&direntry->d_inode->i_lock);
        }
 
        cifsInode = CIFS_I(direntry->d_inode);
@@ -1123,6 +1134,46 @@ static int cifs_truncate_page(struct address_space *mapping, loff_t from)
        return rc;
 }
 
+int cifs_vmtruncate(struct inode * inode, loff_t offset)
+{
+       struct address_space *mapping = inode->i_mapping;
+       unsigned long limit;
+
+       if (inode->i_size < offset)
+               goto do_expand;
+       /*
+        * truncation of in-use swapfiles is disallowed - it would cause
+        * subsequent swapout to scribble on the now-freed blocks.
+        */
+       if (IS_SWAPFILE(inode))
+               goto out_busy;
+       spin_lock(&inode->i_lock);              
+       i_size_write(inode, offset);
+       spin_unlock(&inode->i_lock);
+       unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
+       truncate_inode_pages(mapping, offset);
+       goto out_truncate;
+
+do_expand:
+       limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
+       if (limit != RLIM_INFINITY && offset > limit)
+               goto out_sig;
+       if (offset > inode->i_sb->s_maxbytes)
+               goto out_big;
+       i_size_write(inode, offset);
+
+out_truncate:
+       if (inode->i_op && inode->i_op->truncate)
+               inode->i_op->truncate(inode);
+       return 0;
+out_sig:
+       send_sig(SIGXFSZ, current, 0);
+out_big:
+       return -EFBIG;
+out_busy:
+       return -ETXTBSY;
+}
+
 int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
 {
        int xid;
@@ -1239,7 +1290,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
                   */
 
                if (rc == 0) {
-                       rc = vmtruncate(direntry->d_inode, attrs->ia_size);
+                       rc = cifs_vmtruncate(direntry->d_inode, attrs->ia_size);
                        cifs_truncate_page(direntry->d_inode->i_mapping,
                                           direntry->d_inode->i_size);
                } else 
@@ -1359,7 +1410,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
                and this check ensures that we are not being called from
                sys_utimes in which case we ought to fail the call back to
                the user when the server rejects the call */
-               if((rc) && (attrs->ia_valid &&
+               if((rc) && (attrs->ia_valid &
                         (ATTR_MODE | ATTR_GID | ATTR_UID | ATTR_SIZE)))
                        rc = 0;
        }