Source code of Windows XP (NT5)
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.

2879 lines
80 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. smbfile.c
  5. Abstract:
  6. This module implements file-control SMB processors:
  7. Flush
  8. Delete
  9. Rename
  10. Move
  11. Copy
  12. Author:
  13. David Treadwell (davidtr) 15-Dec-1989
  14. Revision History:
  15. --*/
  16. #include "precomp.h"
  17. #include "smbfile.tmh"
  18. #pragma hdrstop
  19. #define BugCheckFileId SRV_FILE_SMBFILE
  20. //
  21. // Forward declarations
  22. //
  23. VOID SRVFASTCALL
  24. BlockingDelete (
  25. IN OUT PWORK_CONTEXT WorkContext
  26. );
  27. VOID SRVFASTCALL
  28. BlockingMove (
  29. IN OUT PWORK_CONTEXT WorkContext
  30. );
  31. VOID SRVFASTCALL
  32. BlockingRename (
  33. IN OUT PWORK_CONTEXT WorkContext
  34. );
  35. NTSTATUS
  36. DoDelete (
  37. IN PUNICODE_STRING FullFileName,
  38. IN PUNICODE_STRING RelativeFileName,
  39. IN PWORK_CONTEXT WorkContext,
  40. IN USHORT SmbSearchAttributes,
  41. IN PSHARE Share
  42. );
  43. NTSTATUS
  44. FindAndFlushFile (
  45. IN PWORK_CONTEXT WorkContext
  46. );
  47. VOID SRVFASTCALL
  48. RestartFlush (
  49. IN OUT PWORK_CONTEXT WorkContext
  50. );
  51. NTSTATUS
  52. StartFlush (
  53. IN PWORK_CONTEXT WorkContext,
  54. IN PRFCB Rfcb
  55. );
  56. #ifdef ALLOC_PRAGMA
  57. #pragma alloc_text( PAGE, SrvSmbFlush )
  58. #pragma alloc_text( PAGE, RestartFlush )
  59. #pragma alloc_text( PAGE, StartFlush )
  60. #pragma alloc_text( PAGE, SrvSmbDelete )
  61. #pragma alloc_text( PAGE, BlockingDelete )
  62. #pragma alloc_text( PAGE, DoDelete )
  63. #pragma alloc_text( PAGE, SrvSmbRename )
  64. #pragma alloc_text( PAGE, BlockingRename )
  65. #pragma alloc_text( PAGE, SrvSmbMove )
  66. #pragma alloc_text( PAGE, BlockingMove )
  67. #pragma alloc_text( PAGE, SrvSmbNtRename )
  68. #endif
  69. #if 0
  70. #pragma alloc_text( PAGECONN, FindAndFlushFile )
  71. #endif
  72. SMB_PROCESSOR_RETURN_TYPE
  73. SrvSmbFlush (
  74. SMB_PROCESSOR_PARAMETERS
  75. )
  76. /*++
  77. Routine Description:
  78. This routine processes the Flush SMB. It ensures that all data and
  79. allocation information for the specified file has been written out
  80. before the response is sent.
  81. Arguments:
  82. SMB_PROCESSOR_PARAMETERS - See smbtypes.h for a description
  83. of the parameters to SMB processor routines.
  84. Return Value:
  85. SMB_PROCESSOR_RETURN_TYPE - See smbtypes.h
  86. --*/
  87. {
  88. PREQ_FLUSH request;
  89. PRESP_FLUSH response;
  90. NTSTATUS status = STATUS_SUCCESS;
  91. SMB_STATUS SmbStatus = SmbStatusInProgress;
  92. PRFCB rfcb;
  93. PAGED_CODE( );
  94. if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
  95. WorkContext->PreviousSMB = EVENT_TYPE_SMB_FLUSH;
  96. SrvWmiStartContext(WorkContext);
  97. request = (PREQ_FLUSH)WorkContext->RequestParameters;
  98. response = (PRESP_FLUSH)WorkContext->ResponseParameters;
  99. IF_SMB_DEBUG(FILE_CONTROL1) {
  100. KdPrint(( "Flush request; FID 0x%lx\n",
  101. SmbGetUshort( &request->Fid ) ));
  102. }
  103. //
  104. // If a FID was specified, flush just that file. If FID == -1,
  105. // then flush all files corresponding to the PID passed in the
  106. // SMB header.
  107. //
  108. if ( SmbGetUshort( &request->Fid ) == (USHORT)0xFFFF ) {
  109. //
  110. // Find a single file to flush and flush it. We'll start one
  111. // flush here, then RestartFlush will handle flushing the rest
  112. // of the files.
  113. //
  114. WorkContext->Parameters.CurrentTableIndex = 0;
  115. status = FindAndFlushFile( WorkContext );
  116. if ( status == STATUS_NO_MORE_FILES ) {
  117. //
  118. // There were no files that needed to be flushed. Build and
  119. // send a response SMB.
  120. //
  121. response->WordCount = 0;
  122. SmbPutUshort( &response->ByteCount, 0 );
  123. WorkContext->ResponseParameters =
  124. NEXT_LOCATION( response, RESP_FLUSH, 0 );
  125. SmbStatus = SmbStatusSendResponse;
  126. goto Cleanup;
  127. }
  128. SmbStatus = SmbStatusInProgress;
  129. goto Cleanup;
  130. }
  131. //
  132. // Flush of a specific file. Verify the FID. If verified, the
  133. // RFCB block is referenced and its address is stored in the
  134. // WorkContext block, and the RFCB address is returned.
  135. //
  136. rfcb = SrvVerifyFid(
  137. WorkContext,
  138. SmbGetUshort( &request->Fid ),
  139. TRUE,
  140. SrvRestartSmbReceived, // serialize with raw write
  141. &status
  142. );
  143. if ( rfcb == SRV_INVALID_RFCB_POINTER ) {
  144. if ( !NT_SUCCESS( status ) ) {
  145. //
  146. // Invalid file ID or write behind error. Reject the request.
  147. //
  148. IF_DEBUG(ERRORS) {
  149. KdPrint((
  150. "SrvSmbFlush: Status %X on FID: 0x%lx\n",
  151. status,
  152. SmbGetUshort( &request->Fid )
  153. ));
  154. }
  155. SrvSetSmbError( WorkContext, status );
  156. SmbStatus = SmbStatusSendResponse;
  157. goto Cleanup;
  158. }
  159. //
  160. // The work item has been queued because a raw write is in
  161. // progress.
  162. //
  163. SmbStatus = SmbStatusInProgress;
  164. goto Cleanup;
  165. }
  166. //
  167. // Set the CurrentTableIndex field of the work context block to
  168. // NULL so that the restart routine will know that only a single
  169. // file is to be flushed.
  170. //
  171. WorkContext->Parameters.CurrentTableIndex = -1;
  172. IF_SMB_DEBUG(FILE_CONTROL2) {
  173. KdPrint(( "Flushing buffers for FID %lx, RFCB %p\n", rfcb->Fid, rfcb ));
  174. }
  175. //
  176. // Start the flush operation on the file corresponding to the RFCB.
  177. //
  178. status = StartFlush( WorkContext, rfcb );
  179. if ( !NT_SUCCESS(status) ) {
  180. //
  181. // Unable to start the I/O. Clean up the I/O request. Return
  182. // an error to the client.
  183. //
  184. SrvSetSmbError( WorkContext, status );
  185. SmbStatus = SmbStatusSendResponse;
  186. goto Cleanup;
  187. }
  188. //
  189. // The flush request was successfully started. Return the InProgress
  190. // status to the caller, indicating that the caller should do
  191. // nothing further with the SMB/WorkContext at the present time.
  192. //
  193. SmbStatus = SmbStatusInProgress;
  194. IF_DEBUG(TRACE2) KdPrint(( "SrvSmbFlush complete\n" ));
  195. Cleanup:
  196. SrvWmiEndContext(WorkContext);
  197. return SmbStatus;
  198. } // SrvSmbFlush
  199. NTSTATUS
  200. FindAndFlushFile (
  201. IN PWORK_CONTEXT WorkContext
  202. )
  203. {
  204. NTSTATUS status;
  205. LONG currentTableIndex;
  206. PRFCB rfcb;
  207. USHORT pid = SmbGetAlignedUshort( &WorkContext->RequestHeader->Pid );
  208. PCONNECTION connection = WorkContext->Connection;
  209. PTABLE_HEADER tableHeader;
  210. KIRQL oldIrql;
  211. //UNLOCKABLE_CODE( CONN );
  212. IF_SMB_DEBUG(FILE_CONTROL1) {
  213. KdPrint(( "Flush FID == -1; flush all files for PID %lx\n", pid ));
  214. }
  215. //
  216. // Walk the connection's file table, looking an RFCB with a PID
  217. // equal to the PID passed in the SMB header.
  218. //
  219. // Acquire the lock that protects the connection's file table.
  220. // This prevents an RFCB from going away between when we find a
  221. // pointer to it and when we reference it.
  222. //
  223. tableHeader = &connection->FileTable;
  224. ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
  225. for ( currentTableIndex = WorkContext->Parameters.CurrentTableIndex;
  226. currentTableIndex < (LONG)tableHeader->TableSize;
  227. currentTableIndex++ ) {
  228. rfcb = tableHeader->Table[currentTableIndex].Owner;
  229. IF_SMB_DEBUG(FILE_CONTROL1) {
  230. KdPrint(( "Looking at RFCB %p, PID %lx, FID %lx\n",
  231. rfcb, rfcb != NULL ? rfcb->Pid : 0,
  232. rfcb != NULL ? rfcb->Fid : 0 ));
  233. }
  234. if ( rfcb == NULL || rfcb->Pid != pid ) {
  235. continue;
  236. }
  237. //
  238. // Reference the rfcb if it is active.
  239. //
  240. if ( GET_BLOCK_STATE(rfcb) != BlockStateActive ) {
  241. continue;
  242. }
  243. rfcb->BlockHeader.ReferenceCount++;
  244. //
  245. // Now that the RFCB has been referenced, we can safely
  246. // release the lock that protects the connection's file
  247. // table.
  248. //
  249. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  250. WorkContext->Rfcb = rfcb;
  251. //
  252. // Mark the rfcb as active
  253. //
  254. rfcb->IsActive = TRUE;
  255. //
  256. // Set the CurrentTableIndex field of the work context
  257. // block so that the restart routine knows where to
  258. // continue looking for RFCBs to flush.
  259. //
  260. WorkContext->Parameters.CurrentTableIndex = currentTableIndex;
  261. IF_SMB_DEBUG(FILE_CONTROL2) {
  262. KdPrint(( "Flushing buffers for FID %lx, RFCB %p\n",
  263. rfcb->Fid, rfcb ));
  264. }
  265. //
  266. // Start the I/O to flush the file.
  267. //
  268. status = StartFlush( WorkContext, rfcb );
  269. //
  270. // If there was an access violation or some other error,
  271. // simply continue walking through the file table.
  272. // We ignore these errors for flush with FID=-1.
  273. //
  274. // Note that StartFlush only returns an error if the IO
  275. // operation *was*not* started. If the operation was
  276. // started, then errors will be processed in this routine
  277. // when it is called later by IoCompleteRequest.
  278. //
  279. if ( status != STATUS_PENDING ) {
  280. SrvDereferenceRfcb( rfcb );
  281. WorkContext->Rfcb = NULL;
  282. ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
  283. continue;
  284. }
  285. //
  286. // The flush request has been started.
  287. //
  288. IF_DEBUG(TRACE2) KdPrint(( "RestartFlush complete\n" ));
  289. return STATUS_SUCCESS;
  290. } // for ( ; ; ) (walk file table)
  291. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  292. return STATUS_NO_MORE_FILES;
  293. } // FindAndFlushFile
  294. VOID SRVFASTCALL
  295. RestartFlush (
  296. IN OUT PWORK_CONTEXT WorkContext
  297. )
  298. /*++
  299. Routine Description:
  300. Processes flush completion.
  301. Arguments:
  302. WorkContext - Supplies a pointer to the work context block
  303. describing server-specific context for the request.
  304. Return Value:
  305. None.
  306. --*/
  307. {
  308. NTSTATUS status = STATUS_SUCCESS;
  309. PRESP_FLUSH response;
  310. PAGED_CODE( );
  311. if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
  312. WorkContext->PreviousSMB = EVENT_TYPE_SMB_FLUSH;
  313. SrvWmiStartContext(WorkContext);
  314. IF_DEBUG(WORKER1) KdPrint(( " - RestartFlush\n" ));
  315. response = (PRESP_FLUSH)WorkContext->ResponseParameters;
  316. //
  317. // If the flush request failed, set an error status in the response
  318. // header.
  319. //
  320. status = WorkContext->Irp->IoStatus.Status;
  321. //
  322. // If an error occurred during processing of the flush, return the
  323. // error to the client. No more further files will be flushed.
  324. //
  325. // *** This should be very rare. STATUS_DISK_FULL is probably the
  326. // main culprit.
  327. if ( !NT_SUCCESS(status) ) {
  328. IF_DEBUG(ERRORS) KdPrint(( "Flush failed: %X\n", status ));
  329. SrvSetSmbError( WorkContext, status );
  330. SrvEndSmbProcessing( WorkContext, SmbStatusSendResponse );
  331. IF_DEBUG(TRACE2) KdPrint(( "RestartFlush complete\n" ));
  332. return;
  333. }
  334. IF_SMB_DEBUG(FILE_CONTROL1) {
  335. KdPrint(( "Flush operation for RFCB %p was successful.\n",
  336. WorkContext->Rfcb ));
  337. }
  338. //
  339. // If the FID in the original request was -1, look for more files
  340. // to flush.
  341. //
  342. if ( WorkContext->Parameters.CurrentTableIndex != -1 ) {
  343. //
  344. // Dereference the RFCB that was stored in the work context block,
  345. // and set the pointer to NULL so that it isn't accidentally
  346. // dereferenced again later.
  347. //
  348. SrvDereferenceRfcb( WorkContext->Rfcb );
  349. WorkContext->Rfcb = NULL;
  350. //
  351. // Find a file to flush and flush it.
  352. //
  353. WorkContext->Parameters.CurrentTableIndex++;
  354. status = FindAndFlushFile( WorkContext );
  355. //
  356. // If a file was found and IO operation started, then return. If
  357. // all the appropriate files have been flushed, send a response SMB.
  358. //
  359. if ( status != STATUS_NO_MORE_FILES ) {
  360. return;
  361. }
  362. } // if ( WorkContext->Parameters.CurrentTableIndex != -1 )
  363. //
  364. // All files have been flushed. Build the response SMB.
  365. //
  366. response->WordCount = 0;
  367. SmbPutUshort( &response->ByteCount, 0 );
  368. WorkContext->ResponseParameters = NEXT_LOCATION( response, RESP_FLUSH, 0 );
  369. //
  370. // Processing of the SMB is complete. Call SrvEndSmbProcessing to
  371. // send the response.
  372. //
  373. SrvEndSmbProcessing( WorkContext, SmbStatusSendResponse );
  374. IF_DEBUG(TRACE2) KdPrint(( "SrvSmbFlush complete.\n" ));
  375. SrvWmiEndContext(WorkContext);
  376. return;
  377. } // RestartFlush
  378. NTSTATUS
  379. StartFlush (
  380. IN PWORK_CONTEXT WorkContext,
  381. IN PRFCB Rfcb
  382. )
  383. /*++
  384. Routine Description:
  385. Processes the actual file flush.
  386. Arguments:
  387. WorkContext - Supplies a pointer to the work context block
  388. describing server-specific context for the request.
  389. Rfcb - a pointer to the RFCB corresponding to the file to flush.
  390. Return Value:
  391. STATUS_PENDING if the IO operation was started, or an error from
  392. CHECK_FUNCTION_ACCESS (STATUS_ACCESS_DENIED, for example).
  393. --*/
  394. {
  395. NTSTATUS status;
  396. PAGED_CODE( );
  397. //
  398. // Verify that the client has write access to the file via the
  399. // specified handle.
  400. //
  401. CHECK_FUNCTION_ACCESS(
  402. Rfcb->GrantedAccess,
  403. IRP_MJ_FLUSH_BUFFERS,
  404. 0,
  405. 0,
  406. &status
  407. );
  408. if ( !NT_SUCCESS(status) ) {
  409. IF_DEBUG(ERRORS) {
  410. KdPrint(( "StartFlush: IoCheckFunctionAccess failed: "
  411. "0x%X, GrantedAccess: %lx. Access granted anyway.\n",
  412. status, Rfcb->GrantedAccess ));
  413. }
  414. //
  415. // Some dumb apps flush files opened for r/o. If this happens,
  416. // assume the flush worked. OS/2 let's the
  417. // flush through and we should do the same.
  418. //
  419. WorkContext->Irp->IoStatus.Status = STATUS_SUCCESS;
  420. RestartFlush( WorkContext );
  421. return(STATUS_PENDING);
  422. }
  423. //
  424. // Flush the file's buffers.
  425. //
  426. SrvBuildFlushRequest(
  427. WorkContext->Irp, // input IRP address
  428. Rfcb->Lfcb->FileObject, // target file object address
  429. WorkContext // context
  430. );
  431. //
  432. // Pass the request to the file system.
  433. //
  434. WorkContext->FsdRestartRoutine = SrvQueueWorkToFspAtDpcLevel;
  435. WorkContext->FspRestartRoutine = RestartFlush;
  436. (VOID)IoCallDriver( Rfcb->Lfcb->DeviceObject, WorkContext->Irp );
  437. return STATUS_PENDING;
  438. } // StartFlush
  439. SMB_PROCESSOR_RETURN_TYPE
  440. SrvSmbDelete (
  441. SMB_PROCESSOR_PARAMETERS
  442. )
  443. /*++
  444. Routine Description:
  445. Processes the Delete SMB.
  446. Arguments:
  447. SMB_PROCESSOR_PARAMETERS - See smbprocs.h for a description
  448. of the parameters to SMB processor routines.
  449. Return Value:
  450. SMB_PROCESSOR_RETURN_TYPE - See smbprocs.h
  451. --*/
  452. {
  453. PAGED_CODE();
  454. //
  455. // This SMB must be processed in a blocking thread.
  456. //
  457. if( !WorkContext->UsingBlockingThread ) {
  458. WorkContext->FspRestartRoutine = BlockingDelete;
  459. SrvQueueWorkToBlockingThread( WorkContext );
  460. } else {
  461. BlockingDelete( WorkContext );
  462. }
  463. return SmbStatusInProgress;
  464. } // SrvSmbDelete
  465. VOID SRVFASTCALL
  466. BlockingDelete (
  467. IN OUT PWORK_CONTEXT WorkContext
  468. )
  469. /*++
  470. Routine Description:
  471. This routine processes the Delete SMB.
  472. Arguments:
  473. SMB_PROCESSOR_PARAMETERS - See smbtypes.h for a description
  474. of the parameters to SMB processor routines.
  475. Return Value:
  476. SMB_PROCESSOR_RETURN_TYPE - See smbtypes.h
  477. --*/
  478. {
  479. PREQ_DELETE request;
  480. PRESP_DELETE response;
  481. NTSTATUS status = STATUS_SUCCESS;
  482. UNICODE_STRING filePathName;
  483. UNICODE_STRING fullPathName;
  484. PTREE_CONNECT treeConnect;
  485. PSESSION session;
  486. PSHARE share;
  487. BOOLEAN isUnicode;
  488. ULONG deleteRetries;
  489. PSRV_DIRECTORY_INFORMATION directoryInformation;
  490. PAGED_CODE( );
  491. if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
  492. WorkContext->PreviousSMB = EVENT_TYPE_SMB_DELETE;
  493. SrvWmiStartContext(WorkContext);
  494. IF_SMB_DEBUG(FILE_CONTROL1) {
  495. KdPrint(( "Delete file request header at 0x%p, response header at 0x%p\n",
  496. WorkContext->RequestHeader,
  497. WorkContext->ResponseHeader ));
  498. KdPrint(( "Delete file request parameters at 0x%p, response parameters at 0x%p\n",
  499. WorkContext->RequestParameters,
  500. WorkContext->ResponseParameters ));
  501. }
  502. request = (PREQ_DELETE)WorkContext->RequestParameters;
  503. response = (PRESP_DELETE)WorkContext->ResponseParameters;
  504. //
  505. // If a session block has not already been assigned to the current
  506. // work context , verify the UID. If verified, the address of the
  507. // session block corresponding to this user is stored in the
  508. // WorkContext block and the session block is referenced.
  509. //
  510. // Find tree connect corresponding to given TID if a tree connect
  511. // pointer has not already been put in the WorkContext block by an
  512. // AndX command.
  513. //
  514. status = SrvVerifyUidAndTid(
  515. WorkContext,
  516. &session,
  517. &treeConnect,
  518. ShareTypeDisk
  519. );
  520. if ( !NT_SUCCESS(status) ) {
  521. IF_DEBUG(SMB_ERRORS) {
  522. KdPrint(( "SrvSmbDelete: Invalid UID or TID\n" ));
  523. }
  524. goto error_exit;
  525. }
  526. //
  527. // If the session has expired, return that info
  528. //
  529. if( session->IsSessionExpired )
  530. {
  531. status = SESSION_EXPIRED_STATUS_CODE;
  532. goto error_exit;
  533. }
  534. //
  535. // Get the share block from the tree connect block. This doesn't need
  536. // to be a referenced pointer becsue the tree connect has it referenced,
  537. // and we just referenced the tree connect.
  538. //
  539. share = treeConnect->Share;
  540. //
  541. // Initialize the string containing the path name. The +1 is to account
  542. // for the ASCII token in the Buffer field of the request SMB.
  543. //
  544. isUnicode = SMB_IS_UNICODE( WorkContext );
  545. status = SrvCanonicalizePathName(
  546. WorkContext,
  547. share,
  548. NULL,
  549. (PVOID)(request->Buffer + 1),
  550. END_OF_REQUEST_SMB( WorkContext ),
  551. TRUE,
  552. isUnicode,
  553. &filePathName
  554. );
  555. if( !NT_SUCCESS( status ) ) {
  556. IF_DEBUG(SMB_ERRORS) {
  557. KdPrint(( "SrvSmbDelete: illegal path name: %s\n",
  558. (PSZ)request->Buffer + 1 ));
  559. }
  560. goto error_exit;
  561. }
  562. //
  563. // Find out whether there are wildcards in the file name. If so,
  564. // then call SrvQueryDirectoryFile to expand the wildcards; if not,
  565. // just delete the file directly.
  566. //
  567. if ( !FsRtlDoesNameContainWildCards( &filePathName ) ) {
  568. //
  569. // Build a full pathname to the file.
  570. //
  571. SrvAllocateAndBuildPathName(
  572. &treeConnect->Share->DosPathName,
  573. &filePathName,
  574. NULL,
  575. &fullPathName
  576. );
  577. if ( fullPathName.Buffer == NULL ) {
  578. IF_DEBUG(ERRORS) {
  579. KdPrint(( "SrvSmbDelete: SrvAllocateAndBuildPathName failed\n" ));
  580. }
  581. if ( !isUnicode ) {
  582. RtlFreeUnicodeString( &filePathName );
  583. }
  584. status = STATUS_INSUFF_SERVER_RESOURCES;
  585. goto error_exit;
  586. }
  587. IF_SMB_DEBUG(FILE_CONTROL2) {
  588. KdPrint(( "Full path name to file is %wZ\n", &fullPathName ));
  589. }
  590. //
  591. // Perform the actual delete operation on this filename.
  592. //
  593. deleteRetries = SrvSharingViolationRetryCount;
  594. start_retry1:
  595. status = DoDelete(
  596. &fullPathName,
  597. &filePathName,
  598. WorkContext,
  599. SmbGetUshort( &request->SearchAttributes ),
  600. treeConnect->Share
  601. );
  602. if ( (status == STATUS_SHARING_VIOLATION) &&
  603. (deleteRetries-- > 0) ) {
  604. (VOID) KeDelayExecutionThread(
  605. KernelMode,
  606. FALSE,
  607. &SrvSharingViolationDelay
  608. );
  609. goto start_retry1;
  610. }
  611. FREE_HEAP( fullPathName.Buffer );
  612. if ( !isUnicode ) {
  613. RtlFreeUnicodeString( &filePathName );
  614. }
  615. if ( !NT_SUCCESS(status) ) {
  616. goto error_exit;
  617. }
  618. } else {
  619. BOOLEAN firstCall = TRUE;
  620. CLONG bufferLength;
  621. UNICODE_STRING subdirInfo;
  622. BOOLEAN filterLongNames;
  623. //
  624. // A buffer of non-paged pool is required for
  625. // SrvQueryDirectoryFile. Since this routine does not use any
  626. // of the SMB buffer after the pathname of the file to delete,
  627. // we can use this. The buffer should be quadword-aligned.
  628. //
  629. directoryInformation =
  630. (PSRV_DIRECTORY_INFORMATION)( (ULONG_PTR)((PCHAR)request->Buffer +
  631. SmbGetUshort( &request->ByteCount ) + 7) & ~7 );
  632. bufferLength = WorkContext->RequestBuffer->BufferLength -
  633. PTR_DIFF(directoryInformation,
  634. WorkContext->RequestBuffer->Buffer);
  635. //
  636. // We need the full path name of each file that is returned by
  637. // SrvQueryDirectoryFile, so we need to find the part of the
  638. // passed filename that contains subdirectory information (e.g.
  639. // for a\b\c\*.*, we want a string that indicates a\b\c).
  640. //
  641. subdirInfo.Buffer = filePathName.Buffer;
  642. subdirInfo.Length = SrvGetSubdirectoryLength( &filePathName );
  643. subdirInfo.MaximumLength = subdirInfo.Length;
  644. IF_SMB_DEBUG(FILE_CONTROL2) {
  645. KdPrint(( "Subdirectory info is %wZ\n", &subdirInfo ));
  646. }
  647. //
  648. // Determine whether long filenames (non-8.3) should be filtered out
  649. // or processed.
  650. //
  651. if ( (SmbGetAlignedUshort( &WorkContext->RequestHeader->Flags2 ) &
  652. SMB_FLAGS2_KNOWS_LONG_NAMES) != 0 ) {
  653. filterLongNames = FALSE;
  654. } else {
  655. filterLongNames = TRUE;
  656. }
  657. //
  658. // When we call SrvQueryDirectoryFile, it will open the file for
  659. // us, so all we have to do is delete it with
  660. // NtSetInformationFile.
  661. //
  662. // *** We ask for FileBothDirectoryInformation so that we will
  663. // pick up long names on NTFS that have short name
  664. // equivalents. Without this, DOS clients will not be able
  665. // to delete long names on NTFS volumes.
  666. //
  667. while ( ( status = SrvQueryDirectoryFile(
  668. WorkContext,
  669. firstCall,
  670. filterLongNames,
  671. FALSE,
  672. FileBothDirectoryInformation,
  673. 0,
  674. &filePathName,
  675. NULL,
  676. SmbGetUshort( &request->SearchAttributes ),
  677. directoryInformation,
  678. bufferLength
  679. ) ) != STATUS_NO_MORE_FILES ) {
  680. PFILE_BOTH_DIR_INFORMATION bothDirInfo;
  681. UNICODE_STRING name;
  682. UNICODE_STRING relativeName;
  683. if ( !NT_SUCCESS(status) ) {
  684. IF_DEBUG(ERRORS) {
  685. KdPrint(( "SrvSmbDelete: SrvQueryDirectoryFile failed: "
  686. "%X\n", status ));
  687. }
  688. if ( !isUnicode ) {
  689. RtlFreeUnicodeString( &filePathName );
  690. }
  691. goto error_exit1;
  692. }
  693. bothDirInfo =
  694. (PFILE_BOTH_DIR_INFORMATION)directoryInformation->CurrentEntry;
  695. //
  696. // Note that we use the standard name to do the delete, even
  697. // though we may have matched on the NTFS short name. The
  698. // client doesn't care which name we use to do the delete.
  699. //
  700. name.Length = (SHORT)bothDirInfo->FileNameLength;
  701. name.MaximumLength = name.Length;
  702. name.Buffer = bothDirInfo->FileName;
  703. IF_SMB_DEBUG(FILE_CONTROL2) {
  704. KdPrint(( "SrvQueryDirectoryFile--name %wZ, length = %ld, "
  705. "status = %X\n",
  706. &name,
  707. directoryInformation->CurrentEntry->FileNameLength,
  708. status ));
  709. }
  710. firstCall = FALSE;
  711. //
  712. // Build a full pathname to the file.
  713. //
  714. SrvAllocateAndBuildPathName(
  715. &treeConnect->Share->DosPathName,
  716. &subdirInfo,
  717. &name,
  718. &fullPathName
  719. );
  720. if ( fullPathName.Buffer == NULL ) {
  721. IF_DEBUG(ERRORS) {
  722. KdPrint(( "SrvSmbDelete: SrvAllocateAndBuildPathName "
  723. "failed\n" ));
  724. }
  725. if ( !isUnicode ) {
  726. RtlFreeUnicodeString( &filePathName );
  727. }
  728. status = STATUS_INSUFFICIENT_RESOURCES;
  729. goto error_exit1;
  730. }
  731. IF_SMB_DEBUG(FILE_CONTROL2) {
  732. KdPrint(( "Full path name to file is %wZ\n", &fullPathName ));
  733. }
  734. //
  735. // Build the relative path name to the file.
  736. //
  737. SrvAllocateAndBuildPathName(
  738. &subdirInfo,
  739. &name,
  740. NULL,
  741. &relativeName
  742. );
  743. if ( relativeName.Buffer == NULL ) {
  744. IF_DEBUG(ERRORS) {
  745. KdPrint(( "SrvSmbDelete: SrvAllocateAndBuildPathName failed\n" ));
  746. }
  747. FREE_HEAP( fullPathName.Buffer );
  748. if ( !isUnicode ) {
  749. RtlFreeUnicodeString( &filePathName );
  750. }
  751. status = STATUS_INSUFF_SERVER_RESOURCES;
  752. goto error_exit1;
  753. }
  754. IF_SMB_DEBUG(FILE_CONTROL2) {
  755. KdPrint(( "Full path name to file is %wZ\n", &fullPathName ));
  756. }
  757. //
  758. // Perform the actual delete operation on this filename.
  759. //
  760. // *** SrvQueryDirectoryFile has already filtered based on
  761. // the search attributes, so tell DoDelete that files
  762. // with the system and hidden bits are OK. This will
  763. // prevent the call to NtQueryDirectoryFile performed
  764. // in SrvCheckSearchAttributesForHandle.
  765. deleteRetries = SrvSharingViolationRetryCount;
  766. start_retry2:
  767. status = DoDelete(
  768. &fullPathName,
  769. &relativeName,
  770. WorkContext,
  771. (USHORT)(FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN),
  772. treeConnect->Share
  773. );
  774. if ( (status == STATUS_SHARING_VIOLATION) &&
  775. (deleteRetries-- > 0) ) {
  776. (VOID) KeDelayExecutionThread(
  777. KernelMode,
  778. FALSE,
  779. &SrvSharingViolationDelay
  780. );
  781. goto start_retry2;
  782. }
  783. FREE_HEAP( relativeName.Buffer );
  784. FREE_HEAP( fullPathName.Buffer );
  785. if ( !NT_SUCCESS(status) ) {
  786. if ( !isUnicode ) {
  787. RtlFreeUnicodeString( &filePathName );
  788. }
  789. goto error_exit1;
  790. }
  791. }
  792. //
  793. // Close the directory search.
  794. //
  795. if ( !isUnicode ) {
  796. RtlFreeUnicodeString( &filePathName );
  797. }
  798. SrvCloseQueryDirectory( directoryInformation );
  799. //
  800. // If no files were found, return an error to the client.
  801. //
  802. if ( firstCall ) {
  803. status = STATUS_NO_SUCH_FILE;
  804. goto error_exit;
  805. }
  806. }
  807. //
  808. // Build the response SMB.
  809. //
  810. response->WordCount = 0;
  811. SmbPutUshort( &response->ByteCount, 0 );
  812. WorkContext->ResponseParameters = NEXT_LOCATION(
  813. response,
  814. RESP_DELETE,
  815. 0
  816. );
  817. IF_DEBUG(TRACE2) KdPrint(( "SrvSmbDelete complete.\n" ));
  818. goto normal_exit;
  819. error_exit1:
  820. SrvCloseQueryDirectory( directoryInformation );
  821. error_exit:
  822. SrvSetSmbError( WorkContext, status );
  823. normal_exit:
  824. SrvEndSmbProcessing( WorkContext, SmbStatusSendResponse );
  825. SrvWmiEndContext(WorkContext);
  826. return;
  827. } // BlockingDelete
  828. NTSTATUS
  829. DoDelete (
  830. IN PUNICODE_STRING FullFileName,
  831. IN PUNICODE_STRING RelativeFileName,
  832. IN PWORK_CONTEXT WorkContext,
  833. IN USHORT SmbSearchAttributes,
  834. IN PSHARE Share
  835. )
  836. /*++
  837. Routine Description:
  838. This routine performs the core of a file delete.
  839. Arguments:
  840. FileName - a full path name, from the system name space root, to the
  841. file to delete.
  842. RelativeFileName - the name of the file relative to the share root.
  843. WorkContext - context block for the operation. The RequestHeader and
  844. Session fields are used.
  845. SmbSearchAttributes - the search attributes passed in the request
  846. SMB. The actual file attributes are verified against these to
  847. make sure that the operation is legitimate.
  848. Return Value:
  849. NTSTATUS - indicates result of operation.
  850. --*/
  851. {
  852. NTSTATUS status;
  853. PMFCB mfcb;
  854. PNONPAGED_MFCB nonpagedMfcb;
  855. FILE_DISPOSITION_INFORMATION fileDispositionInformation;
  856. HANDLE fileHandle = NULL;
  857. ULONG caseInsensitive;
  858. IO_STATUS_BLOCK ioStatusBlock;
  859. PSRV_LOCK mfcbLock;
  860. ULONG hashValue;
  861. PAGED_CODE( );
  862. //
  863. // See if that file is already open. If it is open in
  864. // compatibility mode or is an FCB open, we have to close all of
  865. // that client's opens.
  866. //
  867. // *** SrvFindMfcb references the MFCB--remember to dereference it.
  868. //
  869. if ( (WorkContext->RequestHeader->Flags & SMB_FLAGS_CASE_INSENSITIVE) ||
  870. WorkContext->Session->UsingUppercasePaths ) {
  871. caseInsensitive = OBJ_CASE_INSENSITIVE;
  872. mfcb = SrvFindMfcb( FullFileName, TRUE, &mfcbLock, &hashValue, WorkContext );
  873. } else {
  874. caseInsensitive = 0;
  875. mfcb = SrvFindMfcb( FullFileName, FALSE, &mfcbLock, &hashValue, WorkContext );
  876. }
  877. if ( mfcb != NULL ) {
  878. nonpagedMfcb = mfcb->NonpagedMfcb;
  879. ACQUIRE_LOCK( &nonpagedMfcb->Lock );
  880. }
  881. if( mfcbLock ) {
  882. RELEASE_LOCK( mfcbLock );
  883. }
  884. if ( mfcb == NULL || !mfcb->CompatibilityOpen ) {
  885. ACCESS_MASK deleteAccess = DELETE;
  886. OBJECT_ATTRIBUTES objectAttributes;
  887. //
  888. // Either the file wasn't opened by the server or it was not
  889. // a compatibility/FCB open, so open it here for the delete.
  890. //
  891. del_no_file_handle:
  892. //
  893. // If there was an MFCB for this file, we now hold its lock and a
  894. // referenced pointer. Undo both.
  895. //
  896. if ( mfcb != NULL ) {
  897. RELEASE_LOCK( &nonpagedMfcb->Lock );
  898. SrvDereferenceMfcb( mfcb );
  899. }
  900. SrvInitializeObjectAttributes_U(
  901. &objectAttributes,
  902. RelativeFileName,
  903. caseInsensitive,
  904. NULL,
  905. NULL
  906. );
  907. INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpenAttempts );
  908. INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpensForPathOperations );
  909. //
  910. // !!! Currently we can't specify complete if oplocked, because
  911. // this won't break a batch oplock. Unfortunately this also
  912. // means that we can't timeout the open (if the oplock break
  913. // takes too long) and fail this SMB gracefully.
  914. //
  915. status = SrvIoCreateFile(
  916. WorkContext,
  917. &fileHandle,
  918. DELETE, // DesiredAccess
  919. &objectAttributes,
  920. &ioStatusBlock,
  921. NULL, // AllocationSize
  922. 0L, // FileAttributes
  923. 0L, // ShareAccess
  924. FILE_OPEN, // Disposition
  925. FILE_NON_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT, // CreateOptions
  926. NULL, // EaBuffer
  927. 0L, // EaLength
  928. CreateFileTypeNone,
  929. NULL, // ExtraCreateParameters
  930. IO_FORCE_ACCESS_CHECK, // Options
  931. WorkContext->TreeConnect->Share
  932. );
  933. if( status == STATUS_INVALID_PARAMETER ) {
  934. status = SrvIoCreateFile(
  935. WorkContext,
  936. &fileHandle,
  937. DELETE, // DesiredAccess
  938. &objectAttributes,
  939. &ioStatusBlock,
  940. NULL, // AllocationSize
  941. 0L, // FileAttributes
  942. 0L, // ShareAccess
  943. FILE_OPEN, // Disposition
  944. FILE_NON_DIRECTORY_FILE, // CreateOptions
  945. NULL, // EaBuffer
  946. 0L, // EaLength
  947. CreateFileTypeNone,
  948. NULL, // ExtraCreateParameters
  949. IO_FORCE_ACCESS_CHECK, // Options
  950. WorkContext->TreeConnect->Share
  951. );
  952. }
  953. if ( NT_SUCCESS(status) ) {
  954. SRVDBG_CLAIM_HANDLE( fileHandle, "FIL", 27, 0 );
  955. }
  956. ASSERT( status != STATUS_OPLOCK_BREAK_IN_PROGRESS );
  957. if ( !NT_SUCCESS(status) ) {
  958. IF_DEBUG(ERRORS) {
  959. KdPrint(( "SrvSmbDelete: SrvIoCreateFile failed: %X\n",
  960. status ));
  961. }
  962. //
  963. // If the user didn't have this permission, update the
  964. // statistics database.
  965. //
  966. if ( status == STATUS_ACCESS_DENIED ) {
  967. SrvStatistics.AccessPermissionErrors++;
  968. }
  969. if ( fileHandle != NULL ) {
  970. SRVDBG_RELEASE_HANDLE( fileHandle, "FIL", 41, 0 );
  971. SrvNtClose( fileHandle, TRUE );
  972. }
  973. return status;
  974. }
  975. //
  976. // Make sure that the search attributes jive with the attributes
  977. // on the file.
  978. //
  979. status = SrvCheckSearchAttributesForHandle( fileHandle, SmbSearchAttributes );
  980. if ( !NT_SUCCESS(status) ) {
  981. SRVDBG_RELEASE_HANDLE( fileHandle, "FIL", 42, 0 );
  982. SrvNtClose( fileHandle, TRUE );
  983. return status;
  984. }
  985. //
  986. // Now that the file has been opened, delete it with
  987. // NtSetInformationFile.
  988. //
  989. SrvStatistics.TotalFilesOpened++;
  990. fileDispositionInformation.DeleteFile = TRUE;
  991. status = NtSetInformationFile(
  992. fileHandle,
  993. &ioStatusBlock,
  994. &fileDispositionInformation,
  995. sizeof(FILE_DISPOSITION_INFORMATION),
  996. FileDispositionInformation
  997. );
  998. if ( !NT_SUCCESS(status) ) {
  999. INTERNAL_ERROR(
  1000. ERROR_LEVEL_UNEXPECTED,
  1001. "SrvSmbDelete: NtSetInformationFile (file disposition) "
  1002. "returned %X",
  1003. status,
  1004. NULL
  1005. );
  1006. SrvLogServiceFailure( SRV_SVC_NT_SET_INFO_FILE, status );
  1007. SRVDBG_RELEASE_HANDLE( fileHandle, "FIL", 43, 0 );
  1008. SrvNtClose( fileHandle, TRUE );
  1009. return status;
  1010. }
  1011. IF_SMB_DEBUG(FILE_CONTROL2) {
  1012. if( NT_SUCCESS( status ) ) {
  1013. KdPrint(( "SrvSmbDelete: %wZ successfully deleted.\n", FullFileName ));
  1014. }
  1015. }
  1016. //
  1017. // Close the opened file so that it can be deleted. This will
  1018. // happen automatically, since the FCB_STATE_FLAG_DELETE_ON_CLOSE
  1019. // flag of the FCB has been set by NtSetInformationFile.
  1020. //
  1021. SRVDBG_RELEASE_HANDLE( fileHandle, "FIL", 44, 0 );
  1022. SrvNtClose( fileHandle, TRUE );
  1023. } else {
  1024. FILE_DISPOSITION_INFORMATION fileDispositionInformation;
  1025. HANDLE fileHandle = NULL;
  1026. //
  1027. // The file was opened by the server in compatibility mode
  1028. // or as an FCB open. Check the granted access to make sure
  1029. // that the file can be deleted.
  1030. //
  1031. ACCESS_MASK deleteAccess = DELETE;
  1032. PLFCB lfcb = CONTAINING_RECORD( mfcb->LfcbList.Blink, LFCB, MfcbListEntry );
  1033. //
  1034. // If this file has been closed. Go back to no mfcb case.
  1035. //
  1036. // *** The specific motivation for this change was to fix a problem
  1037. // where a compatibility mode open was closed, the response was
  1038. // sent, and a Delete SMB was received before the mfcb was
  1039. // completely cleaned up. This resulted in the MFCB and LFCB
  1040. // still being present, which caused the delete processing to
  1041. // try to use the file handle in the LFCB.
  1042. //
  1043. if ( lfcb->FileHandle == 0 ) {
  1044. goto del_no_file_handle;
  1045. }
  1046. //
  1047. // Make sure that the session which sent this request is the
  1048. // same as the one which has the file open.
  1049. //
  1050. if ( lfcb->Session != WorkContext->Session ) {
  1051. //
  1052. // A different session has the file open in compatibility
  1053. // mode, so reject the request.
  1054. //
  1055. RELEASE_LOCK( &nonpagedMfcb->Lock );
  1056. SrvDereferenceMfcb( mfcb );
  1057. return STATUS_SHARING_VIOLATION;
  1058. }
  1059. if ( !NT_SUCCESS(IoCheckDesiredAccess(
  1060. &deleteAccess,
  1061. lfcb->GrantedAccess )) ) {
  1062. //
  1063. // The client cannot delete this file, so close all the
  1064. // RFCBs and return an error.
  1065. //
  1066. SrvCloseRfcbsOnLfcb( lfcb );
  1067. RELEASE_LOCK( &nonpagedMfcb->Lock );
  1068. SrvDereferenceMfcb( mfcb );
  1069. return STATUS_ACCESS_DENIED;
  1070. }
  1071. //
  1072. // Delete the file with NtSetInformationFile.
  1073. //
  1074. fileHandle = lfcb->FileHandle;
  1075. fileDispositionInformation.DeleteFile = TRUE;
  1076. status = NtSetInformationFile(
  1077. fileHandle,
  1078. &ioStatusBlock,
  1079. &fileDispositionInformation,
  1080. sizeof(FILE_DISPOSITION_INFORMATION),
  1081. FileDispositionInformation
  1082. );
  1083. if ( !NT_SUCCESS(status) ) {
  1084. INTERNAL_ERROR(
  1085. ERROR_LEVEL_EXPECTED,
  1086. "SrvSmbDelete: NtSetInformationFile (disposition) "
  1087. "returned %X",
  1088. status,
  1089. NULL
  1090. );
  1091. SrvLogServiceFailure( SRV_SVC_NT_SET_INFO_FILE, status );
  1092. SrvCloseRfcbsOnLfcb( lfcb );
  1093. RELEASE_LOCK( &nonpagedMfcb->Lock );
  1094. SrvDereferenceMfcb( mfcb );
  1095. return status;
  1096. }
  1097. IF_SMB_DEBUG(FILE_CONTROL2) {
  1098. KdPrint(( "SrvSmbDelete: %wZ successfully deleted.\n", FullFileName ));
  1099. }
  1100. //
  1101. // Close the RFCBs on the MFCB. Since this is a compatability
  1102. // or FCB open, there is only a single LFCB for the MFCB. This
  1103. // will result in the LFCB's file handle being closed, so there
  1104. // is no need to call NtClose here.
  1105. //
  1106. SrvCloseRfcbsOnLfcb( lfcb );
  1107. RELEASE_LOCK( &nonpagedMfcb->Lock );
  1108. SrvDereferenceMfcb( mfcb );
  1109. }
  1110. return STATUS_SUCCESS;
  1111. } // DoDelete
  1112. SMB_PROCESSOR_RETURN_TYPE
  1113. SrvSmbRename (
  1114. SMB_PROCESSOR_PARAMETERS
  1115. )
  1116. /*++
  1117. Routine Description:
  1118. Processes the Rename SMB.
  1119. Arguments:
  1120. SMB_PROCESSOR_PARAMETERS - See smbprocs.h for a description
  1121. of the parameters to SMB processor routines.
  1122. Return Value:
  1123. SMB_PROCESSOR_RETURN_TYPE - See smbprocs.h
  1124. --*/
  1125. {
  1126. PAGED_CODE();
  1127. if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
  1128. WorkContext->PreviousSMB = EVENT_TYPE_SMB_RENAME;
  1129. SrvWmiStartContext(WorkContext);
  1130. //
  1131. // This SMB must be processed in a blocking thread.
  1132. //
  1133. WorkContext->FspRestartRoutine = BlockingRename;
  1134. SrvQueueWorkToBlockingThread( WorkContext );
  1135. SrvWmiEndContext(WorkContext);
  1136. return SmbStatusInProgress;
  1137. } // SrvSmbRename
  1138. VOID SRVFASTCALL
  1139. BlockingRename (
  1140. IN OUT PWORK_CONTEXT WorkContext
  1141. )
  1142. /*++
  1143. Routine Description:
  1144. This routine processes the Rename SMB.
  1145. Arguments:
  1146. WorkContext - work context block
  1147. Return Value:
  1148. None.
  1149. --*/
  1150. {
  1151. PREQ_RENAME request;
  1152. PREQ_NTRENAME ntrequest;
  1153. PUCHAR RenameBuffer;
  1154. PRESP_RENAME response;
  1155. NTSTATUS status = STATUS_SUCCESS;
  1156. UNICODE_STRING sourceName;
  1157. UNICODE_STRING targetName;
  1158. USHORT smbFlags;
  1159. USHORT ByteCount;
  1160. PCHAR target;
  1161. PCHAR lastPositionInBuffer;
  1162. PTREE_CONNECT treeConnect;
  1163. PSESSION session;
  1164. PSHARE share;
  1165. BOOLEAN isUnicode;
  1166. BOOLEAN isNtRename;
  1167. BOOLEAN isDfs;
  1168. PSRV_DIRECTORY_INFORMATION directoryInformation;
  1169. ULONG renameRetries;
  1170. PAGED_CODE( );
  1171. if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
  1172. WorkContext->PreviousSMB = EVENT_TYPE_SMB_RENAME;
  1173. SrvWmiStartContext(WorkContext);
  1174. IF_SMB_DEBUG(FILE_CONTROL1) {
  1175. KdPrint(( "Rename file request header at 0x%p, response header at 0x%p\n",
  1176. WorkContext->RequestHeader,
  1177. WorkContext->ResponseHeader ));
  1178. KdPrint(( "Rename file request parameters at 0x%p, response parameters at 0x%p\n",
  1179. WorkContext->RequestParameters,
  1180. WorkContext->ResponseParameters ));
  1181. }
  1182. response = (PRESP_RENAME)WorkContext->ResponseParameters;
  1183. request = (PREQ_RENAME)WorkContext->RequestParameters;
  1184. ntrequest = (PREQ_NTRENAME)WorkContext->RequestParameters;
  1185. isNtRename =
  1186. (BOOLEAN)(WorkContext->RequestHeader->Command == SMB_COM_NT_RENAME);
  1187. if (isNtRename) {
  1188. RenameBuffer = ntrequest->Buffer;
  1189. ByteCount = SmbGetUshort(&ntrequest->ByteCount);
  1190. } else {
  1191. RenameBuffer = request->Buffer;
  1192. ByteCount = SmbGetUshort(&request->ByteCount);
  1193. }
  1194. //
  1195. // If a session block has not already been assigned to the current
  1196. // work context , verify the UID. If verified, the address of the
  1197. // session block corresponding to this user is stored in the
  1198. // WorkContext block and the session block is referenced.
  1199. //
  1200. // Find tree connect corresponding to given TID if a tree connect
  1201. // pointer has not already been put in the WorkContext block by an
  1202. // AndX command.
  1203. //
  1204. status = SrvVerifyUidAndTid(
  1205. WorkContext,
  1206. &session,
  1207. &treeConnect,
  1208. ShareTypeDisk
  1209. );
  1210. if ( !NT_SUCCESS(status) ) {
  1211. IF_DEBUG(SMB_ERRORS) {
  1212. KdPrint(( "BlockingRename: Invalid UID or TID\n" ));
  1213. }
  1214. goto error_exit;
  1215. }
  1216. //
  1217. // If the session has expired, return that info
  1218. //
  1219. if( session->IsSessionExpired )
  1220. {
  1221. status = SESSION_EXPIRED_STATUS_CODE;
  1222. goto error_exit;
  1223. }
  1224. //
  1225. // Get the share block from the tree connect block. This does not need
  1226. // to be a referenced pointer because we have referenced the tree
  1227. // connect, and it has the share referenced.
  1228. //
  1229. share = treeConnect->Share;
  1230. //
  1231. // Set up the path name for the file we will search for. The +1
  1232. // accounts for the ASCII token of the SMB protocol.
  1233. //
  1234. isUnicode = SMB_IS_UNICODE( WorkContext );
  1235. isDfs = SMB_CONTAINS_DFS_NAME( WorkContext );
  1236. status = SrvCanonicalizePathName(
  1237. WorkContext,
  1238. share,
  1239. NULL,
  1240. (PVOID)(RenameBuffer + 1),
  1241. END_OF_REQUEST_SMB( WorkContext ),
  1242. TRUE,
  1243. isUnicode,
  1244. &sourceName
  1245. );
  1246. if( !NT_SUCCESS( status ) ) {
  1247. IF_DEBUG(SMB_ERRORS) {
  1248. KdPrint(( "BlockingRename: illegal path name: %s\n",
  1249. (PSZ)RenameBuffer + 1 ));
  1250. }
  1251. goto error_exit;
  1252. }
  1253. if( !sourceName.Length ) {
  1254. IF_DEBUG(SMB_ERRORS) {
  1255. KdPrint(( "BlockingRename: No source name\n" ));
  1256. }
  1257. status = STATUS_OBJECT_PATH_SYNTAX_BAD;
  1258. goto error_exit;
  1259. }
  1260. //
  1261. // Get a pointer to the new pathname of the file. This is in the
  1262. // buffer field of the request SMB after the source name. The
  1263. // target is delimited by the SMB_FORMAT_ASCII.
  1264. //
  1265. // While doing this, make sure that we do not walk off the end of the
  1266. // SMB buffer if the client did not include the SMB_FORMAT_ASCII
  1267. // token.
  1268. //
  1269. lastPositionInBuffer = (PCHAR)RenameBuffer + ByteCount;
  1270. if( !isUnicode ) {
  1271. for ( target = (PCHAR)RenameBuffer + 1;
  1272. (target < lastPositionInBuffer) && (*target != SMB_FORMAT_ASCII);
  1273. target++ ) {
  1274. ;
  1275. }
  1276. } else {
  1277. PWCHAR p = (PWCHAR)(RenameBuffer + 1);
  1278. //
  1279. // Skip the Original filename part. The name is null-terminated
  1280. // (see rdr\utils.c RdrCopyNetworkPath())
  1281. //
  1282. //
  1283. // Ensure p is suitably aligned
  1284. //
  1285. p = ALIGN_SMB_WSTR(p);
  1286. //
  1287. // Skip over the source filename
  1288. //
  1289. for( p = ALIGN_SMB_WSTR(p);
  1290. p < (PWCHAR)lastPositionInBuffer && *p != UNICODE_NULL;
  1291. p++ ) {
  1292. ;
  1293. }
  1294. //
  1295. // Search for SMB_FORMAT_ASCII which preceeds the target name
  1296. //
  1297. //
  1298. for ( target = (PUCHAR)(p + 1);
  1299. target < lastPositionInBuffer && *target != SMB_FORMAT_ASCII;
  1300. target++ ) {
  1301. ;
  1302. }
  1303. }
  1304. //
  1305. // If there was no SMB_FORMAT_ASCII in the passed buffer, fail.
  1306. //
  1307. if ( (target >= lastPositionInBuffer) || (*target != SMB_FORMAT_ASCII) ) {
  1308. if ( !isUnicode ) {
  1309. RtlFreeUnicodeString( &sourceName );
  1310. }
  1311. status = STATUS_INVALID_SMB;
  1312. goto error_exit;
  1313. }
  1314. //
  1315. // If the SMB was originally marked as containing Dfs names, then the
  1316. // call to SrvCanonicalizePathName for the source path has cleared that
  1317. // flag. So, re-mark the SMB as containing Dfs names before calling
  1318. // SrvCanonicalizePathName on the target path.
  1319. //
  1320. if (isDfs) {
  1321. SMB_MARK_AS_DFS_NAME( WorkContext );
  1322. }
  1323. status = SrvCanonicalizePathName(
  1324. WorkContext,
  1325. share,
  1326. NULL,
  1327. target + 1,
  1328. END_OF_REQUEST_SMB( WorkContext ),
  1329. TRUE,
  1330. isUnicode,
  1331. &targetName
  1332. );
  1333. if( !NT_SUCCESS( status ) ) {
  1334. IF_DEBUG(SMB_ERRORS) {
  1335. KdPrint(( "BlockingRename: illegal path name: %s\n", target + 1 ));
  1336. }
  1337. if ( !isUnicode ) {
  1338. RtlFreeUnicodeString( &sourceName );
  1339. }
  1340. goto error_exit;
  1341. }
  1342. if( !targetName.Length ) {
  1343. IF_DEBUG(SMB_ERRORS) {
  1344. KdPrint(( "BlockingRename: No target name\n" ));
  1345. }
  1346. if( !isUnicode ) {
  1347. RtlFreeUnicodeString( &sourceName );
  1348. }
  1349. status = STATUS_OBJECT_PATH_SYNTAX_BAD;
  1350. goto error_exit;
  1351. }
  1352. //
  1353. // Ensure this client's RFCB cache is empty. This covers the case
  1354. // where a client has open files in a directory we are trying
  1355. // to rename.
  1356. //
  1357. SrvCloseCachedRfcbsOnConnection( WorkContext->Connection );
  1358. if ( !FsRtlDoesNameContainWildCards( &sourceName ) ) {
  1359. USHORT InformationLevel = SMB_NT_RENAME_RENAME_FILE;
  1360. ULONG ClusterCount = 0;
  1361. if (isNtRename) {
  1362. InformationLevel = SmbGetUshort(&ntrequest->InformationLevel);
  1363. ClusterCount = SmbGetUlong(&ntrequest->ClusterCount);
  1364. }
  1365. smbFlags = 0;
  1366. //
  1367. // Use SrvMoveFile to rename the file. The SmbOpenFunction is
  1368. // set to indicate that existing files may not be overwritten,
  1369. // and we may create new files. Also, the target may not be
  1370. // a directory; if it already exists as a directory, fail.
  1371. //
  1372. renameRetries = SrvSharingViolationRetryCount;
  1373. start_retry1:
  1374. status = SrvMoveFile(
  1375. WorkContext,
  1376. WorkContext->TreeConnect->Share,
  1377. SMB_OFUN_CREATE_CREATE | SMB_OFUN_OPEN_FAIL,
  1378. &smbFlags,
  1379. SmbGetUshort( &request->SearchAttributes ),
  1380. TRUE,
  1381. InformationLevel,
  1382. ClusterCount,
  1383. &sourceName,
  1384. &targetName
  1385. );
  1386. if ( (status == STATUS_SHARING_VIOLATION) &&
  1387. (renameRetries-- > 0) ) {
  1388. (VOID) KeDelayExecutionThread(
  1389. KernelMode,
  1390. FALSE,
  1391. &SrvSharingViolationDelay
  1392. );
  1393. goto start_retry1;
  1394. }
  1395. if ( !isUnicode ) {
  1396. RtlFreeUnicodeString( &targetName );
  1397. RtlFreeUnicodeString( &sourceName );
  1398. }
  1399. if ( !NT_SUCCESS(status) ) {
  1400. goto error_exit;
  1401. }
  1402. } else if (isNtRename) { // Wild cards not allowed!
  1403. status = STATUS_OBJECT_PATH_SYNTAX_BAD;
  1404. goto error_exit;
  1405. } else {
  1406. BOOLEAN firstCall = TRUE;
  1407. UNICODE_STRING subdirInfo;
  1408. CLONG bufferLength;
  1409. BOOLEAN filterLongNames;
  1410. //
  1411. // We need the full path name of each file that is returned by
  1412. // SrvQueryDirectoryFile, so we need to find the part of the
  1413. // passed filename that contains subdirectory information (e.g.
  1414. // for a\b\c\*.*, we want a string that indicates a\b\c).
  1415. //
  1416. subdirInfo.Buffer = sourceName.Buffer;
  1417. subdirInfo.Length = SrvGetSubdirectoryLength( &sourceName );
  1418. subdirInfo.MaximumLength = subdirInfo.Length;
  1419. //
  1420. // SrvQueryDirectoryFile requires a buffer from nonpaged pool.
  1421. // Since this routine does not use the buffer field of the
  1422. // request SMB after the pathname, use this. The buffer must be
  1423. // quadword-aligned.
  1424. //
  1425. directoryInformation =
  1426. (PSRV_DIRECTORY_INFORMATION)((ULONG_PTR)((PCHAR)RenameBuffer + ByteCount + 7) & ~7);
  1427. bufferLength = WorkContext->RequestBuffer->BufferLength -
  1428. PTR_DIFF(directoryInformation,
  1429. WorkContext->RequestBuffer->Buffer);
  1430. smbFlags = 0;
  1431. //
  1432. // Determine whether long filenames (non-8.3) should be filtered out
  1433. // or processed.
  1434. //
  1435. if ( (SmbGetAlignedUshort( &WorkContext->RequestHeader->Flags2 ) &
  1436. SMB_FLAGS2_KNOWS_LONG_NAMES) != 0 ) {
  1437. filterLongNames = FALSE;
  1438. } else {
  1439. filterLongNames = TRUE;
  1440. }
  1441. //
  1442. // Call SrvQueryDirectoryFile to get file(s) to rename, renaming as
  1443. // we get each file.
  1444. //
  1445. // *** We ask for FileBothDirectoryInformation so that we will
  1446. // pick up long names on NTFS that have short name
  1447. // equivalents. Without this, DOS clients will not be able
  1448. // to rename long names on NTFS volumes.
  1449. //
  1450. while ( ( status = SrvQueryDirectoryFile(
  1451. WorkContext,
  1452. firstCall,
  1453. filterLongNames,
  1454. FALSE,
  1455. FileBothDirectoryInformation,
  1456. 0,
  1457. &sourceName,
  1458. NULL,
  1459. SmbGetUshort( &request->SearchAttributes ),
  1460. directoryInformation,
  1461. bufferLength
  1462. ) ) != STATUS_NO_MORE_FILES ) {
  1463. PFILE_BOTH_DIR_INFORMATION bothDirInfo;
  1464. UNICODE_STRING sourceFileName;
  1465. UNICODE_STRING sourcePathName;
  1466. if ( !NT_SUCCESS(status) ) {
  1467. IF_DEBUG(ERRORS) {
  1468. KdPrint(( "BlockingRename: SrvQueryDirectoryFile failed: %X\n",
  1469. status ));
  1470. }
  1471. if ( !isUnicode ) {
  1472. RtlFreeUnicodeString( &targetName );
  1473. RtlFreeUnicodeString( &sourceName );
  1474. }
  1475. goto error_exit1;
  1476. }
  1477. bothDirInfo =
  1478. (PFILE_BOTH_DIR_INFORMATION)directoryInformation->CurrentEntry;
  1479. //
  1480. // Note that we use the standard name to do the delete, even
  1481. // though we may have matched on the NTFS short name. The
  1482. // client doesn't care which name we use to do the delete.
  1483. //
  1484. sourceFileName.Length = (SHORT)bothDirInfo->FileNameLength;
  1485. sourceFileName.MaximumLength = sourceFileName.Length;
  1486. sourceFileName.Buffer = bothDirInfo->FileName;
  1487. IF_SMB_DEBUG(FILE_CONTROL2) {
  1488. KdPrint(( "SrvQueryDirectoryFile--name %wZ, length = %ld, "
  1489. "status = %X\n",
  1490. &sourceFileName,
  1491. sourceFileName.Length,
  1492. status ));
  1493. }
  1494. firstCall = FALSE;
  1495. //
  1496. // Set up the full source name string.
  1497. //
  1498. SrvAllocateAndBuildPathName(
  1499. &subdirInfo,
  1500. &sourceFileName,
  1501. NULL,
  1502. &sourcePathName
  1503. );
  1504. if ( sourcePathName.Buffer == NULL ) {
  1505. IF_DEBUG(ERRORS) {
  1506. KdPrint(( "BlockingRename: SrvAllocateAndBuildPathName failed: "
  1507. "%X\n", status ));
  1508. }
  1509. if ( !isUnicode ) {
  1510. RtlFreeUnicodeString( &targetName );
  1511. RtlFreeUnicodeString( &sourceName );
  1512. }
  1513. status = STATUS_INSUFF_SERVER_RESOURCES;
  1514. goto error_exit1;
  1515. }
  1516. //
  1517. // Use SrvMoveFile to copy or rename the file. The
  1518. // SmbOpenFunction is set to indicate that existing files
  1519. // may not be overwritten, and we may create new files.
  1520. //
  1521. // *** SrvQueryDirectoryFile has already filtered based on
  1522. // the search attributes, so tell SrvMoveFile that files
  1523. // with the system and hidden bits are OK. This will
  1524. // prevent the call to NtQueryDirectoryFile performed in
  1525. // SrvCheckSearchAttributesForHandle.
  1526. //
  1527. renameRetries = SrvSharingViolationRetryCount;
  1528. start_retry2:
  1529. status = SrvMoveFile(
  1530. WorkContext,
  1531. WorkContext->TreeConnect->Share,
  1532. SMB_OFUN_CREATE_CREATE | SMB_OFUN_OPEN_FAIL,
  1533. &smbFlags,
  1534. (USHORT)(FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN),
  1535. TRUE,
  1536. SMB_NT_RENAME_RENAME_FILE,
  1537. 0,
  1538. &sourcePathName,
  1539. &targetName
  1540. );
  1541. if ( (status == STATUS_SHARING_VIOLATION) &&
  1542. (renameRetries-- > 0) ) {
  1543. (VOID) KeDelayExecutionThread(
  1544. KernelMode,
  1545. FALSE,
  1546. &SrvSharingViolationDelay
  1547. );
  1548. goto start_retry2;
  1549. }
  1550. FREE_HEAP( sourcePathName.Buffer );
  1551. if ( !NT_SUCCESS(status) ) {
  1552. if ( !isUnicode ) {
  1553. RtlFreeUnicodeString( &targetName );
  1554. RtlFreeUnicodeString( &sourceName );
  1555. }
  1556. goto error_exit1;
  1557. }
  1558. }
  1559. //
  1560. // Clean up now that the search is done.
  1561. //
  1562. if ( !isUnicode ) {
  1563. RtlFreeUnicodeString( &targetName );
  1564. RtlFreeUnicodeString( &sourceName );
  1565. }
  1566. SrvCloseQueryDirectory( directoryInformation );
  1567. //
  1568. // If no files were found, return an error to the client.
  1569. //
  1570. if ( firstCall ) {
  1571. status = STATUS_NO_SUCH_FILE;
  1572. goto error_exit;
  1573. }
  1574. }
  1575. //
  1576. // Build the response SMB.
  1577. //
  1578. response->WordCount = 0;
  1579. SmbPutUshort( &response->ByteCount, 0 );
  1580. WorkContext->ResponseParameters = NEXT_LOCATION(
  1581. response,
  1582. RESP_RENAME,
  1583. 0
  1584. );
  1585. IF_DEBUG(TRACE2) KdPrint(( "BlockingRename complete.\n" ));
  1586. goto normal_exit;
  1587. error_exit1:
  1588. SrvCloseQueryDirectory( directoryInformation );
  1589. error_exit:
  1590. SrvSetSmbError( WorkContext, status );
  1591. normal_exit:
  1592. SrvEndSmbProcessing( WorkContext, SmbStatusSendResponse );
  1593. SrvWmiEndContext(WorkContext);
  1594. return;
  1595. } // BlockingRename
  1596. SMB_PROCESSOR_RETURN_TYPE
  1597. SrvSmbMove (
  1598. SMB_PROCESSOR_PARAMETERS
  1599. )
  1600. /*++
  1601. Routine Description:
  1602. Processes the Move SMB.
  1603. Arguments:
  1604. SMB_PROCESSOR_PARAMETERS - See smbprocs.h for a description
  1605. of the parameters to SMB processor routines.
  1606. Return Value:
  1607. SMB_PROCESSOR_RETURN_TYPE - See smbprocs.h
  1608. --*/
  1609. {
  1610. PAGED_CODE();
  1611. if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
  1612. WorkContext->PreviousSMB = EVENT_TYPE_SMB_MOVE;
  1613. SrvWmiStartContext(WorkContext);
  1614. //
  1615. // This SMB must be processed in a blocking thread.
  1616. //
  1617. WorkContext->FspRestartRoutine = BlockingMove;
  1618. SrvQueueWorkToBlockingThread( WorkContext );
  1619. SrvWmiEndContext(WorkContext);
  1620. return SmbStatusInProgress;
  1621. } // SrvSmbMove
  1622. VOID SRVFASTCALL
  1623. BlockingMove (
  1624. IN OUT PWORK_CONTEXT WorkContext
  1625. )
  1626. /*++
  1627. Routine Description:
  1628. This routine processes the Move SMB.
  1629. Arguments:
  1630. WorkContext - work context block
  1631. Return Value:
  1632. None.
  1633. --*/
  1634. {
  1635. PREQ_MOVE request;
  1636. PRESP_MOVE response;
  1637. NTSTATUS status = STATUS_SUCCESS;
  1638. UNICODE_STRING sourceName;
  1639. UNICODE_STRING sourceFileName;
  1640. UNICODE_STRING sourcePathName;
  1641. UNICODE_STRING targetName;
  1642. PSRV_DIRECTORY_INFORMATION directoryInformation;
  1643. USHORT tid2;
  1644. USHORT smbFlags;
  1645. PCHAR lastPositionInBuffer;
  1646. PCHAR target;
  1647. BOOLEAN isRenameOperation;
  1648. BOOLEAN isUnicode = TRUE;
  1649. BOOLEAN isDfs;
  1650. USHORT smbOpenFunction;
  1651. USHORT errorPathNameLength = 0;
  1652. USHORT count = 0;
  1653. PTREE_CONNECT sourceTreeConnect, targetTreeConnect;
  1654. PSESSION session;
  1655. PSHARE share;
  1656. PAGED_CODE( );
  1657. if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
  1658. WorkContext->PreviousSMB = EVENT_TYPE_SMB_MOVE;
  1659. SrvWmiStartContext(WorkContext);
  1660. IF_SMB_DEBUG(FILE_CONTROL1) {
  1661. KdPrint(( "Move/Copy request header at 0x%p, response header at 0x%p\n",
  1662. WorkContext->RequestHeader,
  1663. WorkContext->ResponseHeader ));
  1664. KdPrint(( "Move/Copy request parameters at 0x%p, response parameters at 0x%p\n",
  1665. WorkContext->RequestParameters,
  1666. WorkContext->ResponseParameters ));
  1667. }
  1668. request = (PREQ_MOVE)WorkContext->RequestParameters;
  1669. response = (PRESP_MOVE)WorkContext->ResponseParameters;
  1670. //
  1671. // Set pointers to NULL so that we know how to clean up on exit.
  1672. //
  1673. directoryInformation = NULL;
  1674. targetTreeConnect = NULL;
  1675. sourceName.Buffer = NULL;
  1676. targetName.Buffer = NULL;
  1677. sourcePathName.Buffer = NULL;
  1678. //
  1679. // If a session block has not already been assigned to the current
  1680. // work context , verify the UID. If verified, the address of the
  1681. // session block corresponding to this user is stored in the WorkContext
  1682. // block and the session block is referenced.
  1683. //
  1684. // Find tree connect corresponding to given TID if a tree connect
  1685. // pointer has not already been put in the WorkContext block by an
  1686. // AndX command.
  1687. //
  1688. status = SrvVerifyUidAndTid(
  1689. WorkContext,
  1690. &session,
  1691. &sourceTreeConnect,
  1692. ShareTypeDisk
  1693. );
  1694. if ( !NT_SUCCESS(status) ) {
  1695. IF_DEBUG(SMB_ERRORS) {
  1696. KdPrint(( "BlockingMove: Invalid UID or TID\n" ));
  1697. }
  1698. goto exit;
  1699. }
  1700. if( session->IsSessionExpired )
  1701. {
  1702. status = SESSION_EXPIRED_STATUS_CODE;
  1703. goto exit;
  1704. }
  1705. //
  1706. // Get the share block from the tree connect block. This does not need
  1707. // to be a referenced pointer because we have referenced the tree
  1708. // connect, and it has the share referenced.
  1709. //
  1710. share = sourceTreeConnect->Share;
  1711. //
  1712. // Get the target tree connect. The TID for this is in the Tid2
  1713. // field of the request SMB. Because SrvVerifyTid sets the
  1714. // TreeConnect field of the WorkContext block, set it back after
  1715. // calling the routine. Remember to dereference this pointer before
  1716. // exiting this routine, as it will not be automatically
  1717. // dereferenced because it is not in the WorkContext block.
  1718. //
  1719. // If Tid2 is -1 (0xFFFF), then the TID specified in the SMB header
  1720. // is used.
  1721. //
  1722. tid2 = SmbGetUshort( &request->Tid2 );
  1723. if ( tid2 == (USHORT)0xFFFF ) {
  1724. tid2 = SmbGetAlignedUshort( &WorkContext->RequestHeader->Tid );
  1725. }
  1726. WorkContext->TreeConnect = NULL; // Must be NULL for SrvVerifyTid
  1727. targetTreeConnect = SrvVerifyTid( WorkContext, tid2 );
  1728. WorkContext->TreeConnect = sourceTreeConnect;
  1729. if ( targetTreeConnect == NULL ||
  1730. targetTreeConnect->Share->ShareType != ShareTypeDisk ) {
  1731. IF_DEBUG(SMB_ERRORS) {
  1732. KdPrint(( "BlockingMove: Invalid TID2: 0x%lx\n", tid2 ));
  1733. }
  1734. status = STATUS_SMB_BAD_TID;
  1735. goto exit;
  1736. }
  1737. //
  1738. // Determine whether this is a rename or a copy.
  1739. //
  1740. if ( WorkContext->RequestHeader->Command == SMB_COM_MOVE ) {
  1741. isRenameOperation = TRUE;
  1742. } else {
  1743. isRenameOperation = FALSE;
  1744. }
  1745. //
  1746. // Store the open function.
  1747. //
  1748. smbOpenFunction = SmbGetUshort( &request->OpenFunction );
  1749. //
  1750. // Set up the target pathnames. We must do the target first, as the
  1751. // SMB rename extended protocol does not use the ASCII tokens, so we
  1752. // will lose the information about the start of the target name when
  1753. // we canonicalize the source name.
  1754. //
  1755. // Instead of using strlen() to find the end of the source string,
  1756. // do it here so that we can make a check to ensure that we don't
  1757. // walk off the end of the SMB buffer and cause an access violation.
  1758. //
  1759. lastPositionInBuffer = (PCHAR)request->Buffer +
  1760. SmbGetUshort( &request->ByteCount );
  1761. for ( target = (PCHAR)request->Buffer;
  1762. (target < lastPositionInBuffer) && (*target != 0);
  1763. target++ ) {
  1764. ;
  1765. }
  1766. //
  1767. // If there was no zero terminator in the buffer, fail.
  1768. //
  1769. if ( (target == lastPositionInBuffer) || (*target != 0) ) {
  1770. IF_DEBUG(SMB_ERRORS) {
  1771. KdPrint(( "No terminator on first name.\n" ));
  1772. }
  1773. SrvLogInvalidSmb( WorkContext );
  1774. status = STATUS_INVALID_SMB;
  1775. goto exit;
  1776. }
  1777. target++;
  1778. isUnicode = SMB_IS_UNICODE( WorkContext );
  1779. isDfs = SMB_CONTAINS_DFS_NAME( WorkContext );
  1780. status = SrvCanonicalizePathName(
  1781. WorkContext,
  1782. share,
  1783. NULL,
  1784. target,
  1785. END_OF_REQUEST_SMB( WorkContext ),
  1786. TRUE,
  1787. isUnicode,
  1788. &targetName
  1789. );
  1790. if( !NT_SUCCESS( status ) ) {
  1791. IF_DEBUG(SMB_ERRORS) {
  1792. KdPrint(( "BlockingMove: illegal path name (target): %wZ\n",
  1793. &targetName ));
  1794. }
  1795. goto exit;
  1796. }
  1797. //
  1798. // If the SMB was originally marked as containing Dfs names, then the
  1799. // call to SrvCanonicalizePathName for the target path has cleared that
  1800. // flag. So, re-mark the SMB as containing Dfs names before calling
  1801. // SrvCanonicalizePathName on the source path.
  1802. //
  1803. if (isDfs) {
  1804. SMB_MARK_AS_DFS_NAME( WorkContext );
  1805. }
  1806. //
  1807. // Set up the source name.
  1808. //
  1809. status = SrvCanonicalizePathName(
  1810. WorkContext,
  1811. share,
  1812. NULL,
  1813. request->Buffer,
  1814. END_OF_REQUEST_SMB( WorkContext ),
  1815. TRUE,
  1816. isUnicode,
  1817. &sourceName
  1818. );
  1819. if( !NT_SUCCESS( status ) ) {
  1820. IF_DEBUG(SMB_ERRORS) {
  1821. KdPrint(( "BlockingMove: illegal path name (source): %s\n",
  1822. request->Buffer ));
  1823. }
  1824. goto exit;
  1825. }
  1826. smbFlags = SmbGetUshort( &request->Flags );
  1827. //
  1828. // Copy interprets ; as *. If the last character was ; and this was
  1829. // not at the end of a file name with other characters (as in
  1830. // "file;" then convert the ; to *.
  1831. //
  1832. if ( sourceName.Buffer[(sourceName.Length/sizeof(WCHAR))-1] == ';' &&
  1833. ( sourceName.Length == 2 ||
  1834. sourceName.Buffer[(sourceName.Length/sizeof(WCHAR))-2] == '\\' ) ) {
  1835. sourceName.Buffer[(sourceName.Length/sizeof(WCHAR))-1] = '*';
  1836. }
  1837. //
  1838. // Tree copy not implemented. If this is a single file copy,
  1839. // let it go through. For now, we make sure that it does not
  1840. // have any wild card characters, we do additional checking
  1841. // inside SrvMoveFile.
  1842. //
  1843. if ( ( (smbFlags & SMB_COPY_TREE) != 0 ) &&
  1844. FsRtlDoesNameContainWildCards(&sourceName) ) {
  1845. INTERNAL_ERROR(
  1846. ERROR_LEVEL_EXPECTED,
  1847. "Tree copy not implemented.",
  1848. NULL,
  1849. NULL
  1850. );
  1851. status = STATUS_NOT_IMPLEMENTED;
  1852. goto exit;
  1853. }
  1854. if ( !FsRtlDoesNameContainWildCards( &sourceName ) ) {
  1855. //
  1856. // Use SrvMoveFile to copy or move the file.
  1857. //
  1858. // *** These SMBs do not include search attributes, so set
  1859. // this field equal to zero. If will not be possible
  1860. // to move a file that has the system or hidden bits on.
  1861. status = SrvMoveFile(
  1862. WorkContext,
  1863. targetTreeConnect->Share,
  1864. smbOpenFunction,
  1865. &smbFlags,
  1866. (USHORT)0, // SmbSearchAttributes
  1867. FALSE,
  1868. (USHORT)(isRenameOperation?
  1869. SMB_NT_RENAME_RENAME_FILE : SMB_NT_RENAME_MOVE_FILE),
  1870. 0,
  1871. &sourceName,
  1872. &targetName
  1873. );
  1874. if ( !NT_SUCCESS(status) ) {
  1875. goto exit;
  1876. }
  1877. count = 1;
  1878. } else {
  1879. UNICODE_STRING subdirInfo;
  1880. BOOLEAN firstCall = TRUE;
  1881. CLONG bufferLength;
  1882. BOOLEAN filterLongNames;
  1883. //
  1884. // If wildcards were in the original source name, we set the
  1885. // SmbFlags to SMB_TARGET_IS_DIRECTORY to indicate that the
  1886. // target must be a directory--this is always the case when
  1887. // wildcards are used for a rename. (For a copy, it is legal to
  1888. // specify that the destination is a file and append to that
  1889. // file--then all the source files are concatenated to that one
  1890. // target file.)
  1891. //
  1892. if ( isRenameOperation ) {
  1893. smbFlags |= SMB_TARGET_IS_DIRECTORY;
  1894. }
  1895. //
  1896. // SrvQueryDirectoryFile requires a buffer from nonpaged pool.
  1897. // Since this routine does not use the buffer field of the
  1898. // request SMB after the pathname, use this. The buffer must be
  1899. // quadword-aligned.
  1900. //
  1901. directoryInformation =
  1902. (PSRV_DIRECTORY_INFORMATION)( (ULONG_PTR)((PCHAR)request->Buffer +
  1903. SmbGetUshort( &request->ByteCount ) + 7) & ~7 );
  1904. bufferLength = WorkContext->RequestBuffer->BufferLength -
  1905. PTR_DIFF(directoryInformation,
  1906. WorkContext->RequestBuffer->Buffer);
  1907. //
  1908. // We need the full path name of each file that is returned by
  1909. // SrvQueryDirectoryFile, so we need to find the part of the
  1910. // passed filename that contains subdirectory information (e.g.
  1911. // for a\b\c\*.*, we want a string that indicates a\b\c).
  1912. //
  1913. subdirInfo.Buffer = sourceName.Buffer;
  1914. subdirInfo.Length = SrvGetSubdirectoryLength( &sourceName );
  1915. subdirInfo.MaximumLength = subdirInfo.Length;
  1916. //
  1917. // Determine whether long filenames (non-8.3) should be filtered out
  1918. // or processed.
  1919. //
  1920. if ( (SmbGetAlignedUshort( &WorkContext->RequestHeader->Flags2 ) &
  1921. SMB_FLAGS2_KNOWS_LONG_NAMES) != 0 ) {
  1922. filterLongNames = FALSE;
  1923. } else {
  1924. filterLongNames = TRUE;
  1925. }
  1926. //
  1927. // As long as SrvQueryDirectoryFile is able to return file names,
  1928. // keep renaming.
  1929. //
  1930. // *** Set search attributes to find archive files, but not
  1931. // system or hidden files. This duplicates the LM 2.0
  1932. // server behavior.
  1933. //
  1934. // *** We ask for FileBothDirectoryInformation so that we will
  1935. // pick up long names on NTFS that have short name
  1936. // equivalents. Without this, DOS clients will not be able
  1937. // to move long names on NTFS volumes.
  1938. //
  1939. while ( ( status = SrvQueryDirectoryFile(
  1940. WorkContext,
  1941. firstCall,
  1942. filterLongNames,
  1943. FALSE,
  1944. FileBothDirectoryInformation,
  1945. 0,
  1946. &sourceName,
  1947. NULL,
  1948. FILE_ATTRIBUTE_ARCHIVE, // SmbSearchAttributes
  1949. directoryInformation,
  1950. bufferLength
  1951. ) ) != STATUS_NO_MORE_FILES ) {
  1952. PFILE_BOTH_DIR_INFORMATION bothDirInfo;
  1953. if ( !NT_SUCCESS(status) ) {
  1954. IF_DEBUG(ERRORS) {
  1955. KdPrint(( "BlockingMove: SrvQueryDirectoryFile failed: %X\n",
  1956. status ));
  1957. }
  1958. goto exit;
  1959. }
  1960. bothDirInfo =
  1961. (PFILE_BOTH_DIR_INFORMATION)directoryInformation->CurrentEntry;
  1962. //
  1963. // If we're filtering long names, and the file has a short
  1964. // name equivalent, then use that name to do the delete. We
  1965. // do this because we need to return a name to the client if
  1966. // the operation fails, and we don't want to return a long
  1967. // name. Note that if the file has no short name, and we're
  1968. // filtering, then the standard name must be a valid 8.3
  1969. // name, so it's OK to return to the client.
  1970. //
  1971. if ( filterLongNames && (bothDirInfo->ShortNameLength != 0) ) {
  1972. sourceFileName.Length = (SHORT)bothDirInfo->ShortNameLength;
  1973. sourceFileName.Buffer = bothDirInfo->ShortName;
  1974. } else {
  1975. sourceFileName.Length = (SHORT)bothDirInfo->FileNameLength;
  1976. sourceFileName.Buffer = bothDirInfo->FileName;
  1977. }
  1978. sourceFileName.MaximumLength = sourceFileName.Length;
  1979. IF_SMB_DEBUG(FILE_CONTROL2) {
  1980. KdPrint(( "SrvQueryDirectoryFile--name %wZ, length = %ld, "
  1981. "status = %X\n",
  1982. &sourceFileName,
  1983. sourceFileName.Length,
  1984. status ));
  1985. }
  1986. firstCall = FALSE;
  1987. //
  1988. // Set up the full source name string.
  1989. //
  1990. SrvAllocateAndBuildPathName(
  1991. &subdirInfo,
  1992. &sourceFileName,
  1993. NULL,
  1994. &sourcePathName
  1995. );
  1996. if ( sourcePathName.Buffer == NULL ) {
  1997. status = STATUS_INSUFF_SERVER_RESOURCES;
  1998. goto exit;
  1999. }
  2000. //
  2001. // Use SrvMoveFile to copy or rename the file.
  2002. //
  2003. status = SrvMoveFile(
  2004. WorkContext,
  2005. targetTreeConnect->Share,
  2006. smbOpenFunction,
  2007. &smbFlags,
  2008. (USHORT)0, // SmbSearchAttributes
  2009. FALSE,
  2010. (USHORT)(isRenameOperation?
  2011. SMB_NT_RENAME_RENAME_FILE : SMB_NT_RENAME_MOVE_FILE),
  2012. 0,
  2013. &sourcePathName,
  2014. &targetName
  2015. );
  2016. if ( !NT_SUCCESS(status) ) {
  2017. goto exit;
  2018. }
  2019. count++;
  2020. //
  2021. // Free the buffer that holds that source name.
  2022. //
  2023. FREE_HEAP( sourcePathName.Buffer );
  2024. sourcePathName.Buffer = NULL;
  2025. //
  2026. // If this is a copy operation with wildcards and the target is
  2027. // a file, then all files should be appended to the target. The
  2028. // target is truncated on the first call to SrvMoveFile if that
  2029. // was specified by the caller.
  2030. //
  2031. // This is done by turning off the truncate bit in the
  2032. // SmbOpenFunction and turning on the append bit.
  2033. //
  2034. if ( !isRenameOperation && directoryInformation->Wildcards &&
  2035. (smbFlags & SMB_TARGET_IS_FILE) ) {
  2036. smbOpenFunction &= ~SMB_OFUN_OPEN_TRUNCATE;
  2037. smbOpenFunction |= SMB_OFUN_OPEN_APPEND;
  2038. }
  2039. }
  2040. //
  2041. // If no files were found, return an error to the client.
  2042. //
  2043. if ( firstCall ) {
  2044. status = STATUS_NO_SUCH_FILE;
  2045. goto exit;
  2046. }
  2047. }
  2048. //
  2049. // Build the response SMB.
  2050. //
  2051. SmbPutUshort( &response->ByteCount, 0 );
  2052. WorkContext->ResponseParameters = NEXT_LOCATION( response, RESP_MOVE, 0 );
  2053. status = STATUS_SUCCESS;
  2054. exit:
  2055. response->WordCount = 1;
  2056. SmbPutUshort( &response->Count, count );
  2057. if ( directoryInformation != NULL ) {
  2058. SrvCloseQueryDirectory( directoryInformation );
  2059. }
  2060. if ( targetTreeConnect != NULL) {
  2061. SrvDereferenceTreeConnect( targetTreeConnect );
  2062. }
  2063. if ( !NT_SUCCESS(status) ) {
  2064. SrvSetSmbError( WorkContext, status );
  2065. if ( sourcePathName.Buffer != NULL ) {
  2066. //
  2067. // Put the name of the file where the error occurred in the
  2068. // buffer field of the response SMB.
  2069. //
  2070. RtlCopyMemory(
  2071. response->Buffer,
  2072. sourcePathName.Buffer,
  2073. sourcePathName.Length
  2074. );
  2075. response->Buffer[sourcePathName.Length] = '\0';
  2076. SmbPutUshort( &response->ByteCount, (SHORT)(sourcePathName.Length+1) );
  2077. WorkContext->ResponseParameters = NEXT_LOCATION(
  2078. response,
  2079. RESP_MOVE,
  2080. sourcePathName.Length+1
  2081. );
  2082. FREE_HEAP( sourcePathName.Buffer );
  2083. } else if ( sourceName.Buffer != NULL ) {
  2084. //
  2085. // Put the name of the file where the error occurred in the
  2086. // buffer field of the response SMB.
  2087. //
  2088. RtlCopyMemory(
  2089. response->Buffer,
  2090. sourceName.Buffer,
  2091. sourceName.Length
  2092. );
  2093. response->Buffer[sourceName.Length] = '\0';
  2094. SmbPutUshort( &response->ByteCount, (SHORT)(sourceName.Length+1) );
  2095. WorkContext->ResponseParameters = NEXT_LOCATION(
  2096. response,
  2097. RESP_MOVE,
  2098. sourceName.Length+1
  2099. );
  2100. }
  2101. }
  2102. if ( !isUnicode ) {
  2103. if ( targetName.Buffer != NULL ) {
  2104. RtlFreeUnicodeString( &targetName );
  2105. }
  2106. if ( sourceName.Buffer != NULL ) {
  2107. RtlFreeUnicodeString( &sourceName );
  2108. }
  2109. }
  2110. IF_DEBUG(TRACE2) KdPrint(( "BlockingMove complete.\n" ));
  2111. SrvEndSmbProcessing( WorkContext, SmbStatusSendResponse );
  2112. SrvWmiEndContext(WorkContext);
  2113. return;
  2114. } // BlockingMove
  2115. SMB_TRANS_STATUS
  2116. SrvSmbNtRename (
  2117. IN OUT PWORK_CONTEXT WorkContext
  2118. )
  2119. /*++
  2120. Routine Description:
  2121. Processes the NT rename request. This request arrives in an NT
  2122. transact SMB.
  2123. Arguments:
  2124. WorkContext - Supplies the address of a Work Context Block
  2125. describing the current request. See smbtypes.h for a more
  2126. complete description of the valid fields.
  2127. Return Value:
  2128. SMB_TRANS_STATUS - Indicates whether an error occurred, and, if so,
  2129. whether data should be returned to the client. See smbtypes.h
  2130. for a more complete description.
  2131. --*/
  2132. {
  2133. PREQ_NT_RENAME request;
  2134. NTSTATUS status;
  2135. PTRANSACTION transaction;
  2136. PRFCB rfcb;
  2137. PAGED_CODE( );
  2138. transaction = WorkContext->Parameters.Transaction;
  2139. IF_SMB_DEBUG( FILE_CONTROL1 ) {
  2140. KdPrint(( "SrvSmbNtRename entered; transaction 0x%p\n",
  2141. transaction ));
  2142. }
  2143. request = (PREQ_NT_RENAME)transaction->InParameters;
  2144. //
  2145. // Verify that enough parameter bytes were sent and that we're allowed
  2146. // to return enough parameter bytes.
  2147. //
  2148. if ( transaction->ParameterCount < sizeof(REQ_NT_RENAME) ) {
  2149. //
  2150. // Not enough parameter bytes were sent.
  2151. //
  2152. IF_SMB_DEBUG( FILE_CONTROL1 ) {
  2153. KdPrint(( "SrvSmbNtRename: bad parameter byte count: "
  2154. "%ld\n", transaction->ParameterCount ));
  2155. }
  2156. SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
  2157. return SmbTransStatusErrorWithoutData;
  2158. }
  2159. //
  2160. // Verify the FID. If verified, the RFCB block is referenced
  2161. // and its addresses is stored in the WorkContext block, and the
  2162. // RFCB address is returned.
  2163. //
  2164. rfcb = SrvVerifyFid(
  2165. WorkContext,
  2166. SmbGetUshort( &request->Fid ),
  2167. TRUE,
  2168. NULL, // don't serialize with raw write
  2169. &status
  2170. );
  2171. if ( rfcb == SRV_INVALID_RFCB_POINTER ) {
  2172. //
  2173. // Invalid file ID or write behind error. Reject the request.
  2174. //
  2175. IF_DEBUG(ERRORS) {
  2176. KdPrint((
  2177. "SrvSmbNtRename: Status %X on FID: 0x%lx\n",
  2178. status,
  2179. SmbGetUshort( &request->Fid )
  2180. ));
  2181. }
  2182. SrvSetSmbError( WorkContext, status );
  2183. return SmbTransStatusErrorWithoutData;
  2184. }
  2185. //
  2186. // Verify the information level and the number of input and output
  2187. // data bytes available.
  2188. //
  2189. IF_DEBUG(TRACE2) KdPrint(( "SrvSmbNtRename complete.\n" ));
  2190. return SmbTransStatusSuccess;
  2191. } // SrvSmbNtRename