blob: 74530088d17d0316bb8998d52039b8982748c0e0 [file] [edit]
// 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