X-Git-Url: http://git.onelab.eu/?p=linux-2.6.git;a=blobdiff_plain;f=fs%2Fcifs%2Finode.c;h=c12231b486351418024a5c5ee02b841468b90605;hp=e48ed8cc87a91abfe4fda32fec5c4f6d33a45ff7;hb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;hpb=c449269f45c2cdf53af08c8d0af37472f66539d9 diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index e48ed8cc8..c12231b48 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -30,6 +30,8 @@ #include "cifs_debug.h" #include "cifs_fs_sb.h" +extern int is_size_safe_to_change(struct cifsInodeInfo *); + int cifs_get_inode_info_unix(struct inode **pinode, const unsigned char *search_path, @@ -43,9 +45,6 @@ cifs_get_inode_info_unix(struct inode **pinode, struct cifs_sb_info *cifs_sb = CIFS_SB(sb); char *tmp_path; -/* BB add caching check so we do not go to server to overwrite inode info to cached file - where the local file sizes are correct and the server info is stale BB */ - xid = GetXid(); pTcon = cifs_sb->tcon; @@ -125,13 +124,29 @@ cifs_get_inode_info_unix(struct inode **pinode, inode->i_nlink = le64_to_cpu(findData.Nlinks); findData.NumOfBytes = le64_to_cpu(findData.NumOfBytes); findData.EndOfFile = le64_to_cpu(findData.EndOfFile); - i_size_write(inode,findData.EndOfFile); + + 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(inode,findData.EndOfFile); /* blksize needs to be multiple of two. So safer to default to blksize and blkbits set in superblock so 2**blkbits and blksize will match */ /* inode->i_blksize = (pTcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFE00;*/ - inode->i_blocks = - (inode->i_blksize - 1 + findData.NumOfBytes) >> inode->i_blkbits; + + /* This seems incredibly stupid but it turns out that + i_blocks is not related to (i_size / i_blksize), instead a + size of 512 is required to be used for calculating num blocks */ + + +/* inode->i_blocks = + (inode->i_blksize - 1 + findData.NumOfBytes) >> inode->i_blkbits;*/ + + /* 512 bytes (2**9) is the fake blocksize that must be used */ + /* for this calculation */ + inode->i_blocks = (512 - 1 + findData.NumOfBytes) >> 9; + } if (findData.NumOfBytes < findData.EndOfFile) cFYI(1, ("Server inconsistency Error: it says allocation size less than end of file ")); @@ -273,10 +288,18 @@ cifs_get_inode_info(struct inode **pinode, const unsigned char *search_path, inode->i_mode &= ~(S_IWUGO); /* BB add code here - validate if device or weird share or device type? */ } - i_size_write(inode,le64_to_cpu(pfindData->EndOfFile)); + 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(inode,le64_to_cpu(pfindData->EndOfFile)); + + /* 512 bytes (2**9) is the fake blocksize that must be used */ + /* for this calculation */ + inode->i_blocks = (512 - 1 + pfindData->AllocationSize) + >> 9; + } pfindData->AllocationSize = le64_to_cpu(pfindData->AllocationSize); - inode->i_blocks = - (inode->i_blksize - 1 + pfindData->AllocationSize) >> inode->i_blkbits; inode->i_nlink = le32_to_cpu(pfindData->NumberOfLinks); @@ -345,8 +368,15 @@ cifs_unlink(struct inode *inode, struct dentry *direntry) cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; +/* Unlink can be called from rename so we can not grab + the sem here since we deadlock otherwise */ +/* down(&direntry->d_sb->s_vfs_rename_sem);*/ full_path = build_path_from_dentry(direntry); - +/* up(&direntry->d_sb->s_vfs_rename_sem);*/ + if(full_path == NULL) { + FreeXid(xid); + return -ENOMEM; + } rc = CIFSSMBDelFile(xid, pTcon, full_path, cifs_sb->local_nls); if (!rc) { @@ -427,7 +457,13 @@ cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; + down(&inode->i_sb->s_vfs_rename_sem); full_path = build_path_from_dentry(direntry); + up(&inode->i_sb->s_vfs_rename_sem); + if(full_path == NULL) { + FreeXid(xid); + return -ENOMEM; + } /* BB add setting the equivalent of mode via CreateX w/ACLs */ rc = CIFSSMBMkDir(xid, pTcon, full_path, cifs_sb->local_nls); if (rc) { @@ -480,7 +516,13 @@ cifs_rmdir(struct inode *inode, struct dentry *direntry) cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; + down(&inode->i_sb->s_vfs_rename_sem); full_path = build_path_from_dentry(direntry); + up(&inode->i_sb->s_vfs_rename_sem); + if(full_path == NULL) { + FreeXid(xid); + return -ENOMEM; + } rc = CIFSSMBRmDir(xid, pTcon, full_path, cifs_sb->local_nls); @@ -525,15 +567,50 @@ cifs_rename(struct inode *source_inode, struct dentry *source_direntry, different share. Might eventually add support for this */ } + /* we already have the rename sem so we do not need + to grab it again here to protect the path integrity */ fromName = build_path_from_dentry(source_direntry); toName = build_path_from_dentry(target_direntry); + if((fromName == NULL) || (toName == NULL)) { + rc = -ENOMEM; + goto cifs_rename_exit; + } rc = CIFSSMBRename(xid, pTcon, fromName, toName, cifs_sb_source->local_nls); if(rc == -EEXIST) { - cifs_unlink(target_inode, target_direntry); - rc = CIFSSMBRename(xid, pTcon, fromName, toName, - cifs_sb_source->local_nls); + /* check if they are the same file + because rename of hardlinked files is a noop */ + FILE_UNIX_BASIC_INFO * info_buf_source; + FILE_UNIX_BASIC_INFO * info_buf_target; + + info_buf_source = + kmalloc(2 * sizeof(FILE_UNIX_BASIC_INFO),GFP_KERNEL); + if(info_buf_source != NULL) { + info_buf_target = info_buf_source+1; + rc = CIFSSMBUnixQPathInfo(xid, pTcon, fromName, + info_buf_source, cifs_sb_source->local_nls); + if(rc == 0) { + rc = CIFSSMBUnixQPathInfo(xid,pTcon,toName, + info_buf_target, + cifs_sb_target->local_nls); + } + if((rc == 0) && + (info_buf_source->UniqueId == + info_buf_target->UniqueId)) { + /* do not rename since the files are hardlinked + which is a noop */ + } else { + /* we either can not tell the files are hardlinked + (as with Windows servers) or files are not hardlinked + so delete the target manually before renaming to + follow POSIX rather than Windows semantics */ + cifs_unlink(target_inode, target_direntry); + rc = CIFSSMBRename(xid, pTcon, fromName, toName, + cifs_sb_source->local_nls); + } + kfree(info_buf_source); + } /* if we can not get memory just leave rc as EEXIST */ } if((rc == -EIO)||(rc == -EEXIST)) { @@ -549,6 +626,8 @@ cifs_rename(struct inode *source_inode, struct dentry *source_direntry, CIFSSMBClose(xid, pTcon, netfid); } } + +cifs_rename_exit: if (fromName) kfree(fromName); if (toName) @@ -586,7 +665,13 @@ cifs_revalidate(struct dentry *direntry) cifs_sb = CIFS_SB(direntry->d_sb); + /* can not safely grab the rename sem here if + rename calls revalidate since that would deadlock */ full_path = build_path_from_dentry(direntry); + if(full_path == NULL) { + FreeXid(xid); + return -ENOMEM; + } cFYI(1, ("Revalidate: %s inode 0x%p count %d dentry: 0x%p d_time %ld jiffies %ld", full_path, direntry->d_inode, @@ -647,9 +732,12 @@ cifs_revalidate(struct dentry *direntry) } } - + /* can not grab this sem since kernel filesys locking + documentation indicates i_sem may be taken by the kernel + on lookup and rename which could deadlock if we grab + the i_sem here as well */ +/* down(&direntry->d_inode->i_sem);*/ /* need to write out dirty pages here */ - down(&direntry->d_inode->i_sem); if(direntry->d_inode->i_mapping) { /* do we need to lock inode until after invalidate completes below? */ filemap_fdatawrite(direntry->d_inode->i_mapping); @@ -663,9 +751,7 @@ cifs_revalidate(struct dentry *direntry) invalidate_remote_inode(direntry->d_inode); } } - - - up(&direntry->d_inode->i_sem); +/* up(&direntry->d_inode->i_sem);*/ if (full_path) kfree(full_path); @@ -729,7 +815,13 @@ cifs_setattr(struct dentry *direntry, struct iattr *attrs) cifs_sb = CIFS_SB(direntry->d_inode->i_sb); pTcon = cifs_sb->tcon; + down(&direntry->d_sb->s_vfs_rename_sem); full_path = build_path_from_dentry(direntry); + up(&direntry->d_sb->s_vfs_rename_sem); + if(full_path == NULL) { + FreeXid(xid); + return -ENOMEM; + } cifsInode = CIFS_I(direntry->d_inode); /* BB check if we need to refresh inode from server now ? BB */