Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[safe/jmp/linux-2.6] / fs / cifs / file.c
index 0f05cab..6449e1a 100644 (file)
@@ -2,8 +2,8 @@
  *   fs/cifs/file.c
  *
  *   vfs operations that deal with files
- * 
- *   Copyright (C) International Business Machines  Corp., 2002,2003
+ *
+ *   Copyright (C) International Business Machines  Corp., 2002,2007
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *              Jeremy Allison (jra@samba.org)
  *
@@ -27,7 +27,6 @@
 #include <linux/fcntl.h>
 #include <linux/pagemap.h>
 #include <linux/pagevec.h>
-#include <linux/smp_lock.h>
 #include <linux/writeback.h>
 #include <linux/task_io_accounting_ops.h>
 #include <linux/delay.h>
@@ -46,19 +45,19 @@ static inline struct cifsFileInfo *cifs_init_private(
 {
        memset(private_data, 0, sizeof(struct cifsFileInfo));
        private_data->netfid = netfid;
-       private_data->pid = current->tgid;      
+       private_data->pid = current->tgid;
        init_MUTEX(&private_data->fh_sem);
-       init_MUTEX(&private_data->lock_sem);
+       mutex_init(&private_data->lock_mutex);
        INIT_LIST_HEAD(&private_data->llist);
        private_data->pfile = file; /* needed for writepage */
        private_data->pInode = inode;
-       private_data->invalidHandle = FALSE;
-       private_data->closePend = FALSE;
+       private_data->invalidHandle = false;
+       private_data->closePend = false;
        /* we have to track num writers to the inode, since writepages
        does not tell us which handle the write is for so there can
        be a close (overlapping with write) of the filehandle that
        cifs_writepages chose to use */
-       atomic_set(&private_data->wrtPending,0); 
+       atomic_set(&private_data->wrtPending, 0);
 
        return private_data;
 }
@@ -76,7 +75,11 @@ static inline int cifs_convert_flags(unsigned int flags)
                return (GENERIC_READ | GENERIC_WRITE);
        }
 
-       return 0x20197;
+       return (READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES |
+               FILE_WRITE_EA | FILE_APPEND_DATA | FILE_WRITE_DATA |
+               FILE_READ_DATA);
+
+
 }
 
 static inline int cifs_get_disposition(unsigned int flags)
@@ -104,9 +107,9 @@ static inline int cifs_open_inode_helper(struct inode *inode, struct file *file,
 
        /* want handles we can use to read with first
           in the list so we do not have to walk the
-          list to search for one in prepare_write */
+          list to search for one in write_begin */
        if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
-               list_add_tail(&pCifsFile->flist, 
+               list_add_tail(&pCifsFile->flist,
                              &pCifsInode->openFileList);
        } else {
                list_add(&pCifsFile->flist,
@@ -131,7 +134,9 @@ static inline int cifs_open_inode_helper(struct inode *inode, struct file *file,
                if (file->f_path.dentry->d_inode->i_mapping) {
                /* BB no need to lock inode until after invalidate
                   since namei code should already have it locked? */
-                       filemap_write_and_wait(file->f_path.dentry->d_inode->i_mapping);
+                       rc = filemap_write_and_wait(file->f_path.dentry->d_inode->i_mapping);
+                       if (rc != 0)
+                               CIFS_I(file->f_path.dentry->d_inode)->write_behind_rc = rc;
                }
                cFYI(1, ("invalidating remote inode since open detected it "
                         "changed"));
@@ -139,20 +144,20 @@ static inline int cifs_open_inode_helper(struct inode *inode, struct file *file,
        }
 
 client_can_cache:
-       if (pTcon->ses->capabilities & CAP_UNIX)
+       if (pTcon->unix_ext)
                rc = cifs_get_inode_info_unix(&file->f_path.dentry->d_inode,
                        full_path, inode->i_sb, xid);
        else
                rc = cifs_get_inode_info(&file->f_path.dentry->d_inode,
-                       full_path, buf, inode->i_sb, xid);
+                       full_path, buf, inode->i_sb, xid, NULL);
 
        if ((*oplock & 0xF) == OPLOCK_EXCLUSIVE) {
-               pCifsInode->clientCanCacheAll = TRUE;
-               pCifsInode->clientCanCacheRead = TRUE;
+               pCifsInode->clientCanCacheAll = true;
+               pCifsInode->clientCanCacheRead = true;
                cFYI(1, ("Exclusive Oplock granted on inode %p",
                         file->f_path.dentry->d_inode));
        } else if ((*oplock & 0xF) == OPLOCK_READ)
-               pCifsInode->clientCanCacheRead = TRUE;
+               pCifsInode->clientCanCacheRead = true;
 
        return rc;
 }
@@ -190,7 +195,7 @@ int cifs_open(struct inode *inode, struct file *file)
 
                                /* needed for writepage */
                                pCifsFile->pfile = file;
-                               
+
                                file->private_data = pCifsFile;
                                break;
                        }
@@ -213,15 +218,15 @@ int cifs_open(struct inode *inode, struct file *file)
                return -ENOMEM;
        }
 
