Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

721 lines
20 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. srvstat.c
  5. Abstract:
  6. Contains data and modules for error handling.
  7. Author:
  8. David Treadwell (davidtr) 10-May-1990
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. #include "srvstat.tmh"
  13. #pragma hdrstop
  14. #define DISK_HARD_ERROR 0x38
  15. NTSTATUS DbgBreakError = STATUS_SUCCESS;
  16. VOID
  17. MapErrorForDosClient (
  18. IN PWORK_CONTEXT WorkContext,
  19. IN ULONG Error,
  20. OUT PUSHORT DosError,
  21. OUT PUCHAR DosErrorClass
  22. );
  23. #ifdef ALLOC_PRAGMA
  24. #pragma alloc_text( PAGE, _SrvSetSmbError2 )
  25. #pragma alloc_text( PAGE, MapErrorForDosClient )
  26. #pragma alloc_text( PAGE8FIL, SrvSetBufferOverflowError )
  27. #endif
  28. VOID
  29. _SrvSetSmbError2 (
  30. IN PWORK_CONTEXT WorkContext,
  31. IN NTSTATUS Status,
  32. IN BOOLEAN HeaderOnly,
  33. IN ULONG Line,
  34. IN PCHAR File
  35. )
  36. /*++
  37. Routine Description:
  38. Loads error information into a response SMB. If the client is
  39. NT, the status is placed directly into the outgoing SMB. If
  40. the client is DOS or OS/2 and this is a special status code that
  41. has the DOS/OS|2/SMB error code embedded, put the code and class
  42. from the status code into the outgoing SMB. If that doesn't work,
  43. use RtlNtStatusToDosError to try to map the status to an OS/2
  44. error code, then map to DOS if necessary. If we still haven't
  45. mapped it, use our own array to try to find a mapping. And,
  46. finally, if that doesn't work, return the generic error code.
  47. Arguments:
  48. WorkContext - Supplies a pointer to the work context block for the
  49. current SMB. In particular, the Connection block pointer is
  50. used to find the negotiated dialect for the connection, and
  51. the ResponseHeader and ResponseParameter pointers are used
  52. to determine where to write the error information.
  53. Status - Supplies an NT status code.
  54. Return Value:
  55. None.
  56. --*/
  57. {
  58. PSMB_HEADER header = WorkContext->ResponseHeader;
  59. PSMB_PARAMS params = WorkContext->ResponseParameters;
  60. SMB_DIALECT smbDialect;
  61. ULONG error;
  62. CCHAR errorClass;
  63. USHORT errorCode;
  64. USHORT flags;
  65. PAGED_CODE( );
  66. if( (DbgBreakError != STATUS_SUCCESS) &&
  67. (Status == DbgBreakError) )
  68. {
  69. DbgPrint( "Caught error %x\n", DbgBreakError );
  70. DbgPrint( "WorkContext = %p, Line = %d, File = %x\n", WorkContext, Line, File );
  71. DbgBreakPoint();
  72. }
  73. IF_DEBUG( ERRORS ) { \
  74. KdPrint(( "SrvSetSmbError %X (%s,%d)\n",Status,File,Line )); \
  75. }
  76. smbDialect = WorkContext->Connection->SmbDialect;
  77. //
  78. // Update the SMB body, if necessary.
  79. //
  80. if ( !HeaderOnly ) {
  81. params->WordCount = 0;
  82. SmbPutUshort( &params->ByteCount, 0 );
  83. WorkContext->ResponseParameters = (PVOID)(params + 1);
  84. }
  85. //
  86. // If the status code is a real NT status, then either return it
  87. // directly to the client or map it to a Win32 error code.
  88. //
  89. if ( !SrvIsSrvStatus( Status ) ) {
  90. //
  91. // Map STATUS_INSUFFICIENT_RESOURCES to the server form. If we
  92. // get an insufficient resources error from the system, we
  93. // report it as a server shortage. This helps keep things
  94. // clearer for the client.
  95. //
  96. if ( Status == STATUS_WORKING_SET_QUOTA ) {
  97. Status = STATUS_INSUFF_SERVER_RESOURCES;
  98. if( WorkContext->Rfcb )
  99. {
  100. PNT_SMB_HEADER pHeader = (PNT_SMB_HEADER)WorkContext->RequestHeader;
  101. BOOL PagingIo = (pHeader->Flags2 & SMB_FLAGS2_PAGING_IO) ? TRUE : FALSE;
  102. IF_SYSCACHE_RFCB( WorkContext->Rfcb ) {
  103. KdPrint(("Op on %p failed with C00000A1, Paging=%d, Ofst=%x, Ln=%x\n", WorkContext->Rfcb, PagingIo,
  104. WorkContext->Parameters.WriteAndX.Offset.u.LowPart, WorkContext->Parameters.WriteAndX.CurrentWriteLength ));
  105. }
  106. }
  107. }
  108. if ( Status == STATUS_INSUFFICIENT_RESOURCES ) {
  109. Status = STATUS_INSUFF_SERVER_RESOURCES;
  110. }
  111. if ( CLIENT_CAPABLE_OF(NT_STATUS, WorkContext->Connection) ) {
  112. //
  113. // The client understands NT status codes. Load the status
  114. // directly into the SMB header.
  115. //
  116. SmbPutUlong( (PULONG)&header->ErrorClass, Status );
  117. flags = SmbGetAlignedUshort( &header->Flags2 ) | SMB_FLAGS2_NT_STATUS;
  118. SmbPutAlignedUshort( &header->Flags2, flags );
  119. return;
  120. }
  121. //
  122. // This is an NT status, but the client doesn't understand them.
  123. // Indicate that we're not returning an NT status code. Then
  124. // map the NT status to a Win32 status. Some NT status codes
  125. // require special mapping.
  126. //
  127. flags = SmbGetAlignedUshort( &header->Flags2 ) & ~SMB_FLAGS2_NT_STATUS;
  128. SmbPutAlignedUshort( &header->Flags2, flags );
  129. switch ( Status ) {
  130. case STATUS_TIMEOUT:
  131. header->ErrorClass = SMB_ERR_CLASS_SERVER;
  132. SmbPutUshort( &header->Error, SMB_ERR_TIMEOUT );
  133. return;
  134. case STATUS_INVALID_SYSTEM_SERVICE:
  135. //
  136. // This status code is returned by XACTSRV when an invalid API
  137. // number is specified.
  138. //
  139. header->ErrorClass = SMB_ERR_CLASS_DOS;
  140. SmbPutUshort( &header->Error, NERR_InvalidAPI );
  141. return;
  142. case STATUS_PATH_NOT_COVERED:
  143. //
  144. // This code indicates that the server does not cover this part
  145. // of the DFS namespace.
  146. //
  147. header->ErrorClass = SMB_ERR_CLASS_SERVER;
  148. SmbPutUshort( &header->Error, SMB_ERR_BAD_PATH );
  149. return;
  150. default:
  151. //
  152. // This is not a special status code. Map the NT status
  153. // code to a Win32 error code. If there is no mapping,
  154. // return the generic SMB error.
  155. //
  156. error = RtlNtStatusToDosErrorNoTeb( Status );
  157. if ( error == ERROR_MR_MID_NOT_FOUND || error == (ULONG)Status ) {
  158. header->ErrorClass = SMB_ERR_CLASS_HARDWARE;
  159. SmbPutUshort( &header->Error, SMB_ERR_GENERAL );
  160. return;
  161. }
  162. //
  163. // We now have a Win32 error. Drop through to the code
  164. // that maps Win32 errors for downlevel clients.
  165. //
  166. break;
  167. }
  168. } else {
  169. //
  170. // The status code is not an NT status. Deal with it based on
  171. // the error class.
  172. //
  173. errorClass = SrvErrorClass( Status );
  174. //
  175. // Clear the FLAGS2_NT_STATUS bit to indicate that this is *not* an
  176. // NT_STATUS
  177. //
  178. flags = SmbGetAlignedUshort( &header->Flags2 ) & ~SMB_FLAGS2_NT_STATUS;
  179. SmbPutAlignedUshort( &header->Flags2, flags );
  180. switch ( errorClass ) {
  181. case SMB_ERR_CLASS_DOS:
  182. case SMB_ERR_CLASS_SERVER:
  183. case SMB_ERR_CLASS_HARDWARE:
  184. //
  185. // The status code has the SMB error class and code
  186. // embedded.
  187. //
  188. header->ErrorClass = errorClass;
  189. //
  190. // Because SMB_ERR_NO_SUPPORT in the SERVER class is 0xFFFF
  191. // (16 bits), we must special-case for it. The code
  192. // SMB_ERR_NO_SUPPORT_INTERNAL in the error code field of
  193. // the status, along with class = 2 (SERVER), indicates that
  194. // we should use SMB_ERR_NO_SUPPORT.
  195. //
  196. if ( errorClass == SMB_ERR_CLASS_SERVER &&
  197. SrvErrorCode( Status ) == SMB_ERR_NO_SUPPORT_INTERNAL ) {
  198. SmbPutUshort( &header->Error, SMB_ERR_NO_SUPPORT );
  199. } else {
  200. SmbPutUshort( &header->Error, SrvErrorCode( Status ) );
  201. }
  202. return;
  203. case 0xF:
  204. //
  205. // The error code is defined in OS/2 but not in the SMB
  206. // protocol. If the client is speaking a dialect after
  207. // LanMan 1.0 and is not a DOS client, send the OS/2 error
  208. // code. Otherwise, send the generic SMB error code.
  209. //
  210. if ( smbDialect <= SmbDialectLanMan10 &&
  211. !IS_DOS_DIALECT(smbDialect) ) {
  212. header->ErrorClass = SMB_ERR_CLASS_DOS;
  213. SmbPutUshort( &header->Error, SrvErrorCode( Status ) );
  214. } else {
  215. header->ErrorClass = SMB_ERR_CLASS_HARDWARE;
  216. SmbPutUshort( &header->Error, SMB_ERR_GENERAL );
  217. }
  218. return;
  219. case 0xE:
  220. //
  221. // This is a Win32 error. Drop through to the code that
  222. // maps Win32 errors for downlevel clients.
  223. //
  224. error = SrvErrorCode( Status );
  225. break;
  226. case 0x0:
  227. default:
  228. //
  229. // This is an internal server error (class 0) or some other
  230. // undefined class. We should never get here. But since we
  231. // did, return the generic error.
  232. //
  233. KdPrint(( "SRV: Unmapped error: %lx\n", Status ));
  234. header->ErrorClass = SMB_ERR_CLASS_HARDWARE;
  235. SmbPutUshort( &header->Error, SMB_ERR_GENERAL );
  236. return;
  237. }
  238. }
  239. //
  240. // At this point we have a Win32 error code and need to map it for
  241. // the downlevel client. Some errors need to be specially mapped.
  242. //
  243. errorClass = SMB_ERR_CLASS_DOS;
  244. switch ( error ) {
  245. case ERROR_NOT_ENOUGH_SERVER_MEMORY:
  246. error = ERROR_NOT_ENOUGH_MEMORY;
  247. break;
  248. case ERROR_INSUFFICIENT_BUFFER:
  249. error = ERROR_BUFFER_OVERFLOW;
  250. break;
  251. case ERROR_ACCOUNT_LOCKED_OUT:
  252. case ERROR_PRIVILEGE_NOT_HELD:
  253. case ERROR_NO_SUCH_USER:
  254. case ERROR_LOGON_FAILURE:
  255. case ERROR_LOGON_TYPE_NOT_GRANTED:
  256. case ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT:
  257. case ERROR_NOLOGON_WORKSTATION_TRUST_ACCOUNT:
  258. case ERROR_NOLOGON_SERVER_TRUST_ACCOUNT:
  259. case ERROR_TRUSTED_RELATIONSHIP_FAILURE:
  260. case ERROR_TRUSTED_DOMAIN_FAILURE:
  261. case ERROR_TRUST_FAILURE:
  262. case ERROR_NO_TRUST_SAM_ACCOUNT:
  263. case ERROR_NO_TRUST_LSA_SECRET:
  264. error = ERROR_ACCESS_DENIED;
  265. break;
  266. //
  267. // For the following four errors, we return an ERROR_ACCESS_DENIED
  268. // for clients older than doslm20. The error class for these
  269. // must be SMB_ERR_CLASS_SERVER.
  270. //
  271. case ERROR_INVALID_LOGON_HOURS:
  272. if ( IS_DOS_DIALECT(smbDialect) && (smbDialect > SmbDialectDosLanMan20) ) {
  273. error = ERROR_ACCESS_DENIED;
  274. } else {
  275. errorClass = SMB_ERR_CLASS_SERVER;
  276. error = NERR_InvalidLogonHours;
  277. }
  278. break;
  279. case ERROR_INVALID_WORKSTATION:
  280. if ( IS_DOS_DIALECT(smbDialect) && (smbDialect > SmbDialectDosLanMan20) ) {
  281. error = ERROR_ACCESS_DENIED;
  282. } else {
  283. errorClass = SMB_ERR_CLASS_SERVER;
  284. error = NERR_InvalidWorkstation;
  285. }
  286. break;
  287. case ERROR_ACCOUNT_DISABLED:
  288. case ERROR_ACCOUNT_EXPIRED:
  289. if ( IS_DOS_DIALECT(smbDialect) && (smbDialect > SmbDialectDosLanMan20) ) {
  290. error = ERROR_ACCESS_DENIED;
  291. } else {
  292. errorClass = SMB_ERR_CLASS_SERVER;
  293. error = NERR_AccountExpired;
  294. }
  295. break;
  296. case ERROR_PASSWORD_MUST_CHANGE:
  297. case ERROR_PASSWORD_EXPIRED:
  298. if ( IS_DOS_DIALECT(smbDialect) && (smbDialect > SmbDialectDosLanMan20) ) {
  299. error = ERROR_ACCESS_DENIED;
  300. } else {
  301. errorClass = SMB_ERR_CLASS_SERVER;
  302. error = NERR_PasswordExpired;
  303. }
  304. break;
  305. //
  306. // The only NERR codes that DOSLM20 understands are the 4 above.
  307. // According to larryo, the rest of the NERR codes have to be
  308. // mapped to ERROR_ACCESS_DENIED.
  309. //
  310. case ERROR_NETLOGON_NOT_STARTED:
  311. if ( IS_DOS_DIALECT(smbDialect) && (smbDialect > SmbDialectDosLanMan21) ) {
  312. error = ERROR_ACCESS_DENIED;
  313. } else {
  314. error = NERR_NetlogonNotStarted;
  315. }
  316. break;
  317. case ERROR_NO_LOGON_SERVERS:
  318. if ( IS_DOS_DIALECT(smbDialect) ) {
  319. error = ERROR_ACCESS_DENIED;
  320. } else {
  321. error = NERR_LogonServerNotFound;
  322. }
  323. break;
  324. case ERROR_DIR_NOT_EMPTY:
  325. if ( IS_DOS_DIALECT(smbDialect) ) {
  326. error = ERROR_ACCESS_DENIED;
  327. }
  328. break;
  329. default:
  330. break;
  331. }
  332. //
  333. // Now map the error to a DOS or OS/2 error code.
  334. //
  335. if ( error == ERROR_ACCESS_DENIED &&
  336. smbDialect == SmbDialectDosLanMan21 &&
  337. WorkContext->ShareAclFailure ) {
  338. //
  339. // WfW & DOS LM2.1 want SMB_ERR_ACCESS to be in the server
  340. // error class when it's due to ACL restrictions, but in the
  341. // DOS class otherwise.
  342. //
  343. errorClass = SMB_ERR_CLASS_SERVER;
  344. errorCode = SMB_ERR_ACCESS;
  345. } else if ( smbDialect > SmbDialectLanMan10 ) {
  346. MapErrorForDosClient(
  347. WorkContext,
  348. error,
  349. &errorCode,
  350. &errorClass
  351. );
  352. } else if ( (error > ERROR_ARITHMETIC_OVERFLOW) &&
  353. ((error < NERR_BASE) || (error > MAX_NERR)) ) {
  354. //
  355. // Win32 errors above ERROR_ARITHMETIC_OVERFLOW (but not in the
  356. // NERR_xxx range) do not map to DOS or OS/2 errors, so we
  357. // return the generic error for those.
  358. //
  359. errorClass = SMB_ERR_CLASS_HARDWARE;
  360. errorCode = SMB_ERR_GENERAL;
  361. } else {
  362. errorCode = (USHORT)error;
  363. }
  364. header->ErrorClass = errorClass;
  365. SmbPutUshort( &header->Error, errorCode );
  366. return;
  367. } // _SrvSetSmbError2
  368. VOID
  369. MapErrorForDosClient (
  370. IN PWORK_CONTEXT WorkContext,
  371. IN ULONG Error,
  372. OUT PUSHORT DosError,
  373. OUT PUCHAR DosErrorClass
  374. )
  375. /*++
  376. Routine Description:
  377. Maps an Win32 error to a DOS error.
  378. Arguments:
  379. WorkContext - Supplies a pointer to the work context block for the
  380. current SMB.
  381. Error - the Win32 error code to map.
  382. DosError - the corresponding DOS error.
  383. DosErrorClass - the error class to put in the outgoing SMB.
  384. Return Value:
  385. None.
  386. --*/
  387. {
  388. PSMB_HEADER header = WorkContext->ResponseHeader;
  389. PAGED_CODE( );
  390. //
  391. // Default to using the initial error code and the win32 error.
  392. //
  393. *DosError = (USHORT)Error;
  394. *DosErrorClass = SMB_ERR_CLASS_DOS;
  395. //
  396. // If the error is more recent and not part of the set of DOS errors
  397. // (value greater than ERROR_NET_WRITE_FAULT) and the SMB command is
  398. // not a newer SMB (errors for these get mapped by the newer DOS
  399. // redir that sent them), then map the OS/2 error to the DOS range.
  400. // This code was lifted from the ring 3 OS/2 server.
  401. //
  402. if ( Error > ERROR_NET_WRITE_FAULT &&
  403. !( header->Command == SMB_COM_COPY ||
  404. header->Command == SMB_COM_MOVE ||
  405. header->Command == SMB_COM_TRANSACTION ||
  406. header->Command == SMB_COM_TRANSACTION_SECONDARY ) ) {
  407. switch( Error ) {
  408. case ERROR_OPEN_FAILED:
  409. *DosError = ERROR_FILE_NOT_FOUND;
  410. break;
  411. case ERROR_BUFFER_OVERFLOW:
  412. case ERROR_INSUFFICIENT_BUFFER:
  413. case ERROR_INVALID_NAME:
  414. case ERROR_INVALID_LEVEL:
  415. case ERROR_SEEK_ON_DEVICE:
  416. //
  417. // These don't get mapped to anything. No explanation was
  418. // given in the ring 3 code.
  419. //
  420. break;
  421. case ERROR_BAD_EXE_FORMAT:
  422. case ERROR_INVALID_STARTING_CODESEG:
  423. case ERROR_INVALID_STACKSEG:
  424. case ERROR_INVALID_MODULETYPE:
  425. case ERROR_INVALID_EXE_SIGNATURE:
  426. case ERROR_EXE_MARKED_INVALID:
  427. case ERROR_ITERATED_DATA_EXCEEDS_64k:
  428. case ERROR_INVALID_MINALLOCSIZE:
  429. case ERROR_DYNLINK_FROM_INVALID_RING:
  430. case ERROR_IOPL_NOT_ENABLED:
  431. case ERROR_INVALID_SEGDPL:
  432. case ERROR_AUTODATASEG_EXCEEDS_64k:
  433. case ERROR_RING2SEG_MUST_BE_MOVABLE:
  434. case ERROR_RELOC_CHAIN_XEEDS_SEGLIM:
  435. case ERROR_INFLOOP_IN_RELOC_CHAIN:
  436. //case ERROR_BAD_DYNALINK:
  437. case ERROR_TOO_MANY_MODULES:
  438. //
  439. // About these, the ring 3 server code says "map to bad
  440. // format errors." Whatever that means. It doesn't do
  441. // anything with them, so we don't either.
  442. //
  443. break;
  444. case ERROR_DISK_CHANGE:
  445. *DosErrorClass = SMB_ERR_CLASS_HARDWARE;
  446. *DosError = ERROR_WRONG_DISK;
  447. break;
  448. case ERROR_DRIVE_LOCKED:
  449. *DosErrorClass = SMB_ERR_CLASS_HARDWARE;
  450. *DosError = ERROR_NOT_READY;
  451. break;
  452. case ERROR_ALREADY_EXISTS:
  453. *DosError = ERROR_FILE_EXISTS;
  454. break;
  455. case ERROR_DISK_FULL:
  456. //
  457. // Per LarryO, map to the "old" disk full error code.
  458. //
  459. *DosErrorClass = SMB_ERR_CLASS_HARDWARE;
  460. *DosError = ERROR_HANDLE_DISK_FULL;
  461. break;
  462. case ERROR_NO_MORE_SEARCH_HANDLES:
  463. *DosError = ERROR_OUT_OF_STRUCTURES;
  464. break;
  465. case ERROR_INVALID_TARGET_HANDLE:
  466. *DosError = ERROR_INVALID_HANDLE;
  467. break;
  468. case ERROR_BROKEN_PIPE:
  469. case ERROR_BAD_PIPE:
  470. case ERROR_PIPE_BUSY:
  471. case ERROR_NO_DATA:
  472. case ERROR_PIPE_NOT_CONNECTED:
  473. case ERROR_MORE_DATA:
  474. //
  475. // If this is a pipe share, return these unmolested. If
  476. // it's not a pipe share, so map to the generic error.
  477. //
  478. if ( (WorkContext->Rfcb != NULL &&
  479. WorkContext->Rfcb->ShareType == ShareTypePipe)
  480. ||
  481. (WorkContext->TreeConnect != NULL &&
  482. WorkContext->TreeConnect->Share->ShareType == ShareTypePipe) ) {
  483. break;
  484. } else {
  485. *DosErrorClass = SMB_ERR_CLASS_HARDWARE;
  486. *DosError = SMB_ERR_GENERAL;
  487. }
  488. break;
  489. case ERROR_BAD_PATHNAME:
  490. break;
  491. //
  492. // The following error mappings (not including default) were not
  493. // copied from the OS/2 server mapping.
  494. //
  495. case ERROR_LOCK_FAILED:
  496. case ERROR_NOT_LOCKED:
  497. *DosError = ERROR_LOCK_VIOLATION;
  498. break;
  499. case NERR_InvalidLogonHours:
  500. case NERR_InvalidWorkstation:
  501. case NERR_PasswordExpired:
  502. case NERR_AccountUndefined:
  503. case ERROR_ACCESS_DENIED:
  504. *DosError = ERROR_ACCESS_DENIED;
  505. break;
  506. default:
  507. *DosErrorClass = SMB_ERR_CLASS_HARDWARE;
  508. *DosError = SMB_ERR_GENERAL;
  509. }
  510. }
  511. //
  512. // The DOS redirector uses the reserved field for the hard error action.
  513. // Set it now.
  514. //
  515. if ( *DosErrorClass == SMB_ERR_CLASS_HARDWARE ) {
  516. WorkContext->ResponseHeader->Reserved = DISK_HARD_ERROR;
  517. }
  518. } // MapErrorForDosClient
  519. VOID
  520. SrvSetBufferOverflowError (
  521. IN PWORK_CONTEXT WorkContext
  522. )
  523. {
  524. PSMB_HEADER header = WorkContext->ResponseHeader;
  525. USHORT flags = SmbGetAlignedUshort( &header->Flags2 );
  526. UNLOCKABLE_CODE( 8FIL );
  527. if ( CLIENT_CAPABLE_OF(NT_STATUS, WorkContext->Connection) ) {
  528. SmbPutUlong(
  529. (PULONG)&header->ErrorClass,
  530. (ULONG)STATUS_BUFFER_OVERFLOW
  531. );
  532. flags |= SMB_FLAGS2_NT_STATUS;
  533. } else {
  534. header->ErrorClass = SMB_ERR_CLASS_DOS;
  535. SmbPutUshort( &header->Error, ERROR_MORE_DATA );
  536. flags &= ~SMB_FLAGS2_NT_STATUS;
  537. }
  538. SmbPutAlignedUshort( &header->Flags2, flags );
  539. return;
  540. } // SrvSetBufferOverflowError