[PATCH 37/50] CIFS: Add SMB2 support for rename operation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Pavel Shilovsky <piastryyy@xxxxxxxxx>
---
 fs/cifs/smb2inode.c |   72 ++++++++++++++++++++++++++++++++++++++++++++++++-
 fs/cifs/smb2pdu.c   |   73 +++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/cifs/smb2pdu.h   |   28 +++++++++++++++++++
 fs/cifs/smb2proto.h |    5 +++
 4 files changed, 176 insertions(+), 2 deletions(-)

diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
index b533844..6bebed1 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 *srch_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;
@@ -729,3 +733,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 = cifs_build_utf16path_from_dentry(source_dentry);
+	if (from_name == NULL) {
+		rc = -ENOMEM;
+		goto smb2_rename_exit;
+	}
+
+	to_name = cifs_build_utf16path_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/smb2pdu.c b/fs/cifs/smb2pdu.c
index bfc1498..e3168ba 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -1695,3 +1695,76 @@ qdir_exit:
 	free_rsp_buf(resp_buftype, pSMB2r);
 	return rc;
 }
+
+int SMB2_rename(const int xid, struct cifs_tcon *tcon,
+		u64 persistent_fid, u64 volatile_fid, __le16 *target_file)
+{
+	struct smb2_set_info_req *pSMB2;
+	struct smb2_set_info_rsp *pSMB2r = NULL;
+	struct kvec iov[2];
+	int rc = 0;
+	int resp_buftype;
+	int status;
+	int uni_path_len;
+	struct TCP_Server_Info *server;
+	struct FileRenameInformation *pfli;
+	struct cifs_ses *ses = tcon->ses;
+
+	cFYI(1, "rename");
+
+	if (ses && (ses->server))
+		server = ses->server;
+	else
+		return -EIO;
+
+	rc = small_smb2_init(SMB2_SET_INFO, tcon, (void **) &pSMB2);
+	if (rc)
+		return rc;
+
+	pSMB2->InfoType = SMB2_O_INFO_FILE;
+	pSMB2->FileInfoClass = FILE_RENAME_INFORMATION;
+	pSMB2->PersistentFileId = persistent_fid;
+	pSMB2->VolatileFileId = volatile_fid;
+	pSMB2->BufferOffset = cpu_to_le16(sizeof(struct smb2_set_info_req)
+			- 1 /* pad */ - 4 /* do not count rfc1001 len field */);
+
+	uni_path_len = (2 * UniStrnlen((wchar_t *)target_file, PATH_MAX)) + 2;
+	pSMB2->BufferOffset = cpu_to_le16(sizeof(struct smb2_set_info_req) - 1
+		/* pad */ - 4 /* do not count rfc1001 len field */);
+	pSMB2->BufferLength = cpu_to_le32(sizeof(struct FileRenameInformation) +
+					uni_path_len);
+
+	iov[0].iov_base = (char *)pSMB2;
+	iov[0].iov_len = be32_to_cpu(pSMB2->hdr.smb2_buf_length) + 4 - 1;
+	iov[0].iov_len += sizeof(struct FileRenameInformation);
+	iov[1].iov_base = target_file;
+	iov[1].iov_len = uni_path_len; /* This is two bytes more - includes
+					  null - is it better to send without */
+
+	pfli = (struct FileRenameInformation *)pSMB2->Buffer;
+	pfli->ReplaceIfExists = 0; /* 1 = replace existing link with new */
+			      /* 0 = fail if link already exists */
+	pfli->RootDirectory = 0;  /* MBZ for network ops (why does spec say?) */
+	pfli->FileNameLength = cpu_to_le32(uni_path_len - 2);
+	pSMB2->hdr.smb2_buf_length =
+		cpu_to_be32(be32_to_cpu(pSMB2->hdr.smb2_buf_length) - 1 +
+			sizeof(struct FileRenameInformation) + uni_path_len);
+
+	rc = smb2_sendrcv2(xid, ses, iov, 2, &resp_buftype /* ret */, &status,
+			   CIFS_STD_OP | CIFS_LOG_ERROR);
+	pSMB2r = (struct smb2_set_info_rsp *)iov[0].iov_base;
+
+	if (rc != 0) {
+		cifs_stats_fail_inc(tcon, SMB2SET_INFO);
+		goto rename_exit;
+	}
+
+	if (pSMB2r == NULL) {
+		rc = -EIO;
+		goto rename_exit;
+	}
+
+rename_exit:
+	free_rsp_buf(resp_buftype, pSMB2r);
+	return rc;
+}
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index 9c8b59d..1f795ca 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -569,6 +569,25 @@ struct smb2_query_info_rsp {
 	__u8   Buffer[1];
 } __packed;
 
+struct smb2_set_info_req {
+	struct smb2_hdr hdr;
+	__le16 StructureSize; /* Must be 33 */
+	__u8   InfoType;
+	__u8   FileInfoClass;
+	__le32 BufferLength;
+	__le16 BufferOffset;
+	__u16  Reserved;
+	__le32 AdditionalInformation;
+	__u64  PersistentFileId; /* opaque endianness */
+	__u64  VolatileFileId; /* opaque endianness */
+	__u8   Buffer[1];
+} __packed;
+
+struct smb2_set_info_rsp {
+	struct smb2_hdr hdr;
+	__le16 StructureSize; /* Must be 2 */
+} __packed;
+
 /*
  *	PDU infolevel structure definitions
  *	BB consider moving to a different header
@@ -639,6 +658,15 @@ struct smb2_file_full_directory_info {
 	char   filename[0];
 } __packed;
 
+struct FileRenameInformation { /* encoding of request for level 10 */
+	__u8   ReplaceIfExists; /* 1 = replace existing file with new */
+				/* 0 = fail if target file already exists */
+	__u8   Reserved[7];
+	__u64  RootDirectory;  /* MBZ for network operations (why says spec?) */
+	__le32 FileNameLength;
+	char   FileName[0];     /* New name to be assigned */
+} __packed; /* level 10 set */
+
 /*
  * This level 18, although with struct with same name is different from cifs
  * level 0x107. Level 0x107 has an extra u64 between AccessFlags and
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index 468f184..5317221 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -86,6 +86,8 @@ extern int smb2_unlink(struct inode *dir, struct dentry *dentry);
 extern int smb2_create(struct inode *dir, struct dentry *direntry, int mode,
 		       struct nameidata *nd);
 extern int smb2_readdir(struct file *file, void *direntry, filldir_t filldir);
+extern int smb2_rename(struct inode *source_dir, struct dentry *source_dentry,
+		       struct inode *target_dir, struct dentry *target_dentry);
 
 extern struct cifsFileInfo *smb2_new_fileinfo(__u64 persist_fid,
 					      __u64 volatile_fid,
@@ -148,5 +150,8 @@ extern int SMB2_echo(struct TCP_Server_Info *server);
 extern int SMB2_query_directory(const int xid, struct cifs_tcon *tcon,
 				u64 persistent_fid, u64 volatile_fid, int index,
 				struct cifs_search_info *);
+extern int SMB2_rename(const int xid, struct cifs_tcon *tcon,
+		       u64 persistent_fid, u64 volatile_fid,
+		       __le16 *target_file);
 
 #endif			/* _SMB2PROTO_H */
-- 
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


[Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux