X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fcifs%2Ffile.c;h=73505c6ccf3cf2b063392651de68e1f2f604fa95;hb=9bf4aaab3e101692164d49b7ca357651eb691cb6;hp=8068c61d9a9ee049325ca672ad3af9ae9866e750;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 8068c61d9..73505c6cc 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -80,7 +80,13 @@ cifs_open(struct inode *inode, struct file *file) } } + down(&inode->i_sb->s_vfs_rename_sem); full_path = build_path_from_dentry(file->f_dentry); + up(&inode->i_sb->s_vfs_rename_sem); + if(full_path == NULL) { + FreeXid(xid); + return -ENOMEM; + } cFYI(1, (" inode = 0x%p file flags are 0x%x for %s", inode, file->f_flags,full_path)); if ((file->f_flags & O_ACCMODE) == O_RDONLY) @@ -137,6 +143,10 @@ cifs_open(struct inode *inode, struct file *file) /* Also refresh inode by passing in file_info buf returned by SMBOpen 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 + able to simply do a filemap_fdatawrite/filemap_fdatawait first */ buf = kmalloc(sizeof(FILE_ALL_INFO),GFP_KERNEL); if(buf==0) { if (full_path) @@ -150,8 +160,6 @@ cifs_open(struct inode *inode, struct file *file) cFYI(1, ("cifs_open returned 0x%x ", rc)); cFYI(1, ("oplock: %d ", oplock)); } else { - if(file->private_data) - kfree(file->private_data); file->private_data = kmalloc(sizeof (struct cifsFileInfo), GFP_KERNEL); if (file->private_data) { @@ -169,7 +177,14 @@ cifs_open(struct inode *inode, struct file *file) list_add(&pCifsFile->tlist,&pTcon->openFileList); pCifsInode = CIFS_I(file->f_dentry->d_inode); if(pCifsInode) { - list_add(&pCifsFile->flist,&pCifsInode->openFileList); + /* 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 */ + if ((file->f_flags & O_ACCMODE) == O_WRONLY) { + list_add_tail(&pCifsFile->flist,&pCifsInode->openFileList); + } else { + list_add(&pCifsFile->flist,&pCifsInode->openFileList); + } write_unlock(&GlobalSMBSeslock); write_unlock(&file->f_owner.lock); if(pCifsInode->clientCanCacheRead) { @@ -200,10 +215,10 @@ cifs_open(struct inode *inode, struct file *file) } if (pTcon->ses->capabilities & CAP_UNIX) rc = cifs_get_inode_info_unix(&file->f_dentry->d_inode, - full_path, inode->i_sb); + full_path, inode->i_sb,xid); else rc = cifs_get_inode_info(&file->f_dentry->d_inode, - full_path, buf, inode->i_sb); + full_path, buf, inode->i_sb,xid); if((oplock & 0xF) == OPLOCK_EXCLUSIVE) { pCifsInode->clientCanCacheAll = TRUE; @@ -252,7 +267,7 @@ static int cifs_relock_file(struct cifsFileInfo * cifsFile) return rc; } -static int cifs_reopen_file(struct inode *inode, struct file *file) +static int cifs_reopen_file(struct inode *inode, struct file *file, int can_flush) { int rc = -EACCES; int xid, oplock; @@ -264,7 +279,6 @@ static int cifs_reopen_file(struct inode *inode, struct file *file) int desiredAccess = 0x20197; int disposition = FILE_OPEN; __u16 netfid; - FILE_ALL_INFO * buf = NULL; if(inode == NULL) return -EBADF; @@ -281,11 +295,24 @@ static int cifs_reopen_file(struct inode *inode, struct file *file) return 0; } - + if(file->f_dentry == NULL) { + up(&pCifsFile->fh_sem); + cFYI(1,("failed file reopen, no valid name if dentry freed")); + FreeXid(xid); + return -EBADF; + } 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_dentry); + if(full_path == NULL) { + up(&pCifsFile->fh_sem); + FreeXid(xid); + return -ENOMEM; + } cFYI(1, (" inode = 0x%p file flags are 0x%x for %s", inode, file->f_flags,full_path)); if ((file->f_flags & O_ACCMODE) == O_RDONLY) @@ -304,21 +331,23 @@ static int cifs_reopen_file(struct inode *inode, struct file *file) else oplock = FALSE; - /* BB pass O_SYNC flag through on file attributes .. BB */ + + /* 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 + and server version of file size can be stale. If we + knew for sure that inode was not dirty locally we could do this */ - /* Also refresh inode by passing in file_info buf returned by SMBOpen - and calling get_inode_info with returned buf (at least - helps non-Unix server case */ - buf = kmalloc(sizeof(FILE_ALL_INFO),GFP_KERNEL); +/* buf = kmalloc(sizeof(FILE_ALL_INFO),GFP_KERNEL); if(buf==0) { up(&pCifsFile->fh_sem); if (full_path) kfree(full_path); FreeXid(xid); return -ENOMEM; - } + }*/ rc = CIFSSMBOpen(xid, pTcon, full_path, disposition, desiredAccess, - CREATE_NOT_DIR, &netfid, &oplock, buf, cifs_sb->local_nls); + CREATE_NOT_DIR, &netfid, &oplock, NULL, cifs_sb->local_nls); if (rc) { up(&pCifsFile->fh_sem); cFYI(1, ("cifs_open returned 0x%x ", rc)); @@ -329,13 +358,25 @@ static int cifs_reopen_file(struct inode *inode, struct file *file) up(&pCifsFile->fh_sem); pCifsInode = CIFS_I(inode); if(pCifsInode) { - if (pTcon->ses->capabilities & CAP_UNIX) - rc = cifs_get_inode_info_unix(&inode, - full_path, inode->i_sb); - else - rc = cifs_get_inode_info(&inode, - full_path, buf, inode->i_sb); - + if(can_flush) { + filemap_fdatawrite(inode->i_mapping); + filemap_fdatawait(inode->i_mapping); + /* temporarily disable caching while we + go to server to get inode info */ + pCifsInode->clientCanCacheAll = FALSE; + pCifsInode->clientCanCacheRead = FALSE; + if (pTcon->ses->capabilities & CAP_UNIX) + 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); + } /* 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 + invalidate the current end of file on the server + we can not go to the server to get the new + inod info */ if((oplock & 0xF) == OPLOCK_EXCLUSIVE) { pCifsInode->clientCanCacheAll = TRUE; pCifsInode->clientCanCacheRead = TRUE; @@ -351,8 +392,6 @@ static int cifs_reopen_file(struct inode *inode, struct file *file) } } - if (buf) - kfree(buf); if (full_path) kfree(full_path); FreeXid(xid); @@ -546,9 +585,13 @@ cifs_write(struct file * file, const char *write_data, int xid, long_op; struct cifsFileInfo * open_file; - xid = GetXid(); + if(file->f_dentry == NULL) + return -EBADF; cifs_sb = CIFS_SB(file->f_dentry->d_sb); + if(cifs_sb == NULL) { + return -EBADF; + } pTcon = cifs_sb->tcon; /*cFYI(1, @@ -556,11 +599,12 @@ cifs_write(struct file * file, const char *write_data, *poffset, file->f_dentry->d_name.name)); */ if (file->private_data == NULL) { - FreeXid(xid); return -EBADF; + } else { + open_file = (struct cifsFileInfo *) file->private_data; } - open_file = (struct cifsFileInfo *) file->private_data; - + + xid = GetXid(); if(file->f_dentry->d_inode == NULL) { FreeXid(xid); return -EBADF; @@ -578,14 +622,29 @@ cifs_write(struct file * file, const char *write_data, if(file->private_data == NULL) { /* file has been closed on us */ 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 write */ return total_written; + } + if(open_file->closePend) { + FreeXid(xid); + if(total_written) + return total_written; + else + return -EBADF; } - if ((open_file->invalidHandle) && (!open_file->closePend)) { - if((file->f_dentry == NULL) || (file->f_dentry->d_inode == NULL)) { + if (open_file->invalidHandle) { + if((file->f_dentry == NULL) || + (file->f_dentry->d_inode == NULL)) { FreeXid(xid); return total_written; } - rc = cifs_reopen_file(file->f_dentry->d_inode,file); + /* 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_dentry->d_inode, + file,FALSE); if(rc != 0) break; } @@ -608,12 +667,21 @@ cifs_write(struct file * file, const char *write_data, long_op = FALSE; /* subsequent writes fast - 15 seconds is plenty */ } +#ifdef CONFIG_CIFS_STATS + if(total_written > 0) { + atomic_inc(&pTcon->num_writes); + spin_lock(&pTcon->stat_lock); + pTcon->bytes_written += total_written; + spin_unlock(&pTcon->stat_lock); + } +#endif + /* since the write may have blocked check these pointers again */ if(file->f_dentry) { if(file->f_dentry->d_inode) { file->f_dentry->d_inode->i_ctime = file->f_dentry->d_inode->i_mtime = CURRENT_TIME; - if (bytes_written > 0) { + if (total_written > 0) { if (*poffset > file->f_dentry->d_inode->i_size) i_size_write(file->f_dentry->d_inode, *poffset); } @@ -634,26 +702,22 @@ cifs_partialpagewrite(struct page *page,unsigned from, unsigned to) int bytes_written = 0; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; - struct inode *inode = page->mapping->host; + struct inode *inode; struct cifsInodeInfo *cifsInode; struct cifsFileInfo *open_file = NULL; struct list_head *tmp; struct list_head *tmp1; - cifs_sb = CIFS_SB(inode->i_sb); - pTcon = cifs_sb->tcon; - - /* figure out which file struct to use - if (file->private_data == NULL) { - return -EBADF; - } - */ if (!mapping) { return -EFAULT; } else if(!mapping->host) { return -EFAULT; } + inode = page->mapping->host; + cifs_sb = CIFS_SB(inode->i_sb); + pTcon = cifs_sb->tcon; + offset += (loff_t)from; write_data = kmap(page); write_data += from; @@ -676,8 +740,11 @@ cifs_partialpagewrite(struct page *page,unsigned from, unsigned to) cifsInode = CIFS_I(mapping->host); read_lock(&GlobalSMBSeslock); + /* BB we should start at the end */ list_for_each_safe(tmp, tmp1, &cifsInode->openFileList) { open_file = list_entry(tmp,struct cifsFileInfo, flist); + if(open_file->closePend) + continue; /* We check if file is open for writing first */ if((open_file->pfile) && ((open_file->pfile->f_flags & O_RDWR) || @@ -691,7 +758,15 @@ cifs_partialpagewrite(struct page *page,unsigned from, unsigned to) if ((bytes_written > 0) && (offset)) { rc = 0; } else if(bytes_written < 0) { - rc = bytes_written; + if(rc == -EBADF) { + /* have seen a case in which + kernel seemed to have closed/freed a file + even with writes active so we might as well + see if there are other file structs to try + for the same inode before giving up */ + continue; + } else + rc = bytes_written; } break; /* now that we found a valid file handle and tried to write to it we are done, no @@ -735,6 +810,9 @@ cifs_writepage(struct page* page, struct writeback_control *wbc) xid = GetXid(); /* BB add check for wbc flags */ page_cache_get(page); + if (!PageUptodate(page)) { + cFYI(1,("ppw - page not up to date")); + } rc = cifs_partialpagewrite(page,0,PAGE_CACHE_SIZE); SetPageUptodate(page); /* BB add check for error and Clearuptodate? */ @@ -752,8 +830,7 @@ cifs_commit_write(struct file *file, struct page *page, unsigned offset, int rc = 0; struct inode *inode = page->mapping->host; loff_t position = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to; - /* struct cifsFileInfo *open_file; - struct cifs_sb_info *cifs_sb; */ + char * page_data; xid = GetXid(); cFYI(1,("commit write for page %p up to position %lld for %d",page,position,to)); @@ -784,7 +861,31 @@ cifs_commit_write(struct file *file, struct page *page, unsigned offset, cFYI(1,(" SetEOF (commit write) rc = %d",rc)); }*/ } - set_page_dirty(page); + 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; + } + /* 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? */ + kunmap(page); + } else { + set_page_dirty(page); + } FreeXid(xid); return rc; @@ -889,13 +990,18 @@ cifs_read(struct file * file, char *read_data, size_t read_size, } open_file = (struct cifsFileInfo *)file->private_data; + 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,cifs_sb->rsize); rc = -EAGAIN; while(rc == -EAGAIN) { if ((open_file->invalidHandle) && (!open_file->closePend)) { - rc = cifs_reopen_file(file->f_dentry->d_inode,file); + rc = cifs_reopen_file(file->f_dentry->d_inode, + file,TRUE); if(rc != 0) break; } @@ -913,10 +1019,15 @@ cifs_read(struct file * file, char *read_data, size_t read_size, return rc; } } else { +#ifdef CONFIG_CIFS_STATS + atomic_inc(&pTcon->num_reads); + spin_lock(&pTcon->stat_lock); + pTcon->bytes_read += total_read; + spin_unlock(&pTcon->stat_lock); +#endif *poffset += bytes_read; } } - FreeXid(xid); return total_read; } @@ -994,7 +1105,7 @@ cifs_readpages(struct file *file, struct address_space *mapping, struct cifsTconInfo *pTcon; int bytes_read = 0; unsigned int read_size,i; - char * smb_read_data = 0; + char * smb_read_data = NULL; struct smb_com_read_rsp * pSMBr; struct pagevec lru_pvec; struct cifsFileInfo * open_file; @@ -1045,7 +1156,8 @@ cifs_readpages(struct file *file, struct address_space *mapping, rc = -EAGAIN; while(rc == -EAGAIN) { if ((open_file->invalidHandle) && (!open_file->closePend)) { - rc = cifs_reopen_file(file->f_dentry->d_inode,file); + rc = cifs_reopen_file(file->f_dentry->d_inode, + file, TRUE); if(rc != 0) break; } @@ -1058,7 +1170,7 @@ cifs_readpages(struct file *file, struct address_space *mapping, if(rc== -EAGAIN) { if(smb_read_data) { cifs_buf_release(smb_read_data); - smb_read_data = 0; + smb_read_data = NULL; } } } @@ -1078,7 +1190,12 @@ cifs_readpages(struct file *file, struct address_space *mapping, le16_to_cpu(pSMBr->DataOffset), &lru_pvec); i += bytes_read >> PAGE_CACHE_SHIFT; - +#ifdef CONFIG_CIFS_STATS + atomic_inc(&pTcon->num_reads); + spin_lock(&pTcon->stat_lock); + pTcon->bytes_read += bytes_read; + spin_unlock(&pTcon->stat_lock); +#endif if((int)(bytes_read & PAGE_CACHE_MASK) != bytes_read) { cFYI(1,("Partial page %d of %d read to cache",i++,num_pages)); @@ -1107,7 +1224,7 @@ cifs_readpages(struct file *file, struct address_space *mapping, } if(smb_read_data) { cifs_buf_release(smb_read_data); - smb_read_data = 0; + smb_read_data = NULL; } bytes_read = 0; } @@ -1117,60 +1234,112 @@ cifs_readpages(struct file *file, struct address_space *mapping, /* need to free smb_read_data buf before exit */ if(smb_read_data) { cifs_buf_release(smb_read_data); - smb_read_data = 0; + smb_read_data = NULL; } FreeXid(xid); return rc; } -static int -cifs_readpage(struct file *file, struct page *page) +static int cifs_readpage_worker(struct file *file, struct page *page, loff_t * poffset) { - loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT; char * read_data; - int rc = -EACCES; - int xid; - - xid = GetXid(); - - if (file->private_data == NULL) { - FreeXid(xid); - return -EBADF; - } - - cFYI(0,("readpage %p at offset %d 0x%x\n",page,(int)offset,(int)offset)); + int rc; 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, &offset); - + + rc = cifs_read(file, read_data, PAGE_CACHE_SIZE, poffset); + if (rc < 0) goto io_error; else { cFYI(1,("Bytes read %d ",rc)); } - + file->f_dentry->d_inode->i_atime = CURRENT_TIME; - + 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; +} + +static int +cifs_readpage(struct file *file, struct page *page) +{ + loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT; + int rc = -EACCES; + int xid; + + xid = GetXid(); + + if (file->private_data == NULL) { + FreeXid(xid); + return -EBADF; + } + + cFYI(1,("readpage %p at offset %d 0x%x\n",page,(int)offset,(int)offset)); + + rc = cifs_readpage_worker(file,page,&offset); + unlock_page(page); - page_cache_release(page); FreeXid(xid); return rc; } +/* 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 + 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) +{ + struct list_head *tmp; + struct list_head *tmp1; + struct cifsFileInfo *open_file = NULL; + int rc = TRUE; + + if(cifsInode == NULL) + return rc; + + read_lock(&GlobalSMBSeslock); + list_for_each_safe(tmp, tmp1, &cifsInode->openFileList) { + open_file = list_entry(tmp,struct cifsFileInfo, flist); + if(open_file == NULL) + break; + if(open_file->closePend) + continue; + /* We check if file is open for writing, + BB we could supplement this with a check to see if file size + changes have been flushed to server - ie inode metadata dirty */ + if((open_file->pfile) && + ((open_file->pfile->f_flags & O_RDWR) || + (open_file->pfile->f_flags & O_WRONLY))) { + rc = FALSE; + break; + } + if(tmp->next == NULL) { + cFYI(1,("File instance %p removed",tmp)); + break; + } + } + read_unlock(&GlobalSMBSeslock); + return rc; +} + + void fill_in_inode(struct inode *tmp_inode, FILE_DIRECTORY_INFO * pfindData, int *pobject_type) @@ -1230,9 +1399,16 @@ fill_in_inode(struct inode *tmp_inode, atomic_set(&cifsInfo->inUse,1); } - i_size_write(tmp_inode,pfindData->EndOfFile); - tmp_inode->i_blocks = - (tmp_inode->i_blksize - 1 + pfindData->AllocationSize) >> tmp_inode->i_blkbits; + if(is_size_safe_to_change(cifsInfo)) { + /* can not safely change the file size here if the + client is writing to it due to potential races */ + i_size_write(tmp_inode,pfindData->EndOfFile); + + /* 512 bytes (2**9) is the fake blocksize that must be used */ + /* for this calculation, even though the reported blocksize is larger */ + tmp_inode->i_blocks = (512 - 1 + pfindData->AllocationSize) >> 9; + } + if (pfindData->AllocationSize < pfindData->EndOfFile) cFYI(1, ("Possible sparse file: allocation size less than end of file ")); cFYI(1, @@ -1287,9 +1463,13 @@ unix_fill_in_inode(struct inode *tmp_inode, } else if (pfindData->Type == UNIX_CHARDEV) { *pobject_type = DT_CHR; tmp_inode->i_mode |= S_IFCHR; + tmp_inode->i_rdev = MKDEV(le64_to_cpu(pfindData->DevMajor), + le64_to_cpu(pfindData->DevMinor) & MINORMASK); } else if (pfindData->Type == UNIX_BLOCKDEV) { *pobject_type = DT_BLK; tmp_inode->i_mode |= S_IFBLK; + tmp_inode->i_rdev = MKDEV(le64_to_cpu(pfindData->DevMajor), + le64_to_cpu(pfindData->DevMinor) & MINORMASK); } else if (pfindData->Type == UNIX_FIFO) { *pobject_type = DT_FIFO; tmp_inode->i_mode |= S_IFIFO; @@ -1303,10 +1483,17 @@ unix_fill_in_inode(struct inode *tmp_inode, tmp_inode->i_nlink = le64_to_cpu(pfindData->Nlinks); pfindData->NumOfBytes = le64_to_cpu(pfindData->NumOfBytes); - pfindData->EndOfFile = le64_to_cpu(pfindData->EndOfFile); - i_size_write(tmp_inode,pfindData->EndOfFile); - tmp_inode->i_blocks = - (tmp_inode->i_blksize - 1 + pfindData->NumOfBytes) >> tmp_inode->i_blkbits; + + if(is_size_safe_to_change(cifsInfo)) { + /* can not safely change the file size here if the + client is writing to it due to potential races */ + pfindData->EndOfFile = le64_to_cpu(pfindData->EndOfFile); + i_size_write(tmp_inode,pfindData->EndOfFile); + + /* 512 bytes (2**9) is the fake blocksize that must be used */ + /* for this calculation, not the real blocksize */ + tmp_inode->i_blocks = (512 - 1 + pfindData->NumOfBytes) >> 9; + } if (S_ISREG(tmp_inode->i_mode)) { cFYI(1, ("File inode")); @@ -1348,21 +1535,26 @@ construct_dentry(struct qstr *qstring, struct file *file, /* BB overwrite the old name? i.e. tmp_dentry->d_name and tmp_dentry->d_name.len ?? */ if(*ptmp_inode == NULL) { *ptmp_inode = new_inode(file->f_dentry->d_sb); + if(*ptmp_inode == NULL) + return; d_instantiate(tmp_dentry, *ptmp_inode); + insert_inode_hash(*ptmp_inode); } } else { tmp_dentry = d_alloc(file->f_dentry, qstring); if(tmp_dentry == NULL) { cERROR(1,("Failed allocating dentry")); + *ptmp_inode = NULL; return; } *ptmp_inode = new_inode(file->f_dentry->d_sb); tmp_dentry->d_op = &cifs_dentry_ops; - cFYI(0, (" instantiate dentry 0x%p with inode 0x%p ", - tmp_dentry, *ptmp_inode)); + if(*ptmp_inode == NULL) + return; d_instantiate(tmp_dentry, *ptmp_inode); d_rehash(tmp_dentry); + insert_inode_hash(*ptmp_inode); } tmp_dentry->d_time = jiffies; @@ -1417,7 +1609,9 @@ cifs_filldir(struct qstr *pqstring, FILE_DIRECTORY_INFO * pfindData, pqstring->len = pfindData->FileNameLength; construct_dentry(pqstring, file, &tmp_inode, &tmp_dentry); - + if((tmp_inode == NULL) || (tmp_dentry == NULL)) { + return -ENOMEM; + } fill_in_inode(tmp_inode, pfindData, &object_type); rc = filldir(direntry, pfindData->FileName, pqstring->len, file->f_pos, tmp_inode->i_ino, object_type); @@ -1443,6 +1637,9 @@ cifs_filldir_unix(struct qstr *pqstring, pqstring->len = strnlen(pUnixFindData->FileName, MAX_PATHCONF); construct_dentry(pqstring, file, &tmp_inode, &tmp_dentry); + if((tmp_inode == NULL) || (tmp_dentry == NULL)) { + return -ENOMEM; + } unix_fill_in_inode(tmp_inode, pUnixFindData, &object_type); rc = filldir(direntry, pUnixFindData->FileName, pqstring->len, @@ -1489,7 +1686,14 @@ cifs_readdir(struct file *file, void *direntry, filldir_t filldir) data = kmalloc(bufsize, GFP_KERNEL); pfindData = (FILE_DIRECTORY_INFO *) data; + if(file->f_dentry == NULL) { + FreeXid(xid); + return -EIO; + } + down(&file->f_dentry->d_sb->s_vfs_rename_sem); full_path = build_wildcard_path_from_dentry(file->f_dentry); + up(&file->f_dentry->d_sb->s_vfs_rename_sem); + cFYI(1, ("Full path: %s start at: %lld ", full_path, file->f_pos)); @@ -1793,10 +1997,10 @@ cifs_readdir(struct file *file, void *direntry, filldir_t filldir) cifsFile->search_resume_name = kmalloc(cifsFile->resume_name_length, GFP_KERNEL); cFYI(1,("fnext last file: %s with name %d bytes long", - lastFindData->FileName, + pfindDataUnix->FileName, cifsFile->resume_name_length)); memcpy(cifsFile->search_resume_name, - lastFindData->FileName, + pfindDataUnix->FileName, cifsFile->resume_name_length); } @@ -1905,17 +2109,34 @@ cifs_readdir(struct file *file, void *direntry, filldir_t filldir) int cifs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) { + 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) { + /* 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); + + /* 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 */ } - SetPageUptodate(page); } + + /* BB should we pass any errors back? e.g. if we do not have read access to the file */ return 0; } @@ -1924,9 +2145,9 @@ struct address_space_operations cifs_addr_ops = { .readpage = cifs_readpage, .readpages = cifs_readpages, .writepage = cifs_writepage, - .prepare_write = simple_prepare_write, /* BB fixme BB */ -/* .prepare_write = cifs_prepare_write, */ /* BB removeme BB */ + .prepare_write = cifs_prepare_write, .commit_write = cifs_commit_write, + .set_page_dirty = __set_page_dirty_nobuffers, /* .sync_page = cifs_sync_page, */ /*.direct_IO = */ };