From: Pavel Shilovsky <piastryyy@xxxxxxxxx> Signed-off-by: Pavel Shilovsky <piastryyy@xxxxxxxxx> --- fs/cifs/smb2inode.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++- fs/cifs/smb2proto.h | 2 + 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index ff9d174..f881055 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c @@ -45,7 +45,7 @@ const struct inode_operations smb2_dir_inode_ops = { .link = cifs_hardlink, .mkdir = smb2_mkdir, .rmdir = smb2_rmdir, - .rename = cifs_rename, + .rename = smb2_rename, .permission = cifs_permission, /* revalidate:cifs_revalidate, */ .setattr = cifs_setattr, @@ -63,7 +63,7 @@ const struct inode_operations smb2_file_inode_ops = { /* revalidate:cifs_revalidate, */ .setattr = cifs_setattr, .getattr = cifs_getattr, /* do we need this anymore? */ - .rename = cifs_rename, + .rename = smb2_rename, .permission = cifs_permission, #ifdef CONFIG_CIFS_XATTR .setxattr = cifs_setxattr, @@ -167,6 +167,10 @@ smb2_open_op_close(int xid, struct cifs_tcon *tcon, __le16 *path, /* Directories are created through parameters in the * SMB2_open() call. */ break; + case SMB2_OP_RENAME: + tmprc = SMB2_rename(xid, tcon, persist_fid, volatile_fid, + (__le16 *)data); + break; default: cERROR(1, "Invalid command"); break; @@ -752,3 +756,67 @@ smb2_create_out: FreeXid(xid); return rc; } + +static int +smb2_do_rename(struct dentry *from_dentry, __le16 *from_path, + struct dentry *to_dentry, __le16 *to_path) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(from_dentry->d_sb); + struct tcon_link *tlink; + struct cifs_tcon *tcon; + int rc, xid; + + xid = GetXid(); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + + rc = smb2_open_op_close(xid, tcon, from_path, DELETE, + FILE_OPEN, 0, 0, to_path, SMB2_OP_RENAME); + + cifs_put_tlink(tlink); + FreeXid(xid); + return rc; +} + +int smb2_rename(struct inode *source_dir, struct dentry *source_dentry, + struct inode *target_dir, struct dentry *target_dentry) +{ + __le16 *from_name = NULL; + __le16 *to_name = NULL; + int rc, tmprc; + + /* + * we already have the rename sem so we do not need to + * grab it again here to protect the path integrity + */ + from_name = build_ucspath_from_dentry(source_dentry); + if (from_name == NULL) { + rc = -ENOMEM; + goto smb2_rename_exit; + } + + to_name = build_ucspath_from_dentry(target_dentry); + if (to_name == NULL) { + rc = -ENOMEM; + goto smb2_rename_exit; + } + + rc = smb2_do_rename(source_dentry, from_name, target_dentry, to_name); + + /* Try unlinking the target dentry if it's not negative */ + if (target_dentry->d_inode && (rc == -EACCES || rc == -EEXIST)) { + tmprc = smb2_unlink(target_dir, target_dentry); + if (tmprc) + goto smb2_rename_exit; + + rc = smb2_do_rename(source_dentry, from_name, target_dentry, + to_name); + } + +smb2_rename_exit: + kfree(from_name); + kfree(to_name); + return rc; +} diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index edc8c3b..0ed7773 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -154,6 +154,8 @@ extern int smb2_create(struct inode *dir, struct dentry *direntry, int mode, extern int smb2_mkdir(struct inode *inode, struct dentry *direntry, int mode); extern int smb2_rmdir(struct inode *inode, struct dentry *direntry); extern int smb2_unlink(struct inode *dir, struct dentry *dentry); +extern int smb2_rename(struct inode *source_dir, struct dentry *source_dentry, + struct inode *target_dir, struct dentry *target_dentry); extern int smb2_readdir(struct file *file, void *direntry, filldir_t filldir); -- 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe linux-cifs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html