| // SPDX-License-Identifier: GPL-2.0-or-later |
| /* |
| * |
| * Copyright (c) International Business Machines Corp., 2002,2008 |
| * Author(s): Steve French (sfrench@us.ibm.com) |
| * |
| * Error mapping routines from Samba libsmb/errormap.c |
| * Copyright (C) Andrew Tridgell 2001 |
| * Copyright (C) Luke Kenneth Casson Leighton 1997-2001. |
| */ |
| |
| #include <linux/bsearch.h> |
| #include "cifsproto.h" |
| #include "smb1proto.h" |
| #include "smberr.h" |
| #include "nterr.h" |
| #include "cifs_debug.h" |
| |
| static __always_inline int smb1_posix_error_cmp(const void *_key, const void *_pivot) |
| { |
| __u16 key = *(__u16 *)_key; |
| const struct smb_to_posix_error *pivot = _pivot; |
| |
| if (key < pivot->smb_err) |
| return -1; |
| if (key > pivot->smb_err) |
| return 1; |
| return 0; |
| } |
| |
| static const struct smb_to_posix_error mapping_table_ERRDOS[] = { |
| /* |
| * Automatically generated by the `gen_smb1_mapping` script, |
| * sorted by DOS error code (ascending). |
| */ |
| #include "smb1_err_dos_map.c" |
| }; |
| |
| static const struct smb_to_posix_error mapping_table_ERRSRV[] = { |
| /* |
| * Automatically generated by the `gen_smb1_mapping` script, |
| * sorted by SRV error code (ascending). |
| */ |
| #include "smb1_err_srv_map.c" |
| }; |
| |
| /***************************************************************************** |
| *convert a NT status code to a dos class/code |
| *****************************************************************************/ |
| |
| static __always_inline int ntstatus_to_dos_cmp(const void *_key, const void *_pivot) |
| { |
| __u32 key = *(__u32 *)_key; |
| const struct ntstatus_to_dos_err *pivot = _pivot; |
| |
| if (key < pivot->ntstatus) |
| return -1; |
| if (key > pivot->ntstatus) |
| return 1; |
| return 0; |
| } |
| |
| /* NT status -> dos error map */ |
| static const struct ntstatus_to_dos_err ntstatus_to_dos_map[] = { |
| /* |
| * Automatically generated by the `gen_smb1_mapping` script, |
| * sorted by NT status code (ascending). |
| */ |
| #include "smb1_mapping_table.c" |
| }; |
| |
| static const struct ntstatus_to_dos_err * |
| search_ntstatus_to_dos_map(__u32 ntstatus) |
| { |
| return __inline_bsearch(&ntstatus, ntstatus_to_dos_map, |
| ARRAY_SIZE(ntstatus_to_dos_map), |
| sizeof(struct ntstatus_to_dos_err), |
| ntstatus_to_dos_cmp); |
| } |
| |
| static const struct smb_to_posix_error * |
| search_mapping_table_ERRDOS(__u16 smb_err) |
| { |
| return __inline_bsearch(&smb_err, mapping_table_ERRDOS, |
| ARRAY_SIZE(mapping_table_ERRDOS), |
| sizeof(struct smb_to_posix_error), |
| smb1_posix_error_cmp); |
| } |
| |
| static const struct smb_to_posix_error * |
| search_mapping_table_ERRSRV(__u16 smb_err) |
| { |
| return __inline_bsearch(&smb_err, mapping_table_ERRSRV, |
| ARRAY_SIZE(mapping_table_ERRSRV), |
| sizeof(struct smb_to_posix_error), |
| smb1_posix_error_cmp); |
| } |
| |
| int |
| map_smb_to_linux_error(char *buf, bool logErr) |
| { |
| struct smb_hdr *smb = (struct smb_hdr *)buf; |
| int rc = -EIO; /* if transport error smb error may not be set */ |
| __u8 smberrclass; |
| __u16 smberrcode; |
| const struct smb_to_posix_error *err_map = NULL; |
| |
| /* BB if NT Status codes - map NT BB */ |
| |
| /* old style smb error codes */ |
| if (smb->Status.CifsError == 0) |
| return 0; |
| |
| if (smb->Flags2 & SMBFLG2_ERR_STATUS) { |
| /* translate the newer STATUS codes to old style SMB errors |
| * and then to POSIX errors */ |
| __u32 err = le32_to_cpu(smb->Status.CifsError); |
| const struct ntstatus_to_dos_err *map = search_ntstatus_to_dos_map(err); |
| |
| if (map) { |
| if ((logErr && err != NT_STATUS_MORE_PROCESSING_REQUIRED) || |
| (cifsFYI & CIFS_RC)) |
| pr_notice("Status code returned 0x%08x %s\n", |
| map->ntstatus, map->nt_errstr); |
| |
| smberrclass = map->dos_class; |
| smberrcode = map->dos_code; |
| } else { |
| smberrclass = ERRHRD; |
| smberrcode = ERRgeneral; |
| } |
| } else { |
| smberrclass = smb->Status.DosError.ErrorClass; |
| smberrcode = le16_to_cpu(smb->Status.DosError.Error); |
| } |
| |
| /* old style errors */ |
| |
| if (smberrclass == ERRDOS) { |
| /* DOS class smb error codes - map DOS */ |
| /* 1 byte field no need to byte reverse */ |
| err_map = search_mapping_table_ERRDOS(smberrcode); |
| } else if (smberrclass == ERRSRV) { |
| /* server class of error codes */ |
| err_map = search_mapping_table_ERRSRV(smberrcode); |
| } |
| if (err_map) |
| rc = err_map->posix_code; |
| /* else ERRHRD class errors or junk - return EIO */ |
| |
| /* special cases for NT status codes which cannot be translated to DOS codes */ |
| if (smb->Flags2 & SMBFLG2_ERR_STATUS) { |
| __u32 err = le32_to_cpu(smb->Status.CifsError); |
| if (err == (NT_STATUS_NOT_A_REPARSE_POINT)) |
| rc = -ENODATA; |
| else if (err == (NT_STATUS_PRIVILEGE_NOT_HELD)) |
| rc = -EPERM; |
| } |
| |
| cifs_dbg(FYI, "Mapping smb error code 0x%x to POSIX err %d\n", |
| le32_to_cpu(smb->Status.CifsError), rc); |
| |
| /* generic corrective action e.g. reconnect SMB session on |
| * ERRbaduid could be added */ |
| |
| if (rc == -EIO) |
| smb_EIO2(smb_eio_trace_smb1_received_error, |
| le32_to_cpu(smb->Status.CifsError), |
| le16_to_cpu(smb->Flags2)); |
| return rc; |
| } |
| |
| int |
| map_and_check_smb_error(struct TCP_Server_Info *server, |
| struct mid_q_entry *mid, bool logErr) |
| { |
| int rc; |
| struct smb_hdr *smb = (struct smb_hdr *)mid->resp_buf; |
| |
| rc = map_smb_to_linux_error((char *)smb, logErr); |
| if (rc == -EACCES && !(smb->Flags2 & SMBFLG2_ERR_STATUS)) { |
| /* possible ERRBaduid */ |
| __u8 class = smb->Status.DosError.ErrorClass; |
| __u16 code = le16_to_cpu(smb->Status.DosError.Error); |
| |
| /* switch can be used to handle different errors */ |
| if (class == ERRSRV && code == ERRbaduid) { |
| cifs_dbg(FYI, "Server returned 0x%x, reconnecting session...\n", |
| code); |
| cifs_signal_cifsd_for_reconnect(server, false); |
| } |
| } |
| |
| return rc; |
| } |
| |
| #define DEFINE_CHECK_SORT_FUNC(__array, __field) \ |
| static int __init __array ## _is_sorted(void) \ |
| { \ |
| unsigned int i; \ |
| \ |
| /* Check whether the array is sorted in ascending order */ \ |
| for (i = 1; i < ARRAY_SIZE(__array); i++) { \ |
| if (__array[i].__field >= \ |
| __array[i - 1].__field) \ |
| continue; \ |
| \ |
| pr_err(#__array " array order is incorrect\n"); \ |
| return -EINVAL; \ |
| } \ |
| \ |
| return 0; \ |
| } |
| |
| /* ntstatus_to_dos_map_is_sorted */ |
| DEFINE_CHECK_SORT_FUNC(ntstatus_to_dos_map, ntstatus); |
| /* mapping_table_ERRDOS_is_sorted */ |
| DEFINE_CHECK_SORT_FUNC(mapping_table_ERRDOS, smb_err); |
| /* mapping_table_ERRSRV_is_sorted */ |
| DEFINE_CHECK_SORT_FUNC(mapping_table_ERRSRV, smb_err); |
| |
| int __init smb1_init_maperror(void) |
| { |
| int rc; |
| |
| rc = ntstatus_to_dos_map_is_sorted(); |
| if (rc) |
| return rc; |
| |
| rc = mapping_table_ERRDOS_is_sorted(); |
| if (rc) |
| return rc; |
| |
| rc = mapping_table_ERRSRV_is_sorted(); |
| if (rc) |
| return rc; |
| |
| return rc; |
| } |
| |
| #if IS_ENABLED(CONFIG_SMB1_KUNIT_TESTS) |
| #define EXPORT_SYMBOL_FOR_SMB_TEST(sym) \ |
| EXPORT_SYMBOL_FOR_MODULES(sym, "smb1maperror_test") |
| |
| const struct ntstatus_to_dos_err * |
| search_ntstatus_to_dos_map_test(__u32 ntstatus) |
| { |
| return search_ntstatus_to_dos_map(ntstatus); |
| } |
| EXPORT_SYMBOL_FOR_SMB_TEST(search_ntstatus_to_dos_map_test); |
| |
| const struct ntstatus_to_dos_err * |
| ntstatus_to_dos_map_test = ntstatus_to_dos_map; |
| EXPORT_SYMBOL_FOR_SMB_TEST(ntstatus_to_dos_map_test); |
| |
| unsigned int ntstatus_to_dos_num = ARRAY_SIZE(ntstatus_to_dos_map); |
| EXPORT_SYMBOL_FOR_SMB_TEST(ntstatus_to_dos_num); |
| |
| const struct smb_to_posix_error * |
| search_mapping_table_ERRDOS_test(__u16 smb_err) |
| { |
| return search_mapping_table_ERRDOS(smb_err); |
| } |
| EXPORT_SYMBOL_FOR_SMB_TEST(search_mapping_table_ERRDOS_test); |
| |
| const struct smb_to_posix_error * |
| mapping_table_ERRDOS_test = mapping_table_ERRDOS; |
| EXPORT_SYMBOL_FOR_SMB_TEST(mapping_table_ERRDOS_test); |
| |
| unsigned int mapping_table_ERRDOS_num = ARRAY_SIZE(mapping_table_ERRDOS); |
| EXPORT_SYMBOL_FOR_SMB_TEST(mapping_table_ERRDOS_num); |
| |
| const struct smb_to_posix_error * |
| search_mapping_table_ERRSRV_test(__u16 smb_err) |
| { |
| return search_mapping_table_ERRSRV(smb_err); |
| } |
| EXPORT_SYMBOL_FOR_SMB_TEST(search_mapping_table_ERRSRV_test); |
| |
| const struct smb_to_posix_error * |
| mapping_table_ERRSRV_test = mapping_table_ERRSRV; |
| EXPORT_SYMBOL_FOR_SMB_TEST(mapping_table_ERRSRV_test); |
| |
| unsigned int mapping_table_ERRSRV_num = ARRAY_SIZE(mapping_table_ERRSRV); |
| EXPORT_SYMBOL_FOR_SMB_TEST(mapping_table_ERRSRV_num); |
| #endif |