Signed-off-by: David Howells <dhowells@xxxxxxxxxx> cc: Steve French <sfrench@xxxxxxxxx> cc: Paulo Alcantara <pc@xxxxxxxxxxxxx> cc: Shyam Prasad N <sprasad@xxxxxxxxxxxxx> cc: Tom Talpey <tom@xxxxxxxxxx> cc: linux-cifs@xxxxxxxxxxxxxxx cc: netfs@xxxxxxxxxxxxxxx cc: linux-fsdevel@xxxxxxxxxxxxxxx --- fs/smb/client/cached_dir.c | 41 +- fs/smb/client/cifsglob.h | 5 +- fs/smb/client/smb2inode.c | 6 +- fs/smb/client/smb2ops.c | 34 +- fs/smb/client/smb2pdu.c | 877 +++++++++++++++---------------------- fs/smb/client/smb2proto.h | 29 +- 6 files changed, 399 insertions(+), 593 deletions(-) diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c index 368e870624da..e4534f6b31da 100644 --- a/fs/smb/client/cached_dir.c +++ b/fs/smb/client/cached_dir.c @@ -141,9 +141,7 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, struct cifs_open_parms oparms; struct smb2_create_rsp *o_rsp = NULL; struct smb2_query_info_rsp *qi_rsp = NULL; - int resp_buftype[2]; - struct smb_rqst rqst[2]; - struct kvec rsp_iov[2]; + struct smb_message *smb; struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; struct kvec qi_iov[1]; int rc, flags = 0; @@ -260,29 +258,20 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, server->ops->new_lease_key(pfid); - memset(rqst, 0, sizeof(rqst)); - resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER; - memset(rsp_iov, 0, sizeof(rsp_iov)); - /* Open */ - memset(&open_iov, 0, sizeof(open_iov)); - rqst[0].rq_iov = open_iov; - rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = path, - .create_options = cifs_create_options(cifs_sb, CREATE_NOT_FILE), - .desired_access = FILE_READ_DATA | FILE_READ_ATTRIBUTES | - FILE_READ_EA, - .disposition = FILE_OPEN, - .fid = pfid, - .lease_flags = lease_flags, - .replay = !!(retries), + .tcon = tcon, + .path = path, + .create_options = cifs_create_options(cifs_sb, CREATE_NOT_FILE), + .desired_access = FILE_READ_DATA | FILE_READ_ATTRIBUTES | + FILE_READ_EA, + .disposition = FILE_OPEN, + .fid = pfid, + .lease_flags = lease_flags, + .replay = !!(retries), }; - rc = SMB2_open_init(tcon, server, - &rqst[0], &oplock, &oparms, utf16_path); + smb = SMB2_open_init(tcon, server, &oplock, &oparms, utf16_path); if (rc) goto oshr_free; smb2_set_next_command(tcon, &rqst[0]); @@ -336,10 +325,10 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, goto oshr_free; } - rc = smb2_parse_contexts(server, rsp_iov, - &oparms.fid->epoch, - oparms.fid->lease_key, - &oplock, NULL, NULL); + rc = smb2_parse_create_response(server, rsp_iov, + &oparms.fid->epoch, + oparms.fid->lease_key, + &oplock, NULL, NULL); if (rc) { spin_unlock(&cfids->cfid_list_lock); goto oshr_free; diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index 0cc71f504c68..b96c45f57c2b 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -554,8 +554,9 @@ struct smb_version_operations { /* set oplock level for the inode */ void (*set_oplock_level)(struct cifsInodeInfo *cinode, __u32 oplock, __u16 epoch, bool *purge_cache); - /* create lease context buffer for CREATE request */ - char * (*create_lease_buf)(u8 *lease_key, u8 oplock, u8 *parent_lease_key, __le32 le_flags); + /* Fill in a lease context buffer for CREATE request */ + void (*fill_lease_buf)(struct smb_message *smb, const u8 *lease_key, u8 oplock, + const u8 *parent_lease_key, __le32 le_flags); /* parse lease context buffer and return oplock/epoch info */ __u8 (*parse_lease_buf)(void *buf, __u16 *epoch, char *lkey); ssize_t (*copychunk_range)(const unsigned int, diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c index 2a3e46b8e15a..c5041fa94779 100644 --- a/fs/smb/client/smb2inode.c +++ b/fs/smb/client/smb2inode.c @@ -663,9 +663,9 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, idata->fi.DeletePending = 0; idata->fi.Directory = !!(le32_to_cpu(create_rsp->FileAttributes) & ATTR_DIRECTORY); - /* smb2_parse_contexts() fills idata->fi.IndexNumber */ - rc = smb2_parse_contexts(server, &rsp_iov[0], &oparms->fid->epoch, - oparms->fid->lease_key, &oplock, &idata->fi, NULL); + /* smb2_parse_create_response() fills idata->fi.IndexNumber */ + rc = smb2_parse_create_response(server, &rsp_iov[0], &oparms->fid->epoch, + oparms->fid->lease_key, &oplock, &idata->fi, NULL); if (rc) cifs_dbg(VFS, "rc: %d parsing context of compound op\n", rc); } diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 9db383ec22e8..7c30475054b8 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -4086,14 +4086,11 @@ map_oplock_to_lease(u8 oplock) return 0; } -static char * -smb2_create_lease_buf(u8 *lease_key, u8 oplock, u8 *parent_lease_key, __le32 flags) +static void +smb2_fill_lease_buf(struct smb_message *smb, const u8 *lease_key, u8 oplock, + const u8 *parent_lease_key, __le32 flags) { - struct create_lease *buf; - - buf = kzalloc(sizeof(struct create_lease), GFP_KERNEL); - if (!buf) - return NULL; + struct create_lease *buf = cifs_begin_extension(smb); memcpy(&buf->lcontext.LeaseKey, lease_key, SMB2_LEASE_KEY_SIZE); buf->lcontext.LeaseState = map_oplock_to_lease(oplock); @@ -4109,17 +4106,14 @@ smb2_create_lease_buf(u8 *lease_key, u8 oplock, u8 *parent_lease_key, __le32 fla buf->Name[1] = 'q'; buf->Name[2] = 'L'; buf->Name[3] = 's'; - return (char *)buf; + cifs_end_extension(smb, sizeof(*buf)); } -static char * -smb3_create_lease_buf(u8 *lease_key, u8 oplock, u8 *parent_lease_key, __le32 flags) +static void +smb3_fill_lease_buf(struct smb_message *smb, const u8 *lease_key, u8 oplock, + const u8 *parent_lease_key, __le32 flags) { - struct create_lease_v2 *buf; - - buf = kzalloc(sizeof(struct create_lease_v2), GFP_KERNEL); - if (!buf) - return NULL; + struct create_lease_v2 *buf = cifs_begin_extension(smb); memcpy(&buf->lcontext.LeaseKey, lease_key, SMB2_LEASE_KEY_SIZE); buf->lcontext.LeaseState = map_oplock_to_lease(oplock); @@ -4138,7 +4132,7 @@ smb3_create_lease_buf(u8 *lease_key, u8 oplock, u8 *parent_lease_key, __le32 fla buf->Name[1] = 'q'; buf->Name[2] = 'L'; buf->Name[3] = 's'; - return (char *)buf; + cifs_end_extension(smb, sizeof(*buf)); } static __u8 @@ -5263,7 +5257,7 @@ struct smb_version_operations smb20_operations = { .calc_signature = smb2_calc_signature, .is_read_op = smb2_is_read_op, .set_oplock_level = smb2_set_oplock_level, - .create_lease_buf = smb2_create_lease_buf, + .fill_lease_buf = smb2_fill_lease_buf, .parse_lease_buf = smb2_parse_lease_buf, .copychunk_range = smb2_copychunk_range, .wp_retry_size = smb2_wp_retry_size, @@ -5366,7 +5360,7 @@ struct smb_version_operations smb21_operations = { .calc_signature = smb2_calc_signature, .is_read_op = smb21_is_read_op, .set_oplock_level = smb21_set_oplock_level, - .create_lease_buf = smb2_create_lease_buf, + .fill_lease_buf = smb2_fill_lease_buf, .parse_lease_buf = smb2_parse_lease_buf, .copychunk_range = smb2_copychunk_range, .wp_retry_size = smb2_wp_retry_size, @@ -5476,7 +5470,7 @@ struct smb_version_operations smb30_operations = { .set_integrity = smb3_set_integrity, .is_read_op = smb21_is_read_op, .set_oplock_level = smb3_set_oplock_level, - .create_lease_buf = smb3_create_lease_buf, + .fill_lease_buf = smb3_fill_lease_buf, .parse_lease_buf = smb3_parse_lease_buf, .copychunk_range = smb2_copychunk_range, .duplicate_extents = smb2_duplicate_extents, @@ -5592,7 +5586,7 @@ struct smb_version_operations smb311_operations = { .set_integrity = smb3_set_integrity, .is_read_op = smb21_is_read_op, .set_oplock_level = smb3_set_oplock_level, - .create_lease_buf = smb3_create_lease_buf, + .fill_lease_buf = smb3_fill_lease_buf, .parse_lease_buf = smb3_parse_lease_buf, .copychunk_range = smb2_copychunk_range, .duplicate_extents = smb2_duplicate_extents, diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 9fc55b315474..e9827aeab43f 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -2387,298 +2387,179 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) return rc; } - -static void fill_posix_buf(struct create_posix *buf, umode_t mode) +/* + * Begin a create context record. This fills in the chain pointer on the + * previous record if there was one. + */ +static void *cifs_begin_ccontext(struct smb_message *smb) { - buf->ccontext.DataOffset = - cpu_to_le16(offsetof(struct create_posix, Mode)); - buf->ccontext.DataLength = cpu_to_le32(4); - buf->ccontext.NameOffset = - cpu_to_le16(offsetof(struct create_posix, Name)); - buf->ccontext.NameLength = cpu_to_le16(16); + struct create_context_hdr *ccontext = cifs_begin_extension(smb); - /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ - buf->Name[0] = 0x93; - buf->Name[1] = 0xAD; - buf->Name[2] = 0x25; - buf->Name[3] = 0x50; - buf->Name[4] = 0x9C; - buf->Name[5] = 0xB4; - buf->Name[6] = 0x11; - buf->Name[7] = 0xE7; - buf->Name[8] = 0xB4; - buf->Name[9] = 0x23; - buf->Name[10] = 0x83; - buf->Name[11] = 0xDE; - buf->Name[12] = 0x96; - buf->Name[13] = 0x8B; - buf->Name[14] = 0xCD; - buf->Name[15] = 0x7C; - buf->Mode = cpu_to_le32(mode); - cifs_dbg(FYI, "mode on posix create 0%o\n", mode); + if (smb->latest_record) { + struct create_context_hdr *latest = smb->request + smb->latest_record; + + latest->Next = cpu_to_le32(smb->offset - smb->latest_record); + } + smb->latest_record = smb->offset; + return ccontext; } -static int -add_posix_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode) +#define CREATE_CONTEXT_HDR(ptr, name, data, namelen) { \ + .NameOffset = cpu_to_le16(offsetof(ptr, name)), \ + .NameLength = cpu_to_le16(namelen), \ + .DataOffset = cpu_to_le16(offsetof(ptr, data)), \ + .DataLength = cpu_to_le32(sizeof_field(ptr, data)), \ + } + +static void fill_posix_context(struct smb_message *smb, umode_t mode) { - struct create_posix *buf; - unsigned int num = *num_iovec; + struct create_posix *buf = cifs_begin_ccontext(smb); if (mode == ACL_NO_MODE) cifs_dbg(FYI, "%s: no mode\n", __func__); - buf = kzalloc(sizeof(struct create_posix), GFP_KERNEL); - if (!buf) - return -ENOMEM; - - fill_posix_buf(buf, mode); - - iov[num].iov_base = buf; - iov[num].iov_len = sizeof(struct create_posix); - *num_iovec = num + 1; - return 0; + *buf = (struct create_posix){ + .ccontext = CREATE_CONTEXT_HDR(struct create_posix, Name, Mode, 16), + /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ + .Name = { + 0x93, 0xAD, 0x25, 0x50, 0x9C, 0xB4, 0x11, 0xE7, + 0xB4, 0x23, 0x83, 0xDE, 0x96, 0x8B, 0xCD, 0x7C + }, + .Mode = cpu_to_le32(mode), + }; + cifs_dbg(FYI, "mode on posix create 0%o\n", mode); + cifs_end_extension(smb, sizeof(*buf)); } -static struct create_durable * -create_durable_buf(void) +static void fill_lease_context(struct TCP_Server_Info *server, + struct smb2_create_req *req, + struct smb_message *smb, + u8 *lease_key, + __u8 *oplock, + u8 *parent_lease_key, + __le32 flags) { - struct create_durable *buf; - - buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL); - if (!buf) - return NULL; - - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_durable, Data)); - buf->ccontext.DataLength = cpu_to_le32(16); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_durable, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DHnQ" */ - buf->Name[0] = 'D'; - buf->Name[1] = 'H'; - buf->Name[2] = 'n'; - buf->Name[3] = 'Q'; - return buf; + req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_LEASE; + server->ops->fill_lease_buf(smb, lease_key, *oplock, + parent_lease_key, flags); } -static struct create_durable * -create_reconnect_durable_buf(struct cifs_fid *fid) +static void fill_durable_v1_context(struct smb_message *smb) { - struct create_durable *buf; + struct create_durable *buf = cifs_begin_ccontext(smb); - buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL); - if (!buf) - return NULL; - - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_durable, Data)); - buf->ccontext.DataLength = cpu_to_le32(16); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_durable, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - buf->Data.Fid.PersistentFileId = fid->persistent_fid; - buf->Data.Fid.VolatileFileId = fid->volatile_fid; - /* SMB2_CREATE_DURABLE_HANDLE_RECONNECT is "DHnC" */ - buf->Name[0] = 'D'; - buf->Name[1] = 'H'; - buf->Name[2] = 'n'; - buf->Name[3] = 'C'; - return buf; + *buf = (struct create_durable){ + .ccontext = CREATE_CONTEXT_HDR(struct create_durable, Name, Data, 4), + /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DHnQ" */ + .Name = {'D', 'H', 'n', 'Q' }, + }; + cifs_end_extension(smb, sizeof(*buf)); } -static int -add_lease_context(struct TCP_Server_Info *server, - struct smb2_create_req *req, - struct kvec *iov, - unsigned int *num_iovec, - u8 *lease_key, - __u8 *oplock, - u8 *parent_lease_key, - __le32 flags) +static void fill_reconnect_durable_context(struct smb_message *smb, + struct cifs_open_parms *oparms) { - unsigned int num = *num_iovec; + struct create_durable *buf = cifs_begin_ccontext(smb); + struct cifs_fid *fid = oparms->fid; - iov[num].iov_base = server->ops->create_lease_buf(lease_key, *oplock, - parent_lease_key, flags); - if (iov[num].iov_base == NULL) - return -ENOMEM; - iov[num].iov_len = server->vals->create_lease_size; - req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_LEASE; - *num_iovec = num + 1; - return 0; + /* indicate that we don't need to relock the file */ + oparms->reconnect = false; + + *buf = (struct create_durable){ + .ccontext = CREATE_CONTEXT_HDR(struct create_durable, Name, Data, 4), + /* SMB2_CREATE_DURABLE_HANDLE_RECONNECT is "DHnC" */ + .Name = {'D', 'H', 'n', 'C' }, + .Data.Fid.PersistentFileId = fid->persistent_fid, + .Data.Fid.VolatileFileId = fid->volatile_fid, + }; + cifs_end_extension(smb, sizeof(*buf)); } -static struct create_durable_v2 * -create_durable_v2_buf(struct cifs_open_parms *oparms) +static void fill_durable_v2_context(struct smb_message *smb, + struct cifs_open_parms *oparms) { - struct cifs_fid *pfid = oparms->fid; - struct create_durable_v2 *buf; + struct create_durable_v2 *buf = cifs_begin_ccontext(smb); + struct cifs_fid *fid = oparms->fid; - buf = kzalloc(sizeof(struct create_durable_v2), GFP_KERNEL); - if (!buf) - return NULL; + *buf = (struct create_durable_v2){ + .ccontext = CREATE_CONTEXT_HDR(struct create_durable_v2, Name, dcontext, 4), + /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DH2Q" */ + .Name = {'D', 'H', '2', 'Q' }, - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_durable_v2, dcontext)); - buf->ccontext.DataLength = cpu_to_le32(sizeof(struct durable_context_v2)); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_durable_v2, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - - /* - * NB: Handle timeout defaults to 0, which allows server to choose - * (most servers default to 120 seconds) and most clients default to 0. - * This can be overridden at mount ("handletimeout=") if the user wants - * a different persistent (or resilient) handle timeout for all opens - * on a particular SMB3 mount. - */ - buf->dcontext.Timeout = cpu_to_le32(oparms->tcon->handle_timeout); - buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); + /* + * NB: Handle timeout defaults to 0, which allows server to + * choose (most servers default to 120 seconds) and most + * clients default to 0. This can be overridden at mount + * ("handletimeout=") if the user wants a different persistent + * (or resilient) handle timeout for all opens on a particular + * SMB3 mount. + */ + .dcontext.Timeout = cpu_to_le32(oparms->tcon->handle_timeout), + .dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT), + }; /* for replay, we should not overwrite the existing create guid */ if (!oparms->replay) { generate_random_uuid(buf->dcontext.CreateGuid); - memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16); - } else - memcpy(buf->dcontext.CreateGuid, pfid->create_guid, 16); - - /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DH2Q" */ - buf->Name[0] = 'D'; - buf->Name[1] = 'H'; - buf->Name[2] = '2'; - buf->Name[3] = 'Q'; - return buf; -} - -static struct create_durable_handle_reconnect_v2 * -create_reconnect_durable_v2_buf(struct cifs_fid *fid) -{ - struct create_durable_handle_reconnect_v2 *buf; - - buf = kzalloc(sizeof(struct create_durable_handle_reconnect_v2), - GFP_KERNEL); - if (!buf) - return NULL; - - buf->ccontext.DataOffset = - cpu_to_le16(offsetof(struct create_durable_handle_reconnect_v2, - dcontext)); - buf->ccontext.DataLength = - cpu_to_le32(sizeof(struct durable_reconnect_context_v2)); - buf->ccontext.NameOffset = - cpu_to_le16(offsetof(struct create_durable_handle_reconnect_v2, - Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - - buf->dcontext.Fid.PersistentFileId = fid->persistent_fid; - buf->dcontext.Fid.VolatileFileId = fid->volatile_fid; - buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); - memcpy(buf->dcontext.CreateGuid, fid->create_guid, 16); - - /* SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 is "DH2C" */ - buf->Name[0] = 'D'; - buf->Name[1] = 'H'; - buf->Name[2] = '2'; - buf->Name[3] = 'C'; - return buf; -} - -static int -add_durable_v2_context(struct kvec *iov, unsigned int *num_iovec, - struct cifs_open_parms *oparms) -{ - unsigned int num = *num_iovec; + memcpy(fid->create_guid, buf->dcontext.CreateGuid, 16); + } else { + memcpy(buf->dcontext.CreateGuid, fid->create_guid, 16); + } - iov[num].iov_base = create_durable_v2_buf(oparms); - if (iov[num].iov_base == NULL) - return -ENOMEM; - iov[num].iov_len = sizeof(struct create_durable_v2); - *num_iovec = num + 1; - return 0; + cifs_end_extension(smb, sizeof(*buf)); } -static int -add_durable_reconnect_v2_context(struct kvec *iov, unsigned int *num_iovec, - struct cifs_open_parms *oparms) +static void fill_durable_reconnect_v2_context(struct smb_message *smb, + struct cifs_open_parms *oparms) { - unsigned int num = *num_iovec; + struct create_durable_handle_reconnect_v2 *buf = cifs_begin_ccontext(smb); + struct cifs_fid *fid = oparms->fid; /* indicate that we don't need to relock the file */ oparms->reconnect = false; - iov[num].iov_base = create_reconnect_durable_v2_buf(oparms->fid); - if (iov[num].iov_base == NULL) - return -ENOMEM; - iov[num].iov_len = sizeof(struct create_durable_handle_reconnect_v2); - *num_iovec = num + 1; - return 0; + *buf = (struct create_durable_handle_reconnect_v2){ + .ccontext = CREATE_CONTEXT_HDR(struct create_durable_handle_reconnect_v2, + Name, dcontext, 4), + /* SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 is "DH2C" */ + .Name = {'D', 'H', '2', 'C' }, + .dcontext.Fid.PersistentFileId = fid->persistent_fid, + .dcontext.Fid.VolatileFileId = fid->volatile_fid, + .dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT), + }; + + memcpy(buf->dcontext.CreateGuid, fid->create_guid, 16); + cifs_end_extension(smb, sizeof(*buf)); } -static int -add_durable_context(struct kvec *iov, unsigned int *num_iovec, - struct cifs_open_parms *oparms, bool use_persistent) +static void +fill_durable_context(struct smb_message *smb, + struct cifs_open_parms *oparms, bool use_persistent) { - unsigned int num = *num_iovec; - if (use_persistent) { if (oparms->reconnect) - return add_durable_reconnect_v2_context(iov, num_iovec, - oparms); - else - return add_durable_v2_context(iov, num_iovec, oparms); + return fill_durable_reconnect_v2_context(smb, oparms); + return fill_durable_v2_context(smb, oparms); } - if (oparms->reconnect) { - iov[num].iov_base = create_reconnect_durable_buf(oparms->fid); - /* indicate that we don't need to relock the file */ - oparms->reconnect = false; - } else - iov[num].iov_base = create_durable_buf(); - if (iov[num].iov_base == NULL) - return -ENOMEM; - iov[num].iov_len = sizeof(struct create_durable); - *num_iovec = num + 1; - return 0; + if (oparms->reconnect) + return fill_reconnect_durable_context(smb, oparms); + return fill_durable_v1_context(smb); } /* See MS-SMB2 2.2.13.2.7 */ -static struct crt_twarp_ctxt * -create_twarp_buf(__u64 timewarp) +static void fill_twarp_context(struct smb_message *smb, __u64 timewarp) { - struct crt_twarp_ctxt *buf; - - buf = kzalloc(sizeof(struct crt_twarp_ctxt), GFP_KERNEL); - if (!buf) - return NULL; - - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct crt_twarp_ctxt, Timestamp)); - buf->ccontext.DataLength = cpu_to_le32(8); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct crt_twarp_ctxt, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - /* SMB2_CREATE_TIMEWARP_TOKEN is "TWrp" */ - buf->Name[0] = 'T'; - buf->Name[1] = 'W'; - buf->Name[2] = 'r'; - buf->Name[3] = 'p'; - buf->Timestamp = cpu_to_le64(timewarp); - return buf; -} + struct crt_twarp_ctxt *buf = cifs_begin_ccontext(smb); -/* See MS-SMB2 2.2.13.2.7 */ -static int -add_twarp_context(struct kvec *iov, unsigned int *num_iovec, __u64 timewarp) -{ - unsigned int num = *num_iovec; + *buf = (struct crt_twarp_ctxt){ + .ccontext = CREATE_CONTEXT_HDR(struct crt_twarp_ctxt, Name, Timestamp, 4), + /* SMB2_CREATE_TIMEWARP_TOKEN is "TWrp" */ + .Name = {'T', 'W', 'r', 'p' }, + .Timestamp = cpu_to_le64(timewarp), + }; - iov[num].iov_base = create_twarp_buf(timewarp); - if (iov[num].iov_base == NULL) - return -ENOMEM; - iov[num].iov_len = sizeof(struct crt_twarp_ctxt); - *num_iovec = num + 1; - return 0; + cifs_end_extension(smb, sizeof(*buf)); } /* See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx */ @@ -2706,26 +2587,21 @@ static void setup_owner_group_sids(char *buf) } /* See MS-SMB2 2.2.13.2.2 and MS-DTYP 2.4.6 */ -static struct crt_sd_ctxt * -create_sd_buf(umode_t mode, bool set_owner, unsigned int *len) +static void fill_sd_context(struct smb_message *smb, umode_t mode, bool set_owner) { - struct crt_sd_ctxt *buf; + struct crt_sd_ctxt *buf = cifs_begin_extension(smb); __u8 *ptr, *aclptr; unsigned int acelen, acl_size, ace_count; unsigned int owner_offset = 0; unsigned int group_offset = 0; + unsigned int len; struct smb3_acl acl = {}; - *len = ALIGN8(sizeof(struct crt_sd_ctxt) + (sizeof(struct smb_ace) * 4)); + len = ALIGN8(sizeof(struct crt_sd_ctxt) + (sizeof(struct smb_ace) * 4)); - if (set_owner) { + if (set_owner) /* sizeof(struct owner_group_sids) is already multiple of 8 so no need to round */ - *len += sizeof(struct owner_group_sids); - } - - buf = kzalloc(*len, GFP_KERNEL); - if (buf == NULL) - return buf; + len += sizeof(struct owner_group_sids); ptr = (__u8 *)&buf[1]; if (set_owner) { @@ -2791,33 +2667,15 @@ create_sd_buf(umode_t mode, bool set_owner, unsigned int *len) memcpy(aclptr, &acl, sizeof(struct smb3_acl)); buf->ccontext.DataLength = cpu_to_le32(ptr - (__u8 *)&buf->sd); - *len = ALIGN8((unsigned int)(ptr - (__u8 *)buf)); - - return buf; -} - -static int -add_sd_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode, bool set_owner) -{ - unsigned int num = *num_iovec; - unsigned int len = 0; - - iov[num].iov_base = create_sd_buf(mode, set_owner, &len); - if (iov[num].iov_base == NULL) - return -ENOMEM; - iov[num].iov_len = len; - *num_iovec = num + 1; - return 0; + len = ALIGN8((unsigned int)(ptr - (__u8 *)buf)); + cifs_end_extension(smb, len); + cifs_pad_to_8(smb); } -static struct crt_query_id_ctxt * -create_query_id_buf(void) +/* See MS-SMB2 2.2.13.2.9 */ +static void fill_query_id_context(struct smb_message *smb) { - struct crt_query_id_ctxt *buf; - - buf = kzalloc(sizeof(struct crt_query_id_ctxt), GFP_KERNEL); - if (!buf) - return NULL; + struct crt_query_id_ctxt *buf = cifs_begin_extension(smb); buf->ccontext.DataOffset = cpu_to_le16(0); buf->ccontext.DataLength = cpu_to_le32(0); @@ -2829,78 +2687,19 @@ create_query_id_buf(void) buf->Name[1] = 'F'; buf->Name[2] = 'i'; buf->Name[3] = 'd'; - return buf; -} - -/* See MS-SMB2 2.2.13.2.9 */ -static int -add_query_id_context(struct kvec *iov, unsigned int *num_iovec) -{ - unsigned int num = *num_iovec; - - iov[num].iov_base = create_query_id_buf(); - if (iov[num].iov_base == NULL) - return -ENOMEM; - iov[num].iov_len = sizeof(struct crt_query_id_ctxt); - *num_iovec = num + 1; - return 0; + cifs_end_extension(smb, sizeof(*buf)); } -static void add_ea_context(struct cifs_open_parms *oparms, - struct kvec *rq_iov, unsigned int *num_iovs) +static void fill_ea_context(struct smb_message *smb, const struct cifs_open_parms *oparms) { - struct kvec *iov = oparms->ea_cctx; + const struct kvec *iov = oparms->ea_cctx; if (iov && iov->iov_base && iov->iov_len) { - rq_iov[(*num_iovs)++] = *iov; - memset(iov, 0, sizeof(*iov)); - } -} - -static int -alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len, - const char *treename, const __le16 *path) -{ - int treename_len, path_len; - struct nls_table *cp; - const __le16 sep[] = {cpu_to_le16('\\'), cpu_to_le16(0x0000)}; + void *p = cifs_begin_extension(smb); - /* - * skip leading "\\" - */ - treename_len = strlen(treename); - if (treename_len < 2 || !(treename[0] == '\\' && treename[1] == '\\')) - return -EINVAL; - - treename += 2; - treename_len -= 2; - - path_len = UniStrnlen((wchar_t *)path, PATH_MAX); - - /* make room for one path separator only if @path isn't empty */ - *out_len = treename_len + (path[0] ? 1 : 0) + path_len; - - /* - * final path needs to be 8-byte aligned as specified in - * MS-SMB2 2.2.13 SMB2 CREATE Request. - */ - *out_size = ALIGN8(*out_len * sizeof(__le16)); - *out_path = kzalloc(*out_size + sizeof(__le16) /* null */, GFP_KERNEL); - if (!*out_path) - return -ENOMEM; - - cp = load_nls_default(); - cifs_strtoUTF16(*out_path, treename, treename_len, cp); - - /* Do not append the separator if the path is empty */ - if (path[0] != cpu_to_le16(0x0000)) { - UniStrcat((wchar_t *)*out_path, (wchar_t *)sep); - UniStrcat((wchar_t *)*out_path, (wchar_t *)path); + memcpy(p, iov->iov_base, iov->iov_len); + cifs_end_extension(smb, iov->iov_len); } - - unload_nls(cp); - - return 0; } struct smb2_create_layout { @@ -2912,7 +2711,6 @@ struct smb2_create_layout { u16 name_offset; /* Offset in message of name */ u16 contexts_offset; /* Offset in message contexts */ u16 contexts_len; /* Length of contexts (or 0) */ - u16 posix_offset; /* Offset in message of posix context */ u8 name_pad; /* Amount of name padding needed */ }; @@ -2920,8 +2718,6 @@ static int smb311_posix_mkdir_layout(struct cifs_tcon *tcon, struct smb2_create_layout *lay) { size_t offset = lay->offset; - size_t tmp; - bool has_contexts = false; /* [MS-SMB2] 2.2.13 NameOffset: If SMB2_FLAGS_DFS_OPERATIONS * is set in the Flags field of the SMB2 header, the file name @@ -2950,22 +2746,17 @@ static int smb311_posix_mkdir_layout(struct cifs_tcon *tcon, } offset += lay->name_len; - tmp = offset; - offset = ALIGN8(offset); - lay->name_pad = offset - tmp; + offset = ALIGN(offset, 8); lay->contexts_offset = offset; - if (tcon->posix_extensions) { + if (tcon->posix_extensions) /* resource #3: posix buf */ offset += sizeof(struct create_posix); - has_contexts = true; - } - if (has_contexts) - lay->contexts_len = offset - lay->contexts_offset; + if (offset == lay->contexts_offset) + lay->contexts_offset = 0; /* None */ else - lay->contexts_offset = 0; - + lay->contexts_len = offset - lay->contexts_offset; lay->offset = offset; return 0; } @@ -3065,13 +2856,12 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, memcpy(name, utf16_path, layout.path_len); } - if (layout.name_pad) - memset(smb->request + layout.name_offset + layout.name_len, - 0, layout.name_pad); + smb->offset = layout.name_offset + layout.name_len; + cifs_pad_to_8(smb); if (tcon->posix_extensions) /* resource #3: posix buf */ - fill_posix_buf(smb->request + layout.posix_offset, mode); + fill_posix_context(smb, mode); /* no need to inc num_remote_opens because we close it just below */ trace_smb3_posix_mkdir_enter(xid, tcon->tid, ses->Suid, full_path, CREATE_NOT_FILE, @@ -3119,81 +2909,185 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, return rc; } -int -SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, - struct smb_rqst *rqst, __u8 *oplock, - struct cifs_open_parms *oparms, __le16 *path) +/* + * Calculate the request size and layout for an "open" (i.e. Create) request. + */ +static int smb2_lay_out_open(const struct cifs_tcon *tcon, + const struct TCP_Server_Info *server, + __u8 oplock, + const struct cifs_open_parms *oparms, + struct smb2_create_layout *lay) +{ + size_t offset = lay->offset, csize, tmp; + + /* [MS-SMB2] 2.2.13 NameOffset: If SMB2_FLAGS_DFS_OPERATIONS + * is set in the Flags field of the SMB2 header, the file name + * includes a prefix that will be processed during DFS name + * normalization as specified in section 3.3.5.9. Otherwise, + * the file name is relative to the share that is identified + * by the TreeId in the SMB2 header. + */ + lay->name_offset = offset; + if (tcon->share_flags & SHI1005_FLAGS_DFS) { + const char *treename = tcon->tree_name; + int treename_len; + + /* skip leading "\\" */ + if (!(treename[0] == '\\' && treename[1] == '\\')) + return -EINVAL; + + treename_len = cifs_size_strtoUTF16(treename + 2, INT_MAX, lay->cp); + + lay->treename_len = treename_len; + lay->name_len = treename_len; + if (lay->path_len) + lay->name_len += 2 + lay->path_len; + } else { + lay->name_len = lay->path_len; + } + + offset += lay->name_len; + tmp = offset; + offset = round_up(offset, 8); + lay->name_pad = offset - tmp; + lay->contexts_offset = offset; + + /* TODO: This bit of code is very suspicious. */ + if (!(server->capabilities & SMB2_GLOBAL_CAP_LEASING) || + oplock == SMB2_OPLOCK_LEVEL_NONE) + ; + else if (!(server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) && + (oparms->create_options & CREATE_NOT_FILE)) + ; /* no srv lease support */ + else + offset = ALIGN(offset, 8) + server->vals->create_lease_size; + + if (oplock == SMB2_OPLOCK_LEVEL_BATCH) { + if (!tcon->use_persistent) + csize = sizeof(struct create_durable); + else if (oparms->reconnect) + csize = sizeof(struct create_durable_handle_reconnect_v2); + else + csize = sizeof(struct create_durable_v2); + offset = ALIGN(offset, 8) + csize; + } + + if (tcon->posix_extensions) + offset = ALIGN(offset, 8) + sizeof(struct create_posix); + if (tcon->snapshot_time) + offset = ALIGN(offset, 8) + sizeof(struct crt_twarp_ctxt); + + if (oparms->disposition != FILE_OPEN && oparms->cifs_sb) { + bool set_mode; + bool set_owner; + + set_mode = (oparms->cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) && + (oparms->mode != ACL_NO_MODE); + + set_owner = oparms->cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL; + + if (set_owner || set_mode) { + csize = sizeof(struct crt_sd_ctxt) + sizeof(struct smb_ace) * 4; + csize = round_up(csize, 8); + + if (set_owner) { + /* sizeof(struct owner_group_sids) is already + * multiple of 8 so no need to round */ + csize += sizeof(struct owner_group_sids); + } + } + offset = ALIGN(offset, 8) + csize; + } + + offset = ALIGN(offset, 8) + sizeof(struct crt_query_id_ctxt); + + /* TODO: Pass down the desired EA list and render directly into the buffer. */ + if (oparms->ea_cctx->iov_base && oparms->ea_cctx->iov_len) + offset = ALIGN(offset, 8) + oparms->ea_cctx->iov_len; + + if (tcon->posix_extensions) + offset = ALIGN(offset, 8) + sizeof(struct create_posix); + + lay->contexts_len = offset - lay->contexts_offset; + lay->offset = offset; + return 0; +} + +struct smb_message *SMB2_open_init(struct cifs_tcon *tcon, + struct TCP_Server_Info *server, __u8 *oplock, + struct cifs_open_parms *oparms, __le16 *path) { struct smb2_create_req *req; - unsigned int n_iov = 2; + struct smb_message *smb; __u32 file_attributes = 0; - int copy_size; - int uni_path_len; - unsigned int total_len; - struct kvec *iov = rqst->rq_iov; - __le16 *copy_path; int rc; - rc = smb2_plain_req_init(SMB2_CREATE, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; + if (!server->oplocks || tcon->no_lease) + *oplock = SMB2_OPLOCK_LEVEL_NONE; - iov[0].iov_base = (char *)req; - /* -1 since last byte is buf[0] which is sent below (path) */ - iov[0].iov_len = total_len - 1; + struct smb2_create_layout layout = { + .cp = oparms->cifs_sb->local_nls, + .offset = sizeof(struct smb2_create_req), + .path_len = UniStrnlen((wchar_t *)path, PATH_MAX) * 2, + }; + + rc = smb2_lay_out_open(tcon, server, *oplock, oparms, &layout); + if (rc < 0) + return ERR_PTR(rc); + + smb = smb2_create_request(SMB2_CREATE, server, tcon, + sizeof(*req), layout.offset, 0, + SMB2_REQ_DYNAMIC); + if (!smb) + return ERR_PTR(-ENOMEM); if (oparms->create_options & CREATE_OPTION_READONLY) file_attributes |= ATTR_READONLY; if (oparms->create_options & CREATE_OPTION_SPECIAL) file_attributes |= ATTR_SYSTEM; - req->ImpersonationLevel = IL_IMPERSONATION; - req->DesiredAccess = cpu_to_le32(oparms->desired_access); + req->ImpersonationLevel = IL_IMPERSONATION; + req->DesiredAccess = cpu_to_le32(oparms->desired_access); /* File attributes ignored on open (used in create though) */ - req->FileAttributes = cpu_to_le32(file_attributes); - req->ShareAccess = FILE_SHARE_ALL_LE; - - req->CreateDisposition = cpu_to_le32(oparms->disposition); - req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK); - req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req)); + req->FileAttributes = cpu_to_le32(file_attributes); + req->ShareAccess = FILE_SHARE_ALL_LE; + req->CreateDisposition = cpu_to_le32(oparms->disposition); + req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK); + req->NameOffset = cpu_to_le16(layout.name_offset); + req->NameLength = cpu_to_le16(layout.name_len); + req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE; + req->CreateContextsOffset = cpu_to_le32(layout.contexts_offset); + req->CreateContextsLength = cpu_to_le32(layout.contexts_len); /* [MS-SMB2] 2.2.13 NameOffset: - * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of - * the SMB2 header, the file name includes a prefix that will - * be processed during DFS name normalization as specified in - * section 3.3.5.9. Otherwise, the file name is relative to - * the share that is identified by the TreeId in the SMB2 - * header. + * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of the SMB2 + * header, the file name includes a prefix that will be processed + * during DFS name normalization as specified in section + * 3.3.5.9. Otherwise, the file name is relative to the share that is + * identified by the TreeId in the SMB2 header. */ + __le16 *name = smb->request + layout.name_offset; + if (tcon->share_flags & SHI1005_FLAGS_DFS) { - int name_len; + int tmp; req->hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS; - rc = alloc_path_with_tree_prefix(©_path, ©_size, - &name_len, - tcon->tree_name, path); - if (rc) - return rc; - req->NameLength = cpu_to_le16(name_len * 2); - uni_path_len = copy_size; - path = copy_path; - } else { - uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2; - /* MUST set path len (NameLength) to 0 opening root of share */ - req->NameLength = cpu_to_le16(uni_path_len - 2); - copy_size = ALIGN8(uni_path_len); - copy_path = kzalloc(copy_size, GFP_KERNEL); - if (!copy_path) - return -ENOMEM; - memcpy((char *)copy_path, (const char *)path, - uni_path_len); - uni_path_len = copy_size; - path = copy_path; + + tmp = cifs_strtoUTF16(name, tcon->tree_name + 2, INT_MAX, + layout.cp); + WARN_ON(tmp != layout.treename_len); + name += tmp; + if (layout.path_len) { + *name++ = cpu_to_le16('\\'); + memcpy(name, path, layout.path_len); + } + } else if (layout.path_len) { + memcpy(name, path, layout.path_len); } - iov[1].iov_len = uni_path_len; - iov[1].iov_base = path; + smb->offset = layout.name_offset + layout.name_len; + cifs_pad_to_8(smb); + WARN_ON_ONCE(smb->offset != layout.contexts_offset); if ((!server->oplocks) || (tcon->no_lease)) *oplock = SMB2_OPLOCK_LEVEL_NONE; @@ -3205,32 +3099,21 @@ SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, (oparms->create_options & CREATE_NOT_FILE)) req->RequestedOplockLevel = *oplock; /* no srv lease support */ else { - rc = add_lease_context(server, req, iov, &n_iov, - oparms->fid->lease_key, oplock, - oparms->fid->parent_lease_key, - oparms->lease_flags); - if (rc) - return rc; + fill_lease_context(server, req, smb, + oparms->fid->lease_key, oplock, + oparms->fid->parent_lease_key, + oparms->lease_flags); } - if (*oplock == SMB2_OPLOCK_LEVEL_BATCH) { - rc = add_durable_context(iov, &n_iov, oparms, - tcon->use_persistent); - if (rc) - return rc; - } + if (*oplock == SMB2_OPLOCK_LEVEL_BATCH) + fill_durable_context(smb, oparms, tcon->use_persistent); - if (tcon->posix_extensions) { - rc = add_posix_context(iov, &n_iov, oparms->mode); - if (rc) - return rc; - } + if (tcon->posix_extensions) + fill_posix_context(smb, oparms->mode); if (tcon->snapshot_time) { cifs_dbg(FYI, "adding snapshot context\n"); - rc = add_twarp_context(iov, &n_iov, tcon->snapshot_time); - if (rc) - return rc; + fill_twarp_context(smb, tcon->snapshot_time); } if ((oparms->disposition != FILE_OPEN) && (oparms->cifs_sb)) { @@ -3252,40 +3135,13 @@ SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, if (set_owner | set_mode) { cifs_dbg(FYI, "add sd with mode 0x%x\n", oparms->mode); - rc = add_sd_context(iov, &n_iov, oparms->mode, set_owner); - if (rc) - return rc; + fill_sd_context(smb, oparms->mode, set_owner); } } - add_query_id_context(iov, &n_iov); - add_ea_context(oparms, iov, &n_iov); - - if (n_iov > 2) { - /* - * We have create contexts behind iov[1] (the file - * name), point at them from the main create request - */ - req->CreateContextsOffset = cpu_to_le32( - sizeof(struct smb2_create_req) + - iov[1].iov_len); - req->CreateContextsLength = 0; - - for (unsigned int i = 2; i < (n_iov-1); i++) { - struct kvec *v = &iov[i]; - size_t len = v->iov_len; - struct create_context *cctx = - (struct create_context *)v->iov_base; - - cctx->Next = cpu_to_le32(len); - le32_add_cpu(&req->CreateContextsLength, len); - } - le32_add_cpu(&req->CreateContextsLength, - iov[n_iov-1].iov_len); - } - - rqst->rq_nvec = n_iov; - return 0; + fill_query_id_context(smb); + fill_ea_context(smb, oparms); + return smb; } static void @@ -3333,14 +3189,14 @@ parse_posix_ctxt(struct create_context *cc, struct smb2_file_all_info *info, posix->nlink, posix->mode, posix->reparse_tag); } -int smb2_parse_contexts(struct TCP_Server_Info *server, - struct kvec *rsp_iov, - __u16 *epoch, - char *lease_key, __u8 *oplock, - struct smb2_file_all_info *buf, - struct create_posix_rsp *posix) +int smb2_parse_create_response(struct TCP_Server_Info *server, + struct smb_message *smb, + __u16 *epoch, + char *lease_key, __u8 *oplock, + struct smb2_file_all_info *buf, + struct create_posix_rsp *posix) { - struct smb2_create_rsp *rsp = rsp_iov->iov_base; + struct smb2_create_rsp *rsp = smb->response; struct create_context *cc; size_t rem, off, len; size_t doff, dlen; @@ -3356,7 +3212,7 @@ int smb2_parse_contexts(struct TCP_Server_Info *server, off = le32_to_cpu(rsp->CreateContextsOffset); rem = le32_to_cpu(rsp->CreateContextsLength); - if (check_add_overflow(off, rem, &len) || len > rsp_iov->iov_len) + if (check_add_overflow(off, rem, &len) || len > smb->response_len) return -EINVAL; cc = (struct create_context *)((u8 *)rsp + off); @@ -3412,39 +3268,18 @@ int smb2_parse_contexts(struct TCP_Server_Info *server, return 0; } -/* rq_iov[0] is the request and is released by cifs_small_buf_release(). - * All other vectors are freed by kfree(). - */ -void -SMB2_open_free(struct smb_rqst *rqst) -{ - int i; - - if (rqst && rqst->rq_iov) { - cifs_small_buf_release(rqst->rq_iov[0].iov_base); - for (i = 1; i < rqst->rq_nvec; i++) - if (rqst->rq_iov[i].iov_base != smb2_padding) - kfree(rqst->rq_iov[i].iov_base); - } -} - -int -SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, - __u8 *oplock, struct smb2_file_all_info *buf, - struct create_posix_rsp *posix, - struct kvec *err_iov, int *buftype) +int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, + __u8 *oplock, struct smb2_file_all_info *buf, + struct create_posix_rsp *posix, struct kvec *err_iov) { - struct smb_rqst rqst; + struct TCP_Server_Info *server; struct smb2_create_rsp *rsp = NULL; + struct smb_message *smb; struct cifs_tcon *tcon = oparms->tcon; struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server; - struct kvec iov[SMB2_CREATE_IOV_SIZE]; - struct kvec rsp_iov = {NULL, 0}; - int resp_buftype = CIFS_NO_BUFFER; - int rc = 0; - int flags = 0; int retries = 0, cur_sleep = 1; + int flags = 0; + int rc = 0; replay_again: /* reinitialize for possible replay */ @@ -3459,34 +3294,27 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; - memset(&rqst, 0, sizeof(struct smb_rqst)); - memset(&iov, 0, sizeof(iov)); - rqst.rq_iov = iov; - rqst.rq_nvec = SMB2_CREATE_IOV_SIZE; - - rc = SMB2_open_init(tcon, server, - &rqst, oplock, oparms, path); - if (rc) + smb = SMB2_open_init(tcon, server, oplock, oparms, path); + if (IS_ERR(smb)) { + rc = PTR_ERR(smb); + smb = NULL; goto creat_exit; + } trace_smb3_open_enter(xid, tcon->tid, tcon->ses->Suid, oparms->path, oparms->create_options, oparms->desired_access); if (retries) - smb2_set_replay(server, &rqst); + smb2_set_replay_smb(server, smb); - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, - &rsp_iov); - rsp = (struct smb2_create_rsp *)rsp_iov.iov_base; + rc = smb_send_recv_messages(xid, ses, server, smb, flags); + rsp = (struct smb2_create_rsp *)smb->response; if (rc != 0) { cifs_stats_fail_inc(tcon, SMB2_CREATE); if (err_iov && rsp) { - *err_iov = rsp_iov; - *buftype = resp_buftype; - resp_buftype = CIFS_NO_BUFFER; - rsp = NULL; + err_iov->iov_base = smb->response; + err_iov->iov_len = smb->response_len; } trace_smb3_open_err(xid, tcon->tid, ses->Suid, oparms->create_options, oparms->desired_access, rc); @@ -3496,10 +3324,11 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, tcon->need_reconnect = true; } goto creat_exit; - } else if (rsp == NULL) /* unlikely to happen, but safer to check */ + } + if (!rsp) /* unlikely to happen, but safer to check */ goto creat_exit; - else - trace_smb3_open_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid, + + trace_smb3_open_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid, oparms->create_options, oparms->desired_access); atomic_inc(&tcon->num_remote_opens); @@ -3523,12 +3352,10 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, } - rc = smb2_parse_contexts(server, &rsp_iov, &oparms->fid->epoch, - oparms->fid->lease_key, oplock, buf, posix); + rc = smb2_parse_create_response(server, smb, &oparms->fid->epoch, + oparms->fid->lease_key, oplock, buf, posix); creat_exit: - SMB2_open_free(&rqst); - free_rsp_buf(resp_buftype, rsp); - + smb_put_messages(smb); if (is_replayable_error(rc) && smb2_should_replay(tcon, &retries, &cur_sleep)) goto replay_again; diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h index 22284a52f300..f854093dd92c 100644 --- a/fs/smb/client/smb2proto.h +++ b/fs/smb/client/smb2proto.h @@ -151,17 +151,12 @@ extern int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, struct cifs_tcon *tcon, const struct nls_table *); extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon); -extern int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, - __le16 *path, __u8 *oplock, - struct smb2_file_all_info *buf, - struct create_posix_rsp *posix, - struct kvec *err_iov, int *resp_buftype); -extern int SMB2_open_init(struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - struct smb_rqst *rqst, - __u8 *oplock, struct cifs_open_parms *oparms, - __le16 *path); -extern void SMB2_open_free(struct smb_rqst *rqst); +int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, + __u8 *oplock, struct smb2_file_all_info *buf, + struct create_posix_rsp *posix, struct kvec *err_iov); +struct smb_message *SMB2_open_init(struct cifs_tcon *tcon, + struct TCP_Server_Info *server, __u8 *oplock, + struct cifs_open_parms *oparms, __le16 *path); extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, u32 opcode, char *in_data, u32 indatalen, u32 maxoutlen, @@ -276,12 +271,12 @@ extern int smb3_validate_negotiate(const unsigned int, struct cifs_tcon *); extern enum securityEnum smb2_select_sectype(struct TCP_Server_Info *, enum securityEnum); -int smb2_parse_contexts(struct TCP_Server_Info *server, - struct kvec *rsp_iov, - __u16 *epoch, - char *lease_key, __u8 *oplock, - struct smb2_file_all_info *buf, - struct create_posix_rsp *posix); +int smb2_parse_create_response(struct TCP_Server_Info *server, + struct smb_message *smb, + __u16 *epoch, + char *lease_key, __u8 *oplock, + struct smb2_file_all_info *buf, + struct create_posix_rsp *posix); extern int smb3_encryption_required(const struct cifs_tcon *tcon); extern int smb2_validate_iov(unsigned int offset, unsigned int buffer_length,