root/fs/smb/client/smb2maperror.c
// SPDX-License-Identifier: LGPL-2.1
/*
 *
 *   Functions which do error mapping of SMB2 status codes to POSIX errors
 *
 *   Copyright (C) International Business Machines  Corp., 2009
 *   Author(s): Steve French (sfrench@us.ibm.com)
 *
 */
#include <linux/errno.h>
#include "cifsproto.h"
#include "cifs_debug.h"
#include "smb2proto.h"
#include "smb2glob.h"
#include "../common/smb2status.h"
#include "trace.h"

static const struct status_to_posix_error smb2_error_map_table[] = {
/*
 * Automatically generated by the `gen_smb2_mapping` script,
 * sorted by NT status code (cpu-endian, ascending)
 */
#include "smb2_mapping_table.c"
};

static __always_inline int cmp_smb2_status(const void *_key, const void *_pivot)
{
        __u32 key = *(__u32 *)_key;
        const struct status_to_posix_error *pivot = _pivot;

        if (key < pivot->smb2_status)
                return -1;
        if (key > pivot->smb2_status)
                return 1;
        return 0;
}

static const struct status_to_posix_error *smb2_get_err_map(__u32 smb2_status)
{
        const struct status_to_posix_error *err_map;

        err_map = __inline_bsearch(&smb2_status, smb2_error_map_table,
                                   ARRAY_SIZE(smb2_error_map_table),
                                   sizeof(struct status_to_posix_error),
                                   cmp_smb2_status);
        return err_map;
}

int
map_smb2_to_linux_error(char *buf, bool log_err)
{
        struct smb2_hdr *shdr = (struct smb2_hdr *)buf;
        int rc = -EIO;
        __le32 smb2err = shdr->Status;
        const struct status_to_posix_error *err_map;

        if (smb2err == 0) {
                trace_smb3_cmd_done(le32_to_cpu(shdr->Id.SyncId.TreeId),
                              le64_to_cpu(shdr->SessionId),
                              le16_to_cpu(shdr->Command),
                              le64_to_cpu(shdr->MessageId));
                return 0;
        }

        log_err = (log_err && (smb2err != STATUS_MORE_PROCESSING_REQUIRED) &&
                   (smb2err != STATUS_END_OF_FILE)) ||
                  (cifsFYI & CIFS_RC);

        err_map = smb2_get_err_map(le32_to_cpu(smb2err));
        if (!err_map)
                goto out;

        rc = err_map->posix_error;
        if (log_err)
                pr_notice("Status code returned 0x%08x %s\n",
                          err_map->smb2_status, err_map->status_string);

out:
        /* on error mapping not found  - return EIO */

        cifs_dbg(FYI, "Mapping SMB2 status code 0x%08x to POSIX err %d\n",
                 le32_to_cpu(smb2err), rc);

        trace_smb3_cmd_err(le32_to_cpu(shdr->Id.SyncId.TreeId),
                           le64_to_cpu(shdr->SessionId),
                           le16_to_cpu(shdr->Command),
                           le64_to_cpu(shdr->MessageId),
                           le32_to_cpu(smb2err), rc);
        if (rc == -EIO)
                smb_EIO1(smb_eio_trace_smb2_received_error, le32_to_cpu(smb2err));
        return rc;
}

int __init smb2_init_maperror(void)
{
        unsigned int i;

        /* Check whether the array is sorted in ascending order */
        for (i = 1; i < ARRAY_SIZE(smb2_error_map_table); i++) {
                if (smb2_error_map_table[i].smb2_status >=
                    smb2_error_map_table[i - 1].smb2_status)
                        continue;

                pr_err("smb2_error_map_table array order is incorrect\n");
                return -EINVAL;
        }

        return 0;
}

#if IS_ENABLED(CONFIG_SMB_KUNIT_TESTS)
#define EXPORT_SYMBOL_FOR_SMB_TEST(sym) \
        EXPORT_SYMBOL_FOR_MODULES(sym, "smb2maperror_test")

/* Previous prototype for eliminating the build warning. */
const struct status_to_posix_error *smb2_get_err_map_test(__u32 smb2_status);

const struct status_to_posix_error *smb2_get_err_map_test(__u32 smb2_status)
{
        return smb2_get_err_map(smb2_status);
}
EXPORT_SYMBOL_FOR_SMB_TEST(smb2_get_err_map_test);

const struct status_to_posix_error *smb2_error_map_table_test = smb2_error_map_table;
EXPORT_SYMBOL_FOR_SMB_TEST(smb2_error_map_table_test);

unsigned int smb2_error_map_num = ARRAY_SIZE(smb2_error_map_table);
EXPORT_SYMBOL_FOR_SMB_TEST(smb2_error_map_num);
#endif