#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,
- struct super_block *sb)
+ struct super_block *sb,int xid)
{
- int xid;
int rc = 0;
FILE_UNIX_BASIC_INFO findData;
struct cifsTconInfo *pTcon;
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;
cFYI(1, (" Getting info on %s ", search_path));
/* we could have done a find first instead but this returns more info */
strnlen(search_path, MAX_PATHCONF) + 1,
GFP_KERNEL);
if (tmp_path == NULL) {
- FreeXid(xid);
return -ENOMEM;
}
/* have to skip first of the double backslash of UNC name */
/* BB fix up inode etc. */
} else if (rc) {
- FreeXid(xid);
return rc;
}
/* get new inode */
if (*pinode == NULL) {
*pinode = new_inode(sb);
+ if(*pinode == NULL)
+ return -ENOMEM;
+ insert_inode_hash(*pinode);
}
+
inode = *pinode;
-
cifsInfo = CIFS_I(inode);
cFYI(1, (" Old time %ld ", cifsInfo->time));
inode->i_mode |= S_IFDIR;
} else if (findData.Type == UNIX_CHARDEV) {
inode->i_mode |= S_IFCHR;
+ inode->i_rdev = MKDEV(le64_to_cpu(findData.DevMajor),
+ le64_to_cpu(findData.DevMinor) & MINORMASK);
} else if (findData.Type == UNIX_BLOCKDEV) {
inode->i_mode |= S_IFBLK;
+ inode->i_rdev = MKDEV(le64_to_cpu(findData.DevMajor),
+ le64_to_cpu(findData.DevMinor) & MINORMASK);
} else if (findData.Type == UNIX_FIFO) {
inode->i_mode |= S_IFIFO;
} else if (findData.Type == UNIX_SOCKET) {
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 "));
inode->i_rdev);
}
}
- FreeXid(xid);
return rc;
}
int
cifs_get_inode_info(struct inode **pinode, const unsigned char *search_path,
- FILE_ALL_INFO * pfindData, struct super_block *sb)
+ FILE_ALL_INFO * pfindData, struct super_block *sb, int xid)
{
- int xid;
int rc = 0;
struct cifsTconInfo *pTcon;
struct inode *inode;
char *tmp_path;
char *buf = NULL;
- xid = GetXid();
-
pTcon = cifs_sb->tcon;
cFYI(1,("Getting info on %s ", search_path));
if((pfindData == NULL) && (*pinode != NULL)) {
if(CIFS_I(*pinode)->clientCanCacheRead) {
cFYI(1,("No need to revalidate inode sizes on cached file "));
- FreeXid(xid);
return rc;
}
}
if (tmp_path == NULL) {
if(buf)
kfree(buf);
- FreeXid(xid);
return -ENOMEM;
}
} else if (rc) {
if(buf)
kfree(buf);
- FreeXid(xid);
return rc;
}
} else {
/* get new inode */
if (*pinode == NULL) {
*pinode = new_inode(sb);
+ if(*pinode == NULL)
+ return -ENOMEM;
+ insert_inode_hash(*pinode);
}
-
inode = *pinode;
cifsInfo = CIFS_I(inode);
pfindData->Attributes = le32_to_cpu(pfindData->Attributes);
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);
}
if(buf)
kfree(buf);
- FreeXid(xid);
return rc;
}
void
cifs_read_inode(struct inode *inode)
{ /* gets root inode */
-
+ int xid;
struct cifs_sb_info *cifs_sb;
cifs_sb = CIFS_SB(inode->i_sb);
-
+ xid = GetXid();
if (cifs_sb->tcon->ses->capabilities & CAP_UNIX)
- cifs_get_inode_info_unix(&inode, "", inode->i_sb);
+ cifs_get_inode_info_unix(&inode, "", inode->i_sb,xid);
else
- cifs_get_inode_info(&inode, "", NULL, inode->i_sb);
+ cifs_get_inode_info(&inode, "", NULL, inode->i_sb,xid);
+ /* can not call macro FreeXid here since in a void func */
+ _FreeXid(xid);
}
int
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) {
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) {
inode->i_nlink++;
if (pTcon->ses->capabilities & CAP_UNIX)
rc = cifs_get_inode_info_unix(&newinode, full_path,
- inode->i_sb);
+ inode->i_sb,xid);
else
rc = cifs_get_inode_info(&newinode, full_path,NULL,
- inode->i_sb);
+ inode->i_sb,xid);
direntry->d_op = &cifs_dentry_ops;
d_instantiate(direntry, newinode);
if(direntry->d_inode)
direntry->d_inode->i_nlink = 2;
- if (cifs_sb->tcon->ses->capabilities & CAP_UNIX)
- CIFSSMBUnixSetPerms(xid, pTcon, full_path, mode,
- (__u64)-1,
- (__u64)-1,
- 0 /* dev_t */,
- cifs_sb->local_nls);
+ if (cifs_sb->tcon->ses->capabilities & CAP_UNIX)
+ if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
+ CIFSSMBUnixSetPerms(xid, pTcon, full_path, mode,
+ (__u64)current->euid,
+ (__u64)current->egid,
+ 0 /* dev_t */,
+ cifs_sb->local_nls);
+ } else {
+ CIFSSMBUnixSetPerms(xid, pTcon, full_path, mode,
+ (__u64)-1,
+ (__u64)-1,
+ 0 /* dev_t */,
+ cifs_sb->local_nls);
+ }
else { /* BB to be implemented via Windows secrty descriptors*/
/* eg CIFSSMBWinSetPerms(xid,pTcon,full_path,mode,-1,-1,local_nls);*/
}
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);
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)) {
CIFSSMBClose(xid, pTcon, netfid);
}
}
+
+cifs_rename_exit:
if (fromName)
kfree(fromName);
if (toName)
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,
if (cifs_sb->tcon->ses->capabilities & CAP_UNIX) {
rc = cifs_get_inode_info_unix(&direntry->d_inode, full_path,
- direntry->d_sb);
+ direntry->d_sb,xid);
if(rc) {
cFYI(1,("error on getting revalidate info %d",rc));
/* if(rc != -ENOENT)
}
} else {
rc = cifs_get_inode_info(&direntry->d_inode, full_path, NULL,
- direntry->d_sb);
+ direntry->d_sb,xid);
if(rc) {
cFYI(1,("error on getting revalidate info %d",rc));
/* if(rc != -ENOENT)
}
}
-
+ /* 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);
invalidate_remote_inode(direntry->d_inode);
}
}
-
-
- up(&direntry->d_inode->i_sem);
+/* up(&direntry->d_inode->i_sem);*/
if (full_path)
kfree(full_path);
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 */
}
/* do not need local check to inode_check_ok since the server does that */
- inode_setattr(direntry->d_inode, attrs);
+ if (!rc)
+ rc = inode_setattr(direntry->d_inode, attrs);
if (full_path)
kfree(full_path);
FreeXid(xid);