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.

1285 lines
40 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. dir.c
  5. Abstract:
  6. This module implements Win32 Directory functions.
  7. Author:
  8. Mark Lucovsky (markl) 26-Sep-1990
  9. Revision History:
  10. --*/
  11. #include "basedll.h"
  12. #include "mountmgr.h"
  13. BOOL
  14. APIENTRY
  15. CreateDirectoryA(
  16. LPCSTR lpPathName,
  17. LPSECURITY_ATTRIBUTES lpSecurityAttributes
  18. )
  19. /*++
  20. Routine Description:
  21. ANSI thunk to CreateDirectoryW
  22. --*/
  23. {
  24. PUNICODE_STRING Unicode;
  25. Unicode = Basep8BitStringToStaticUnicodeString( lpPathName );
  26. if (Unicode == NULL) {
  27. return FALSE;
  28. }
  29. return ( CreateDirectoryW((LPCWSTR)Unicode->Buffer,lpSecurityAttributes) );
  30. }
  31. BOOL
  32. APIENTRY
  33. CreateDirectoryW(
  34. LPCWSTR lpPathName,
  35. LPSECURITY_ATTRIBUTES lpSecurityAttributes
  36. )
  37. /*++
  38. Routine Description:
  39. A directory can be created using CreateDirectory.
  40. This API causes a directory with the specified pathname to be
  41. created. If the underlying file system supports security on files
  42. and directories, then the SecurityDescriptor argument is applied to
  43. the new directory.
  44. This call is similar to DOS (int 21h, function 39h) and OS/2's
  45. DosCreateDir.
  46. Arguments:
  47. lpPathName - Supplies the pathname of the directory to be created.
  48. lpSecurityAttributes - An optional parameter that, if present, and
  49. supported on the target file system supplies a security
  50. descriptor for the new directory.
  51. Return Value:
  52. TRUE - The operation was successful.
  53. FALSE/NULL - The operation failed. Extended error status is available
  54. using GetLastError.
  55. --*/
  56. {
  57. NTSTATUS Status;
  58. OBJECT_ATTRIBUTES Obja;
  59. HANDLE Handle;
  60. UNICODE_STRING FileName;
  61. IO_STATUS_BLOCK IoStatusBlock;
  62. BOOLEAN TranslationStatus;
  63. RTL_RELATIVE_NAME RelativeName;
  64. PVOID FreeBuffer;
  65. TranslationStatus = RtlDosPathNameToNtPathName_U(
  66. lpPathName,
  67. &FileName,
  68. NULL,
  69. &RelativeName
  70. );
  71. if ( !TranslationStatus ) {
  72. SetLastError(ERROR_PATH_NOT_FOUND);
  73. return FALSE;
  74. }
  75. //
  76. // dont create a directory unless there is room in the directory for
  77. // at least an 8.3 name. This way everyone will be able to delete all
  78. // files in the directory by using del *.* which expands to path+\*.*
  79. //
  80. if ( FileName.Length > ((MAX_PATH-12)<<1) ) {
  81. DWORD L;
  82. LPWSTR lp;
  83. if ( !(lpPathName[0] == '\\' && lpPathName[1] == '\\' &&
  84. lpPathName[2] == '?' && lpPathName[3] == '\\') ) {
  85. L = GetFullPathNameW(lpPathName,0,NULL,&lp);
  86. if ( !L || L+12 > MAX_PATH ) {
  87. RtlFreeHeap(RtlProcessHeap(), 0,FileName.Buffer);
  88. SetLastError(ERROR_FILENAME_EXCED_RANGE);
  89. return FALSE;
  90. }
  91. }
  92. }
  93. FreeBuffer = FileName.Buffer;
  94. if ( RelativeName.RelativeName.Length ) {
  95. FileName = *(PUNICODE_STRING)&RelativeName.RelativeName;
  96. }
  97. else {
  98. RelativeName.ContainingDirectory = NULL;
  99. }
  100. InitializeObjectAttributes(
  101. &Obja,
  102. &FileName,
  103. OBJ_CASE_INSENSITIVE,
  104. RelativeName.ContainingDirectory,
  105. NULL
  106. );
  107. if ( ARGUMENT_PRESENT(lpSecurityAttributes) ) {
  108. Obja.SecurityDescriptor = lpSecurityAttributes->lpSecurityDescriptor;
  109. }
  110. Status = NtCreateFile(
  111. &Handle,
  112. FILE_LIST_DIRECTORY | SYNCHRONIZE,
  113. &Obja,
  114. &IoStatusBlock,
  115. NULL,
  116. FILE_ATTRIBUTE_NORMAL,
  117. FILE_SHARE_READ | FILE_SHARE_WRITE,
  118. FILE_CREATE,
  119. FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT,
  120. NULL,
  121. 0L
  122. );
  123. RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
  124. if ( NT_SUCCESS(Status) ) {
  125. NtClose(Handle);
  126. return TRUE;
  127. }
  128. else {
  129. if ( RtlIsDosDeviceName_U((LPWSTR)lpPathName) ) {
  130. Status = STATUS_NOT_A_DIRECTORY;
  131. }
  132. BaseSetLastNTError(Status);
  133. return FALSE;
  134. }
  135. }
  136. BOOL
  137. APIENTRY
  138. CreateDirectoryExA(
  139. LPCSTR lpTemplateDirectory,
  140. LPCSTR lpNewDirectory,
  141. LPSECURITY_ATTRIBUTES lpSecurityAttributes
  142. )
  143. /*++
  144. Routine Description:
  145. ANSI thunk to CreateDirectoryFromTemplateW
  146. --*/
  147. {
  148. PUNICODE_STRING StaticUnicode;
  149. UNICODE_STRING DynamicUnicode;
  150. BOOL b;
  151. StaticUnicode = Basep8BitStringToStaticUnicodeString( lpTemplateDirectory );
  152. if (StaticUnicode == NULL) {
  153. return FALSE;
  154. }
  155. if (!Basep8BitStringToDynamicUnicodeString( &DynamicUnicode, lpNewDirectory )) {
  156. return FALSE;
  157. }
  158. b = CreateDirectoryExW(
  159. (LPCWSTR)StaticUnicode->Buffer,
  160. (LPCWSTR)DynamicUnicode.Buffer,
  161. lpSecurityAttributes
  162. );
  163. RtlFreeUnicodeString(&DynamicUnicode);
  164. return b;
  165. }
  166. BOOL
  167. APIENTRY
  168. CreateDirectoryExW(
  169. LPCWSTR lpTemplateDirectory,
  170. LPCWSTR lpNewDirectory,
  171. LPSECURITY_ATTRIBUTES lpSecurityAttributes
  172. )
  173. /*++
  174. Routine Description:
  175. A directory can be created using CreateDirectoryEx, retaining the
  176. attributes of the original directory file.
  177. This API causes a directory with the specified pathname to be
  178. created. If the underlying file system supports security on files
  179. and directories, then the SecurityDescriptor argument is applied to
  180. the new directory. The other attributes of the template directory are
  181. retained when creating the new directory.
  182. If the original directory is a volume mount point then the new directory
  183. is also a volume mount point to the same volume as the original one.
  184. Arguments:
  185. lpTemplateDirectory - Supplies the pathname of the directory to be used as
  186. a template when creating the new directory.
  187. lpPathName - Supplies the pathname of the directory to be created.
  188. lpSecurityAttributes - An optional parameter that, if present, and
  189. supported on the target file system supplies a security
  190. descriptor for the new directory.
  191. Return Value:
  192. TRUE - The operation was successful.
  193. FALSE/NULL - The operation failed. Extended error status is available
  194. using GetLastError.
  195. --*/
  196. {
  197. NTSTATUS Status;
  198. OBJECT_ATTRIBUTES Obja;
  199. HANDLE SourceFile;
  200. HANDLE DestFile;
  201. UNICODE_STRING PathName;
  202. UNICODE_STRING TargetName;
  203. IO_STATUS_BLOCK IoStatusBlock;
  204. BOOLEAN TranslationStatus;
  205. BOOLEAN IsNameGrafting = FALSE;
  206. UNICODE_STRING VolumeName;
  207. UNICODE_STRING MountPoint;
  208. PWCHAR VolumeMountPoint = NULL;
  209. RTL_RELATIVE_NAME RelativeName;
  210. PVOID FreePathBuffer;
  211. PVOID FreeTargetBuffer;
  212. UNICODE_STRING StreamName;
  213. WCHAR FileName[MAXIMUM_FILENAME_LENGTH+1];
  214. HANDLE StreamHandle;
  215. HANDLE OutputStream;
  216. PFILE_STREAM_INFORMATION StreamInfo;
  217. FILE_ATTRIBUTE_TAG_INFORMATION FileTagInformation;
  218. PFILE_STREAM_INFORMATION StreamInfoBase;
  219. PFILE_FULL_EA_INFORMATION EaBuffer;
  220. FILE_EA_INFORMATION EaInfo;
  221. FILE_BASIC_INFORMATION BasicInfo;
  222. ULONG EaSize;
  223. ULONG StreamInfoSize;
  224. ULONG CopySize;
  225. ULONG i;
  226. ULONG DesiredAccess = 0;
  227. DWORD Options;
  228. DWORD b;
  229. LPCOPYFILE_CONTEXT CopyFileContext = NULL;
  230. //
  231. // Process the input template directory name and then open the directory
  232. // file, ensuring that it really is a directory.
  233. //
  234. TranslationStatus = RtlDosPathNameToNtPathName_U(
  235. lpTemplateDirectory,
  236. &PathName,
  237. NULL,
  238. &RelativeName
  239. );
  240. if ( !TranslationStatus ) {
  241. SetLastError(ERROR_PATH_NOT_FOUND);
  242. return FALSE;
  243. }
  244. FreePathBuffer = PathName.Buffer;
  245. if ( RelativeName.RelativeName.Length ) {
  246. PathName = *(PUNICODE_STRING)&RelativeName.RelativeName;
  247. }
  248. else {
  249. RelativeName.ContainingDirectory = NULL;
  250. }
  251. InitializeObjectAttributes(
  252. &Obja,
  253. &PathName,
  254. OBJ_CASE_INSENSITIVE,
  255. RelativeName.ContainingDirectory,
  256. NULL
  257. );
  258. //
  259. // Inhibit the reparse behavior using FILE_OPEN_REPARSE_POINT.
  260. //
  261. Status = NtOpenFile(
  262. &SourceFile,
  263. FILE_LIST_DIRECTORY | FILE_READ_EA | FILE_READ_ATTRIBUTES,
  264. &Obja,
  265. &IoStatusBlock,
  266. FILE_SHARE_READ | FILE_SHARE_WRITE,
  267. FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT
  268. );
  269. if ( !NT_SUCCESS(Status) ) {
  270. //
  271. // Back level file systems may not support reparse points and thus not
  272. // support symbolic links.
  273. // We infer this is the case when the Status is STATUS_INVALID_PARAMETER.
  274. //
  275. if ( Status == STATUS_INVALID_PARAMETER ) {
  276. //
  277. // Re-open not inhibiting the reparse behavior.
  278. //
  279. Status = NtOpenFile(
  280. &SourceFile,
  281. FILE_LIST_DIRECTORY | FILE_READ_EA | FILE_READ_ATTRIBUTES,
  282. &Obja,
  283. &IoStatusBlock,
  284. FILE_SHARE_READ | FILE_SHARE_WRITE,
  285. FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT
  286. );
  287. if ( !NT_SUCCESS(Status) ) {
  288. RtlFreeHeap(RtlProcessHeap(), 0, FreePathBuffer);
  289. BaseSetLastNTError(Status);
  290. return FALSE;
  291. }
  292. }
  293. else {
  294. RtlFreeHeap(RtlProcessHeap(), 0, FreePathBuffer);
  295. BaseSetLastNTError(Status);
  296. return FALSE;
  297. }
  298. }
  299. else {
  300. //
  301. // See whether we have a name grafting operation.
  302. //
  303. BasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
  304. Status = NtQueryInformationFile(
  305. SourceFile,
  306. &IoStatusBlock,
  307. (PVOID)&BasicInfo,
  308. sizeof(BasicInfo),
  309. FileBasicInformation
  310. );
  311. if ( !NT_SUCCESS(Status) ) {
  312. RtlFreeHeap(RtlProcessHeap(), 0, FreePathBuffer);
  313. CloseHandle(SourceFile);
  314. BaseSetLastNTError(Status);
  315. return FALSE;
  316. }
  317. if ( BasicInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ) {
  318. Status = NtQueryInformationFile(
  319. SourceFile,
  320. &IoStatusBlock,
  321. (PVOID)&FileTagInformation,
  322. sizeof(FileTagInformation),
  323. FileAttributeTagInformation
  324. );
  325. if ( !NT_SUCCESS(Status) ) {
  326. RtlFreeHeap(RtlProcessHeap(), 0, FreePathBuffer);
  327. CloseHandle(SourceFile);
  328. BaseSetLastNTError(Status);
  329. return FALSE;
  330. }
  331. if ( FileTagInformation.ReparseTag != IO_REPARSE_TAG_MOUNT_POINT ) {
  332. //
  333. // Close and re-open not inhibiting the reparse behavior.
  334. //
  335. CloseHandle(SourceFile);
  336. Status = NtOpenFile(
  337. &SourceFile,
  338. FILE_LIST_DIRECTORY | FILE_READ_EA | FILE_READ_ATTRIBUTES,
  339. &Obja,
  340. &IoStatusBlock,
  341. FILE_SHARE_READ | FILE_SHARE_WRITE,
  342. FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT
  343. );
  344. if ( !NT_SUCCESS(Status) ) {
  345. RtlFreeHeap(RtlProcessHeap(), 0, FreePathBuffer);
  346. BaseSetLastNTError(Status);
  347. return FALSE;
  348. }
  349. }
  350. else {
  351. IsNameGrafting = TRUE;
  352. }
  353. }
  354. }
  355. TranslationStatus = RtlDosPathNameToNtPathName_U(
  356. lpNewDirectory,
  357. &TargetName,
  358. NULL,
  359. &RelativeName
  360. );
  361. if ( !TranslationStatus ) {
  362. RtlFreeHeap(RtlProcessHeap(), 0, FreePathBuffer);
  363. NtClose(SourceFile);
  364. SetLastError(ERROR_PATH_NOT_FOUND);
  365. return FALSE;
  366. }
  367. FreeTargetBuffer = TargetName.Buffer;
  368. //
  369. // Verify that the source and target are different.
  370. //
  371. if ( RtlEqualUnicodeString(&PathName, &TargetName, TRUE) ) {
  372. //
  373. // Do nothing. Source and target are the same.
  374. //
  375. RtlFreeHeap(RtlProcessHeap(), 0, FreePathBuffer);
  376. RtlFreeHeap(RtlProcessHeap(), 0, FreeTargetBuffer);
  377. NtClose(SourceFile);
  378. SetLastError(ERROR_INVALID_NAME);
  379. return FALSE;
  380. }
  381. RtlFreeHeap(RtlProcessHeap(), 0, FreePathBuffer);
  382. //
  383. // Do not create a directory unless there is room in the directory for
  384. // at least an 8.3 name. This way everyone will be able to delete all
  385. // files in the directory by using del *.* which expands to path+\*.*
  386. //
  387. if ( TargetName.Length > ((MAX_PATH-12)<<1) ) {
  388. DWORD L;
  389. LPWSTR lp;
  390. if ( !(lpNewDirectory[0] == '\\' && lpNewDirectory[1] == '\\' &&
  391. lpNewDirectory[2] == '?' && lpNewDirectory[3] == '\\') ) {
  392. L = GetFullPathNameW(lpNewDirectory,0,NULL,&lp);
  393. if ( !L || L+12 > MAX_PATH ) {
  394. RtlFreeHeap(RtlProcessHeap(), 0, FreeTargetBuffer);
  395. CloseHandle(SourceFile);
  396. SetLastError(ERROR_FILENAME_EXCED_RANGE);
  397. return FALSE;
  398. }
  399. }
  400. }
  401. if ( RelativeName.RelativeName.Length ) {
  402. TargetName = *(PUNICODE_STRING)&RelativeName.RelativeName;
  403. }
  404. else {
  405. RelativeName.ContainingDirectory = NULL;
  406. }
  407. EaBuffer = NULL;
  408. EaSize = 0;
  409. Status = NtQueryInformationFile(
  410. SourceFile,
  411. &IoStatusBlock,
  412. &EaInfo,
  413. sizeof(EaInfo),
  414. FileEaInformation
  415. );
  416. if ( !NT_SUCCESS(Status) ) {
  417. RtlFreeHeap(RtlProcessHeap(), 0, FreeTargetBuffer);
  418. CloseHandle(SourceFile);
  419. BaseSetLastNTError(Status);
  420. return FALSE;
  421. }
  422. if ( NT_SUCCESS(Status) && EaInfo.EaSize ) {
  423. EaSize = EaInfo.EaSize;
  424. do {
  425. EaSize *= 2;
  426. EaBuffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), EaSize);
  427. if ( !EaBuffer ) {
  428. RtlFreeHeap(RtlProcessHeap(), 0, FreeTargetBuffer);
  429. CloseHandle(SourceFile);
  430. BaseSetLastNTError(STATUS_NO_MEMORY);
  431. return FALSE;
  432. }
  433. Status = NtQueryEaFile(
  434. SourceFile,
  435. &IoStatusBlock,
  436. EaBuffer,
  437. EaSize,
  438. FALSE,
  439. (PVOID)NULL,
  440. 0,
  441. (PULONG)NULL,
  442. TRUE
  443. );
  444. if ( !NT_SUCCESS(Status) ) {
  445. RtlFreeHeap(RtlProcessHeap(), 0, EaBuffer);
  446. EaBuffer = NULL;
  447. IoStatusBlock.Information = 0;
  448. }
  449. } while ( Status == STATUS_BUFFER_OVERFLOW ||
  450. Status == STATUS_BUFFER_TOO_SMALL );
  451. EaSize = (ULONG)IoStatusBlock.Information;
  452. }
  453. //
  454. // Open/create the destination directory.
  455. //
  456. InitializeObjectAttributes(
  457. &Obja,
  458. &TargetName,
  459. OBJ_CASE_INSENSITIVE,
  460. RelativeName.ContainingDirectory,
  461. NULL
  462. );
  463. if ( ARGUMENT_PRESENT(lpSecurityAttributes) ) {
  464. Obja.SecurityDescriptor = lpSecurityAttributes->lpSecurityDescriptor;
  465. }
  466. DesiredAccess = FILE_LIST_DIRECTORY | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | SYNCHRONIZE;
  467. if ( BasicInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ) {
  468. //
  469. // To set the reparse point at the target one needs FILE_WRITE_DATA.
  470. //
  471. DesiredAccess |= FILE_WRITE_DATA;
  472. }
  473. //
  474. // Clear the reparse attribute before creating the target. Only the
  475. // name grafting use of reparse points is preserved at this level.
  476. // Open first inhibiting the reparse behavior.
  477. //
  478. BasicInfo.FileAttributes &= ~FILE_ATTRIBUTE_REPARSE_POINT;
  479. Status = NtCreateFile(
  480. &DestFile,
  481. DesiredAccess,
  482. &Obja,
  483. &IoStatusBlock,
  484. NULL,
  485. BasicInfo.FileAttributes,
  486. FILE_SHARE_READ | FILE_SHARE_WRITE,
  487. FILE_CREATE,
  488. FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT,
  489. EaBuffer,
  490. EaSize
  491. );
  492. if ( !NT_SUCCESS(Status) ) {
  493. //
  494. // Back level file systems may not support reparse points.
  495. // We infer this is the case when the Status is STATUS_INVALID_PARAMETER.
  496. //
  497. if ( (Status == STATUS_INVALID_PARAMETER) ||
  498. (Status == STATUS_ACCESS_DENIED) ) {
  499. //
  500. // Either the FS does not support reparse points or we do not have enough
  501. // access to the target.
  502. //
  503. if ( IsNameGrafting ) {
  504. //
  505. // We need to return error, as the target cannot be opened correctly.
  506. //
  507. RtlFreeHeap(RtlProcessHeap(), 0, FreeTargetBuffer);
  508. if ( EaBuffer ) {
  509. RtlFreeHeap(RtlProcessHeap(), 0, EaBuffer);
  510. }
  511. CloseHandle(SourceFile);
  512. BaseSetLastNTError(Status);
  513. return FALSE;
  514. }
  515. Status = NtCreateFile(
  516. &DestFile,
  517. FILE_LIST_DIRECTORY | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
  518. &Obja,
  519. &IoStatusBlock,
  520. NULL,
  521. BasicInfo.FileAttributes,
  522. FILE_SHARE_READ | FILE_SHARE_WRITE,
  523. FILE_CREATE,
  524. FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT,
  525. EaBuffer,
  526. EaSize
  527. );
  528. }
  529. }
  530. RtlFreeHeap(RtlProcessHeap(), 0, FreeTargetBuffer);
  531. if ( EaBuffer ) {
  532. RtlFreeHeap(RtlProcessHeap(), 0, EaBuffer);
  533. }
  534. if ( !NT_SUCCESS(Status) ) {
  535. NtClose(SourceFile);
  536. if ( RtlIsDosDeviceName_U((LPWSTR)lpNewDirectory) ) {
  537. Status = STATUS_NOT_A_DIRECTORY;
  538. }
  539. BaseSetLastNTError(Status);
  540. return FALSE;
  541. }
  542. else {
  543. if ( IsNameGrafting ) {
  544. PREPARSE_DATA_BUFFER ReparseBufferHeader = NULL;
  545. PUCHAR ReparseBuffer = NULL;
  546. //
  547. // Allocate the buffer to get/set the reparse point.
  548. //
  549. ReparseBuffer = RtlAllocateHeap(
  550. RtlProcessHeap(),
  551. MAKE_TAG( TMP_TAG ),
  552. MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
  553. if ( ReparseBuffer == NULL) {
  554. NtClose(SourceFile);
  555. NtClose(DestFile);
  556. BaseSetLastNTError(STATUS_NO_MEMORY);
  557. //
  558. // Notice that we leave behind the target directory.
  559. //
  560. return FALSE;
  561. }
  562. //
  563. // Get the data in the reparse point.
  564. //
  565. Status = NtFsControlFile(
  566. SourceFile,
  567. NULL,
  568. NULL,
  569. NULL,
  570. &IoStatusBlock,
  571. FSCTL_GET_REPARSE_POINT,
  572. NULL, // Input buffer
  573. 0, // Input buffer length
  574. ReparseBuffer, // Output buffer
  575. MAXIMUM_REPARSE_DATA_BUFFER_SIZE // Output buffer length
  576. );
  577. if ( !NT_SUCCESS( Status ) ) {
  578. RtlFreeHeap(RtlProcessHeap(), 0, ReparseBuffer);
  579. NtClose(SourceFile);
  580. NtClose(DestFile);
  581. BaseSetLastNTError(Status);
  582. return FALSE;
  583. }
  584. //
  585. // Defensive sanity check. The reparse buffer should be name grafting.
  586. //
  587. ReparseBufferHeader = (PREPARSE_DATA_BUFFER)ReparseBuffer;
  588. if ( ReparseBufferHeader->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT ) {
  589. RtlFreeHeap(RtlProcessHeap(), 0, ReparseBuffer);
  590. NtClose(SourceFile);
  591. NtClose(DestFile);
  592. BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID);
  593. return FALSE;
  594. }
  595. //
  596. // Finish up the creation of the target directory.
  597. //
  598. VolumeName.Length = VolumeName.MaximumLength =
  599. ReparseBufferHeader->MountPointReparseBuffer.SubstituteNameLength;
  600. VolumeName.Buffer = (PWCHAR)
  601. ((PCHAR) ReparseBufferHeader->MountPointReparseBuffer.PathBuffer +
  602. ReparseBufferHeader->MountPointReparseBuffer.SubstituteNameOffset);
  603. if (MOUNTMGR_IS_NT_VOLUME_NAME_WB(&VolumeName)) {
  604. //
  605. // Set the volume mount point and be done.
  606. //
  607. // SetVolumeMountPoint requires the mount point name
  608. // to have a trailing backslash.
  609. //
  610. RtlInitUnicodeString(&MountPoint, lpNewDirectory);
  611. VolumeMountPoint = RtlAllocateHeap(RtlProcessHeap(),
  612. MAKE_TAG(TMP_TAG),
  613. MountPoint.Length +
  614. 2*sizeof(WCHAR));
  615. if (!VolumeMountPoint) {
  616. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  617. b = FALSE;
  618. }
  619. else {
  620. RtlCopyMemory(VolumeMountPoint, MountPoint.Buffer,
  621. MountPoint.Length);
  622. VolumeMountPoint[MountPoint.Length/sizeof(WCHAR)] = 0;
  623. if (MountPoint.Buffer[MountPoint.Length/sizeof(WCHAR) - 1] != '\\') {
  624. VolumeMountPoint[MountPoint.Length/sizeof(WCHAR)] = '\\';
  625. VolumeMountPoint[MountPoint.Length/sizeof(WCHAR) + 1] = 0;
  626. }
  627. //
  628. // The volume name should be like "\\?\Volume{guid}\"
  629. //
  630. VolumeName.Buffer[1] = '\\';
  631. b = SetVolumeMountPointW(
  632. VolumeMountPoint,
  633. VolumeName.Buffer
  634. );
  635. RtlFreeHeap(RtlProcessHeap(), 0, VolumeMountPoint);
  636. VolumeName.Buffer[1] = '?';
  637. }
  638. }
  639. else {
  640. //
  641. // Copy the directory junction and be done.
  642. //
  643. b = TRUE;
  644. Status = NtFsControlFile(
  645. DestFile,
  646. NULL,
  647. NULL,
  648. NULL,
  649. &IoStatusBlock,
  650. FSCTL_SET_REPARSE_POINT,
  651. ReparseBuffer,
  652. FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) + ReparseBufferHeader->ReparseDataLength,
  653. NULL, // Output buffer
  654. 0 // Output buffer length
  655. );
  656. }
  657. //
  658. // Free the buffer.
  659. //
  660. RtlFreeHeap(RtlProcessHeap(), 0, ReparseBuffer);
  661. //
  662. // Close all files and return appropriatelly.
  663. //
  664. NtClose(SourceFile);
  665. NtClose(DestFile);
  666. if ( !b ) {
  667. //
  668. // No need to set the last error as SetVolumeMountPointW has done it.
  669. //
  670. return FALSE;
  671. }
  672. if ( !NT_SUCCESS( Status ) ) {
  673. BaseSetLastNTError(Status);
  674. return FALSE;
  675. }
  676. return TRUE;
  677. //
  678. // The source directory was a name grafting directory.
  679. // No data streams are copied.
  680. //
  681. }
  682. //
  683. // Attempt to determine whether or not this file has any alternate
  684. // data streams associated with it. If it does, attempt to copy each
  685. // to the output file. If any copy fails, simply drop the error on
  686. // the floor, and continue.
  687. //
  688. StreamInfoSize = 4096;
  689. CopySize = 4096;
  690. do {
  691. StreamInfoBase = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), StreamInfoSize);
  692. if ( !StreamInfoBase ) {
  693. BaseMarkFileForDelete(DestFile, BasicInfo.FileAttributes);
  694. BaseSetLastNTError(STATUS_NO_MEMORY);
  695. b = FALSE;
  696. break;
  697. }
  698. Status = NtQueryInformationFile(
  699. SourceFile,
  700. &IoStatusBlock,
  701. (PVOID) StreamInfoBase,
  702. StreamInfoSize,
  703. FileStreamInformation
  704. );
  705. if ( !NT_SUCCESS(Status) ) {
  706. RtlFreeHeap(RtlProcessHeap(), 0, StreamInfoBase);
  707. StreamInfoBase = NULL;
  708. StreamInfoSize *= 2;
  709. }
  710. } while ( Status == STATUS_BUFFER_OVERFLOW ||
  711. Status == STATUS_BUFFER_TOO_SMALL );
  712. //
  713. // Directories do not always have a stream
  714. //
  715. if ( NT_SUCCESS(Status) && IoStatusBlock.Information ) {
  716. StreamInfo = StreamInfoBase;
  717. for (;;) {
  718. DWORD DestFileFsAttributes = 0;
  719. //
  720. // Build a string descriptor for the name of the stream.
  721. //
  722. StreamName.Buffer = &StreamInfo->StreamName[0];
  723. StreamName.Length = (USHORT) StreamInfo->StreamNameLength;
  724. StreamName.MaximumLength = StreamName.Length;
  725. //
  726. // Open the source stream.
  727. //
  728. InitializeObjectAttributes(
  729. &Obja,
  730. &StreamName,
  731. 0,
  732. SourceFile,
  733. NULL
  734. );
  735. Status = NtCreateFile(
  736. &StreamHandle,
  737. GENERIC_READ | SYNCHRONIZE,
  738. &Obja,
  739. &IoStatusBlock,
  740. NULL,
  741. 0,
  742. FILE_SHARE_READ,
  743. FILE_OPEN,
  744. FILE_SYNCHRONOUS_IO_NONALERT,
  745. NULL,
  746. 0
  747. );
  748. if ( NT_SUCCESS(Status) ) {
  749. for ( i = 0; i < (ULONG) StreamName.Length >> 1; i++ ) {
  750. FileName[i] = StreamName.Buffer[i];
  751. }
  752. FileName[i] = L'\0';
  753. OutputStream = (HANDLE)NULL;
  754. Options = 0;
  755. b = BaseCopyStream(
  756. NULL,
  757. StreamHandle,
  758. GENERIC_READ | SYNCHRONIZE,
  759. FileName,
  760. DestFile,
  761. &StreamInfo->StreamSize,
  762. &Options,
  763. &OutputStream,
  764. &CopySize,
  765. &CopyFileContext,
  766. (LPRESTART_STATE)NULL,
  767. (BOOL)FALSE,
  768. (DWORD)0,
  769. &DestFileFsAttributes
  770. );
  771. NtClose(StreamHandle);
  772. if ( OutputStream ) {
  773. NtClose(OutputStream);
  774. }
  775. }
  776. if ( StreamInfo->NextEntryOffset ) {
  777. StreamInfo = (PFILE_STREAM_INFORMATION)((PCHAR) StreamInfo + StreamInfo->NextEntryOffset);
  778. }
  779. else {
  780. break;
  781. }
  782. }
  783. }
  784. if ( StreamInfoBase ) {
  785. RtlFreeHeap(RtlProcessHeap(), 0, StreamInfoBase);
  786. }
  787. b = TRUE;
  788. }
  789. NtClose(SourceFile);
  790. if ( DestFile ) {
  791. NtClose(DestFile);
  792. }
  793. return b;
  794. }
  795. BOOL
  796. APIENTRY
  797. RemoveDirectoryA(
  798. LPCSTR lpPathName
  799. )
  800. /*++
  801. Routine Description:
  802. ANSI thunk to RemoveDirectoryW
  803. --*/
  804. {
  805. PUNICODE_STRING Unicode;
  806. Unicode = Basep8BitStringToStaticUnicodeString( lpPathName );
  807. if (Unicode == NULL) {
  808. return FALSE;
  809. }
  810. return ( RemoveDirectoryW((LPCWSTR)Unicode->Buffer) );
  811. }
  812. BOOL
  813. APIENTRY
  814. RemoveDirectoryW(
  815. LPCWSTR lpPathName
  816. )
  817. /*++
  818. Routine Description:
  819. An existing directory can be removed using RemoveDirectory.
  820. This API causes a directory with the specified pathname to be
  821. deleted. The directory must be empty before this call can succeed.
  822. This call is similar to DOS (int 21h, function 3Ah) and OS/2's
  823. DosDeleteDir.
  824. Arguments:
  825. lpPathName - Supplies the pathname of the directory to be removed.
  826. The path must specify an empty directory to which the caller has
  827. delete access.
  828. Return Value:
  829. TRUE - The operation was successful.
  830. FALSE/NULL - The operation failed. Extended error status is available
  831. using GetLastError.
  832. --*/
  833. {
  834. NTSTATUS Status;
  835. OBJECT_ATTRIBUTES Obja;
  836. HANDLE Handle;
  837. UNICODE_STRING FileName;
  838. IO_STATUS_BLOCK IoStatusBlock;
  839. FILE_DISPOSITION_INFORMATION Disposition;
  840. BOOLEAN TranslationStatus;
  841. RTL_RELATIVE_NAME RelativeName;
  842. PVOID FreeBuffer;
  843. BOOLEAN IsNameGrafting = FALSE;
  844. FILE_ATTRIBUTE_TAG_INFORMATION FileTagInformation;
  845. PREPARSE_DATA_BUFFER reparse;
  846. BOOL b;
  847. DWORD bytes;
  848. UNICODE_STRING mountName;
  849. PWCHAR volumeMountPoint;
  850. TranslationStatus = RtlDosPathNameToNtPathName_U(
  851. lpPathName,
  852. &FileName,
  853. NULL,
  854. &RelativeName
  855. );
  856. if ( !TranslationStatus ) {
  857. SetLastError(ERROR_PATH_NOT_FOUND);
  858. return FALSE;
  859. }
  860. FreeBuffer = FileName.Buffer;
  861. if ( RelativeName.RelativeName.Length ) {
  862. FileName = *(PUNICODE_STRING)&RelativeName.RelativeName;
  863. }
  864. else {
  865. RelativeName.ContainingDirectory = NULL;
  866. }
  867. InitializeObjectAttributes(
  868. &Obja,
  869. &FileName,
  870. OBJ_CASE_INSENSITIVE,
  871. RelativeName.ContainingDirectory,
  872. NULL
  873. );
  874. //
  875. // Open the directory for delete access.
  876. // Inhibit the reparse behavior using FILE_OPEN_REPARSE_POINT.
  877. //
  878. Status = NtOpenFile(
  879. &Handle,
  880. DELETE | SYNCHRONIZE | FILE_READ_ATTRIBUTES,
  881. &Obja,
  882. &IoStatusBlock,
  883. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  884. FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT
  885. );
  886. if ( !NT_SUCCESS(Status) ) {
  887. //
  888. // Back level file systems may not support reparse points and thus not
  889. // support symbolic links.
  890. // We infer this is the case when the Status is STATUS_INVALID_PARAMETER.
  891. //
  892. if ( Status == STATUS_INVALID_PARAMETER ) {
  893. //
  894. // Re-open not inhibiting the reparse behavior and not needing to read the attributes.
  895. //
  896. Status = NtOpenFile(
  897. &Handle,
  898. DELETE | SYNCHRONIZE,
  899. &Obja,
  900. &IoStatusBlock,
  901. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  902. FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
  903. );
  904. if ( !NT_SUCCESS(Status) ) {
  905. RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
  906. BaseSetLastNTError(Status);
  907. return FALSE;
  908. }
  909. }
  910. else {
  911. RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
  912. BaseSetLastNTError(Status);
  913. return FALSE;
  914. }
  915. }
  916. else {
  917. //
  918. // If we found a reparse point that is not a name grafting operation,
  919. // either a symbolic link or a mount point, we re-open without
  920. // inhibiting the reparse behavior.
  921. //
  922. Status = NtQueryInformationFile(
  923. Handle,
  924. &IoStatusBlock,
  925. (PVOID) &FileTagInformation,
  926. sizeof(FileTagInformation),
  927. FileAttributeTagInformation
  928. );
  929. if ( !NT_SUCCESS(Status) ) {
  930. //
  931. // Not all File Systems implement all information classes.
  932. // The value STATUS_INVALID_PARAMETER is returned when a non-supported
  933. // information class is requested to a back-level File System. As all the
  934. // parameters to NtQueryInformationFile are correct, we can infer that
  935. // we found a back-level system.
  936. //
  937. // If FileAttributeTagInformation is not implemented, we assume that
  938. // the file at hand is not a reparse point.
  939. //
  940. if ( (Status != STATUS_NOT_IMPLEMENTED) &&
  941. (Status != STATUS_INVALID_PARAMETER) ) {
  942. RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
  943. NtClose(Handle);
  944. BaseSetLastNTError(Status);
  945. return FALSE;
  946. }
  947. }
  948. if ( NT_SUCCESS(Status) &&
  949. (FileTagInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ) {
  950. if ( FileTagInformation.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT ) {
  951. //
  952. // If this is a volume mount point then fail with
  953. // "directory not empty".
  954. //
  955. reparse = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG),
  956. MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
  957. if (!reparse) {
  958. RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
  959. NtClose(Handle);
  960. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  961. return FALSE;
  962. }
  963. b = DeviceIoControl(Handle, FSCTL_GET_REPARSE_POINT, NULL, 0,
  964. reparse, MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
  965. &bytes, NULL);
  966. if (b) {
  967. mountName.Length = mountName.MaximumLength =
  968. reparse->MountPointReparseBuffer.SubstituteNameLength;
  969. mountName.Buffer = (PWCHAR)
  970. ((PCHAR) reparse->MountPointReparseBuffer.PathBuffer +
  971. reparse->MountPointReparseBuffer.SubstituteNameOffset);
  972. if (MOUNTMGR_IS_VOLUME_NAME(&mountName)) {
  973. RtlInitUnicodeString(&mountName, lpPathName);
  974. volumeMountPoint = RtlAllocateHeap(RtlProcessHeap(),
  975. MAKE_TAG(TMP_TAG),
  976. mountName.Length +
  977. 2*sizeof(WCHAR));
  978. if (!volumeMountPoint) {
  979. RtlFreeHeap(RtlProcessHeap(), 0, reparse);
  980. RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
  981. NtClose(Handle);
  982. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  983. return FALSE;
  984. }
  985. RtlCopyMemory(volumeMountPoint, mountName.Buffer,
  986. mountName.Length);
  987. volumeMountPoint[mountName.Length/sizeof(WCHAR)] = 0;
  988. if (mountName.Buffer[mountName.Length/sizeof(WCHAR) - 1] != '\\') {
  989. volumeMountPoint[mountName.Length/sizeof(WCHAR)] = '\\';
  990. volumeMountPoint[mountName.Length/sizeof(WCHAR) + 1] = 0;
  991. }
  992. DeleteVolumeMountPointW(volumeMountPoint);
  993. RtlFreeHeap(RtlProcessHeap(), 0, volumeMountPoint);
  994. }
  995. }
  996. RtlFreeHeap(RtlProcessHeap(), 0, reparse);
  997. IsNameGrafting = TRUE;
  998. }
  999. }
  1000. if ( NT_SUCCESS(Status) &&
  1001. (FileTagInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
  1002. !IsNameGrafting) {
  1003. //
  1004. // Re-open without inhibiting the reparse behavior and not needing to
  1005. // read the attributes.
  1006. //
  1007. NtClose(Handle);
  1008. Status = NtOpenFile(
  1009. &Handle,
  1010. DELETE | SYNCHRONIZE,
  1011. &Obja,
  1012. &IoStatusBlock,
  1013. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  1014. FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
  1015. );
  1016. if ( !NT_SUCCESS(Status) ) {
  1017. //
  1018. // When the FS Filter is absent, delete it any way.
  1019. //
  1020. if ( Status == STATUS_IO_REPARSE_TAG_NOT_HANDLED ) {
  1021. //
  1022. // We re-open (possible 3rd open) for delete access inhibiting the reparse behavior.
  1023. //
  1024. Status = NtOpenFile(
  1025. &Handle,
  1026. DELETE | SYNCHRONIZE,
  1027. &Obja,
  1028. &IoStatusBlock,
  1029. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  1030. FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT
  1031. );
  1032. }
  1033. if ( !NT_SUCCESS(Status) ) {
  1034. RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
  1035. BaseSetLastNTError(Status);
  1036. return FALSE;
  1037. }
  1038. }
  1039. }
  1040. }
  1041. RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
  1042. //
  1043. // Delete the file
  1044. //
  1045. #undef DeleteFile
  1046. Disposition.DeleteFile = TRUE;
  1047. Status = NtSetInformationFile(
  1048. Handle,
  1049. &IoStatusBlock,
  1050. &Disposition,
  1051. sizeof(Disposition),
  1052. FileDispositionInformation
  1053. );
  1054. NtClose(Handle);
  1055. if ( NT_SUCCESS(Status) ) {
  1056. return TRUE;
  1057. }
  1058. else {
  1059. BaseSetLastNTError(Status);
  1060. return FALSE;
  1061. }
  1062. }