// 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 #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