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 31a0a33..6449e1a 100644 (file)
@@ -75,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)
@@ -103,7 +107,7 @@ 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,
                              &pCifsInode->openFileList);
@@ -306,18 +310,19 @@ int cifs_open(struct inode *inode, struct file *file)
                /* time to set mode which we can not set earlier due to
                   problems creating new read-only files */
                if (pTcon->unix_ext) {
-                       CIFSSMBUnixSetPerms(xid, pTcon, full_path,
-                                           inode->i_mode,
-                                           (__u64)-1, (__u64)-1, 0 /* dev */,
+                       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_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 */
                }
        }
 
@@ -488,7 +493,7 @@ int cifs_close(struct inode *inode, struct file *file)
                if (pTcon) {
                        /* no sense reconnecting to close a file that is
                           already closed */
-                       if (pTcon->tidStatus != CifsNeedReconnect) {
+                       if (!pTcon->need_reconnect) {
                                timeout = 2;
                                while ((atomic_read(&pSMBFile->wrtPending) != 0)
                                        && (timeout <= 2048)) {
@@ -542,7 +547,6 @@ int cifs_close(struct inode *inode, struct file *file)
                        msleep(timeout);
                        timeout *= 8;
                }
-               kfree(pSMBFile->search_resume_name);
                kfree(file->private_data);
                file->private_data = NULL;
        } else
@@ -601,12 +605,6 @@ int cifs_closedir(struct inode *inode, struct file *file)
                        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;
        }
@@ -835,6 +833,10 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,
                return -EBADF;
        open_file = (struct cifsFileInfo *) file->private_data;
 
+       rc = generic_write_checks(file, poffset, &write_size, 0);
+       if (rc)
+               return rc;
+
        xid = GetXid();
 
        if (*poffset > file->f_path.dentry->d_inode->i_size)
@@ -913,7 +915,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,
 }
 
 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;
@@ -1063,6 +1065,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode)
 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
@@ -1078,8 +1081,10 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode)
        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))) {
@@ -1129,6 +1134,11 @@ refind_writable:
                           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;
 }
@@ -1283,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)) {
@@ -1394,7 +1404,10 @@ retry:
                        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) {
@@ -1445,49 +1458,52 @@ static int cifs_writepage(struct page *page, struct writeback_control *wbc)
        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;
 
-       xid = GetXid();
-       cFYI(1, ("commit write for page %p up to position %lld for %d",
-                page, position, to));
-       spin_lock(&inode->i_lock);
-       if (position > inode->i_size)
-               i_size_write(inode, position);
+       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);
 
-       spin_unlock(&inode->i_lock);
        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);
+
+               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;
 }
 
@@ -1778,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;
@@ -1811,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));
+       cFYI(DBG2, ("rpages: num pages %d", num_pages));
        for (i = 0; i < num_pages; ) {
                unsigned contig_pages;
                struct page *tmp_page;
@@ -1912,7 +1928,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
                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) {
@@ -2033,49 +2049,44 @@ bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file)
                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 i_size;
-       loff_t offset;
+       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;
 
-       cFYI(1, ("prepare write for page %p from %d to %d", page, from, to));
-       if (PageUptodate(page))
+       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 ((to == PAGE_CACHE_SIZE) && (from == 0)) {
-               SetPageUptodate(page);
+       if (len == PAGE_CACHE_SIZE && flags & AOP_FLAG_UNINTERRUPTIBLE)
                return 0;
-       }
 
-       offset = (loff_t)page->index << PAGE_CACHE_SHIFT;
-       i_size = i_size_read(page->mapping->host);
+       if ((file->f_flags & O_ACCMODE) != O_WRONLY) {
+               int rc;
 
-       if ((offset >= i_size) ||
-           ((from == 0) && (offset + to) >= i_size)) {
-               /*
-                * We don't need to read data beyond the end of the file.
-                * zero it, and set the page uptodate
-                */
-               simple_prepare_write(file, page, from, to);
-               SetPageUptodate(page);
-       } else if ((file->f_flags & O_ACCMODE) != O_WRONLY) {
                /* might as well read a page, it is fast enough */
-               rc = cifs_readpage_worker(file, page, &offset);
+               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 commit_write so is fine */
+                  this will be written out by write_end so is fine */
        }
 
-       /* we do not need to pass errors back
-          e.g. if we do not have read access to the file
-          because cifs_commit_write will do the right thing.  -- shaggy */
-
        return 0;
 }
 
@@ -2084,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 = */
@@ -2100,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 = */