Contributors: 8
Author Tokens Token Proportion Commits Commit Proportion
Huiwen He 319 32.45% 6 24.00%
David Howells 273 27.77% 4 16.00%
Youling Tang 223 22.69% 4 16.00%
Steve French 145 14.75% 7 28.00%
Pavel Shilovsky 14 1.42% 1 4.00%
Al Viro 5 0.51% 1 4.00%
Pali Rohár 3 0.31% 1 4.00%
Jeff Layton 1 0.10% 1 4.00%
Total 983 25


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