int cifs_rename(struct inode *source_inode, struct dentry *source_direntry, struct inode *target_inode, struct dentry *target_direntry) { char *fromName; char *toName; struct cifs_sb_info *cifs_sb_source; struct cifs_sb_info *cifs_sb_target; struct cifsTconInfo *pTcon; int xid; int rc = 0; xid = GetXid(); cifs_sb_target = CIFS_SB(target_inode->i_sb); cifs_sb_source = CIFS_SB(source_inode->i_sb); pTcon = cifs_sb_source->tcon; if (pTcon != cifs_sb_target->tcon) { FreeXid(xid); return -EXDEV; /* BB actually could be allowed if same server, but 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) { /* 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)) { int oplock = FALSE; __u16 netfid; rc = CIFSSMBOpen(xid, pTcon, fromName, FILE_OPEN, GENERIC_READ, CREATE_NOT_DIR, &netfid, &oplock, NULL, cifs_sb_source->local_nls); if(rc==0) { CIFSSMBRenameOpenFile(xid,pTcon,netfid, toName, cifs_sb_source->local_nls); CIFSSMBClose(xid, pTcon, netfid); } } cifs_rename_exit: if (fromName) kfree(fromName); if (toName) kfree(toName); FreeXid(xid); return rc; }
int cifs_get_inode_info_unix(struct inode **pinode, const unsigned char *search_path, struct super_block *sb,int xid) { int rc = 0; FILE_UNIX_BASIC_INFO findData; struct cifsTconInfo *pTcon; struct inode *inode; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); char *tmp_path; 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 */ rc = CIFSSMBUnixQPathInfo(xid, pTcon, search_path, &findData, cifs_sb->local_nls); /* dump_mem("\nUnixQPathInfo return data", &findData, sizeof(findData)); */ if (rc) { if (rc == -EREMOTE) { tmp_path = kmalloc(strnlen (pTcon->treeName, MAX_TREE_SIZE + 1) + strnlen(search_path, MAX_PATHCONF) + 1, GFP_KERNEL); if (tmp_path == NULL) { return -ENOMEM; } /* have to skip first of the double backslash of UNC name */ strncpy(tmp_path, pTcon->treeName, MAX_TREE_SIZE); strncat(tmp_path, search_path, MAX_PATHCONF); rc = connect_to_dfs_path(xid, pTcon->ses, /* treename + */ tmp_path, cifs_sb->local_nls); kfree(tmp_path); /* BB fix up inode etc. */ } else if (rc) { return rc; } } else { struct cifsInodeInfo *cifsInfo; /* 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)); cifsInfo->time = jiffies; cFYI(1, (" New time %ld ", cifsInfo->time)); atomic_set(&cifsInfo->inUse,1); /* ok to set on every refresh of inode */ inode->i_atime = cifs_NTtimeToUnix(le64_to_cpu(findData.LastAccessTime)); inode->i_mtime = cifs_NTtimeToUnix(le64_to_cpu (findData.LastModificationTime)); inode->i_ctime = cifs_NTtimeToUnix(le64_to_cpu(findData.LastStatusChange)); inode->i_mode = le64_to_cpu(findData.Permissions); findData.Type = le32_to_cpu(findData.Type); if (findData.Type == UNIX_FILE) { inode->i_mode |= S_IFREG; } else if (findData.Type == UNIX_SYMLINK) { inode->i_mode |= S_IFLNK; } else if (findData.Type == UNIX_DIR) { 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_mode |= S_IFSOCK; } inode->i_uid = le64_to_cpu(findData.Uid); inode->i_gid = le64_to_cpu(findData.Gid); inode->i_nlink = le64_to_cpu(findData.Nlinks); findData.NumOfBytes = le64_to_cpu(findData.NumOfBytes); findData.EndOfFile = le64_to_cpu(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;*/ /* 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 ")); cFYI(1, ("Size %ld and blocks %ld ", (unsigned long) inode->i_size, inode->i_blocks)); if (S_ISREG(inode->i_mode)) { cFYI(1, (" File inode ")); inode->i_op = &cifs_file_inode_ops; inode->i_fop = &cifs_file_ops; inode->i_data.a_ops = &cifs_addr_ops; } else if (S_ISDIR(inode->i_mode)) { cFYI(1, (" Directory inode")); inode->i_op = &cifs_dir_inode_ops; inode->i_fop = &cifs_dir_ops; } else if (S_ISLNK(inode->i_mode)) { cFYI(1, (" Symbolic Link inode ")); inode->i_op = &cifs_symlink_inode_ops; /* tmp_inode->i_fop = *//* do not need to set to anything */ } else { cFYI(1, (" Init special inode ")); init_special_inode(inode, inode->i_mode, inode->i_rdev); } } return rc; }
int cifs_get_inode_info_unix(struct inode **pinode, const unsigned char *search_path, struct super_block *sb, int xid) { int rc = 0; FILE_UNIX_BASIC_INFO findData; struct cifsTconInfo *pTcon; struct inode *inode; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); const unsigned char *full_path; bool is_dfs_referral = false; pTcon = cifs_sb->tcon; cFYI(1, ("Getting info on %s", search_path)); full_path = cifs_get_search_path(cifs_sb, search_path); try_again_CIFSSMBUnixQPathInfo: /* could have done a find first instead but this returns more info */ rc = CIFSSMBUnixQPathInfo(xid, pTcon, full_path, &findData, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); /* dump_mem("\nUnixQPathInfo return data", &findData, sizeof(findData)); */ if (rc) { if (rc == -EREMOTE && !is_dfs_referral) { is_dfs_referral = true; if (full_path != search_path) { kfree(full_path); full_path = search_path; } goto try_again_CIFSSMBUnixQPathInfo; } goto cgiiu_exit; } else { struct cifsInodeInfo *cifsInfo; __u64 num_of_bytes = le64_to_cpu(findData.NumOfBytes); __u64 end_of_file = le64_to_cpu(findData.EndOfFile); /* get new inode */ if (*pinode == NULL) { *pinode = new_inode(sb); if (*pinode == NULL) { rc = -ENOMEM; goto cgiiu_exit; } /* Is an i_ino of zero legal? */ /* Are there sanity checks we can use to ensure that the server is really filling in that field? */ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { (*pinode)->i_ino = (unsigned long)findData.UniqueId; } /* note ino incremented to unique num in new_inode */ if (sb->s_flags & MS_NOATIME) (*pinode)->i_flags |= S_NOATIME | S_NOCMTIME; insert_inode_hash(*pinode); } inode = *pinode; cifsInfo = CIFS_I(inode); cFYI(1, ("Old time %ld", cifsInfo->time)); cifsInfo->time = jiffies; cFYI(1, ("New time %ld", cifsInfo->time)); /* this is ok to set on every inode revalidate */ atomic_set(&cifsInfo->inUse, 1); cifs_unix_info_to_inode(inode, &findData, 0); if (num_of_bytes < end_of_file) cFYI(1, ("allocation size less than end of file")); cFYI(1, ("Size %ld and blocks %llu", (unsigned long) inode->i_size, (unsigned long long)inode->i_blocks)); cifs_set_ops(inode, is_dfs_referral); } cgiiu_exit: if (full_path != search_path) kfree(full_path); return rc; }
int cifs_get_inode_info_unix(struct inode **pinode, const unsigned char *search_path, struct super_block *sb, int xid) { int rc = 0; FILE_UNIX_BASIC_INFO findData; struct cifsTconInfo *pTcon; struct inode *inode; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); char *tmp_path; pTcon = cifs_sb->tcon; cFYI(1, ("Getting info on %s", search_path)); /* could have done a find first instead but this returns more info */ rc = CIFSSMBUnixQPathInfo(xid, pTcon, search_path, &findData, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); /* dump_mem("\nUnixQPathInfo return data", &findData, sizeof(findData)); */ if (rc) { if (rc == -EREMOTE) { tmp_path = kmalloc(strnlen(pTcon->treeName, MAX_TREE_SIZE + 1) + strnlen(search_path, MAX_PATHCONF) + 1, GFP_KERNEL); if (tmp_path == NULL) { return -ENOMEM; } /* have to skip first of the double backslash of UNC name */ strncpy(tmp_path, pTcon->treeName, MAX_TREE_SIZE); strncat(tmp_path, search_path, MAX_PATHCONF); rc = connect_to_dfs_path(xid, pTcon->ses, /* treename + */ tmp_path, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); kfree(tmp_path); /* BB fix up inode etc. */ } else if (rc) { return rc; } } else { struct cifsInodeInfo *cifsInfo; __u32 type = le32_to_cpu(findData.Type); __u64 num_of_bytes = le64_to_cpu(findData.NumOfBytes); __u64 end_of_file = le64_to_cpu(findData.EndOfFile); /* get new inode */ if (*pinode == NULL) { *pinode = new_inode(sb); if (*pinode == NULL) return -ENOMEM; /* Is an i_ino of zero legal? */ /* Are there sanity checks we can use to ensure that the server is really filling in that field? */ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { (*pinode)->i_ino = (unsigned long)findData.UniqueId; } /* note ino incremented to unique num in new_inode */ if(sb->s_flags & MS_NOATIME) (*pinode)->i_flags |= S_NOATIME | S_NOCMTIME; insert_inode_hash(*pinode); } inode = *pinode; cifsInfo = CIFS_I(inode); cFYI(1, ("Old time %ld", cifsInfo->time)); cifsInfo->time = jiffies; cFYI(1, ("New time %ld", cifsInfo->time)); /* this is ok to set on every inode revalidate */ atomic_set(&cifsInfo->inUse,1); inode->i_atime = cifs_NTtimeToUnix(le64_to_cpu(findData.LastAccessTime)); inode->i_mtime = cifs_NTtimeToUnix(le64_to_cpu (findData.LastModificationTime)); inode->i_ctime = cifs_NTtimeToUnix(le64_to_cpu(findData.LastStatusChange)); inode->i_mode = le64_to_cpu(findData.Permissions); /* since we set the inode type below we need to mask off to avoid strange results if bits set above */ inode->i_mode &= ~S_IFMT; if (type == UNIX_FILE) { inode->i_mode |= S_IFREG; } else if (type == UNIX_SYMLINK) { inode->i_mode |= S_IFLNK; } else if (type == UNIX_DIR) { inode->i_mode |= S_IFDIR; } else if (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 (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 (type == UNIX_FIFO) { inode->i_mode |= S_IFIFO; } else if (type == UNIX_SOCKET) { inode->i_mode |= S_IFSOCK; } else { /* safest to call it a file if we do not know */ inode->i_mode |= S_IFREG; cFYI(1,("unknown type %d",type)); } inode->i_uid = le64_to_cpu(findData.Uid); inode->i_gid = le64_to_cpu(findData.Gid); inode->i_nlink = le64_to_cpu(findData.Nlinks); spin_lock(&inode->i_lock); if (is_size_safe_to_change(cifsInfo, end_of_file)) { /* can not safely change the file size here if the client is writing to it due to potential races */ i_size_write(inode, end_of_file); /* 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 rather than setting to: (pTcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFE00;*/ /* This seems incredibly stupid but it turns out that i_blocks is not related to (i_size / i_blksize), instead 512 byte size is required for calculating num blocks */ /* 512 bytes (2**9) is the fake blocksize that must be used */ /* for this calculation */ inode->i_blocks = (512 - 1 + num_of_bytes) >> 9; } spin_unlock(&inode->i_lock); if (num_of_bytes < end_of_file) cFYI(1, ("allocation size less than end of file")); cFYI(1, ("Size %ld and blocks %llu", (unsigned long) inode->i_size, (unsigned long long)inode->i_blocks)); if (S_ISREG(inode->i_mode)) { cFYI(1, ("File inode")); inode->i_op = &cifs_file_inode_ops; if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) { if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) inode->i_fop = &cifs_file_direct_nobrl_ops; else inode->i_fop = &cifs_file_direct_ops; } else if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) inode->i_fop = &cifs_file_nobrl_ops; else /* not direct, send byte range locks */ inode->i_fop = &cifs_file_ops; /* check if server can support readpages */ if(pTcon->ses->server->maxBuf < PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE) inode->i_data.a_ops = &cifs_addr_ops_smallbuf; else inode->i_data.a_ops = &cifs_addr_ops; } else if (S_ISDIR(inode->i_mode)) { cFYI(1, ("Directory inode")); inode->i_op = &cifs_dir_inode_ops; inode->i_fop = &cifs_dir_ops; } else if (S_ISLNK(inode->i_mode)) { cFYI(1, ("Symbolic Link inode")); inode->i_op = &cifs_symlink_inode_ops; /* tmp_inode->i_fop = */ /* do not need to set to anything */ } else { cFYI(1, ("Init special inode")); init_special_inode(inode, inode->i_mode, inode->i_rdev); } } return rc; }
int cifs_rename(struct inode *source_inode, struct dentry *source_direntry, struct inode *target_inode, struct dentry *target_direntry) { char *fromName; char *toName; struct cifs_sb_info *cifs_sb_source; struct cifs_sb_info *cifs_sb_target; struct cifsTconInfo *pTcon; int xid; int rc = 0; xid = GetXid(); cifs_sb_target = CIFS_SB(target_inode->i_sb); cifs_sb_source = CIFS_SB(source_inode->i_sb); pTcon = cifs_sb_source->tcon; if (pTcon != cifs_sb_target->tcon) { FreeXid(xid); return -EXDEV; /* BB actually could be allowed if same server, but 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, cifs_sb_source->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc == -EEXIST) { /* 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; if (pTcon->unix_ext) rc = CIFSSMBUnixQPathInfo(xid, pTcon, fromName, info_buf_source, cifs_sb_source->local_nls, cifs_sb_source->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); /* else rc is still EEXIST so will fall through to unlink the target and retry rename */ if (rc == 0) { rc = CIFSSMBUnixQPathInfo(xid, pTcon, toName, info_buf_target, cifs_sb_target->local_nls, /* remap based on source sb */ cifs_sb_source->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); } 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, cifs_sb_source->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); } kfree(info_buf_source); } /* if we can not get memory just leave rc as EEXIST */ } if (rc) cFYI(1, ("rename rc %d", rc)); if ((rc == -EIO) || (rc == -EEXIST)) { int oplock = FALSE; __u16 netfid; /* BB FIXME Is Generic Read correct for rename? */ /* if renaming directory - we should not say CREATE_NOT_DIR, need to test renaming open directory, also GENERIC_READ might not right be right access to request */ rc = CIFSSMBOpen(xid, pTcon, fromName, FILE_OPEN, GENERIC_READ, FILE_SHARE_ALL, CREATE_NOT_DIR, &netfid, &oplock, NULL, cifs_sb_source->local_nls, cifs_sb_source->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc == 0) { rc = CIFSSMBRenameOpenFile(xid, pTcon, netfid, toName, cifs_sb_source->local_nls, cifs_sb_source->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); CIFSSMBClose(xid, pTcon, netfid); } } cifs_rename_exit: kfree(fromName); kfree(toName); FreeXid(xid); return rc; }