CIFS: Add SMB2 support for rename operation
[linux-3.10.git] / fs / cifs / smb2pdu.c
index 30c92c8..1dc11ce 100644 (file)
@@ -1602,3 +1602,110 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
        }
        return rc;
 }
+
+static int
+send_set_info(const unsigned int xid, struct cifs_tcon *tcon,
+              u64 persistent_fid, u64 volatile_fid, int info_class,
+              unsigned int num, void **data, unsigned int *size)
+{
+       struct smb2_set_info_req *req;
+       struct smb2_set_info_rsp *rsp = NULL;
+       struct kvec *iov;
+       int rc = 0;
+       int resp_buftype;
+       unsigned int i;
+       struct TCP_Server_Info *server;
+       struct cifs_ses *ses = tcon->ses;
+
+       if (ses && (ses->server))
+               server = ses->server;
+       else
+               return -EIO;
+
+       if (!num)
+               return -EINVAL;
+
+       iov = kmalloc(sizeof(struct kvec) * num, GFP_KERNEL);
+       if (!iov)
+               return -ENOMEM;
+
+       rc = small_smb2_init(SMB2_SET_INFO, tcon, (void **) &req);
+       if (rc) {
+               kfree(iov);
+               return rc;
+       }
+
+       req->InfoType = SMB2_O_INFO_FILE;
+       req->FileInfoClass = info_class;
+       req->PersistentFileId = persistent_fid;
+       req->VolatileFileId = volatile_fid;
+
+       /* 4 for RFC1001 length and 1 for Buffer */
+       req->BufferOffset =
+                       cpu_to_le16(sizeof(struct smb2_set_info_req) - 1 - 4);
+       req->BufferLength = cpu_to_le32(*size);
+
+       inc_rfc1001_len(req, *size - 1 /* Buffer */);
+
+       memcpy(req->Buffer, *data, *size);
+
+       iov[0].iov_base = (char *)req;
+       /* 4 for RFC1001 length */
+       iov[0].iov_len = get_rfc1002_length(req) + 4;
+
+       for (i = 1; i < num; i++) {
+               inc_rfc1001_len(req, size[i]);
+               le32_add_cpu(&req->BufferLength, size[i]);
+               iov[i].iov_base = (char *)data[i];
+               iov[i].iov_len = size[i];
+       }
+
+       rc = SendReceive2(xid, ses, iov, num, &resp_buftype, 0);
+       rsp = (struct smb2_set_info_rsp *)iov[0].iov_base;
+
+       if (rc != 0) {
+               cifs_stats_fail_inc(tcon, SMB2_SET_INFO_HE);
+               goto out;
+       }
+
+       if (rsp == NULL) {
+               rc = -EIO;
+               goto out;
+       }
+
+out:
+       free_rsp_buf(resp_buftype, rsp);
+       kfree(iov);
+       return rc;
+}
+
+int
+SMB2_rename(const unsigned int xid, struct cifs_tcon *tcon,
+           u64 persistent_fid, u64 volatile_fid, __le16 *target_file)
+{
+       struct smb2_file_rename_info info;
+       void **data;
+       unsigned int size[2];
+       int rc;
+       int len = (2 * UniStrnlen((wchar_t *)target_file, PATH_MAX));
+
+       data = kmalloc(sizeof(void *) * 2, GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       info.ReplaceIfExists = 1; /* 1 = replace existing target with new */
+                             /* 0 = fail if target already exists */
+       info.RootDirectory = 0;  /* MBZ for network ops (why does spec say?) */
+       info.FileNameLength = cpu_to_le32(len);
+
+       data[0] = &info;
+       size[0] = sizeof(struct smb2_file_rename_info);
+
+       data[1] = target_file;
+       size[1] = len + 2 /* null */;
+
+       rc = send_set_info(xid, tcon, persistent_fid, volatile_fid,
+                          FILE_RENAME_INFORMATION, 2, data, size);
+       kfree(data);
+       return rc;
+}