1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * 4 * Copyright (c) International Business Machines Corp., 2002,2008 5 * Author(s): Steve French (sfrench@us.ibm.com) 6 * 7 * Error mapping routines from Samba libsmb/errormap.c 8 * Copyright (C) Andrew Tridgell 2001 9 * Copyright (C) Luke Kenneth Casson Leighton 1997-2001. 10 */ 11 12 #include <linux/bsearch.h> 13 #include "cifsproto.h" 14 #include "smb1proto.h" 15 #include "smberr.h" 16 #include "nterr.h" 17 #include "cifs_debug.h" 18 19 static __always_inline int smb1_posix_error_cmp(const void *_key, const void *_pivot) 20 { 21 __u16 key = *(__u16 *)_key; 22 const struct smb_to_posix_error *pivot = _pivot; 23 24 if (key < pivot->smb_err) 25 return -1; 26 if (key > pivot->smb_err) 27 return 1; 28 return 0; 29 } 30 31 static const struct smb_to_posix_error mapping_table_ERRDOS[] = { 32 /* 33 * Automatically generated by the `gen_smb1_mapping` script, 34 * sorted by DOS error code (ascending). 35 */ 36 #include "smb1_err_dos_map.c" 37 }; 38 39 static const struct smb_to_posix_error mapping_table_ERRSRV[] = { 40 /* 41 * Automatically generated by the `gen_smb1_mapping` script, 42 * sorted by SRV error code (ascending). 43 */ 44 #include "smb1_err_srv_map.c" 45 }; 46 47 /***************************************************************************** 48 *convert a NT status code to a dos class/code 49 *****************************************************************************/ 50 51 static __always_inline int ntstatus_to_dos_cmp(const void *_key, const void *_pivot) 52 { 53 __u32 key = *(__u32 *)_key; 54 const struct ntstatus_to_dos_err *pivot = _pivot; 55 56 if (key < pivot->ntstatus) 57 return -1; 58 if (key > pivot->ntstatus) 59 return 1; 60 return 0; 61 } 62 63 /* NT status -> dos error map */ 64 static const struct ntstatus_to_dos_err ntstatus_to_dos_map[] = { 65 /* 66 * Automatically generated by the `gen_smb1_mapping` script, 67 * sorted by NT status code (ascending). 68 */ 69 #include "smb1_mapping_table.c" 70 }; 71 72 static const struct ntstatus_to_dos_err * 73 search_ntstatus_to_dos_map(__u32 ntstatus) 74 { 75 return __inline_bsearch(&ntstatus, ntstatus_to_dos_map, 76 ARRAY_SIZE(ntstatus_to_dos_map), 77 sizeof(struct ntstatus_to_dos_err), 78 ntstatus_to_dos_cmp); 79 } 80 81 static const struct smb_to_posix_error * 82 search_mapping_table_ERRDOS(__u16 smb_err) 83 { 84 return __inline_bsearch(&smb_err, mapping_table_ERRDOS, 85 ARRAY_SIZE(mapping_table_ERRDOS), 86 sizeof(struct smb_to_posix_error), 87 smb1_posix_error_cmp); 88 } 89 90 static const struct smb_to_posix_error * 91 search_mapping_table_ERRSRV(__u16 smb_err) 92 { 93 return __inline_bsearch(&smb_err, mapping_table_ERRSRV, 94 ARRAY_SIZE(mapping_table_ERRSRV), 95 sizeof(struct smb_to_posix_error), 96 smb1_posix_error_cmp); 97 } 98 99 int 100 map_smb_to_linux_error(char *buf, bool logErr) 101 { 102 struct smb_hdr *smb = (struct smb_hdr *)buf; 103 int rc = -EIO; /* if transport error smb error may not be set */ 104 __u8 smberrclass; 105 __u16 smberrcode; 106 const struct smb_to_posix_error *err_map = NULL; 107 108 /* BB if NT Status codes - map NT BB */ 109 110 /* old style smb error codes */ 111 if (smb->Status.CifsError == 0) 112 return 0; 113 114 if (smb->Flags2 & SMBFLG2_ERR_STATUS) { 115 /* translate the newer STATUS codes to old style SMB errors 116 * and then to POSIX errors */ 117 __u32 err = le32_to_cpu(smb->Status.CifsError); 118 const struct ntstatus_to_dos_err *map = search_ntstatus_to_dos_map(err); 119 120 if (map) { 121 if ((logErr && err != NT_STATUS_MORE_PROCESSING_REQUIRED) || 122 (cifsFYI & CIFS_RC)) 123 pr_notice("Status code returned 0x%08x %s\n", 124 map->ntstatus, map->nt_errstr); 125 126 smberrclass = map->dos_class; 127 smberrcode = map->dos_code; 128 } else { 129 smberrclass = ERRHRD; 130 smberrcode = ERRgeneral; 131 } 132 } else { 133 smberrclass = smb->Status.DosError.ErrorClass; 134 smberrcode = le16_to_cpu(smb->Status.DosError.Error); 135 } 136 137 /* old style errors */ 138 139 if (smberrclass == ERRDOS) { 140 /* DOS class smb error codes - map DOS */ 141 /* 1 byte field no need to byte reverse */ 142 err_map = search_mapping_table_ERRDOS(smberrcode); 143 } else if (smberrclass == ERRSRV) { 144 /* server class of error codes */ 145 err_map = search_mapping_table_ERRSRV(smberrcode); 146 } 147 if (err_map) 148 rc = err_map->posix_code; 149 /* else ERRHRD class errors or junk - return EIO */ 150 151 /* special cases for NT status codes which cannot be translated to DOS codes */ 152 if (smb->Flags2 & SMBFLG2_ERR_STATUS) { 153 __u32 err = le32_to_cpu(smb->Status.CifsError); 154 if (err == (NT_STATUS_NOT_A_REPARSE_POINT)) 155 rc = -ENODATA; 156 else if (err == (NT_STATUS_PRIVILEGE_NOT_HELD)) 157 rc = -EPERM; 158 } 159 160 cifs_dbg(FYI, "Mapping smb error code 0x%x to POSIX err %d\n", 161 le32_to_cpu(smb->Status.CifsError), rc); 162 163 /* generic corrective action e.g. reconnect SMB session on 164 * ERRbaduid could be added */ 165 166 if (rc == -EIO) 167 smb_EIO2(smb_eio_trace_smb1_received_error, 168 le32_to_cpu(smb->Status.CifsError), 169 le16_to_cpu(smb->Flags2)); 170 return rc; 171 } 172 173 int 174 map_and_check_smb_error(struct TCP_Server_Info *server, 175 struct mid_q_entry *mid, bool logErr) 176 { 177 int rc; 178 struct smb_hdr *smb = (struct smb_hdr *)mid->resp_buf; 179 180 rc = map_smb_to_linux_error((char *)smb, logErr); 181 if (rc == -EACCES && !(smb->Flags2 & SMBFLG2_ERR_STATUS)) { 182 /* possible ERRBaduid */ 183 __u8 class = smb->Status.DosError.ErrorClass; 184 __u16 code = le16_to_cpu(smb->Status.DosError.Error); 185 186 /* switch can be used to handle different errors */ 187 if (class == ERRSRV && code == ERRbaduid) { 188 cifs_dbg(FYI, "Server returned 0x%x, reconnecting session...\n", 189 code); 190 cifs_signal_cifsd_for_reconnect(server, false); 191 } 192 } 193 194 return rc; 195 } 196 197 #define DEFINE_CHECK_SORT_FUNC(__array, __field) \ 198 static int __init __array ## _is_sorted(void) \ 199 { \ 200 unsigned int i; \ 201 \ 202 /* Check whether the array is sorted in ascending order */ \ 203 for (i = 1; i < ARRAY_SIZE(__array); i++) { \ 204 if (__array[i].__field >= \ 205 __array[i - 1].__field) \ 206 continue; \ 207 \ 208 pr_err(#__array " array order is incorrect\n"); \ 209 return -EINVAL; \ 210 } \ 211 \ 212 return 0; \ 213 } 214 215 /* ntstatus_to_dos_map_is_sorted */ 216 DEFINE_CHECK_SORT_FUNC(ntstatus_to_dos_map, ntstatus); 217 /* mapping_table_ERRDOS_is_sorted */ 218 DEFINE_CHECK_SORT_FUNC(mapping_table_ERRDOS, smb_err); 219 /* mapping_table_ERRSRV_is_sorted */ 220 DEFINE_CHECK_SORT_FUNC(mapping_table_ERRSRV, smb_err); 221 222 int __init smb1_init_maperror(void) 223 { 224 int rc; 225 226 rc = ntstatus_to_dos_map_is_sorted(); 227 if (rc) 228 return rc; 229 230 rc = mapping_table_ERRDOS_is_sorted(); 231 if (rc) 232 return rc; 233 234 rc = mapping_table_ERRSRV_is_sorted(); 235 if (rc) 236 return rc; 237 238 return rc; 239 } 240 241 #if IS_ENABLED(CONFIG_SMB1_KUNIT_TESTS) 242 #define EXPORT_SYMBOL_FOR_SMB_TEST(sym) \ 243 EXPORT_SYMBOL_FOR_MODULES(sym, "smb1maperror_test") 244 245 const struct ntstatus_to_dos_err * 246 search_ntstatus_to_dos_map_test(__u32 ntstatus) 247 { 248 return search_ntstatus_to_dos_map(ntstatus); 249 } 250 EXPORT_SYMBOL_FOR_SMB_TEST(search_ntstatus_to_dos_map_test); 251 252 const struct ntstatus_to_dos_err * 253 ntstatus_to_dos_map_test = ntstatus_to_dos_map; 254 EXPORT_SYMBOL_FOR_SMB_TEST(ntstatus_to_dos_map_test); 255 256 unsigned int ntstatus_to_dos_num = ARRAY_SIZE(ntstatus_to_dos_map); 257 EXPORT_SYMBOL_FOR_SMB_TEST(ntstatus_to_dos_num); 258 259 const struct smb_to_posix_error * 260 search_mapping_table_ERRDOS_test(__u16 smb_err) 261 { 262 return search_mapping_table_ERRDOS(smb_err); 263 } 264 EXPORT_SYMBOL_FOR_SMB_TEST(search_mapping_table_ERRDOS_test); 265 266 const struct smb_to_posix_error * 267 mapping_table_ERRDOS_test = mapping_table_ERRDOS; 268 EXPORT_SYMBOL_FOR_SMB_TEST(mapping_table_ERRDOS_test); 269 270 unsigned int mapping_table_ERRDOS_num = ARRAY_SIZE(mapping_table_ERRDOS); 271 EXPORT_SYMBOL_FOR_SMB_TEST(mapping_table_ERRDOS_num); 272 273 const struct smb_to_posix_error * 274 search_mapping_table_ERRSRV_test(__u16 smb_err) 275 { 276 return search_mapping_table_ERRSRV(smb_err); 277 } 278 EXPORT_SYMBOL_FOR_SMB_TEST(search_mapping_table_ERRSRV_test); 279 280 const struct smb_to_posix_error * 281 mapping_table_ERRSRV_test = mapping_table_ERRSRV; 282 EXPORT_SYMBOL_FOR_SMB_TEST(mapping_table_ERRSRV_test); 283 284 unsigned int mapping_table_ERRSRV_num = ARRAY_SIZE(mapping_table_ERRSRV); 285 EXPORT_SYMBOL_FOR_SMB_TEST(mapping_table_ERRSRV_num); 286 #endif 287