]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - fs/cifs/file.c
[CIFS] Respect umask when using POSIX mkdir
[linux-2.6-omap-h63xx.git] / fs / cifs / file.c
index e13592afca9c830565c420295cbc186e9f834fee..79254919386539d5a4883f44b96a1581dcebbec0 100644 (file)
@@ -467,7 +467,7 @@ reopen_error_exit:
 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 =
@@ -485,9 +485,9 @@ int cifs_close(struct inode *inode, struct file *file)
                        /* no sense reconnecting to close a file that is
                           already closed */
                        if (pTcon->tidStatus != CifsNeedReconnect) {
-                               int timeout = 2;
+                               timeout = 2;
                                while ((atomic_read(&pSMBFile->wrtPending) != 0)
-                                        && (timeout < 1000) ) {
+                                       && (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
@@ -522,12 +522,30 @@ int cifs_close(struct inode *inode, struct file *file)
                list_del(&pSMBFile->flist);
                list_del(&pSMBFile->tlist);
                write_unlock(&GlobalSMBSeslock);
+               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(pSMBFile->search_resume_name);
                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
@@ -535,6 +553,7 @@ int cifs_close(struct inode *inode, struct file *file)
                CIFS_I(inode)->clientCanCacheRead = FALSE;
                CIFS_I(inode)->clientCanCacheAll  = FALSE;
        }
+       read_unlock(&GlobalSMBSeslock);
        if ((rc == 0) && CIFS_I(inode)->write_behind_rc)
                rc = CIFS_I(inode)->write_behind_rc;
        FreeXid(xid);
@@ -767,7 +786,8 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
                        mutex_lock(&fid->lock_mutex);
                        list_for_each_entry_safe(li, tmp, &fid->llist, llist) {
                                if (pfLock->fl_start <= li->offset &&
-                                               length >= li->length) {
+                                               (pfLock->fl_start + length) >=
+                                               (li->offset + li->length)) {
                                        stored_rc = CIFSSMBLock(xid, pTcon,
                                                        netfid,
                                                        li->length, li->offset,
@@ -1030,22 +1050,24 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode)
                     (open_file->pfile->f_flags & O_WRONLY))) {
                        atomic_inc(&open_file->wrtPending);
                        read_unlock(&GlobalSMBSeslock);
-                       if ((open_file->invalidHandle) &&
-                          (!open_file->closePend) /* BB fixme -since the second clause can not be true remove it BB */) {
+                       if (open_file->invalidHandle) {
                                rc = cifs_reopen_file(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"));
+                                       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);
+                                       atomic_dec(&open_file->wrtPending);
                                        continue;
                                }
                        }
+                       if (open_file->closePend) {
+                               read_lock(&GlobalSMBSeslock);
+                               atomic_dec(&open_file->wrtPending);
+                               continue;
+                       }
                        return open_file;
                }
        }
@@ -1904,6 +1926,25 @@ 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
@@ -1912,19 +1953,13 @@ static int cifs_readpage(struct file *file, struct page *page)
    page caching in the current Linux kernel design */
 int is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file)
 {
-       struct cifsFileInfo *open_file = NULL;
-
-       if (cifsInode)
-               open_file =  find_writable_file(cifsInode);
+       if (!cifsInode)
+               return 1;
 
-       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