Loading fs/cifs/cifs_unicode.c +61 −0 Original line number Diff line number Diff line Loading @@ -330,3 +330,64 @@ cifsConvertToUTF16(__le16 *target, const char *source, int srclen, ctoUTF16_out: return i; } #ifdef CONFIG_CIFS_SMB2 /* * cifs_local_to_utf16_bytes - how long will a string be after conversion? * @from - pointer to input string * @maxbytes - don't go past this many bytes of input string * @codepage - source codepage * * Walk a string and return the number of bytes that the string will * be after being converted to the given charset, not including any null * termination required. Don't walk past maxbytes in the source buffer. */ static int cifs_local_to_utf16_bytes(const char *from, int len, const struct nls_table *codepage) { int charlen; int i; wchar_t wchar_to; for (i = 0; len && *from; i++, from += charlen, len -= charlen) { charlen = codepage->char2uni(from, len, &wchar_to); /* Failed conversion defaults to a question mark */ if (charlen < 1) charlen = 1; } return 2 * i; /* UTF16 characters are two bytes */ } /* * cifs_strndup_to_utf16 - copy a string to wire format from the local codepage * @src - source string * @maxlen - don't walk past this many bytes in the source string * @utf16_len - the length of the allocated string in bytes (including null) * @cp - source codepage * @remap - map special chars * * Take a string convert it from the local codepage to UTF16 and * put it in a new buffer. Returns a pointer to the new string or NULL on * error. */ __le16 * cifs_strndup_to_utf16(const char *src, const int maxlen, int *utf16_len, const struct nls_table *cp, int remap) { int len; __le16 *dst; len = cifs_local_to_utf16_bytes(src, maxlen, cp); len += 2; /* NULL */ dst = kmalloc(len, GFP_KERNEL); if (!dst) { *utf16_len = 0; return NULL; } cifsConvertToUTF16(dst, src, strlen(src), cp, remap); *utf16_len = len; return dst; } #endif /* CONFIG_CIFS_SMB2 */ fs/cifs/cifs_unicode.h +5 −0 Original line number Diff line number Diff line Loading @@ -84,6 +84,11 @@ char *cifs_strndup_from_utf16(const char *src, const int maxlen, const struct nls_table *codepage); extern int cifsConvertToUTF16(__le16 *target, const char *source, int maxlen, const struct nls_table *cp, int mapChars); #ifdef CONFIG_CIFS_SMB2 extern __le16 *cifs_strndup_to_utf16(const char *src, const int maxlen, int *utf16_len, const struct nls_table *cp, int remap); #endif /* CONFIG_CIFS_SMB2 */ #endif /* Loading fs/cifs/smb2misc.c +25 −0 Original line number Diff line number Diff line Loading @@ -230,6 +230,11 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr) ((struct smb2_sess_setup_rsp *)hdr)->SecurityBufferLength); break; case SMB2_CREATE: *off = le32_to_cpu( ((struct smb2_create_rsp *)hdr)->CreateContextsOffset); *len = le32_to_cpu( ((struct smb2_create_rsp *)hdr)->CreateContextsLength); break; case SMB2_READ: case SMB2_QUERY_INFO: case SMB2_QUERY_DIRECTORY: Loading Loading @@ -315,3 +320,23 @@ smb2_calc_size(struct smb2_hdr *hdr) cFYI(1, "SMB2 len %d", len); return len; } /* Note: caller must free return buffer */ __le16 * cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb) { int len; const char *start_of_path; __le16 *to; /* Windows doesn't allow paths beginning with \ */ if (from[0] == '\\') start_of_path = from + 1; else start_of_path = from; to = cifs_strndup_to_utf16(start_of_path, PATH_MAX, &len, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); return to; } fs/cifs/smb2ops.c +25 −0 Original line number Diff line number Diff line Loading @@ -157,6 +157,30 @@ smb2_negotiate(const unsigned int xid, struct cifs_ses *ses) return rc; } static int smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, const char *full_path) { int rc; __u64 persistent_fid, volatile_fid; __le16 *utf16_path; utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); if (!utf16_path) return -ENOMEM; rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid, FILE_READ_ATTRIBUTES, FILE_OPEN, 0, 0); if (rc) { kfree(utf16_path); return rc; } rc = SMB2_close(xid, tcon, persistent_fid, volatile_fid); kfree(utf16_path); return rc; } struct smb_version_operations smb21_operations = { .setup_request = smb2_setup_request, .check_receive = smb2_check_receive, Loading @@ -174,6 +198,7 @@ struct smb_version_operations smb21_operations = { .logoff = SMB2_logoff, .tree_connect = SMB2_tcon, .tree_disconnect = SMB2_tdis, .is_path_accessible = smb2_is_path_accessible, }; struct smb_version_values smb21_values = { Loading fs/cifs/smb2pdu.c +132 −0 Original line number Diff line number Diff line Loading @@ -829,3 +829,135 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) return rc; } int SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path, u64 *persistent_fid, u64 *volatile_fid, __u32 desired_access, __u32 create_disposition, __u32 file_attributes, __u32 create_options) { struct smb2_create_req *req; struct smb2_create_rsp *rsp; struct TCP_Server_Info *server; struct cifs_ses *ses = tcon->ses; struct kvec iov[2]; int resp_buftype; int uni_path_len; int rc = 0; int num_iovecs = 2; cFYI(1, "create/open"); if (ses && (ses->server)) server = ses->server; else return -EIO; rc = small_smb2_init(SMB2_CREATE, tcon, (void **) &req); if (rc) return rc; if (enable_oplocks) req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_BATCH; else req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE; req->ImpersonationLevel = IL_IMPERSONATION; req->DesiredAccess = cpu_to_le32(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(create_disposition); req->CreateOptions = cpu_to_le32(create_options); uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2; req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req) - 1 /* pad */ - 4 /* do not count rfc1001 len field */); iov[0].iov_base = (char *)req; /* 4 for rfc1002 length field */ iov[0].iov_len = get_rfc1002_length(req) + 4; /* MUST set path len (NameLength) to 0 opening root of share */ if (uni_path_len >= 4) { req->NameLength = cpu_to_le16(uni_path_len - 2); /* -1 since last byte is buf[0] which is sent below (path) */ iov[0].iov_len--; iov[1].iov_len = uni_path_len; iov[1].iov_base = path; /* * -1 since last byte is buf[0] which was counted in * smb2_buf_len. */ inc_rfc1001_len(req, uni_path_len - 1); } else { num_iovecs = 1; req->NameLength = 0; } rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0); rsp = (struct smb2_create_rsp *)iov[0].iov_base; if (rc != 0) { cifs_stats_fail_inc(tcon, SMB2_CREATE_HE); goto creat_exit; } if (rsp == NULL) { rc = -EIO; goto creat_exit; } *persistent_fid = rsp->PersistentFileId; *volatile_fid = rsp->VolatileFileId; creat_exit: free_rsp_buf(resp_buftype, rsp); return rc; } int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid) { struct smb2_close_req *req; struct smb2_close_rsp *rsp; struct TCP_Server_Info *server; struct cifs_ses *ses = tcon->ses; struct kvec iov[1]; int resp_buftype; int rc = 0; cFYI(1, "Close"); if (ses && (ses->server)) server = ses->server; else return -EIO; rc = small_smb2_init(SMB2_CLOSE, tcon, (void **) &req); if (rc) return rc; req->PersistentFileId = persistent_fid; req->VolatileFileId = volatile_fid; iov[0].iov_base = (char *)req; /* 4 for rfc1002 length field */ iov[0].iov_len = get_rfc1002_length(req) + 4; rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, 0); rsp = (struct smb2_close_rsp *)iov[0].iov_base; if (rc != 0) { if (tcon) cifs_stats_fail_inc(tcon, SMB2_CLOSE_HE); goto close_exit; } if (rsp == NULL) { rc = -EIO; goto close_exit; } /* BB FIXME - decode close response, update inode for caching */ close_exit: free_rsp_buf(resp_buftype, rsp); return rc; } Loading
fs/cifs/cifs_unicode.c +61 −0 Original line number Diff line number Diff line Loading @@ -330,3 +330,64 @@ cifsConvertToUTF16(__le16 *target, const char *source, int srclen, ctoUTF16_out: return i; } #ifdef CONFIG_CIFS_SMB2 /* * cifs_local_to_utf16_bytes - how long will a string be after conversion? * @from - pointer to input string * @maxbytes - don't go past this many bytes of input string * @codepage - source codepage * * Walk a string and return the number of bytes that the string will * be after being converted to the given charset, not including any null * termination required. Don't walk past maxbytes in the source buffer. */ static int cifs_local_to_utf16_bytes(const char *from, int len, const struct nls_table *codepage) { int charlen; int i; wchar_t wchar_to; for (i = 0; len && *from; i++, from += charlen, len -= charlen) { charlen = codepage->char2uni(from, len, &wchar_to); /* Failed conversion defaults to a question mark */ if (charlen < 1) charlen = 1; } return 2 * i; /* UTF16 characters are two bytes */ } /* * cifs_strndup_to_utf16 - copy a string to wire format from the local codepage * @src - source string * @maxlen - don't walk past this many bytes in the source string * @utf16_len - the length of the allocated string in bytes (including null) * @cp - source codepage * @remap - map special chars * * Take a string convert it from the local codepage to UTF16 and * put it in a new buffer. Returns a pointer to the new string or NULL on * error. */ __le16 * cifs_strndup_to_utf16(const char *src, const int maxlen, int *utf16_len, const struct nls_table *cp, int remap) { int len; __le16 *dst; len = cifs_local_to_utf16_bytes(src, maxlen, cp); len += 2; /* NULL */ dst = kmalloc(len, GFP_KERNEL); if (!dst) { *utf16_len = 0; return NULL; } cifsConvertToUTF16(dst, src, strlen(src), cp, remap); *utf16_len = len; return dst; } #endif /* CONFIG_CIFS_SMB2 */
fs/cifs/cifs_unicode.h +5 −0 Original line number Diff line number Diff line Loading @@ -84,6 +84,11 @@ char *cifs_strndup_from_utf16(const char *src, const int maxlen, const struct nls_table *codepage); extern int cifsConvertToUTF16(__le16 *target, const char *source, int maxlen, const struct nls_table *cp, int mapChars); #ifdef CONFIG_CIFS_SMB2 extern __le16 *cifs_strndup_to_utf16(const char *src, const int maxlen, int *utf16_len, const struct nls_table *cp, int remap); #endif /* CONFIG_CIFS_SMB2 */ #endif /* Loading
fs/cifs/smb2misc.c +25 −0 Original line number Diff line number Diff line Loading @@ -230,6 +230,11 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr) ((struct smb2_sess_setup_rsp *)hdr)->SecurityBufferLength); break; case SMB2_CREATE: *off = le32_to_cpu( ((struct smb2_create_rsp *)hdr)->CreateContextsOffset); *len = le32_to_cpu( ((struct smb2_create_rsp *)hdr)->CreateContextsLength); break; case SMB2_READ: case SMB2_QUERY_INFO: case SMB2_QUERY_DIRECTORY: Loading Loading @@ -315,3 +320,23 @@ smb2_calc_size(struct smb2_hdr *hdr) cFYI(1, "SMB2 len %d", len); return len; } /* Note: caller must free return buffer */ __le16 * cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb) { int len; const char *start_of_path; __le16 *to; /* Windows doesn't allow paths beginning with \ */ if (from[0] == '\\') start_of_path = from + 1; else start_of_path = from; to = cifs_strndup_to_utf16(start_of_path, PATH_MAX, &len, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); return to; }
fs/cifs/smb2ops.c +25 −0 Original line number Diff line number Diff line Loading @@ -157,6 +157,30 @@ smb2_negotiate(const unsigned int xid, struct cifs_ses *ses) return rc; } static int smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, const char *full_path) { int rc; __u64 persistent_fid, volatile_fid; __le16 *utf16_path; utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); if (!utf16_path) return -ENOMEM; rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid, FILE_READ_ATTRIBUTES, FILE_OPEN, 0, 0); if (rc) { kfree(utf16_path); return rc; } rc = SMB2_close(xid, tcon, persistent_fid, volatile_fid); kfree(utf16_path); return rc; } struct smb_version_operations smb21_operations = { .setup_request = smb2_setup_request, .check_receive = smb2_check_receive, Loading @@ -174,6 +198,7 @@ struct smb_version_operations smb21_operations = { .logoff = SMB2_logoff, .tree_connect = SMB2_tcon, .tree_disconnect = SMB2_tdis, .is_path_accessible = smb2_is_path_accessible, }; struct smb_version_values smb21_values = { Loading
fs/cifs/smb2pdu.c +132 −0 Original line number Diff line number Diff line Loading @@ -829,3 +829,135 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) return rc; } int SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path, u64 *persistent_fid, u64 *volatile_fid, __u32 desired_access, __u32 create_disposition, __u32 file_attributes, __u32 create_options) { struct smb2_create_req *req; struct smb2_create_rsp *rsp; struct TCP_Server_Info *server; struct cifs_ses *ses = tcon->ses; struct kvec iov[2]; int resp_buftype; int uni_path_len; int rc = 0; int num_iovecs = 2; cFYI(1, "create/open"); if (ses && (ses->server)) server = ses->server; else return -EIO; rc = small_smb2_init(SMB2_CREATE, tcon, (void **) &req); if (rc) return rc; if (enable_oplocks) req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_BATCH; else req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE; req->ImpersonationLevel = IL_IMPERSONATION; req->DesiredAccess = cpu_to_le32(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(create_disposition); req->CreateOptions = cpu_to_le32(create_options); uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2; req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req) - 1 /* pad */ - 4 /* do not count rfc1001 len field */); iov[0].iov_base = (char *)req; /* 4 for rfc1002 length field */ iov[0].iov_len = get_rfc1002_length(req) + 4; /* MUST set path len (NameLength) to 0 opening root of share */ if (uni_path_len >= 4) { req->NameLength = cpu_to_le16(uni_path_len - 2); /* -1 since last byte is buf[0] which is sent below (path) */ iov[0].iov_len--; iov[1].iov_len = uni_path_len; iov[1].iov_base = path; /* * -1 since last byte is buf[0] which was counted in * smb2_buf_len. */ inc_rfc1001_len(req, uni_path_len - 1); } else { num_iovecs = 1; req->NameLength = 0; } rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0); rsp = (struct smb2_create_rsp *)iov[0].iov_base; if (rc != 0) { cifs_stats_fail_inc(tcon, SMB2_CREATE_HE); goto creat_exit; } if (rsp == NULL) { rc = -EIO; goto creat_exit; } *persistent_fid = rsp->PersistentFileId; *volatile_fid = rsp->VolatileFileId; creat_exit: free_rsp_buf(resp_buftype, rsp); return rc; } int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid) { struct smb2_close_req *req; struct smb2_close_rsp *rsp; struct TCP_Server_Info *server; struct cifs_ses *ses = tcon->ses; struct kvec iov[1]; int resp_buftype; int rc = 0; cFYI(1, "Close"); if (ses && (ses->server)) server = ses->server; else return -EIO; rc = small_smb2_init(SMB2_CLOSE, tcon, (void **) &req); if (rc) return rc; req->PersistentFileId = persistent_fid; req->VolatileFileId = volatile_fid; iov[0].iov_base = (char *)req; /* 4 for rfc1002 length field */ iov[0].iov_len = get_rfc1002_length(req) + 4; rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, 0); rsp = (struct smb2_close_rsp *)iov[0].iov_base; if (rc != 0) { if (tcon) cifs_stats_fail_inc(tcon, SMB2_CLOSE_HE); goto close_exit; } if (rsp == NULL) { rc = -EIO; goto close_exit; } /* BB FIXME - decode close response, update inode for caching */ close_exit: free_rsp_buf(resp_buftype, rsp); return rc; }