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.

1403 lines
42 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. move.c
  5. Abstract:
  6. This module contains the routine to rename or copy a file. This
  7. routine is used by the routines SrvSmbRenameFile,
  8. SrvSmbRenameFileExtended, and SrvSmbCopyFile.
  9. Author:
  10. David Treadwell (davidtr) 22-Jan-1990
  11. Revision History:
  12. --*/
  13. #include "precomp.h"
  14. #include "move.tmh"
  15. #pragma hdrstop
  16. #define BugCheckFileId SRV_FILE_MOVE
  17. NTSTATUS
  18. DoCopy (
  19. IN PWORK_CONTEXT WorkContext,
  20. IN PUNICODE_STRING Source,
  21. IN HANDLE SourceHandle,
  22. IN PUNICODE_STRING Target,
  23. IN PSHARE TargetShare,
  24. IN USHORT SmbOpenFunction,
  25. IN PUSHORT SmbFlags
  26. );
  27. NTSTATUS
  28. DoRename (
  29. IN PWORK_CONTEXT WorkContext,
  30. IN PUNICODE_STRING Source,
  31. IN HANDLE SourceHandle,
  32. IN PUNICODE_STRING Target,
  33. IN PSHARE TargetShare,
  34. IN USHORT SmbOpenFunction,
  35. IN PUSHORT SmbFlags,
  36. IN BOOLEAN FailIfTargetIsDirectory,
  37. IN USHORT InformationLevel,
  38. IN ULONG ClusterCount
  39. );
  40. #ifdef ALLOC_PRAGMA
  41. #pragma alloc_text( PAGE, SrvMoveFile )
  42. #pragma alloc_text( PAGE, DoCopy )
  43. #pragma alloc_text( PAGE, DoRename )
  44. #endif
  45. NTSTATUS
  46. SrvMoveFile(
  47. IN PWORK_CONTEXT WorkContext,
  48. IN PSHARE TargetShare,
  49. IN USHORT SmbOpenFunction,
  50. IN OUT PUSHORT SmbFlags,
  51. IN USHORT SmbSearchAttributes,
  52. IN BOOLEAN FailIfTargetIsDirectory,
  53. IN USHORT InformationLevel,
  54. IN ULONG ClusterCount,
  55. IN PUNICODE_STRING Source,
  56. IN OUT PUNICODE_STRING Target
  57. )
  58. /*++
  59. Routine Description:
  60. This routine moves a file, which may be a copy or a rename.
  61. Arguments:
  62. WorkContext - a pointer to the work context block for the operation. The
  63. Session, TreeConnect, and RequestHeader fields are used.
  64. TargetShare - a pointer to the share on which the target should
  65. be. The RootDirectoryHandle field is used to do relative opens.
  66. SmbOpenFunction - the "OpenFunction" field of the request SMB. This
  67. parameter is used to determine what should be done if the target
  68. file does or does not exist.
  69. SmbFlags - a pointer to the "Flags" field of the request SMB. This
  70. parameter is used to determine whether we know that the target
  71. is supposed to be a file or directory. In addition, if this has
  72. no information about the target, it is set to reflect whether
  73. the target was a directory or file. This is useful when doing
  74. multiple renames or copies as a result of wildcards--move a*.* b
  75. might call this routine many times, and if b is a directory,
  76. this routine will set this parameter appropiately such that if
  77. does not have to reopen the directory for each move.
  78. SmbSearchAttributes - the search attributes specified in the request
  79. SMB. The attributes on the source file are checked against
  80. these to make sure that the move can be done.
  81. FailIfTargetIsDirectory - if TRUE and the target already exists as
  82. a directory, fail the operation. Otherwise, rename the file
  83. into the directory.
  84. InformationLevel - Move/Rename/CopyOnWrite/Link/MoveCluster
  85. ClusterCount - MoveCluster count
  86. Source - a pointer to a string describing the name of the source file
  87. relative to the share directory in which it is located.
  88. Target - a pathname to the target file. This may contain directory
  89. information--it should be the raw information from the SMB,
  90. unadulterated by the SMB processing routine except for
  91. canonicalization. This name may end in a directory name, in
  92. which case the source name is used as the filename.
  93. Return Value:
  94. Status.
  95. --*/
  96. {
  97. NTSTATUS status;
  98. HANDLE sourceHandle;
  99. BOOLEAN isCompatibilityOpen;
  100. PMFCB mfcb;
  101. PNONPAGED_MFCB nonpagedMfcb;
  102. PLFCB lfcb;
  103. OBJECT_ATTRIBUTES sourceObjectAttributes;
  104. IO_STATUS_BLOCK ioStatusBlock;
  105. ULONG sourceAccess = 0;
  106. BOOLEAN isNtRename;
  107. ULONG hashValue;
  108. PSESSION session;
  109. PSHARE sourceShare;
  110. PSRV_LOCK mfcbLock;
  111. PAGED_CODE( );
  112. IF_SMB_DEBUG(FILE_CONTROL2) SrvPrint0( "SrvMoveFile entered.\n" );
  113. //
  114. // Set handles and pointers to NULL so we know how to clean up on
  115. // exit.
  116. //
  117. sourceHandle = NULL;
  118. isCompatibilityOpen = FALSE;
  119. lfcb = NULL;
  120. //mfcb = NULL; // not really necessary--SrvFindMfcb sets it correctly
  121. //
  122. // Set up the block pointers that will be needed.
  123. //
  124. session = WorkContext->Session;
  125. sourceShare = WorkContext->TreeConnect->Share;
  126. isNtRename = (BOOLEAN)(WorkContext->RequestHeader->Command == SMB_COM_NT_RENAME);
  127. //
  128. // See if we already have this file open in compatibility mode. If
  129. // we do, and this session owns it, then we must use that open
  130. // handle and, if this is a rename, close all the handles when we
  131. // are done.
  132. //
  133. // *** SrvFindMfcb references the MFCB--remember to dereference it.
  134. //
  135. if ( (WorkContext->RequestHeader->Flags & SMB_FLAGS_CASE_INSENSITIVE) ||
  136. WorkContext->Session->UsingUppercasePaths ) {
  137. mfcb = SrvFindMfcb( Source, TRUE, &mfcbLock, &hashValue, WorkContext );
  138. } else {
  139. mfcb = SrvFindMfcb( Source, FALSE, &mfcbLock, &hashValue, WorkContext );
  140. }
  141. if ( mfcb != NULL ) {
  142. nonpagedMfcb = mfcb->NonpagedMfcb;
  143. ACQUIRE_LOCK( &nonpagedMfcb->Lock );
  144. }
  145. if( mfcbLock ) {
  146. RELEASE_LOCK( mfcbLock );
  147. }
  148. if ( mfcb == NULL || !mfcb->CompatibilityOpen ) {
  149. //
  150. // Either the file wasn't opened by the server or it was not
  151. // a compatibility/FCB open, so open it here.
  152. //
  153. // Release the open lock--we don't need it any more.
  154. //
  155. if ( mfcb != NULL ) {
  156. RELEASE_LOCK( &nonpagedMfcb->Lock );
  157. }
  158. //
  159. // Use DELETE access for a rename, and the appropriate copy access
  160. // for Copy/Link/Move/MoveCluster.
  161. //
  162. switch (InformationLevel) {
  163. case SMB_NT_RENAME_RENAME_FILE:
  164. sourceAccess = DELETE;
  165. break;
  166. case SMB_NT_RENAME_MOVE_CLUSTER_INFO:
  167. sourceAccess = SRV_COPY_TARGET_ACCESS & ~(WRITE_DAC | WRITE_OWNER);
  168. break;
  169. case SMB_NT_RENAME_SET_LINK_INFO:
  170. case SMB_NT_RENAME_MOVE_FILE:
  171. sourceAccess = SRV_COPY_SOURCE_ACCESS;
  172. break;
  173. default:
  174. ASSERT(FALSE);
  175. }
  176. SrvInitializeObjectAttributes_U(
  177. &sourceObjectAttributes,
  178. Source,
  179. (WorkContext->RequestHeader->Flags & SMB_FLAGS_CASE_INSENSITIVE ||
  180. session->UsingUppercasePaths) ? OBJ_CASE_INSENSITIVE : 0L,
  181. NULL,
  182. NULL
  183. );
  184. IF_SMB_DEBUG(FILE_CONTROL2) {
  185. SrvPrint1( "Opening source: %wZ\n",
  186. sourceObjectAttributes.ObjectName );
  187. }
  188. //
  189. // Open the source file. We allow read access for other processes.
  190. //
  191. INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpenAttempts );
  192. INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpensForPathOperations );
  193. #ifdef SLMDBG
  194. if ( SrvIsSlmStatus( Target ) ) {
  195. sourceAccess |= GENERIC_READ;
  196. }
  197. #endif
  198. //
  199. // !!! Currently we can't specify complete if oplocked, because
  200. // this won't break a batch oplock. Unfortunately this also
  201. // means that we can't timeout the open (if the oplock break
  202. // takes too long) and fail this SMB gracefully.
  203. //
  204. status = SrvIoCreateFile(
  205. WorkContext,
  206. &sourceHandle,
  207. sourceAccess | SYNCHRONIZE, // DesiredAccess
  208. &sourceObjectAttributes,
  209. &ioStatusBlock,
  210. NULL, // AllocationSize
  211. 0, // FileAttributes
  212. FILE_SHARE_READ, // ShareAccess
  213. FILE_OPEN, // Disposition
  214. FILE_SYNCHRONOUS_IO_NONALERT // CreateOptions
  215. | FILE_OPEN_REPARSE_POINT,
  216. NULL, // EaBuffer
  217. 0, // EaLength
  218. CreateFileTypeNone, // File type
  219. NULL, // ExtraCreateParameters
  220. IO_FORCE_ACCESS_CHECK, // Options
  221. WorkContext->TreeConnect->Share
  222. );
  223. if( status == STATUS_INVALID_PARAMETER ) {
  224. status = SrvIoCreateFile(
  225. WorkContext,
  226. &sourceHandle,
  227. sourceAccess | SYNCHRONIZE, // DesiredAccess
  228. &sourceObjectAttributes,
  229. &ioStatusBlock,
  230. NULL, // AllocationSize
  231. 0, // FileAttributes
  232. FILE_SHARE_READ, // ShareAccess
  233. FILE_OPEN, // Disposition
  234. FILE_SYNCHRONOUS_IO_NONALERT, // CreateOptions
  235. NULL, // EaBuffer
  236. 0, // EaLength
  237. CreateFileTypeNone, // File type
  238. NULL, // ExtraCreateParameters
  239. IO_FORCE_ACCESS_CHECK, // Options
  240. WorkContext->TreeConnect->Share
  241. );
  242. }
  243. if ( NT_SUCCESS(status) ) {
  244. SRVDBG_CLAIM_HANDLE( sourceHandle, "MOV", 4, 0 );
  245. } else if ( status == STATUS_ACCESS_DENIED ) {
  246. //
  247. // If the user didn't have this permission, update the statistics
  248. // database.
  249. //
  250. SrvStatistics.AccessPermissionErrors++;
  251. }
  252. ASSERT( status != STATUS_OPLOCK_BREAK_IN_PROGRESS );
  253. if ( !NT_SUCCESS(status) ) {
  254. IF_DEBUG(ERRORS) {
  255. SrvPrint1( "SrvMoveFile: SrvIoCreateFile failed (source): %X\n",
  256. status );
  257. }
  258. goto exit;
  259. }
  260. IF_SMB_DEBUG(FILE_CONTROL2) {
  261. SrvPrint1( "SrvIoCreateFile succeeded (source), handle = 0x%p\n",
  262. sourceHandle );
  263. }
  264. SrvStatistics.TotalFilesOpened++;
  265. } else {
  266. //
  267. // The file was opened by the server in compatibility mode or as
  268. // an FCB open.
  269. //
  270. lfcb = CONTAINING_RECORD( mfcb->LfcbList.Blink, LFCB, MfcbListEntry );
  271. //
  272. // Make sure that the session which sent this request is the
  273. // same as the one which has the file open.
  274. //
  275. if ( lfcb->Session != session ) {
  276. //
  277. // A different session has the file open in compatibility
  278. // mode, so reject the request.
  279. //
  280. status = STATUS_ACCESS_DENIED;
  281. RELEASE_LOCK( &nonpagedMfcb->Lock );
  282. goto exit;
  283. }
  284. //
  285. // Set isCompatibilityOpen so that we'll know on exit to close
  286. // all the open instances of this file.
  287. //
  288. isCompatibilityOpen = TRUE;
  289. sourceHandle = lfcb->FileHandle;
  290. sourceAccess = lfcb->GrantedAccess;
  291. }
  292. //
  293. // Make sure that the search attributes jive with the attributes
  294. // on the file.
  295. //
  296. status = SrvCheckSearchAttributesForHandle( sourceHandle, SmbSearchAttributes );
  297. if ( !NT_SUCCESS(status) ) {
  298. goto exit;
  299. }
  300. //
  301. // If the target has length 0, then it is the share root, which must
  302. // be a directory. If the target is supposed to be a file, fail,
  303. // otherwise indicate that the target is a directory.
  304. //
  305. if ( Target->Length == 0 ) {
  306. if ( *SmbFlags & SMB_TARGET_IS_FILE ) {
  307. status = STATUS_INVALID_PARAMETER;
  308. goto exit;
  309. }
  310. *SmbFlags |= SMB_TARGET_IS_DIRECTORY;
  311. }
  312. //
  313. // We now have the source file open. Call the appropriate routine
  314. // to rename or copy the file.
  315. //
  316. if (InformationLevel != SMB_NT_RENAME_MOVE_FILE) {
  317. #ifdef SLMDBG
  318. if (InformationLevel == SMB_NT_RENAME_RENAME_FILE &&
  319. SrvIsSlmStatus( Source ) || SrvIsSlmStatus( Target ) ) {
  320. ULONG offset;
  321. status = SrvValidateSlmStatus(
  322. sourceHandle,
  323. WorkContext,
  324. &offset
  325. );
  326. if ( !NT_SUCCESS(status) ) {
  327. SrvReportCorruptSlmStatus(
  328. Source,
  329. status,
  330. offset,
  331. SLMDBG_RENAME,
  332. WorkContext->Session
  333. );
  334. SrvDisallowSlmAccess(
  335. Source,
  336. WorkContext->TreeConnect->Share->RootDirectoryHandle
  337. );
  338. status = STATUS_DISK_CORRUPT_ERROR;
  339. goto exit;
  340. }
  341. }
  342. #endif
  343. status = DoRename(
  344. WorkContext,
  345. Source,
  346. sourceHandle,
  347. Target,
  348. TargetShare,
  349. SmbOpenFunction,
  350. SmbFlags,
  351. FailIfTargetIsDirectory,
  352. InformationLevel,
  353. ClusterCount
  354. );
  355. } else {
  356. FILE_BASIC_INFORMATION fileBasicInformation;
  357. //
  358. // Check whether this is a tree copy request. If so, allow it only if
  359. // this is a single file copy operation.
  360. //
  361. if ( (*SmbFlags & SMB_COPY_TREE) != 0 ) {
  362. //
  363. // Get the attributes on the file.
  364. //
  365. status = SrvQueryBasicAndStandardInformation(
  366. sourceHandle,
  367. NULL,
  368. &fileBasicInformation,
  369. NULL
  370. );
  371. if ( !NT_SUCCESS(status) ) {
  372. INTERNAL_ERROR(
  373. ERROR_LEVEL_UNEXPECTED,
  374. "SrvMoveFile: NtQueryInformationFile (basic "
  375. "information) returned %X",
  376. NULL,
  377. NULL
  378. );
  379. SrvLogServiceFailure( SRV_SVC_NT_QUERY_INFO_FILE, status );
  380. goto exit;
  381. }
  382. if ( ( fileBasicInformation.FileAttributes &
  383. FILE_ATTRIBUTE_DIRECTORY ) != 0 ) {
  384. //
  385. // Fail this copy.
  386. //
  387. INTERNAL_ERROR(
  388. ERROR_LEVEL_EXPECTED,
  389. "Tree copy not implemented.",
  390. NULL,
  391. NULL
  392. );
  393. status = STATUS_NOT_IMPLEMENTED;
  394. goto exit;
  395. }
  396. }
  397. status = DoCopy(
  398. WorkContext,
  399. Source,
  400. sourceHandle,
  401. Target,
  402. TargetShare,
  403. SmbOpenFunction,
  404. SmbFlags
  405. );
  406. }
  407. exit:
  408. if ( sourceHandle != NULL && !isCompatibilityOpen ) {
  409. SRVDBG_RELEASE_HANDLE( sourceHandle, "MOV", 9, 0 );
  410. SrvNtClose( sourceHandle, TRUE );
  411. } else if (isCompatibilityOpen &&
  412. InformationLevel == SMB_NT_RENAME_RENAME_FILE) {
  413. SrvCloseRfcbsOnLfcb( lfcb );
  414. }
  415. //
  416. // If the file is open in compatibility mode, then we have held the
  417. // MFCB lock all along. Release it now.
  418. //
  419. if ( isCompatibilityOpen ) {
  420. RELEASE_LOCK( &nonpagedMfcb->Lock );
  421. }
  422. if ( mfcb != NULL ) {
  423. SrvDereferenceMfcb( mfcb );
  424. }
  425. return status;
  426. } // SrvMoveFile
  427. NTSTATUS
  428. DoCopy (
  429. IN PWORK_CONTEXT WorkContext,
  430. IN PUNICODE_STRING Source,
  431. IN HANDLE SourceHandle,
  432. IN PUNICODE_STRING Target,
  433. IN PSHARE TargetShare,
  434. IN USHORT SmbOpenFunction,
  435. IN PUSHORT SmbFlags
  436. )
  437. /*++
  438. Routine Description:
  439. This routine sets up for a call to SrvCopyFile. It opens the target,
  440. determining, if necessary, whether the target is a file or directory.
  441. If this information is unknown, it writes it into the SmbFlags
  442. location.
  443. Arguments:
  444. WorkContext - a pointer to the work context block for the operation.
  445. The session pointer is used, and the block itself is used for
  446. an impersonation.
  447. Source - the name of the source file relative to its share.
  448. SourceHandle - the handle to the source file.
  449. Target - the name of the target file relative to its share.
  450. TargetShare - the share of the target file. The RootDirectoryHandle
  451. field is used for a relative rename.
  452. SmbOpenFunction - describes whether we are allowed to overwrite an
  453. existing file, or we should append to existing files.
  454. SmbFlags - can tell if the target is a file, directory, or unknown.
  455. This routine writes the true information into the location if
  456. it is unknown.
  457. Return Value:
  458. Status.
  459. --*/
  460. {
  461. NTSTATUS status;
  462. IO_STATUS_BLOCK ioStatusBlock;
  463. ULONG createDisposition;
  464. UNICODE_STRING sourceBaseName;
  465. BOOLEAN create;
  466. HANDLE targetHandle = NULL;
  467. OBJECT_ATTRIBUTES targetObjectAttributes;
  468. UNICODE_STRING targetName;
  469. PAGED_CODE( );
  470. //
  471. // Set the buffer field of targetName to NULL so that we'll know
  472. // if we have to deallocate it at the end.
  473. //
  474. targetName.Buffer = NULL;
  475. //
  476. // Open the target file. If we know that it is a directory, generate
  477. // the full file name. Otherwise, open the target as a file.
  478. //
  479. SrvInitializeObjectAttributes_U(
  480. &targetObjectAttributes,
  481. Target,
  482. (WorkContext->RequestHeader->Flags & SMB_FLAGS_CASE_INSENSITIVE ||
  483. WorkContext->Session->UsingUppercasePaths) ? OBJ_CASE_INSENSITIVE : 0L,
  484. NULL,
  485. NULL
  486. );
  487. //
  488. // Determine the create disposition from the open function.
  489. //
  490. create = SmbOfunCreate( SmbOpenFunction );
  491. if ( SmbOfunTruncate( SmbOpenFunction ) ) {
  492. createDisposition = create ? FILE_OVERWRITE_IF : FILE_OVERWRITE;
  493. } else if ( SmbOfunAppend( SmbOpenFunction ) ) {
  494. createDisposition = create ? FILE_OPEN_IF : FILE_OPEN;
  495. } else {
  496. createDisposition = FILE_CREATE;
  497. }
  498. //
  499. // If we know that the target is a directory, generate the real target
  500. // name.
  501. //
  502. if ( *SmbFlags & SMB_TARGET_IS_DIRECTORY ) {
  503. SrvGetBaseFileName( Source, &sourceBaseName );
  504. SrvAllocateAndBuildPathName(
  505. Target,
  506. &sourceBaseName,
  507. NULL,
  508. &targetName
  509. );
  510. if ( targetName.Buffer == NULL ) {
  511. status = STATUS_INSUFF_SERVER_RESOURCES;
  512. goto copy_done;
  513. }
  514. targetObjectAttributes.ObjectName = &targetName;
  515. }
  516. IF_SMB_DEBUG(FILE_CONTROL2) {
  517. SrvPrint1( "Opening target: %wZ\n", targetObjectAttributes.ObjectName );
  518. }
  519. INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpenAttempts );
  520. INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpensForPathOperations );
  521. //
  522. // !!! Currently we can't specify complete if oplocked, because
  523. // this won't break a batch oplock. Unfortunately this also
  524. // means that we can't timeout the open (if the oplock break
  525. // takes too long) and fail this SMB gracefully.
  526. //
  527. status = SrvIoCreateFile(
  528. WorkContext,
  529. &targetHandle,
  530. SRV_COPY_TARGET_ACCESS | SYNCHRONIZE, // DesiredAccess
  531. &targetObjectAttributes,
  532. &ioStatusBlock,
  533. NULL, // AllocationSize
  534. 0, // FileAttributes
  535. FILE_SHARE_READ, // ShareAccess
  536. createDisposition,
  537. FILE_NON_DIRECTORY_FILE | // CreateOptions
  538. FILE_OPEN_REPARSE_POINT |
  539. FILE_SYNCHRONOUS_IO_NONALERT,
  540. // | FILE_COMPLETE_IF_OPLOCKED,
  541. NULL, // EaBuffer
  542. 0, // EaLength
  543. CreateFileTypeNone, // File type
  544. NULL, // ExtraCreateParameters
  545. IO_FORCE_ACCESS_CHECK, // Options
  546. TargetShare
  547. );
  548. if( status == STATUS_INVALID_PARAMETER ) {
  549. status = SrvIoCreateFile(
  550. WorkContext,
  551. &targetHandle,
  552. SRV_COPY_TARGET_ACCESS | SYNCHRONIZE, // DesiredAccess
  553. &targetObjectAttributes,
  554. &ioStatusBlock,
  555. NULL, // AllocationSize
  556. 0, // FileAttributes
  557. FILE_SHARE_READ, // ShareAccess
  558. createDisposition,
  559. FILE_NON_DIRECTORY_FILE | // CreateOptions
  560. FILE_SYNCHRONOUS_IO_NONALERT,
  561. // | FILE_COMPLETE_IF_OPLOCKED,
  562. NULL, // EaBuffer
  563. 0, // EaLength
  564. CreateFileTypeNone, // File type
  565. NULL, // ExtraCreateParameters
  566. IO_FORCE_ACCESS_CHECK, // Options
  567. TargetShare
  568. );
  569. }
  570. //
  571. // If the open failed because the target is a directory, and we didn't
  572. // know that it was supposed to be a file, then concatenate the
  573. // source base name to the target and retry the open.
  574. //
  575. // !!! NOT THE CORRECT STATUS CODE. It should be something like
  576. // STATUS_FILE_IS_DIRECTORY.
  577. if ( status == STATUS_INVALID_PARAMETER &&
  578. !( *SmbFlags & SMB_TARGET_IS_FILE ) &&
  579. !( *SmbFlags & SMB_TARGET_IS_DIRECTORY ) ) {
  580. //
  581. // Set the flags so that future calls to this routine will do
  582. // the right thing first time around.
  583. //
  584. *SmbFlags |= SMB_TARGET_IS_DIRECTORY;
  585. SrvGetBaseFileName( Source, &sourceBaseName );
  586. SrvAllocateAndBuildPathName(
  587. Target,
  588. &sourceBaseName,
  589. NULL,
  590. &targetName
  591. );
  592. if ( targetName.Buffer == NULL ) {
  593. status = STATUS_INSUFF_SERVER_RESOURCES;
  594. goto copy_done;
  595. }
  596. targetObjectAttributes.ObjectName = &targetName;
  597. IF_SMB_DEBUG(FILE_CONTROL2) {
  598. SrvPrint1( "Opening target: %wZ\n", targetObjectAttributes.ObjectName );
  599. }
  600. INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpenAttempts );
  601. INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpensForPathOperations );
  602. //
  603. // !!! Currently we can't specify complete if oplocked, because
  604. // this won't break a batch oplock. Unfortunately this also
  605. // means that we can't timeout the open (if the oplock break
  606. // takes too long) and fail this SMB gracefully.
  607. //
  608. status = SrvIoCreateFile(
  609. WorkContext,
  610. &targetHandle,
  611. SRV_COPY_TARGET_ACCESS | SYNCHRONIZE, // DesiredAccess
  612. &targetObjectAttributes,
  613. &ioStatusBlock,
  614. NULL, // AllocationSize
  615. 0, // FileAttributes
  616. FILE_SHARE_READ, // ShareAccess
  617. createDisposition,
  618. FILE_NON_DIRECTORY_FILE | // CreateOptions
  619. FILE_OPEN_REPARSE_POINT |
  620. FILE_SYNCHRONOUS_IO_NONALERT,
  621. NULL, // EaBuffer
  622. 0, // EaLength
  623. CreateFileTypeNone, // File Type
  624. NULL, // ExtraCreateParameters
  625. IO_FORCE_ACCESS_CHECK, // Options
  626. TargetShare
  627. );
  628. if( status == STATUS_INVALID_PARAMETER ) {
  629. status = SrvIoCreateFile(
  630. WorkContext,
  631. &targetHandle,
  632. SRV_COPY_TARGET_ACCESS | SYNCHRONIZE, // DesiredAccess
  633. &targetObjectAttributes,
  634. &ioStatusBlock,
  635. NULL, // AllocationSize
  636. 0, // FileAttributes
  637. FILE_SHARE_READ, // ShareAccess
  638. createDisposition,
  639. FILE_NON_DIRECTORY_FILE | // CreateOptions
  640. FILE_SYNCHRONOUS_IO_NONALERT,
  641. NULL, // EaBuffer
  642. 0, // EaLength
  643. CreateFileTypeNone, // File Type
  644. NULL, // ExtraCreateParameters
  645. IO_FORCE_ACCESS_CHECK, // Options
  646. TargetShare
  647. );
  648. }
  649. }
  650. if ( targetHandle != NULL ) {
  651. SRVDBG_CLAIM_HANDLE( targetHandle, "CPY", 5, 0 );
  652. }
  653. //
  654. // Is the target is a directory, and the copy move is append if exists,
  655. // create if the file does not exist, fail the request. We must do
  656. // this, because we have no way of knowing whether the original request
  657. // expects us append to the file, or truncate it.
  658. //
  659. if ( (*SmbFlags & SMB_TARGET_IS_DIRECTORY) &&
  660. ((SmbOpenFunction & SMB_OFUN_OPEN_MASK) == SMB_OFUN_OPEN_OPEN) &&
  661. ((SmbOpenFunction & SMB_OFUN_CREATE_MASK) == SMB_OFUN_CREATE_CREATE)) {
  662. status = STATUS_OS2_CANNOT_COPY;
  663. goto copy_done;
  664. }
  665. //
  666. // If the user didn't have this permission, update the statistics
  667. // database.
  668. //
  669. if ( status == STATUS_ACCESS_DENIED ) {
  670. SrvStatistics.AccessPermissionErrors++;
  671. }
  672. ASSERT( status != STATUS_OPLOCK_BREAK_IN_PROGRESS );
  673. if ( !NT_SUCCESS(status) ) {
  674. IF_DEBUG(ERRORS) {
  675. SrvPrint1( "Unable to open target: %X\n", status );
  676. }
  677. goto copy_done;
  678. }
  679. SrvStatistics.TotalFilesOpened++;
  680. //
  681. // Copy the source to the target handle just opened.
  682. //
  683. status = SrvCopyFile(
  684. SourceHandle,
  685. targetHandle,
  686. SmbOpenFunction,
  687. *SmbFlags,
  688. (ULONG)ioStatusBlock.Information // TargetOpenAction
  689. );
  690. if ( !NT_SUCCESS(status) ) {
  691. IF_DEBUG(ERRORS) {
  692. SrvPrint1( "SrvCopyFile failed, status = %X\n", status );
  693. }
  694. }
  695. copy_done:
  696. if ( targetName.Buffer != NULL ) {
  697. FREE_HEAP( targetName.Buffer );
  698. }
  699. if ( targetHandle != NULL ) {
  700. SRVDBG_RELEASE_HANDLE( targetHandle, "CPY", 10, 0 );
  701. SrvNtClose( targetHandle, TRUE );
  702. }
  703. return status;
  704. } // DoCopy
  705. NTSTATUS
  706. DoRename (
  707. IN PWORK_CONTEXT WorkContext,
  708. IN PUNICODE_STRING Source,
  709. IN HANDLE SourceHandle,
  710. IN PUNICODE_STRING Target,
  711. IN PSHARE TargetShare,
  712. IN USHORT SmbOpenFunction,
  713. IN OUT PUSHORT SmbFlags,
  714. IN BOOLEAN FailIfTargetIsDirectory,
  715. IN USHORT InformationLevel,
  716. IN ULONG ClusterCount
  717. )
  718. /*++
  719. Routine Description:
  720. This routine does the actual rename of an open file. The target may
  721. be a file or directory, but is bound by the constraints of SmbFlags.
  722. If SmbFlags does not indicate what the target is, then it is first
  723. assumed to be a file; if this fails, then the rename if performed
  724. again with the target as the original target string plus the source
  725. base name.
  726. *** If the source and target are on different volumes, then this
  727. routine will fail. We could make this work by doing a copy
  728. then delete, but this seems to be of limited usefulness and
  729. possibly incorrect due to the fact that a big file would take
  730. a long time, something the user would not expect.
  731. Arguments:
  732. WorkContext - a pointer to the work context block for this operation
  733. used for an impersonation.
  734. Source - the name of the source file relative to its share.
  735. SourceHandle - the handle to the source file.
  736. Target - the name of the target file relative to its share.
  737. TargetShare - the share of the target file. The RootDirectoryHandle
  738. field is used for a relative rename.
  739. SmbOpenFunction - describes whether we are allowed to overwrite an
  740. existing file.
  741. SmbFlags - can tell if the target is a file, directory, or unknown.
  742. This routine writes the true information into the location if
  743. it is unknown.
  744. FailIfTargetIsDirectory - if TRUE and the target already exists as
  745. a directory, fail the operation. Otherwise, rename the file
  746. into the directory.
  747. InformationLevel - Rename/CopyOnWrite/Link/MoveCluster
  748. ClusterCount - MoveCluster count
  749. Return Value:
  750. Status.
  751. --*/
  752. {
  753. NTSTATUS status;
  754. IO_STATUS_BLOCK ioStatusBlock;
  755. PFILE_RENAME_INFORMATION fileRenameInformation;
  756. ULONG renameBlockSize;
  757. USHORT NtInformationLevel;
  758. UNICODE_STRING sourceBaseName;
  759. UNICODE_STRING targetBaseName;
  760. PWCH s, es;
  761. PAGED_CODE( );
  762. //
  763. // Allocate enough heap to hold a FILE_RENAME_INFORMATION block and
  764. // the target file name. Allocate enough extra to hold the source
  765. // name in case the target turns out to be a directory and we have
  766. // to concatenate the source and target.
  767. //
  768. renameBlockSize = sizeof(FILE_RENAME_INFORMATION) + Target->Length +
  769. Source->Length;
  770. fileRenameInformation = ALLOCATE_HEAP_COLD(
  771. renameBlockSize,
  772. BlockTypeDataBuffer
  773. );
  774. if ( fileRenameInformation == NULL ) {
  775. IF_DEBUG(ERRORS) {
  776. SrvPrint0( "SrvMoveFile: Unable to allocate heap.\n" );
  777. }
  778. return STATUS_INSUFF_SERVER_RESOURCES;
  779. }
  780. //
  781. // Get the Share root handle.
  782. //
  783. status = SrvGetShareRootHandle( TargetShare );
  784. if ( !NT_SUCCESS(status) ) {
  785. IF_DEBUG(ERRORS) {
  786. SrvPrint1( "DoRename: SrvGetShareRootHandle failed. %X\n", status );
  787. }
  788. FREE_HEAP( fileRenameInformation );
  789. return(status);
  790. }
  791. //
  792. // Set up the rename block.
  793. //
  794. if (InformationLevel == SMB_NT_RENAME_MOVE_CLUSTER_INFO) {
  795. ((FILE_MOVE_CLUSTER_INFORMATION *)fileRenameInformation)->ClusterCount =
  796. ClusterCount;
  797. } else {
  798. fileRenameInformation->ReplaceIfExists =
  799. SmbOfunTruncate( SmbOpenFunction );
  800. }
  801. fileRenameInformation->RootDirectory = TargetShare->RootDirectoryHandle;
  802. //
  803. // If the target file has wildcards, expand name.
  804. //
  805. if ( FsRtlDoesNameContainWildCards( Target ) ) {
  806. ULONG tempUlong;
  807. UNICODE_STRING newTargetBaseName;
  808. if (InformationLevel != SMB_NT_RENAME_RENAME_FILE) {
  809. return(STATUS_OBJECT_PATH_SYNTAX_BAD);
  810. }
  811. //
  812. // Get source and target filenames. The target filename is to be
  813. // used as a template for wildcard expansion.
  814. //
  815. SrvGetBaseFileName( Source, &sourceBaseName );
  816. SrvGetBaseFileName( Target, &targetBaseName );
  817. tempUlong = sourceBaseName.Length + targetBaseName.Length;
  818. newTargetBaseName.Length = (USHORT)tempUlong;
  819. newTargetBaseName.MaximumLength = (USHORT)tempUlong;
  820. newTargetBaseName.Buffer = ALLOCATE_NONPAGED_POOL(
  821. tempUlong,
  822. BlockTypeDataBuffer
  823. );
  824. if ( newTargetBaseName.Buffer == NULL ) {
  825. INTERNAL_ERROR(
  826. ERROR_LEVEL_EXPECTED,
  827. "DoRename: Unable to allocate %d bytes from nonpaged pool.\n",
  828. tempUlong,
  829. NULL
  830. );
  831. //
  832. // Release the share root handle if device is removable
  833. //
  834. SrvReleaseShareRootHandle( TargetShare );
  835. FREE_HEAP( fileRenameInformation );
  836. return STATUS_INSUFF_SERVER_RESOURCES;
  837. }
  838. //
  839. // Get expanded filename
  840. //
  841. status = SrvWildcardRename(
  842. &targetBaseName,
  843. &sourceBaseName,
  844. &newTargetBaseName
  845. );
  846. if ( !NT_SUCCESS( status ) ) {
  847. //
  848. // Release the share root handle if device is removable
  849. //
  850. SrvReleaseShareRootHandle( TargetShare );
  851. DEALLOCATE_NONPAGED_POOL( newTargetBaseName.Buffer );
  852. FREE_HEAP( fileRenameInformation );
  853. return STATUS_OBJECT_NAME_INVALID;
  854. }
  855. //
  856. // tempUlong is equal to the directory path without this filename
  857. // but including the last delimeter.
  858. //
  859. tempUlong = Target->Length - targetBaseName.Length;
  860. //
  861. // Copy the directory path (including the delimeter.
  862. //
  863. RtlCopyMemory(
  864. fileRenameInformation->FileName,
  865. Target->Buffer,
  866. tempUlong
  867. );
  868. s = (PWCH) ((PCHAR)fileRenameInformation->FileName + tempUlong);
  869. //
  870. // Copy the expanded file name
  871. //
  872. RtlCopyMemory(
  873. s,
  874. newTargetBaseName.Buffer,
  875. newTargetBaseName.Length
  876. );
  877. fileRenameInformation->FileNameLength = tempUlong +
  878. newTargetBaseName.Length;
  879. DEALLOCATE_NONPAGED_POOL( newTargetBaseName.Buffer );
  880. } else {
  881. fileRenameInformation->FileNameLength = Target->Length;
  882. RtlCopyMemory(
  883. fileRenameInformation->FileName,
  884. Target->Buffer,
  885. Target->Length
  886. );
  887. // Check if we can do a fast rename if they are in the same path (which is usually the case)
  888. SrvGetBaseFileName( Source, &sourceBaseName );
  889. SrvGetBaseFileName( Target, &targetBaseName );
  890. if ((Source->Length - sourceBaseName.Length) == (Target->Length - targetBaseName.Length)) {
  891. ULONG i;
  892. PWCH sptr,tptr;
  893. i = Source->Length - sourceBaseName.Length;
  894. i=i>>1;
  895. sptr = &Source->Buffer[i-1];
  896. tptr = &Target->Buffer[i-1];
  897. while ( i > 0) {
  898. if (*sptr-- != *tptr--) {
  899. goto no_match;
  900. }
  901. i--;
  902. }
  903. // If the names matched, we're set for a quick rename (where the directory is not needed,
  904. // since they are in the same path)
  905. fileRenameInformation->RootDirectory = NULL;
  906. fileRenameInformation->FileNameLength = targetBaseName.Length;
  907. RtlCopyMemory(
  908. fileRenameInformation->FileName,
  909. targetBaseName.Buffer,
  910. targetBaseName.Length
  911. );
  912. }
  913. }
  914. no_match:
  915. //
  916. // If we know that the target is a directory, then concatenate the
  917. // source base name to the end of the target name.
  918. //
  919. if ( *SmbFlags & SMB_TARGET_IS_DIRECTORY ) {
  920. SrvGetBaseFileName( Source, &sourceBaseName );
  921. s = (PWCH)((PCHAR)fileRenameInformation->FileName +
  922. fileRenameInformation->FileNameLength);
  923. //
  924. // Only add in a directory separator if the target had some path
  925. // information. This avoids having a new name like "\NAME", which
  926. // is illegal with a relative rename (there should be no
  927. // leading backslash).
  928. //
  929. if ( Target->Length > 0 ) {
  930. *s++ = DIRECTORY_SEPARATOR_CHAR;
  931. }
  932. RtlCopyMemory( s, sourceBaseName.Buffer, sourceBaseName.Length );
  933. fileRenameInformation->FileNameLength +=
  934. sizeof(WCHAR) + sourceBaseName.Length;
  935. }
  936. //
  937. // Call NtSetInformationFile to actually rename the file.
  938. //
  939. IF_SMB_DEBUG(FILE_CONTROL2) {
  940. UNICODE_STRING name;
  941. name.Length = (USHORT)fileRenameInformation->FileNameLength;
  942. name.Buffer = fileRenameInformation->FileName;
  943. SrvPrint2( "Renaming %wZ to %wZ\n", Source, &name );
  944. }
  945. switch (InformationLevel) {
  946. case SMB_NT_RENAME_RENAME_FILE:
  947. NtInformationLevel = FileRenameInformation;
  948. //
  949. // If we are renaming a substream, we do not supply
  950. // fileRenameInformation->RootDirectory
  951. //
  952. es = fileRenameInformation->FileName +
  953. fileRenameInformation->FileNameLength / sizeof( WCHAR );
  954. for( s = fileRenameInformation->FileName; s < es; s++ ) {
  955. if( *s == L':' ) {
  956. fileRenameInformation->RootDirectory = 0;
  957. break;
  958. }
  959. }
  960. break;
  961. case SMB_NT_RENAME_MOVE_CLUSTER_INFO:
  962. NtInformationLevel = FileMoveClusterInformation;
  963. break;
  964. case SMB_NT_RENAME_SET_LINK_INFO:
  965. NtInformationLevel = FileLinkInformation;
  966. break;
  967. default:
  968. ASSERT(FALSE);
  969. }
  970. status = IMPERSONATE( WorkContext );
  971. if( NT_SUCCESS( status ) ) {
  972. status = NtSetInformationFile(
  973. SourceHandle,
  974. &ioStatusBlock,
  975. fileRenameInformation,
  976. renameBlockSize,
  977. NtInformationLevel
  978. );
  979. //
  980. // If the media was changed and we can come up with a new share root handle,
  981. // then we should retry the operation
  982. //
  983. if( SrvRetryDueToDismount( TargetShare, status ) ) {
  984. fileRenameInformation->RootDirectory = TargetShare->RootDirectoryHandle;
  985. status = NtSetInformationFile(
  986. SourceHandle,
  987. &ioStatusBlock,
  988. fileRenameInformation,
  989. renameBlockSize,
  990. NtInformationLevel
  991. );
  992. }
  993. REVERT( );
  994. }
  995. if ( NT_SUCCESS(status) ) {
  996. status = ioStatusBlock.Status;
  997. SrvRemoveCachedDirectoryName( WorkContext, Source );
  998. }
  999. //
  1000. // If the status was STATUS_OBJECT_NAME_COLLISION then the target
  1001. // already existed as a directory. Unless the target name was
  1002. // supposed to indicate a file or we have already tried used the
  1003. // source name, retry by concatenating the source base name to the
  1004. // target.
  1005. //
  1006. if ( status == STATUS_OBJECT_NAME_COLLISION &&
  1007. !FailIfTargetIsDirectory &&
  1008. !( *SmbFlags & SMB_TARGET_IS_FILE ) &&
  1009. !( *SmbFlags & SMB_TARGET_IS_DIRECTORY ) ) {
  1010. IF_SMB_DEBUG(FILE_CONTROL2) {
  1011. SrvPrint0( "Retrying rename with source name.\n" );
  1012. }
  1013. //
  1014. // Set the flags so that future calls to this routine will do
  1015. // the right thing first time around.
  1016. //
  1017. *SmbFlags |= SMB_TARGET_IS_DIRECTORY;
  1018. //
  1019. // Generate the new target name.
  1020. //
  1021. SrvGetBaseFileName( Source, &sourceBaseName );
  1022. s = (PWCH)((PCHAR)fileRenameInformation->FileName +
  1023. fileRenameInformation->FileNameLength);
  1024. *s++ = DIRECTORY_SEPARATOR_CHAR;
  1025. RtlCopyMemory( s, sourceBaseName.Buffer, sourceBaseName.Length );
  1026. fileRenameInformation->FileNameLength +=
  1027. sizeof(WCHAR) + sourceBaseName.Length;
  1028. //
  1029. // Do the rename again. If it fails this time, too bad.
  1030. //
  1031. // *** Note that it may fail because the source and target
  1032. // exist on different volumes. This could potentially
  1033. // cause confusion for DOS clients in the presence of
  1034. // links.
  1035. IF_SMB_DEBUG(FILE_CONTROL2) {
  1036. UNICODE_STRING name;
  1037. name.Length = (USHORT)fileRenameInformation->FileNameLength;
  1038. name.Buffer = fileRenameInformation->FileName;
  1039. SrvPrint2( "Renaming %wZ to %wZ\n", Source, &name );
  1040. }
  1041. status = IMPERSONATE( WorkContext );
  1042. if( NT_SUCCESS( status ) ) {
  1043. status = NtSetInformationFile(
  1044. SourceHandle,
  1045. &ioStatusBlock,
  1046. fileRenameInformation,
  1047. renameBlockSize,
  1048. NtInformationLevel
  1049. );
  1050. //
  1051. // If the media was changed and we can come up with a new share root handle,
  1052. // then we should retry the operation
  1053. //
  1054. if( SrvRetryDueToDismount( TargetShare, status ) ) {
  1055. fileRenameInformation->RootDirectory = TargetShare->RootDirectoryHandle;
  1056. status = NtSetInformationFile(
  1057. SourceHandle,
  1058. &ioStatusBlock,
  1059. fileRenameInformation,
  1060. renameBlockSize,
  1061. NtInformationLevel
  1062. );
  1063. }
  1064. REVERT( );
  1065. }
  1066. if ( NT_SUCCESS(status) ) {
  1067. status = ioStatusBlock.Status;
  1068. }
  1069. }
  1070. //
  1071. // Release the share root handle if device is removable
  1072. //
  1073. SrvReleaseShareRootHandle( TargetShare );
  1074. FREE_HEAP( fileRenameInformation );
  1075. if ( !NT_SUCCESS(status) ) {
  1076. IF_DEBUG(ERRORS) {
  1077. SrvPrint1( "DoRename: NtSetInformationFile failed, status = %X\n",
  1078. status );
  1079. }
  1080. }
  1081. return status;
  1082. } // DoRename