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

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