-       cFYI(1, (" inode = 0x%p file flags are 0x%x for %s",
+       cFYI(1, ("inode = 0x%p file flags are 0x%x for %s",
                 inode, file->f_flags, full_path));
        desiredAccess = cifs_convert_flags(file->f_flags);
 
 /*********************************************************************
  *  open flag mapping table:
- *  
+ *
  *     POSIX Flag            CIFS Disposition
- *     ----------            ---------------- 
+ *     ----------            ----------------
  *     O_CREAT               FILE_OPEN_IF
  *     O_CREAT | O_EXCL      FILE_CREATE
  *     O_CREAT | O_TRUNC     FILE_OVERWRITE_IF
@@ -229,12 +234,12 @@ int cifs_open(struct inode *inode, struct file *file)
  *     none of the above     FILE_OPEN
  *
  *     Note that there is not a direct match between disposition
- *     FILE_SUPERSEDE (ie create whether or not file exists although 
+ *     FILE_SUPERSEDE (ie create whether or not file exists although
  *     O_CREAT | O_TRUNC is similar but truncates the existing
  *     file rather than creating a new file as FILE_SUPERSEDE does
  *     (which uses the attributes / metadata passed in on open call)
  *?
- *?  O_SYNC is a reasonable match to CIFS writethrough flag  
+ *?  O_SYNC is a reasonable match to CIFS writethrough flag
  *?  and the read write flags match reasonably.  O_LARGEFILE
  *?  is irrelevant because largefile support is always used
  *?  by this client. Flags O_APPEND, O_DIRECT, O_DIRECTORY,
@@ -246,7 +251,7 @@ int cifs_open(struct inode *inode, struct file *file)
        if (oplockEnabled)
                oplock = REQ_OPLOCK;
        else
-               oplock = FALSE;
+               oplock = 0;
 
        /* BB pass O_SYNC flag through on file attributes .. BB */
 
@@ -254,8 +259,8 @@ int cifs_open(struct inode *inode, struct file *file)
           and calling get_inode_info with returned buf (at least helps
           non-Unix server case) */
 
-       /* BB we can not do this if this is the second open of a file 
-          and the first handle has writebehind data, we might be 
+       /* BB we can not do this if this is the second open of a file
+          and the first handle has writebehind data, we might be
           able to simply do a filemap_fdatawrite/filemap_fdatawait first */
        buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
        if (!buf) {
@@ -264,7 +269,7 @@ int cifs_open(struct inode *inode, struct file *file)
        }
 
        if (cifs_sb->tcon->ses->capabilities & CAP_NT_SMBS)
-               rc = CIFSSMBOpen(xid, pTcon, full_path, disposition, 
+               rc = CIFSSMBOpen(xid, pTcon, full_path, disposition,
                         desiredAccess, CREATE_NOT_DIR, &netfid, &oplock, buf,
                         cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
                                 & CIFS_MOUNT_MAP_SPECIAL_CHR);
@@ -301,22 +306,23 @@ int cifs_open(struct inode *inode, struct file *file)
                write_unlock(&GlobalSMBSeslock);
        }
 
-       if (oplock & CIFS_CREATE_ACTION) {           
+       if (oplock & CIFS_CREATE_ACTION) {
                /* time to set mode which we can not set earlier due to
                   problems creating new read-only files */
-               if (cifs_sb->tcon->ses->capabilities & CAP_UNIX) {
-                       CIFSSMBUnixSetPerms(xid, pTcon, full_path,
-                                           inode->i_mode,
-                                           (__u64)-1, (__u64)-1, 0 /* dev */,
+               if (pTcon->unix_ext) {
+                       struct cifs_unix_set_info_args args = {
+                               .mode   = inode->i_mode,
+                               .uid    = NO_CHANGE_64,
+                               .gid    = NO_CHANGE_64,
+                               .ctime  = NO_CHANGE_64,
+                               .atime  = NO_CHANGE_64,
+                               .mtime  = NO_CHANGE_64,
+                               .device = 0,
+                       };
+                       CIFSSMBUnixSetInfo(xid, pTcon, full_path, &args,
                                            cifs_sb->local_nls,
-                                           cifs_sb->mnt_cifs_flags & 
+                                           cifs_sb->mnt_cifs_flags &
                                                CIFS_MOUNT_MAP_SPECIAL_CHR);
-               } else {
-                       /* BB implement via Windows security descriptors eg
-                          CIFSSMBWinSetPerms(xid, pTcon, full_path, mode,
-                                             -1, -1, local_nls);
-                          in the meantime could set r/o dos attribute when
-                          perms are eg: mode & 0222 == 0 */
                }
        }
 
@@ -338,8 +344,7 @@ static int cifs_relock_file(struct cifsFileInfo *cifsFile)
        return rc;
 }
 
-static int cifs_reopen_file(struct inode *inode, struct file *file, 
-       int can_flush)
+static int cifs_reopen_file(struct file *file, bool can_flush)
 {
        int rc = -EACCES;
        int xid, oplock;
@@ -347,70 +352,74 @@ static int cifs_reopen_file(struct inode *inode, struct file *file,
        struct cifsTconInfo *pTcon;
        struct cifsFileInfo *pCifsFile;
        struct cifsInodeInfo *pCifsInode;
+       struct inode *inode;
        char *full_path = NULL;
        int desiredAccess;
        int disposition = FILE_OPEN;
        __u16 netfid;
 
-       if (inode == NULL)
-               return -EBADF;
-       if (file->private_data) {
+       if (file->private_data)
                pCifsFile = (struct cifsFileInfo *)file->private_data;
-       else
+       else
                return -EBADF;
 
        xid = GetXid();
        down(&pCifsFile->fh_sem);
-       if (pCifsFile->invalidHandle == FALSE) {
+       if (!pCifsFile->invalidHandle) {
                up(&pCifsFile->fh_sem);
                FreeXid(xid);
                return 0;
        }
 
        if (file->f_path.dentry == NULL) {
-               up(&pCifsFile->fh_sem);
-               cFYI(1, ("failed file reopen, no valid name if dentry freed"));
-               FreeXid(xid);
-               return -EBADF;
+               cERROR(1, ("no valid name if dentry freed"));
+               dump_stack();
+               rc = -EBADF;
+               goto reopen_error_exit;
+       }
+
+       inode = file->f_path.dentry->d_inode;
+       if (inode == NULL) {
+               cERROR(1, ("inode not valid"));
+               dump_stack();
+               rc = -EBADF;
+               goto reopen_error_exit;
        }
+
        cifs_sb = CIFS_SB(inode->i_sb);
        pTcon = cifs_sb->tcon;
+
 /* can not grab rename sem here because various ops, including
    those that already have the rename sem can end up causing writepage
    to get called and if the server was down that means we end up here,
    and we can never tell if the caller already has the rename_sem */
        full_path = build_path_from_dentry(file->f_path.dentry);
        if (full_path == NULL) {
+               rc = -ENOMEM;
+reopen_error_exit:
                up(&pCifsFile->fh_sem);
                FreeXid(xid);
-               return -ENOMEM;
+               return rc;
        }
 
-       cFYI(1, (" inode = 0x%p file flags are 0x%x for %s",
-                inode, file->f_flags,full_path));
+       cFYI(1, ("inode = 0x%p file flags 0x%x for %s",
+                inode, file->f_flags, full_path));
        desiredAccess = cifs_convert_flags(file->f_flags);
 
        if (oplockEnabled)
                oplock = REQ_OPLOCK;
        else
-               oplock = FALSE;
+               oplock = 0;
 
        /* Can not refresh inode by passing in file_info buf to be returned
-          by SMBOpen and then calling get_inode_info with returned buf 
-          since file might have write behind data that needs to be flushed 
+          by SMBOpen and then calling get_inode_info with returned buf
+          since file might have write behind data that needs to be flushed
           and server version of file size can be stale. If we knew for sure
           that inode was not dirty locally we could do this */
 
-/*     buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
-       if (buf == 0) {
-               up(&pCifsFile->fh_sem);
-               kfree(full_path);
-               FreeXid(xid);
-               return -ENOMEM;
-       } */
        rc = CIFSSMBOpen(xid, pTcon, full_path, disposition, desiredAccess,
                         CREATE_NOT_DIR, &netfid, &oplock, NULL,
-                        cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & 
+                        cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
                                CIFS_MOUNT_MAP_SPECIAL_CHR);
        if (rc) {
                up(&pCifsFile->fh_sem);
@@ -418,23 +427,25 @@ static int cifs_reopen_file(struct inode *inode, struct file *file,
                cFYI(1, ("oplock: %d", oplock));
        } else {
                pCifsFile->netfid = netfid;
-               pCifsFile->invalidHandle = FALSE;
+               pCifsFile->invalidHandle = false;
                up(&pCifsFile->fh_sem);
                pCifsInode = CIFS_I(inode);
                if (pCifsInode) {
                        if (can_flush) {
-                               filemap_write_and_wait(inode->i_mapping);
+                               rc = filemap_write_and_wait(inode->i_mapping);
+                               if (rc != 0)
+                                       CIFS_I(inode)->write_behind_rc = rc;
                        /* temporarily disable caching while we
                           go to server to get inode info */
-                               pCifsInode->clientCanCacheAll = FALSE;
-                               pCifsInode->clientCanCacheRead = FALSE;
-                               if (pTcon->ses->capabilities & CAP_UNIX)
+                               pCifsInode->clientCanCacheAll = false;
+                               pCifsInode->clientCanCacheRead = false;
+                               if (pTcon->unix_ext)
                                        rc = cifs_get_inode_info_unix(&inode,
                                                full_path, inode->i_sb, xid);
                                else
                                        rc = cifs_get_inode_info(&inode,
                                                full_path, NULL, inode->i_sb,
-                                               xid);
+                                               xid, NULL);
                        } /* else we are writing out data to server already
                             and could deadlock if we tried to flush data, and
                             since we do not know if we have data that would
@@ -442,16 +453,16 @@ static int cifs_reopen_file(struct inode *inode, struct file *file,
                             we can not go to the server to get the new inod
                             info */
                        if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
-                               pCifsInode->clientCanCacheAll = TRUE;
-                               pCifsInode->clientCanCacheRead = TRUE;
+                               pCifsInode->clientCanCacheAll = true;
+                               pCifsInode->clientCanCacheRead = true;
                                cFYI(1, ("Exclusive Oplock granted on inode %p",
                                         file->f_path.dentry->d_inode));
                        } else if ((oplock & 0xF) == OPLOCK_READ) {
-                               pCifsInode->clientCanCacheRead = TRUE;
-                               pCifsInode->clientCanCacheAll = FALSE;
+                               pCifsInode->clientCanCacheRead = true;
+                               pCifsInode->clientCanCacheAll = false;
                        } else {
-                               pCifsInode->clientCanCacheRead = FALSE;
-                               pCifsInode->clientCanCacheAll = FALSE;
+                               pCifsInode->clientCanCacheRead = false;
+                               pCifsInode->clientCanCacheAll = false;
                        }
                        cifs_relock_file(pCifsFile);
                }
@@ -465,7 +476,7 @@ static int cifs_reopen_file(struct inode *inode, struct file *file,
 int cifs_close(struct inode *inode, struct file *file)
 {
        int rc = 0;
-       int xid;
+       int xid, timeout;
        struct cifs_sb_info *cifs_sb;
        struct cifsTconInfo *pTcon;
        struct cifsFileInfo *pSMBFile =
@@ -478,29 +489,29 @@ int cifs_close(struct inode *inode, struct file *file)
        if (pSMBFile) {
                struct cifsLockInfo *li, *tmp;
 
-               pSMBFile->closePend = TRUE;
+               pSMBFile->closePend = true;
                if (pTcon) {
                        /* no sense reconnecting to close a file that is
                           already closed */
-                       if (pTcon->tidStatus != CifsNeedReconnect) {
-                               int timeout = 2;
-                               while((atomic_read(&pSMBFile->wrtPending) != 0)
-                                        && (timeout < 1000) ) {
+                       if (!pTcon->need_reconnect) {
+                               timeout = 2;
+                               while ((atomic_read(&pSMBFile->wrtPending) != 0)
+                                       && (timeout <= 2048)) {
                                        /* Give write a better chance to get to
                                        server ahead of the close.  We do not
                                        want to add a wait_q here as it would
                                        increase the memory utilization as
                                        the struct would be in each open file,
-                                       but this should give enough time to 
+                                       but this should give enough time to
                                        clear the socket */
-#ifdef CONFIG_CIFS_DEBUG2
-                                       cFYI(1,("close delay, write pending"));
-#endif /* DEBUG2 */
+                                       cFYI(DBG2,
+                                               ("close delay, write pending"));
                                        msleep(timeout);
                                        timeout *= 4;
                                }
-                               if(atomic_read(&pSMBFile->wrtPending))
-                                       cERROR(1,("close with pending writes"));
+                               if (atomic_read(&pSMBFile->wrtPending))
+                                       cERROR(1,
+                                               ("close with pending writes"));
                                rc = CIFSSMBClose(xid, pTcon,
                                                  pSMBFile->netfid);
                        }
@@ -508,31 +519,49 @@ int cifs_close(struct inode *inode, struct file *file)
 
                /* Delete any outstanding lock records.
                   We'll lose them when the file is closed anyway. */
-               down(&pSMBFile->lock_sem);
+               mutex_lock(&pSMBFile->lock_mutex);
                list_for_each_entry_safe(li, tmp, &pSMBFile->llist, llist) {
                        list_del(&li->llist);
                        kfree(li);
                }
-               up(&pSMBFile->lock_sem);
+               mutex_unlock(&pSMBFile->lock_mutex);
 
                write_lock(&GlobalSMBSeslock);
                list_del(&pSMBFile->flist);
                list_del(&pSMBFile->tlist);
                write_unlock(&GlobalSMBSeslock);
-               kfree(pSMBFile->search_resume_name);
+               timeout = 10;
+               /* We waited above to give the SMBWrite a chance to issue
+                  on the wire (so we do not get SMBWrite returning EBADF
+                  if writepages is racing with close.  Note that writepages
+                  does not specify a file handle, so it is possible for a file
+                  to be opened twice, and the application close the "wrong"
+                  file handle - in these cases we delay long enough to allow
+                  the SMBWrite to get on the wire before the SMB Close.
+                  We allow total wait here over 45 seconds, more than
+                  oplock break time, and more than enough to allow any write
+                  to complete on the server, or to time out on the client */
+               while ((atomic_read(&pSMBFile->wrtPending) != 0)
+                               && (timeout <= 50000)) {
+                       cERROR(1, ("writes pending, delay free of handle"));
+                       msleep(timeout);
+                       timeout *= 8;
+               }
                kfree(file->private_data);
                file->private_data = NULL;
        } else
                rc = -EBADF;
 
+       read_lock(&GlobalSMBSeslock);
        if (list_empty(&(CIFS_I(inode)->openFileList))) {
                cFYI(1, ("closing last open instance for inode %p", inode));
                /* if the file is not open we do not know if we can cache info
                   on this inode, much less write behind and read ahead */
-               CIFS_I(inode)->clientCanCacheRead = FALSE;
-               CIFS_I(inode)->clientCanCacheAll  = FALSE;
+               CIFS_I(inode)->clientCanCacheRead = false;
+               CIFS_I(inode)->clientCanCacheAll  = false;
        }
-       if ((rc ==0) && CIFS_I(inode)->write_behind_rc)
+       read_unlock(&GlobalSMBSeslock);
+       if ((rc == 0) && CIFS_I(inode)->write_behind_rc)
                rc = CIFS_I(inode)->write_behind_rc;
        FreeXid(xid);
        return rc;
@@ -552,14 +581,15 @@ int cifs_closedir(struct inode *inode, struct file *file)
 
        if (pCFileStruct) {
                struct cifsTconInfo *pTcon;
-               struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
+               struct cifs_sb_info *cifs_sb =
+                       CIFS_SB(file->f_path.dentry->d_sb);
 
                pTcon = cifs_sb->tcon;
 
                cFYI(1, ("Freeing private data in close dir"));
-               if ((pCFileStruct->srch_inf.endOfSearch == FALSE) &&
-                  (pCFileStruct->invalidHandle == FALSE)) {
-                       pCFileStruct->invalidHandle = TRUE;
+               if (!pCFileStruct->srch_inf.endOfSearch &&
+                   !pCFileStruct->invalidHandle) {
+                       pCFileStruct->invalidHandle = true;
                        rc = CIFSFindClose(xid, pTcon, pCFileStruct->netfid);
                        cFYI(1, ("Closing uncompleted readdir with rc %d",
                                 rc));
@@ -570,17 +600,11 @@ int cifs_closedir(struct inode *inode, struct file *file)
                if (ptmp) {
                        cFYI(1, ("closedir free smb buf in srch struct"));
                        pCFileStruct->srch_inf.ntwrk_buf_start = NULL;
-                       if(pCFileStruct->srch_inf.smallBuf)
+                       if (pCFileStruct->srch_inf.smallBuf)
                                cifs_small_buf_release(ptmp);
                        else
                                cifs_buf_release(ptmp);
                }
-               ptmp = pCFileStruct->search_resume_name;
-               if (ptmp) {
-                       cFYI(1, ("closedir free resume name"));
-                       pCFileStruct->search_resume_name = NULL;
-                       kfree(ptmp);
-               }
                kfree(file->private_data);
                file->private_data = NULL;
        }
@@ -592,15 +616,16 @@ int cifs_closedir(struct inode *inode, struct file *file)
 static int store_file_lock(struct cifsFileInfo *fid, __u64 len,
                                __u64 offset, __u8 lockType)
 {
-       struct cifsLockInfo *li = kmalloc(sizeof(struct cifsLockInfo), GFP_KERNEL);
+       struct cifsLockInfo *li =
+               kmalloc(sizeof(struct cifsLockInfo), GFP_KERNEL);
        if (li == NULL)
                return -ENOMEM;
        li->offset = offset;
        li->length = len;
        li->type = lockType;
-       down(&fid->lock_sem);
+       mutex_lock(&fid->lock_mutex);
        list_add(&li->llist, &fid->llist);
-       up(&fid->lock_sem);
+       mutex_unlock(&fid->lock_mutex);
        return 0;
 }
 
@@ -610,12 +635,12 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
        __u32 numLock = 0;
        __u32 numUnlock = 0;
        __u64 length;
-       int wait_flag = FALSE;
+       bool wait_flag = false;
        struct cifs_sb_info *cifs_sb;
        struct cifsTconInfo *pTcon;
        __u16 netfid;
        __u8 lockType = LOCKING_ANDX_LARGE_FILES;
-       int posix_locking;
+       bool posix_locking;
 
        length = 1 + pfLock->fl_end - pfLock->fl_start;
        rc = -EACCES;
@@ -623,8 +648,8 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
 
        cFYI(1, ("Lock parm: 0x%x flockflags: "
                 "0x%x flocktype: 0x%x start: %lld end: %lld",
-               cmd, pfLock->fl_flags, pfLock->fl_type, pfLock->fl_start,
-               pfLock->fl_end));
+               cmd, pfLock->fl_flags, pfLock->fl_type, pfLock->fl_start,
+               pfLock->fl_end));
 
        if (pfLock->fl_flags & FL_POSIX)
                cFYI(1, ("Posix"));
@@ -632,14 +657,14 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
                cFYI(1, ("Flock"));
        if (pfLock->fl_flags & FL_SLEEP) {
                cFYI(1, ("Blocking lock"));
-               wait_flag = TRUE;
+               wait_flag = true;
        }
        if (pfLock->fl_flags & FL_ACCESS)
                cFYI(1, ("Process suspended by mandatory locking - "
                         "not implemented yet"));
        if (pfLock->fl_flags & FL_LEASE)
                cFYI(1, ("Lease on file - not implemented yet"));
-       if (pfLock->fl_flags & 
+       if (pfLock->fl_flags &
            (~(FL_POSIX | FL_FLOCK | FL_SLEEP | FL_ACCESS | FL_LEASE)))
                cFYI(1, ("Unknown lock flags 0x%x", pfLock->fl_flags));
 
@@ -681,9 +706,9 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
        account for negative length which we can not accept over the
        wire */
        if (IS_GETLK(cmd)) {
-               if(posix_locking) {
+               if (posix_locking) {
                        int posix_lock_type;
-                       if(lockType & LOCKING_ANDX_SHARED_LOCK)
+                       if (lockType & LOCKING_ANDX_SHARED_LOCK)
                                posix_lock_type = CIFS_RDLCK;
                        else
                                posix_lock_type = CIFS_WRLCK;
@@ -698,7 +723,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
                rc = CIFSSMBLock(xid, pTcon, netfid, length, pfLock->fl_start,
                                 0, 1, lockType, 0 /* wait flag */ );
                if (rc == 0) {
-                       rc = CIFSSMBLock(xid, pTcon, netfid, length, 
+                       rc = CIFSSMBLock(xid, pTcon, netfid, length,
                                         pfLock->fl_start, 1 /* numUnlock */ ,
                                         0 /* numLock */ , lockType,
                                         0 /* wait flag */ );
@@ -727,22 +752,24 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
 
        if (posix_locking) {
                int posix_lock_type;
-               if(lockType & LOCKING_ANDX_SHARED_LOCK)
+               if (lockType & LOCKING_ANDX_SHARED_LOCK)
                        posix_lock_type = CIFS_RDLCK;
                else
                        posix_lock_type = CIFS_WRLCK;
-               
-               if(numUnlock == 1)
+
+               if (numUnlock == 1)
                        posix_lock_type = CIFS_UNLCK;
 
                rc = CIFSSMBPosixLock(xid, pTcon, netfid, 0 /* set */,
                                      length, pfLock,
                                      posix_lock_type, wait_flag);
        } else {
-               struct cifsFileInfo *fid = (struct cifsFileInfo *)file->private_data;
+               struct cifsFileInfo *fid =
+                       (struct cifsFileInfo *)file->private_data;
 
                if (numLock) {
-                       rc = CIFSSMBLock(xid, pTcon, netfid, length, pfLock->fl_start,
+                       rc = CIFSSMBLock(xid, pTcon, netfid, length,
+                                       pfLock->fl_start,
                                        0, numLock, lockType, wait_flag);
 
                        if (rc == 0) {
@@ -757,13 +784,15 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
                        struct cifsLockInfo *li, *tmp;
 
                        rc = 0;
-                       down(&fid->lock_sem);
+                       mutex_lock(&fid->lock_mutex);
                        list_for_each_entry_safe(li, tmp, &fid->llist, llist) {
                                if (pfLock->fl_start <= li->offset &&
-                                               length >= li->length) {
-                                       stored_rc = CIFSSMBLock(xid, pTcon, netfid,
+                                               (pfLock->fl_start + length) >=
+                                               (li->offset + li->length)) {
+                                       stored_rc = CIFSSMBLock(xid, pTcon,
+                                                       netfid,
                                                        li->length, li->offset,
-                                                       1, 0, li->type, FALSE);
+                                                       1, 0, li->type, false);
                                        if (stored_rc)
                                                rc = stored_rc;
 
@@ -771,7 +800,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
                                        kfree(li);
                                }
                        }
-                       up(&fid->lock_sem);
+                       mutex_unlock(&fid->lock_mutex);
                }
        }
 
@@ -792,12 +821,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,
        int xid, long_op;
        struct cifsFileInfo *open_file;
 
-       if (file->f_path.dentry == NULL)
-               return -EBADF;
-
        cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
-       if (cifs_sb == NULL)
-               return -EBADF;
 
        pTcon = cifs_sb->tcon;
 
@@ -807,19 +831,18 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,
 
        if (file->private_data == NULL)
                return -EBADF;
-       else
-               open_file = (struct cifsFileInfo *) file->private_data;
-       
+       open_file = (struct cifsFileInfo *) file->private_data;
+
+       rc = generic_write_checks(file, poffset, &write_size, 0);
+       if (rc)
+               return rc;
+
        xid = GetXid();
-       if (file->f_path.dentry->d_inode == NULL) {
-               FreeXid(xid);
-               return -EBADF;
-       }
 
        if (*poffset > file->f_path.dentry->d_inode->i_size)
-               long_op = 2; /* writes past end of file can take a long time */
+               long_op = CIFS_VLONG_OP; /* writes past EOF take long time */
        else
-               long_op = 1;
+               long_op = CIFS_LONG_OP;
 
        for (total_written = 0; write_size > total_written;
             total_written += bytes_written) {
@@ -832,7 +855,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,
                           and blocked, and the file has been freed on us while
                           we blocked so return what we managed to write */
                                return total_written;
-                       } 
+                       }
                        if (open_file->closePend) {
                                FreeXid(xid);
                                if (total_written)
@@ -841,17 +864,11 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,
                                        return -EBADF;
                        }
                        if (open_file->invalidHandle) {
-                               if ((file->f_path.dentry == NULL) ||
-                                   (file->f_path.dentry->d_inode == NULL)) {
-                                       FreeXid(xid);
-                                       return total_written;
-                               }
                                /* we could deadlock if we called
                                   filemap_fdatawait from here so tell
                                   reopen_file not to flush data to server
                                   now */
-                               rc = cifs_reopen_file(file->f_path.dentry->d_inode,
-                                       file, FALSE);
+                               rc = cifs_reopen_file(file, false);
                                if (rc != 0)
                                        break;
                        }
@@ -872,32 +889,33 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,
                        }
                } else
                        *poffset += bytes_written;
-               long_op = FALSE; /* subsequent writes fast -
+               long_op = CIFS_STD_OP; /* subsequent writes fast -
                                    15 seconds is plenty */
        }
 
        cifs_stats_bytes_written(pTcon, total_written);
 
        /* since the write may have blocked check these pointers again */
-       if (file->f_path.dentry) {
-               if (file->f_path.dentry->d_inode) {
-                       struct inode *inode = file->f_path.dentry->d_inode;
-                       inode->i_ctime = inode->i_mtime =
-                               current_fs_time(inode->i_sb);
-                       if (total_written > 0) {
-                               if (*poffset > file->f_path.dentry->d_inode->i_size)
-                                       i_size_write(file->f_path.dentry->d_inode,
+       if ((file->f_path.dentry) && (file->f_path.dentry->d_inode)) {
+               struct inode *inode = file->f_path.dentry->d_inode;
+/* Do not update local mtime - server will set its actual value on write
+ *             inode->i_ctime = inode->i_mtime =
+ *                     current_fs_time(inode->i_sb);*/
+               if (total_written > 0) {
+                       spin_lock(&inode->i_lock);
+                       if (*poffset > file->f_path.dentry->d_inode->i_size)
+                               i_size_write(file->f_path.dentry->d_inode,
                                        *poffset);
-                       }
-                       mark_inode_dirty_sync(file->f_path.dentry->d_inode);
+                       spin_unlock(&inode->i_lock);
                }
+               mark_inode_dirty_sync(file->f_path.dentry->d_inode);
        }
        FreeXid(xid);
        return total_written;
 }
 
 static ssize_t cifs_write(struct file *file, const char *write_data,
-       size_t write_size, loff_t *poffset)
+                         size_t write_size, loff_t *poffset)
 {
        int rc = 0;
        unsigned int bytes_written = 0;
@@ -907,33 +925,23 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
        int xid, long_op;
        struct cifsFileInfo *open_file;
 
-       if (file->f_path.dentry == NULL)
-               return -EBADF;
-
        cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
-       if (cifs_sb == NULL)
-               return -EBADF;
 
        pTcon = cifs_sb->tcon;
 
-       cFYI(1,("write %zd bytes to offset %lld of %s", write_size,
+       cFYI(1, ("write %zd bytes to offset %lld of %s", write_size,
           *poffset, file->f_path.dentry->d_name.name));
 
        if (file->private_data == NULL)
                return -EBADF;
-       else
-               open_file = (struct cifsFileInfo *)file->private_data;
-       
+       open_file = (struct cifsFileInfo *)file->private_data;
+
        xid = GetXid();
-       if (file->f_path.dentry->d_inode == NULL) {
-               FreeXid(xid);
-               return -EBADF;
-       }
 
        if (*poffset > file->f_path.dentry->d_inode->i_size)
-               long_op = 2; /* writes past end of file can take a long time */
+               long_op = CIFS_VLONG_OP; /* writes past EOF can be slow */
        else
-               long_op = 1;
+               long_op = CIFS_LONG_OP;
 
        for (total_written = 0; write_size > total_written;
             total_written += bytes_written) {
@@ -944,10 +952,10 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
                                FreeXid(xid);
                        /* if we have gotten here we have written some data
                           and blocked, and the file has been freed on us
-                          while we blocked so return what we managed to 
+                          while we blocked so return what we managed to
                           write */
                                return total_written;
-                       } 
+                       }
                        if (open_file->closePend) {
                                FreeXid(xid);
                                if (total_written)
@@ -956,22 +964,16 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
                                        return -EBADF;
                        }
                        if (open_file->invalidHandle) {
-                               if ((file->f_path.dentry == NULL) ||
-                                  (file->f_path.dentry->d_inode == NULL)) {
-                                       FreeXid(xid);
-                                       return total_written;
-                               }
                                /* we could deadlock if we called
                                   filemap_fdatawait from here so tell
-                                  reopen_file not to flush data to 
+                                  reopen_file not to flush data to
                                   server now */
-                               rc = cifs_reopen_file(file->f_path.dentry->d_inode,
-                                       file, FALSE);
+                               rc = cifs_reopen_file(file, false);
                                if (rc != 0)
                                        break;
                        }
-                       if(experimEnabled || (pTcon->ses->server &&
-                               ((pTcon->ses->server->secMode & 
+                       if (experimEnabled || (pTcon->ses->server &&
+                               ((pTcon->ses->server->secMode &
                                (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
                                == 0))) {
                                struct kvec iov[2];
@@ -1005,72 +1007,138 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
                        }
                } else
                        *poffset += bytes_written;
-               long_op = FALSE; /* subsequent writes fast - 
+               long_op = CIFS_STD_OP; /* subsequent writes fast -
                                    15 seconds is plenty */
        }
 
        cifs_stats_bytes_written(pTcon, total_written);
 
        /* since the write may have blocked check these pointers again */
-       if (file->f_path.dentry) {
-               if (file->f_path.dentry->d_inode) {
-                       file->f_path.dentry->d_inode->i_ctime =
-                       file->f_path.dentry->d_inode->i_mtime = CURRENT_TIME;
-                       if (total_written > 0) {
-                               if (*poffset > file->f_path.dentry->d_inode->i_size)
-                                       i_size_write(file->f_path.dentry->d_inode,
-                                                    *poffset);
-                       }
-                       mark_inode_dirty_sync(file->f_path.dentry->d_inode);
+       if ((file->f_path.dentry) && (file->f_path.dentry->d_inode)) {
+/*BB We could make this contingent on superblock ATIME flag too */
+/*             file->f_path.dentry->d_inode->i_ctime =
+               file->f_path.dentry->d_inode->i_mtime = CURRENT_TIME;*/
+               if (total_written > 0) {
+                       spin_lock(&file->f_path.dentry->d_inode->i_lock);
+                       if (*poffset > file->f_path.dentry->d_inode->i_size)
+                               i_size_write(file->f_path.dentry->d_inode,
+                                            *poffset);
+                       spin_unlock(&file->f_path.dentry->d_inode->i_lock);
                }
+               mark_inode_dirty_sync(file->f_path.dentry->d_inode);
        }
        FreeXid(xid);
        return total_written;
 }
 
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode)
+{
+       struct cifsFileInfo *open_file = NULL;
+
+       read_lock(&GlobalSMBSeslock);
+       /* we could simply get the first_list_entry since write-only entries
+          are always at the end of the list but since the first entry might
+          have a close pending, we go through the whole list */
+       list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
+               if (open_file->closePend)
+                       continue;
+               if (open_file->pfile && ((open_file->pfile->f_flags & O_RDWR) ||
+                   (open_file->pfile->f_flags & O_RDONLY))) {
+                       if (!open_file->invalidHandle) {
+                               /* found a good file */
+                               /* lock it so it will not be closed on us */
+                               atomic_inc(&open_file->wrtPending);
+                               read_unlock(&GlobalSMBSeslock);
+                               return open_file;
+                       } /* else might as well continue, and look for
+                            another, or simply have the caller reopen it
+                            again rather than trying to fix this handle */
+               } else /* write only file */
+                       break; /* write only files are last so must be done */
+       }
+       read_unlock(&GlobalSMBSeslock);
+       return NULL;
+}
+#endif
+
 struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode)
 {
        struct cifsFileInfo *open_file;
+       bool any_available = false;
        int rc;
 
        /* Having a null inode here (because mapping->host was set to zero by
        the VFS or MM) should not happen but we had reports of on oops (due to
        it being zero) during stress testcases so we need to check for it */
 
-       if(cifs_inode == NULL) {
-               cERROR(1,("Null inode passed to cifs_writeable_file"));
+       if (cifs_inode == NULL) {
+               cERROR(1, ("Null inode passed to cifs_writeable_file"));
                dump_stack();
                return NULL;
        }
 
        read_lock(&GlobalSMBSeslock);
+refind_writable:
        list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
-               if (open_file->closePend)
+               if (open_file->closePend ||
+                   (!any_available && open_file->pid != current->tgid))
                        continue;
+
                if (open_file->pfile &&
                    ((open_file->pfile->f_flags & O_RDWR) ||
                     (open_file->pfile->f_flags & O_WRONLY))) {
                        atomic_inc(&open_file->wrtPending);
+
+                       if (!open_file->invalidHandle) {
+                               /* found a good writable file */
+                               read_unlock(&GlobalSMBSeslock);
+                               return open_file;
+                       }
+
                        read_unlock(&GlobalSMBSeslock);
-                       if((open_file->invalidHandle) && 
-                          (!open_file->closePend) /* BB fixme -since the second clause can not be true remove it BB */) {
-                               rc = cifs_reopen_file(&cifs_inode->vfs_inode, 
-                                                     open_file->pfile, FALSE);
-                               /* if it fails, try another handle - might be */
-                               /* dangerous to hold up writepages with retry */
-                               if(rc) {
-                                       cFYI(1,("failed on reopen file in wp"));
+                       /* Had to unlock since following call can block */
+                       rc = cifs_reopen_file(open_file->pfile, false);
+                       if (!rc) {
+                               if (!open_file->closePend)
+                                       return open_file;
+                               else { /* start over in case this was deleted */
+                                      /* since the list could be modified */
                                        read_lock(&GlobalSMBSeslock);
-                                       /* can not use this handle, no write
-                                       pending on this one after all */
-                                       atomic_dec
-                                            (&open_file->wrtPending);
-                                       continue;
+                                       atomic_dec(&open_file->wrtPending);
+                                       goto refind_writable;
                                }
                        }
-                       return open_file;
+
+                       /* if it fails, try another handle if possible -
+                       (we can not do this if closePending since
+                       loop could be modified - in which case we
+                       have to start at the beginning of the list
+                       again. Note that it would be bad
+                       to hold up writepages here (rather than
+                       in caller) with continuous retries */
+                       cFYI(1, ("wp failed on reopen file"));
+                       read_lock(&GlobalSMBSeslock);
+                       /* can not use this handle, no write
+                          pending on this one after all */
+                       atomic_dec(&open_file->wrtPending);
+
+                       if (open_file->closePend) /* list could have changed */
+                               goto refind_writable;
+                       /* else we simply continue to the next entry. Thus
+                          we do not loop on reopen errors.  If we
+                          can not reopen the file, for example if we
+                          reconnected to a server with another client
+                          racing to delete or lock the file we would not
+                          make progress if we restarted before the beginning
+                          of the loop here. */
                }
        }
+       /* couldn't find useable FH with same pid, try any available */
+       if (!any_available) {
+               any_available = true;
+               goto refind_writable;
+       }
        read_unlock(&GlobalSMBSeslock);
        return NULL;
 }
@@ -1111,7 +1179,7 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
 
        /* check to make sure that we are not extending the file */
        if (mapping->host->i_size - offset < (loff_t)to)
-               to = (unsigned)(mapping->host->i_size - offset); 
+               to = (unsigned)(mapping->host->i_size - offset);
 
        open_file = find_writable_file(CIFS_I(mapping->host));
        if (open_file) {
@@ -1120,12 +1188,10 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
                atomic_dec(&open_file->wrtPending);
                /* Does mm or vfs already set times? */
                inode->i_atime = inode->i_mtime = current_fs_time(inode->i_sb);
-               if ((bytes_written > 0) && (offset)) {
+               if ((bytes_written > 0) && (offset))
                        rc = 0;
-               } else if (bytes_written < 0) {
-                       if (rc != -EBADF)
-                               rc = bytes_written;
-               }
+               else if (bytes_written < 0)
+                       rc = bytes_written;
        } else {
                cFYI(1, ("No writeable filehandles for inode"));
                rc = -EIO;
@@ -1145,8 +1211,8 @@ static int cifs_writepages(struct address_space *mapping,
        int done = 0;
        pgoff_t end;
        pgoff_t index;
-       int range_whole = 0;
-       struct kvec iov[32];
+       int range_whole = 0;
+       struct kvec *iov;
        int len;
        int n_iov = 0;
        pgoff_t next;
@@ -1160,7 +1226,7 @@ static int cifs_writepages(struct address_space *mapping,
        int xid;
 
        cifs_sb = CIFS_SB(mapping->host->i_sb);
-       
+
        /*
         * If wsize is smaller that the page cache size, default to writing
         * one page at a time via cifs_writepage
@@ -1168,18 +1234,24 @@ static int cifs_writepages(struct address_space *mapping,
        if (cifs_sb->wsize < PAGE_CACHE_SIZE)
                return generic_writepages(mapping, wbc);
 
-       if((cifs_sb->tcon->ses) && (cifs_sb->tcon->ses->server))
-               if(cifs_sb->tcon->ses->server->secMode &
-                          (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
-                       if(!experimEnabled)
+       if ((cifs_sb->tcon->ses) && (cifs_sb->tcon->ses->server))
+               if (cifs_sb->tcon->ses->server->secMode &
+                               (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+                       if (!experimEnabled)
                                return generic_writepages(mapping, wbc);
 
+       iov = kmalloc(32 * sizeof(struct kvec), GFP_KERNEL);
+       if (iov == NULL)
+               return generic_writepages(mapping, wbc);
+
+
        /*
         * BB: Is this meaningful for a non-block-device file system?
         * If it is, we should test it again after we do I/O
         */
        if (wbc->nonblocking && bdi_write_congested(bdi)) {
                wbc->encountered_congestion = 1;
+               kfree(iov);
                return 0;
        }
 
@@ -1221,7 +1293,7 @@ retry:
 
                        if (first < 0)
                                lock_page(page);
-                       else if (TestSetPageLocked(page))
+                       else if (!trylock_page(page))
                                break;
 
                        if (unlikely(page->mapping != mapping)) {
@@ -1245,14 +1317,21 @@ retry:
                                wait_on_page_writeback(page);
 
                        if (PageWriteback(page) ||
-                                       !test_clear_page_dirty(page)) {
+                                       !clear_page_dirty_for_io(page)) {
                                unlock_page(page);
                                break;
                        }
 
+                       /*
+                        * This actually clears the dirty bit in the radix tree.
+                        * See cifs_writepage() for more commentary.
+                        */
+                       set_page_writeback(page);
+
                        if (page_offset(page) >= mapping->host->i_size) {
                                done = 1;
                                unlock_page(page);
+                               end_page_writeback(page);
                                break;
                        }
 
@@ -1292,14 +1371,17 @@ retry:
                                                   open_file->netfid,
                                                   bytes_to_write, offset,
                                                   &bytes_written, iov, n_iov,
-                                                  1);
+                                                  CIFS_LONG_OP);
                                atomic_dec(&open_file->wrtPending);
                                if (rc || bytes_written < bytes_to_write) {
-                                       cERROR(1,("Write2 ret %d, written = %d",
+                                       cERROR(1, ("Write2 ret %d, wrote %d",
                                                  rc, bytes_written));
                                        /* BB what if continued retry is
                                           requested via mount flags? */
-                                       set_bit(AS_EIO, &mapping->flags);
+                                       if (rc == -ENOSPC)
+                                               set_bit(AS_ENOSPC, &mapping->flags);
+                                       else
+                                               set_bit(AS_EIO, &mapping->flags);
                                } else {
                                        cifs_stats_bytes_written(cifs_sb->tcon,
                                                                 bytes_written);
@@ -1311,17 +1393,21 @@ retry:
                                success rc but too little data written? */
                                /* BB investigate retry logic on temporary
                                server crash cases and how recovery works
-                               when page marked as error */ 
-                               if(rc)
+                               when page marked as error */
+                               if (rc)
                                        SetPageError(page);
                                kunmap(page);
                                unlock_page(page);
+                               end_page_writeback(page);
                                page_cache_release(page);
                        }
                        if ((wbc->nr_to_write -= n_iov) <= 0)
                                done = 1;
                        index = next;
-               }
+               } else
+                       /* Need to re-find the pages we skipped */
+                       index = pvec.pages[0]->index + 1;
+
                pagevec_release(&pvec);
        }
        if (!scanned && !done) {
@@ -1337,11 +1423,11 @@ retry:
                mapping->writeback_index = index;
 
        FreeXid(xid);
-
+       kfree(iov);
        return rc;
 }
 
-static int cifs_writepage(struct pagepage, struct writeback_control *wbc)
+static int cifs_writepage(struct page *page, struct writeback_control *wbc)
 {
        int rc = -EFAULT;
        int xid;
@@ -1349,85 +1435,75 @@ static int cifs_writepage(struct page* page, struct writeback_control *wbc)
        xid = GetXid();
 /* BB add check for wbc flags */
        page_cache_get(page);
-        if (!PageUptodate(page)) {
+       if (!PageUptodate(page))
                cFYI(1, ("ppw - page not up to date"));
-       }
-       
+
+       /*
+        * Set the "writeback" flag, and clear "dirty" in the radix tree.
+        *
+        * A writepage() implementation always needs to do either this,
+        * or re-dirty the page with "redirty_page_for_writepage()" in
+        * the case of a failure.
+        *
+        * Just unlocking the page will cause the radix tree tag-bits
+        * to fail to update with the state of the page correctly.
+        */
+       set_page_writeback(page);
        rc = cifs_partialpagewrite(page, 0, PAGE_CACHE_SIZE);
        SetPageUptodate(page); /* BB add check for error and Clearuptodate? */
        unlock_page(page);
-       page_cache_release(page);       
+       end_page_writeback(page);
+       page_cache_release(page);
        FreeXid(xid);
        return rc;
 }
 
-static int cifs_commit_write(struct file *file, struct page *page,
-       unsigned offset, unsigned to)
+static int cifs_write_end(struct file *file, struct address_space *mapping,
+                       loff_t pos, unsigned len, unsigned copied,
+                       struct page *page, void *fsdata)
 {
-       int xid;
-       int rc = 0;
-       struct inode *inode = page->mapping->host;
-       loff_t position = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
-       char *page_data;
+       int rc;
+       struct inode *inode = mapping->host;
+
+       cFYI(1, ("write_end for page %p from pos %lld with %d bytes",
+                page, pos, copied));
+
+       if (!PageUptodate(page) && copied == PAGE_CACHE_SIZE)
+               SetPageUptodate(page);
 
-       xid = GetXid();
-       cFYI(1, ("commit write for page %p up to position %lld for %d", 
-                page, position, to));
-       if (position > inode->i_size) {
-               i_size_write(inode, position);
-               /* if (file->private_data == NULL) {
-                       rc = -EBADF;
-               } else {
-                       open_file = (struct cifsFileInfo *)file->private_data;
-                       cifs_sb = CIFS_SB(inode->i_sb);
-                       rc = -EAGAIN;
-                       while (rc == -EAGAIN) {
-                               if ((open_file->invalidHandle) && 
-                                   (!open_file->closePend)) {
-                                       rc = cifs_reopen_file(
-                                               file->f_path.dentry->d_inode, file);
-                                       if (rc != 0)
-                                               break;
-                               }
-                               if (!open_file->closePend) {
-                                       rc = CIFSSMBSetFileSize(xid,
-                                               cifs_sb->tcon, position,
-                                               open_file->netfid,
-                                               open_file->pid, FALSE);
-                               } else {
-                                       rc = -EBADF;
-                                       break;
-                               }
-                       }
-                       cFYI(1, (" SetEOF (commit write) rc = %d", rc));
-               } */
-       }
        if (!PageUptodate(page)) {
-               position =  ((loff_t)page->index << PAGE_CACHE_SHIFT) + offset;
-               /* can not rely on (or let) writepage write this data */
-               if (to < offset) {
-                       cFYI(1, ("Illegal offsets, can not copy from %d to %d",
-                               offset, to));
-                       FreeXid(xid);
-                       return rc;
-               }
+               char *page_data;
+               unsigned offset = pos & (PAGE_CACHE_SIZE - 1);
+               int xid;
+
+               xid = GetXid();
                /* this is probably better than directly calling
                   partialpage_write since in this function the file handle is
                   known which we might as well leverage */
                /* BB check if anything else missing out of ppw
                   such as updating last write time */
                page_data = kmap(page);
-               rc = cifs_write(file, page_data + offset, to-offset,
-                               &position);
-               if (rc > 0)
-                       rc = 0;
-               /* else if (rc < 0) should we set writebehind rc? */
+               rc = cifs_write(file, page_data + offset, copied, &pos);
+               /* if (rc < 0) should we set writebehind rc? */
                kunmap(page);
-       } else {        
+
+               FreeXid(xid);
+       } else {
+               rc = copied;
+               pos += copied;
                set_page_dirty(page);
        }
 
-       FreeXid(xid);
+       if (rc > 0) {
+               spin_lock(&inode->i_lock);
+               if (pos > inode->i_size)
+                       i_size_write(inode, pos);
+               spin_unlock(&inode->i_lock);
+       }
+
+       unlock_page(page);
+       page_cache_release(page);
+
        return rc;
 }
 
@@ -1439,12 +1515,14 @@ int cifs_fsync(struct file *file, struct dentry *dentry, int datasync)
 
        xid = GetXid();
 
-       cFYI(1, ("Sync file - name: %s datasync: 0x%x", 
+       cFYI(1, ("Sync file - name: %s datasync: 0x%x",
                dentry->d_name.name, datasync));
-       
-       rc = filemap_fdatawrite(inode->i_mapping);
-       if (rc == 0)
+
+       rc = filemap_write_and_wait(inode->i_mapping);
+       if (rc == 0) {
+               rc = CIFS_I(inode)->write_behind_rc;
                CIFS_I(inode)->write_behind_rc = 0;
+       }
        FreeXid(xid);
        return rc;
 }
@@ -1465,7 +1543,7 @@ int cifs_fsync(struct file *file, struct dentry *dentry, int datasync)
        if (!inode)
                return; */
 
-/*     fill in rpages then 
+/*     fill in rpages then
        result = cifs_pagein_inode(inode, index, rpages); */ /* BB finish */
 
 /*     cFYI(1, ("rpages is %d for sync page of Index %ld", rpages, index));
@@ -1483,7 +1561,7 @@ int cifs_fsync(struct file *file, struct dentry *dentry, int datasync)
  */
 int cifs_flush(struct file *file, fl_owner_t id)
 {
-       struct inode * inode = file->f_path.dentry->d_inode;
+       struct inode *inode = file->f_path.dentry->d_inode;
        int rc = 0;
 
        /* Rather than do the steps manually:
@@ -1496,10 +1574,13 @@ int cifs_flush(struct file *file, fl_owner_t id)
           filemapfdatawrite appears easier for the time being */
 
        rc = filemap_fdatawrite(inode->i_mapping);
-       if (!rc) /* reset wb rc if we were able to write out dirty pages */
+       /* reset wb rc if we were able to write out dirty pages */
+       if (!rc) {
+               rc = CIFS_I(inode)->write_behind_rc;
                CIFS_I(inode)->write_behind_rc = 0;
-               
-       cFYI(1, ("Flush inode %p file %p rc %d",inode,file,rc));
+       }
+
+       cFYI(1, ("Flush inode %p file %p rc %d", inode, file, rc));
 
        return rc;
 }
@@ -1529,22 +1610,21 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data,
        }
        open_file = (struct cifsFileInfo *)file->private_data;
 
-       if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
+       if ((file->f_flags & O_ACCMODE) == O_WRONLY)
                cFYI(1, ("attempting read on write only file instance"));
-       }
+
        for (total_read = 0, current_offset = read_data;
             read_size > total_read;
             total_read += bytes_read, current_offset += bytes_read) {
-               current_read_size = min_t(const int, read_size - total_read, 
+               current_read_size = min_t(const int, read_size - total_read,
                                          cifs_sb->rsize);
                rc = -EAGAIN;
                smb_read_data = NULL;
                while (rc == -EAGAIN) {
                        int buf_type = CIFS_NO_BUFFER;
-                       if ((open_file->invalidHandle) && 
+                       if ((open_file->invalidHandle) &&
                            (!open_file->closePend)) {
-                               rc = cifs_reopen_file(file->f_path.dentry->d_inode,
-                                       file, TRUE);
+                               rc = cifs_reopen_file(file, true);
                                if (rc != 0)
                                        break;
                        }
@@ -1559,13 +1639,12 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data,
                                                smb_read_data +
                                                4 /* RFC1001 length field */ +
                                                le16_to_cpu(pSMBr->DataOffset),
-                                               bytes_read)) {
+                                               bytes_read))
                                        rc = -EFAULT;
-                               }
 
-                               if(buf_type == CIFS_SMALL_BUFFER)
+                               if (buf_type == CIFS_SMALL_BUFFER)
                                        cifs_small_buf_release(smb_read_data);
-                               else if(buf_type == CIFS_LARGE_BUFFER)
+                               else if (buf_type == CIFS_LARGE_BUFFER)
                                        cifs_buf_release(smb_read_data);
                                smb_read_data = NULL;
                        }
@@ -1614,24 +1693,23 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
        if ((file->f_flags & O_ACCMODE) == O_WRONLY)
                cFYI(1, ("attempting read on write only file instance"));
 
-       for (total_read = 0, current_offset = read_data; 
+       for (total_read = 0, current_offset = read_data;
             read_size > total_read;
             total_read += bytes_read, current_offset += bytes_read) {
                current_read_size = min_t(const int, read_size - total_read,
                                          cifs_sb->rsize);
                /* For windows me and 9x we do not want to request more
                than it negotiated since it will refuse the read then */
-               if((pTcon->ses) && 
+               if ((pTcon->ses) &&
                        !(pTcon->ses->capabilities & CAP_LARGE_FILES)) {
                        current_read_size = min_t(const int, current_read_size,
                                        pTcon->ses->server->maxBuf - 128);
                }
                rc = -EAGAIN;
                while (rc == -EAGAIN) {
-                       if ((open_file->invalidHandle) && 
+                       if ((open_file->invalidHandle) &&
                            (!open_file->closePend)) {
-                               rc = cifs_reopen_file(file->f_path.dentry->d_inode,
-                                       file, TRUE);
+                               rc = cifs_reopen_file(file, true);
                                if (rc != 0)
                                        break;
                        }
@@ -1675,7 +1753,7 @@ int cifs_file_mmap(struct file *file, struct vm_area_struct *vma)
 }
 
 
-static void cifs_copy_cache_pages(struct address_space *mapping, 
+static void cifs_copy_cache_pages(struct address_space *mapping,
        struct list_head *pages, int bytes_read, char *data,
        struct pagevec *plru_pvec)
 {
@@ -1698,12 +1776,12 @@ static void cifs_copy_cache_pages(struct address_space *mapping,
                        continue;
                }
 
-               target = kmap_atomic(page,KM_USER0);
+               target = kmap_atomic(page, KM_USER0);
 
                if (PAGE_CACHE_SIZE > bytes_read) {
                        memcpy(target, data, bytes_read);
                        /* zero the tail end of this partial page */
-                       memset(target + bytes_read, 0, 
+                       memset(target + bytes_read, 0,
                               PAGE_CACHE_SIZE - bytes_read);
                        bytes_read = 0;
                } else {
@@ -1716,7 +1794,7 @@ static void cifs_copy_cache_pages(struct address_space *mapping,
                SetPageUptodate(page);
                unlock_page(page);
                if (!pagevec_add(plru_pvec, page))
-                       __pagevec_lru_add(plru_pvec);
+                       __pagevec_lru_add_file(plru_pvec);
                data += PAGE_CACHE_SIZE;
        }
        return;
@@ -1731,8 +1809,8 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
        struct page *page;
        struct cifs_sb_info *cifs_sb;
        struct cifsTconInfo *pTcon;
-       int bytes_read = 0;
-       unsigned int read_size,i;
+       unsigned int bytes_read = 0;
+       unsigned int read_size, i;
        char *smb_read_data = NULL;
        struct smb_com_read_rsp *pSMBr;
        struct pagevec lru_pvec;
@@ -1749,7 +1827,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
        pTcon = cifs_sb->tcon;
 
        pagevec_init(&lru_pvec, 0);
-
+       cFYI(DBG2, ("rpages: num pages %d", num_pages));
        for (i = 0; i < num_pages; ) {
                unsigned contig_pages;
                struct page *tmp_page;
@@ -1763,14 +1841,14 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
 
                /* count adjacent pages that we will read into */
                contig_pages = 0;
-               expected_index = 
+               expected_index =
                        list_entry(page_list->prev, struct page, lru)->index;
-               list_for_each_entry_reverse(tmp_page,page_list,lru) {
+               list_for_each_entry_reverse(tmp_page, page_list, lru) {
                        if (tmp_page->index == expected_index) {
                                contig_pages++;
                                expected_index++;
                        } else
-                               break; 
+                               break;
                }
                if (contig_pages + i >  num_pages)
                        contig_pages = num_pages - i;
@@ -1782,13 +1860,13 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
                /* Read size needs to be in multiples of one page */
                read_size = min_t(const unsigned int, read_size,
                                  cifs_sb->rsize & PAGE_CACHE_MASK);
-
+               cFYI(DBG2, ("rpages: read size 0x%x  contiguous pages %d",
+                               read_size, contig_pages));
                rc = -EAGAIN;
                while (rc == -EAGAIN) {
-                       if ((open_file->invalidHandle) && 
+                       if ((open_file->invalidHandle) &&
                            (!open_file->closePend)) {
-                               rc = cifs_reopen_file(file->f_path.dentry->d_inode,
-                                       file, TRUE);
+                               rc = cifs_reopen_file(file, true);
                                if (rc != 0)
                                        break;
                        }
@@ -1799,11 +1877,11 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
                                         &bytes_read, &smb_read_data,
                                         &buf_type);
                        /* BB more RC checks ? */
-                       if (rc== -EAGAIN) {
+                       if (rc == -EAGAIN) {
                                if (smb_read_data) {
-                                       if(buf_type == CIFS_SMALL_BUFFER)
+                                       if (buf_type == CIFS_SMALL_BUFFER)
                                                cifs_small_buf_release(smb_read_data);
-                                       else if(buf_type == CIFS_LARGE_BUFFER)
+                                       else if (buf_type == CIFS_LARGE_BUFFER)
                                                cifs_buf_release(smb_read_data);
                                        smb_read_data = NULL;
                                }
@@ -1821,13 +1899,13 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
 
                        i +=  bytes_read >> PAGE_CACHE_SHIFT;
                        cifs_stats_bytes_read(pTcon, bytes_read);
-                       if ((int)(bytes_read & PAGE_CACHE_MASK) != bytes_read) {
+                       if ((bytes_read & PAGE_CACHE_MASK) != bytes_read) {
                                i++; /* account for partial page */
 
-                               /* server copy of file can have smaller size 
+                               /* server copy of file can have smaller size
                                   than client */
-                               /* BB do we need to verify this common case ? 
-                                  this case is ok - if we are at server EOF 
+                               /* BB do we need to verify this common case ?
+                                  this case is ok - if we are at server EOF
                                   we will hit it on next read */
 
                                /* break; */
@@ -1836,30 +1914,30 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
                        cFYI(1, ("No bytes read (%d) at offset %lld . "
                                 "Cleaning remaining pages from readahead list",
                                 bytes_read, offset));
-                       /* BB turn off caching and do new lookup on 
+                       /* BB turn off caching and do new lookup on
                           file size at server? */
                        break;
                }
                if (smb_read_data) {
-                       if(buf_type == CIFS_SMALL_BUFFER)
+                       if (buf_type == CIFS_SMALL_BUFFER)
                                cifs_small_buf_release(smb_read_data);
-                       else if(buf_type == CIFS_LARGE_BUFFER)
+                       else if (buf_type == CIFS_LARGE_BUFFER)
                                cifs_buf_release(smb_read_data);
                        smb_read_data = NULL;
                }
                bytes_read = 0;
        }
 
-       pagevec_lru_add(&lru_pvec);
+       pagevec_lru_add_file(&lru_pvec);
 
 /* need to free smb_read_data buf before exit */
        if (smb_read_data) {
-               if(buf_type == CIFS_SMALL_BUFFER)
+               if (buf_type == CIFS_SMALL_BUFFER)
                        cifs_small_buf_release(smb_read_data);
-               else if(buf_type == CIFS_LARGE_BUFFER)
+               else if (buf_type == CIFS_LARGE_BUFFER)
                        cifs_buf_release(smb_read_data);
                smb_read_data = NULL;
-       } 
+       }
 
        FreeXid(xid);
        return rc;
@@ -1874,26 +1952,26 @@ static int cifs_readpage_worker(struct file *file, struct page *page,
        page_cache_get(page);
        read_data = kmap(page);
        /* for reads over a certain size could initiate async read ahead */
-                                                                                                                           
+
        rc = cifs_read(file, read_data, PAGE_CACHE_SIZE, poffset);
-                                                                                                                           
+
        if (rc < 0)
                goto io_error;
        else
-               cFYI(1, ("Bytes read %d",rc));
-                                                                                                                           
+               cFYI(1, ("Bytes read %d", rc));
+
        file->f_path.dentry->d_inode->i_atime =
                current_fs_time(file->f_path.dentry->d_inode->i_sb);
-                                                                                                                           
+
        if (PAGE_CACHE_SIZE > rc)
                memset(read_data + rc, 0, PAGE_CACHE_SIZE - rc);
 
        flush_dcache_page(page);
        SetPageUptodate(page);
        rc = 0;
-                                                                                                                           
+
 io_error:
-        kunmap(page);
+       kunmap(page);
        page_cache_release(page);
        return rc;
 }
@@ -1911,7 +1989,7 @@ static int cifs_readpage(struct file *file, struct page *page)
                return -EBADF;
        }
 
-       cFYI(1, ("readpage %p at offset %d 0x%x\n", 
+       cFYI(1, ("readpage %p at offset %d 0x%x\n",
                 page, (int)offset, (int)offset));
 
        rc = cifs_readpage_worker(file, page, &offset);
@@ -1922,71 +2000,93 @@ static int cifs_readpage(struct file *file, struct page *page)
        return rc;
 }
 
+static int is_inode_writable(struct cifsInodeInfo *cifs_inode)
+{
+       struct cifsFileInfo *open_file;
+
+       read_lock(&GlobalSMBSeslock);
+       list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
+               if (open_file->closePend)
+                       continue;
+               if (open_file->pfile &&
+                   ((open_file->pfile->f_flags & O_RDWR) ||
+                    (open_file->pfile->f_flags & O_WRONLY))) {
+                       read_unlock(&GlobalSMBSeslock);
+                       return 1;
+               }
+       }
+       read_unlock(&GlobalSMBSeslock);
+       return 0;
+}
+
 /* We do not want to update the file size from server for inodes
    open for write - to avoid races with writepage extending
    the file - in the future we could consider allowing
-   refreshing the inode only on increases in the file size 
+   refreshing the inode only on increases in the file size
    but this is tricky to do without racing with writebehind
    page caching in the current Linux kernel design */
-int is_size_safe_to_change(struct cifsInodeInfo *cifsInode)
+bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file)
 {
-       struct cifsFileInfo *open_file = NULL;
+       if (!cifsInode)
+               return true;
 
-       if (cifsInode)
-               open_file =  find_writable_file(cifsInode);
-       if(open_file) {
+       if (is_inode_writable(cifsInode)) {
+               /* This inode is open for write at least once */
                struct cifs_sb_info *cifs_sb;
 
-               /* there is not actually a write pending so let
-               this handle go free and allow it to
-               be closable if needed */
-               atomic_dec(&open_file->wrtPending);
-
                cifs_sb = CIFS_SB(cifsInode->vfs_inode.i_sb);
-               if ( cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO ) {
-                       /* since no page cache to corrupt on directio 
+               if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) {
+                       /* since no page cache to corrupt on directio
                        we can change size safely */
-                       return 1;
+                       return true;
                }
 
-               return 0;
+               if (i_size_read(&cifsInode->vfs_inode) < end_of_file)
+                       return true;
+
+               return false;
        } else
-               return 1;
+               return true;
 }
 
-static int cifs_prepare_write(struct file *file, struct page *page,
-       unsigned from, unsigned to)
+static int cifs_write_begin(struct file *file, struct address_space *mapping,
+                       loff_t pos, unsigned len, unsigned flags,
+                       struct page **pagep, void **fsdata)
 {
-       int rc = 0;
-        loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT;
-       cFYI(1, ("prepare write for page %p from %d to %d",page,from,to));
-       if (!PageUptodate(page)) {
-       /*      if (to - from != PAGE_CACHE_SIZE) {
-                       void *kaddr = kmap_atomic(page, KM_USER0);
-                       memset(kaddr, 0, from);
-                       memset(kaddr + to, 0, PAGE_CACHE_SIZE - to);
-                       flush_dcache_page(page);
-                       kunmap_atomic(kaddr, KM_USER0);
-               } */
-               /* If we are writing a full page it will be up to date,
-                  no need to read from the server */
-               if ((to == PAGE_CACHE_SIZE) && (from == 0))
-                       SetPageUptodate(page);
+       pgoff_t index = pos >> PAGE_CACHE_SHIFT;
+       loff_t offset = pos & (PAGE_CACHE_SIZE - 1);
+
+       cFYI(1, ("write_begin from %lld len %d", (long long)pos, len));
+
+       *pagep = __grab_cache_page(mapping, index);
+       if (!*pagep)
+               return -ENOMEM;
+
+       if (PageUptodate(*pagep))
+               return 0;
+
+       /* If we are writing a full page it will be up to date,
+          no need to read from the server */
+       if (len == PAGE_CACHE_SIZE && flags & AOP_FLAG_UNINTERRUPTIBLE)
+               return 0;
+
+       if ((file->f_flags & O_ACCMODE) != O_WRONLY) {
+               int rc;
 
                /* might as well read a page, it is fast enough */
-               if ((file->f_flags & O_ACCMODE) != O_WRONLY) {
-                       rc = cifs_readpage_worker(file, page, &offset);
-               } else {
-               /* should we try using another file handle if there is one -
-                  how would we lock it to prevent close of that handle
-                  racing with this read?
-                  In any case this will be written out by commit_write */
-               }
+               rc = cifs_readpage_worker(file, *pagep, &offset);
+
+               /* we do not need to pass errors back
+                  e.g. if we do not have read access to the file
+                  because cifs_write_end will attempt synchronous writes
+                  -- shaggy */
+       } else {
+               /* we could try using another file handle if there is one -
+                  but how would we lock it to prevent close of that handle
+                  racing with this read? In any case
+                  this will be written out by write_end so is fine */
        }
 
-       /* BB should we pass any errors back? 
-          e.g. if we do not have read access to the file */
        return 0;
 }
 
@@ -1995,8 +2095,8 @@ const struct address_space_operations cifs_addr_ops = {
        .readpages = cifs_readpages,
        .writepage = cifs_writepage,
        .writepages = cifs_writepages,
-       .prepare_write = cifs_prepare_write,
-       .commit_write = cifs_commit_write,
+       .write_begin = cifs_write_begin,
+       .write_end = cifs_write_end,
        .set_page_dirty = __set_page_dirty_nobuffers,
        /* .sync_page = cifs_sync_page, */
        /* .direct_IO = */
@@ -2011,8 +2111,8 @@ const struct address_space_operations cifs_addr_ops_smallbuf = {
        .readpage = cifs_readpage,
        .writepage = cifs_writepage,
        .writepages = cifs_writepages,
-       .prepare_write = cifs_prepare_write,
-       .commit_write = cifs_commit_write,
+       .write_begin = cifs_write_begin,
+       .write_end = cifs_write_end,
        .set_page_dirty = __set_page_dirty_nobuffers,
        /* .sync_page = cifs_sync_page, */
        /* .direct_IO = */