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.

2143 lines
58 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. flinks.c
  5. Abstract:
  6. This module implements a utlity that creates, deletes, renames, lists
  7. symbolic links.
  8. Author:
  9. Felipe Cabrera [cabrera] 17-October-1996
  10. Revision History:
  11. --*/
  12. #include "flinks.h"
  13. //
  14. // We can have: flinks ?
  15. // or, flinks Path1 Path2
  16. // We can also have: flinks [/dyv] Path2
  17. // or, flinks [/cmrv] Path1 Path2
  18. //
  19. unsigned LinkType = IO_REPARSE_TAG_SYMBOLIC_LINK;
  20. void __cdecl
  21. main(
  22. int argc,
  23. char **argv
  24. )
  25. {
  26. NTSTATUS Status;
  27. ATTRIBUTE_TYPE Attributes1, // Attributes of Path1
  28. Attributes2; // Attributes of Path2
  29. char *Path1, // Will point to the full path name.
  30. *Path2; // Will point to the full path name.
  31. Attributes1 = GetFileAttributeError;
  32. Attributes2 = GetFileAttributeError;
  33. //
  34. // Check argument validity and set global action flags.
  35. //
  36. ParseArgs( argc, argv );
  37. //
  38. // Do the actions in turn.
  39. //
  40. if (fCopy) {
  41. //
  42. // Check for the existence of Path1 getting its attributes.
  43. //
  44. IF_GET_ATTR_FAILS(argv[argc - 2], Attributes1) {
  45. //
  46. // Path1 does not exist, hence we cannot copy it.
  47. //
  48. fprintf( stderr, "Cannot copy Path1, it does not exist.\n" );
  49. exit (1);
  50. }
  51. //
  52. // Path1 needs to be a reparse point to copy a symbolic link.
  53. //
  54. if (Attributes1 & FILE_ATTRIBUTE_REPARSE_POINT) {
  55. //
  56. // If Path2 does not exist, create it.
  57. //
  58. IF_GET_ATTR_FAILS(argv[argc - 1], Attributes2) {
  59. //
  60. // Need to create this file preserving the kind (file or directory).
  61. //
  62. Status = CreateEmptyFile( argv[argc - 1], Attributes1, fVerbose );
  63. if (!NT_SUCCESS( Status )) {
  64. fprintf( stderr, "Cannot create file for symbolic link. Status %x\n", Status );
  65. exit (1);
  66. }
  67. }
  68. //
  69. // Copy into Path2 the symbolic link in Path1.
  70. // Build the full path for Path1 and Path2 and call the copy routine.
  71. //
  72. if ((Path1 = _strlwr(_fullpath( NULL, argv[argc - 2], 0))) == NULL) {
  73. Path1 = argv[argc - 2];
  74. }
  75. if ((Path2 = _strlwr(_fullpath( NULL, argv[argc - 1], 0))) == NULL) {
  76. Path2 = argv[argc - 1];
  77. }
  78. Status = CopySymbolicLink( Path1, Path2, Attributes1, fVerbose );
  79. if (!NT_SUCCESS( Status )) {
  80. fprintf( stderr, "Cannot copy symbolic link. Status %x\n", Status );
  81. }
  82. } else {
  83. fprintf( stderr, "Cannot copy, Path1 is not a symbolic link.\n" );
  84. }
  85. exit (1);
  86. } // fCopy
  87. if (fCreate) {
  88. //
  89. // Check for the existence of Path1 getting its attributes.
  90. //
  91. IF_GET_ATTR_FAILS(argv[argc - 2], Attributes1) {
  92. //
  93. // Need to create this file object. As default we create it as a file.
  94. //
  95. if (fAlternateCreateDefault) {
  96. Attributes1 = FILE_ATTRIBUTE_DIRECTORY;
  97. } else {
  98. //
  99. // We try to create it with the same characteristic of the target,
  100. // when we are able to reach the target. Otherwise we use a file
  101. // as default.
  102. //
  103. Attributes2 = 0xFFFFFFFF;
  104. IF_GET_ATTR_FAILS(argv[argc - 1], Attributes2) {
  105. Attributes1 = FILE_ATTRIBUTE_NORMAL;
  106. } else {
  107. if (Attributes2 & FILE_ATTRIBUTE_DIRECTORY) {
  108. Attributes1 = FILE_ATTRIBUTE_DIRECTORY;
  109. } else {
  110. Attributes1 = FILE_ATTRIBUTE_NORMAL;
  111. }
  112. }
  113. }
  114. Status = CreateEmptyFile( argv[argc - 2], Attributes1, fVerbose );
  115. if (!NT_SUCCESS( Status )) {
  116. fprintf( stderr, "Cannot create file for symbolic link. Status %x\n", Status );
  117. Attributes1 = FILE_ATTRIBUTE_REPARSE_POINT;
  118. }
  119. }
  120. //
  121. // Path1 needs to be a non-reparse point to create a symbolic link.
  122. //
  123. if (!(Attributes1 & FILE_ATTRIBUTE_REPARSE_POINT)) {
  124. //
  125. // Build the full path for Path1 and Path2.
  126. //
  127. if ((Path1 = _strlwr(_fullpath( NULL, argv[argc - 2], 0))) == NULL) {
  128. Path1 = argv[argc - 2];
  129. }
  130. // if ((Path2 = _strlwr(_fullpath( NULL, argv[argc - 1], 0))) == NULL) {
  131. Path2 = argv[argc - 1];
  132. // }
  133. Status = CreateSymbolicLink( Path1, Path2, Attributes1, fVerbose );
  134. if (!NT_SUCCESS( Status )) {
  135. fprintf( stderr, "Cannot create symbolic link. Status %x\n", Status );
  136. }
  137. } else {
  138. fprintf( stderr, "Cannot create, Path1 is a symbolic link.\n" );
  139. }
  140. exit (1);
  141. } // fCreate
  142. if (fDelete) {
  143. //
  144. // Check existence of Path2 path getting the attributes.
  145. //
  146. IF_GET_ATTR_FAILS(argv[argc - 1], Attributes2) {
  147. fprintf( stderr, "Could not find %s (error = %d)\n", argv[argc - 1], GetLastError() );
  148. exit(1);
  149. }
  150. //
  151. // Path2 needs to be a reparse point to delete a symbolic link.
  152. //
  153. if (Attributes2 & FILE_ATTRIBUTE_REPARSE_POINT) {
  154. //
  155. // Build the full path for Path2, the only path name.
  156. //
  157. if ((Path2 = _strlwr(_fullpath( NULL, argv[argc - 1], 0))) == NULL) {
  158. Path2 = argv[argc - 1];
  159. }
  160. Status = DeleteSymbolicLink( Path2, Attributes2, fVerbose );
  161. if (!NT_SUCCESS( Status )) {
  162. fprintf( stderr, "Cannot delete symbolic link. Status %x\n", Status );
  163. }
  164. } else {
  165. fprintf( stderr, "Cannot delete, Path2 is not a symbolic link.\n" );
  166. }
  167. exit (1);
  168. } // fDelete
  169. if (fDisplay) {
  170. //
  171. // Check existence of Path2 path getting the attributes.
  172. //
  173. IF_GET_ATTR_FAILS(argv[argc - 1], Attributes2) {
  174. fprintf( stderr, "Could not find %s (error = %d)\n", argv[argc - 1], GetLastError() );
  175. exit(1);
  176. }
  177. //
  178. // Path2 needs to be a reparse point to display a symbolic link.
  179. //
  180. if (Attributes2 & FILE_ATTRIBUTE_REPARSE_POINT) {
  181. //
  182. // Build the full path for Path2, the only path name.
  183. //
  184. if ((Path2 = _strlwr(_fullpath( NULL, argv[argc - 1], 0))) == NULL) {
  185. Path2 = argv[argc - 1];
  186. }
  187. Status = DisplaySymbolicLink( Path2, Attributes2, fVerbose );
  188. if (!NT_SUCCESS( Status )) {
  189. fprintf( stderr, "Cannot display symbolic link. Status %x\n", Status );
  190. }
  191. } else {
  192. fprintf( stderr, "Cannot display, Path2 is not a symbolic link.\n" );
  193. }
  194. exit (1);
  195. } // fDisplay
  196. if (fModify) {
  197. //
  198. // Check for the existence of Path1 getting its attributes.
  199. //
  200. IF_GET_ATTR_FAILS(argv[argc - 2], Attributes1) {
  201. fprintf( stderr, "Could not find Path1 %s (error = %d)\n", argv[argc - 2], GetLastError() );
  202. exit(1);
  203. }
  204. //
  205. // Path1 needs to be a reparse point to modify a symbolic link.
  206. //
  207. if (Attributes1 & FILE_ATTRIBUTE_REPARSE_POINT) {
  208. //
  209. // Build the full path for Path1 and Path2.
  210. //
  211. if ((Path1 = _strlwr(_fullpath( NULL, argv[argc - 2], 0))) == NULL) {
  212. Path1 = argv[argc - 2];
  213. }
  214. if ((Path2 = _strlwr(_fullpath( NULL, argv[argc - 1], 0))) == NULL) {
  215. Path2 = argv[argc - 1];
  216. }
  217. Status = CreateSymbolicLink( Path1, Path2, Attributes1, fVerbose );
  218. if (!NT_SUCCESS( Status )) {
  219. fprintf( stderr, "Cannot modify symbolic link. Status %x\n", Status );
  220. }
  221. } else {
  222. fprintf( stderr, "Cannot modify, Path1 is not a symbolic link.\n" );
  223. }
  224. exit (1);
  225. } // fModify
  226. if (fRename) {
  227. //
  228. // Check for the existence of Path1 getting its attributes.
  229. //
  230. IF_GET_ATTR_FAILS(argv[argc - 2], Attributes1) {
  231. fprintf( stderr, "Could not find Path1 %s (error = %d)\n", argv[argc - 2], GetLastError() );
  232. exit(1);
  233. }
  234. //
  235. // Path1 needs to be a reparse point to rename a symbolic link.
  236. //
  237. if (Attributes1 & FILE_ATTRIBUTE_REPARSE_POINT) {
  238. //
  239. // Build the full path for Path1 and Path2.
  240. //
  241. if ((Path1 = _strlwr(_fullpath( NULL, argv[argc - 2], 0))) == NULL) {
  242. Path1 = argv[argc - 2];
  243. }
  244. if ((Path2 = _strlwr(_fullpath( NULL, argv[argc - 1], 0))) == NULL) {
  245. Path2 = argv[argc - 1];
  246. }
  247. Status = RenameSymbolicLink( Path1, Path2, Attributes1, fVerbose );
  248. if (!NT_SUCCESS( Status )) {
  249. fprintf( stderr, "Cannot rename symbolic link. Status %x\n", Status );
  250. }
  251. } else {
  252. fprintf( stderr, "Cannot rename, Path1 is not a symbolic link.\n" );
  253. }
  254. exit (1);
  255. } // fRename
  256. //
  257. // We should never go through here ...
  258. //
  259. fprintf( stderr, "flinks : NO ACTION WAS PERFORMED!\n" );
  260. } // main
  261. void
  262. ParseArgs(
  263. int argc,
  264. char *argv[]
  265. )
  266. /*++
  267. Routine Description:
  268. Parses the input setting global flags.
  269. Return Value:
  270. void - no return.
  271. --*/
  272. {
  273. int ArgCount,
  274. FlagCount;
  275. ArgCount = 1;
  276. FlagCount = 0;
  277. //
  278. // Check that the number of arguments is two or more.
  279. //
  280. if (argc < 2) {
  281. fprintf( stderr, "Too few arguments.\n" );
  282. Usage();
  283. }
  284. do {
  285. if (IsFlag( argv[ArgCount] )) {
  286. //
  287. // We want all flags to be immediatelly after the command name flinks and
  288. // before all other arguments.
  289. //
  290. if ((ArgCount > 1) && (FlagCount == 0)) {
  291. fprintf(stderr, "Flags need to precede the path arguments.\n" );
  292. Usage();
  293. }
  294. //
  295. // Verify flag consistency.
  296. //
  297. if ((fCopy) && (fModify)) {
  298. fprintf(stderr, "Cannot do both copy and modify.\n" );
  299. Usage();
  300. }
  301. if ((fCopy) && (fRename)) {
  302. fprintf(stderr, "Cannot do both copy and rename.\n" );
  303. Usage();
  304. }
  305. if ((fCopy) && (fDelete)) {
  306. fprintf(stderr, "Cannot do both copy and delete.\n" );
  307. Usage();
  308. }
  309. if ((fDelete) && (fModify)) {
  310. fprintf(stderr, "Cannot do both delete and modify.\n" );
  311. Usage();
  312. }
  313. if ((fDelete) && (fRename)) {
  314. fprintf(stderr, "Cannot do both delete and rename.\n" );
  315. Usage();
  316. }
  317. if ((fModify) && (fRename)) {
  318. fprintf(stderr, "Cannot do both modify and rename.\n" );
  319. Usage();
  320. }
  321. //
  322. // Account for this flag.
  323. //
  324. FlagCount++;
  325. //
  326. // (IsFlag( argv[ArgCount] ))
  327. //
  328. } else {
  329. //
  330. // No flags were passed as this argument to flinks.
  331. //
  332. // When no flags are present the only valid call is: flinks path1 path2
  333. //
  334. // When flags are present these are valid: flinks -flags- path1
  335. // ' flinks -flags- path1 path2
  336. //
  337. // For starters we only check that we have the correct number.
  338. // We should also check that no more flags are present further along the way ...
  339. //
  340. if (FlagCount == 0) {
  341. if (argc == 2) {
  342. fprintf( stderr, "Too few arguments.\n" );
  343. Usage();
  344. }
  345. if (argc != 3) {
  346. fprintf( stderr, "Wrong number of arguments with flags not preceding path arguments.\n" );
  347. Usage();
  348. }
  349. } else {
  350. if (ArgCount + 3 <= argc) {
  351. fprintf( stderr, "Too many arguments after flags.\n" );
  352. Usage();
  353. }
  354. }
  355. }
  356. } while (ArgCount++ < argc - 1);
  357. //
  358. // When there is only one path argument we have more constraints:
  359. //
  360. if ((ArgCount - FlagCount) == 2) {
  361. if (!fDelete &&
  362. !fDisplay
  363. ) {
  364. fprintf( stderr, "One path argument requires the delete or display flag.\n" );
  365. Usage();
  366. }
  367. }
  368. //
  369. // For delete or display we can only have one path name.
  370. //
  371. if (fDelete ||
  372. fDisplay
  373. ) {
  374. if ((ArgCount - FlagCount) != 2) {
  375. fprintf( stderr, "Delete or display have only one path argument.\n" );
  376. Usage();
  377. }
  378. }
  379. //
  380. // Set fCreate when there are no flags or no actions.
  381. //
  382. if (FlagCount == 0) {
  383. fCreate = TRUE;
  384. }
  385. if (!fCopy &&
  386. !fDelete &&
  387. !fModify &&
  388. !fRename &&
  389. !fDisplay
  390. ) {
  391. fCreate = TRUE;
  392. }
  393. //
  394. // Every argument is correct.
  395. // Print appropriate verbose messages.
  396. //
  397. if (fVVerbose) {
  398. fprintf( stdout, "\n" );
  399. fprintf( stdout, "Very verbose is set.\n" );
  400. }
  401. if (fVerbose) {
  402. if (!fVVerbose) {
  403. fprintf( stdout, "\n" );
  404. }
  405. if (fCopy) {
  406. fprintf( stdout, "Will do verbose copy.\n" );
  407. } else if (fCreate) {
  408. fprintf( stdout, "Will do verbose create.\n" );
  409. } else if (fDelete) {
  410. fprintf( stdout, "Will do verbose delete.\n" );
  411. } else if (fDisplay) {
  412. fprintf( stdout, "Will do verbose display.\n" );
  413. } else if (fModify) {
  414. fprintf( stdout, "Will do verbose modify.\n" );
  415. } else if (fRename) {
  416. fprintf( stdout, "Will do verbose rename.\n" );
  417. }
  418. }
  419. } // ParseArgs
  420. BOOLEAN
  421. IsFlag(
  422. char *argv
  423. )
  424. {
  425. char *TmpArg;
  426. if ((*argv == '/') || (*argv == '-')) {
  427. if (strchr( argv, '?' ))
  428. Usage();
  429. TmpArg = _strlwr(argv);
  430. while (*++TmpArg != '\0') {
  431. switch (*TmpArg) {
  432. case 'a' :
  433. case 'A' :
  434. fAlternateCreateDefault = TRUE;
  435. break;
  436. case 'c' :
  437. case 'C' :
  438. fCopy = TRUE;
  439. break;
  440. case 'd' :
  441. case 'D' :
  442. fDelete = TRUE;
  443. break;
  444. case 'm' :
  445. case 'M' :
  446. fModify = TRUE;
  447. break;
  448. case 'r' :
  449. case 'R' :
  450. fRename = TRUE;
  451. break;
  452. case 's' :
  453. case 'S' :
  454. LinkType = IO_REPARSE_TAG_SIS;
  455. break;
  456. case 'w' :
  457. case 'W' :
  458. fVVerbose = TRUE;
  459. case 'v' :
  460. case 'V' :
  461. fVerbose = TRUE;
  462. break;
  463. case 'y' :
  464. case 'Y' :
  465. fDisplay = TRUE;
  466. break;
  467. case '/' :
  468. case '-' :
  469. break;
  470. default :
  471. fprintf( stderr, "Don't know flag(s) %s\n", argv );
  472. Usage();
  473. }
  474. }
  475. }
  476. else return FALSE;
  477. return TRUE;
  478. } // IsFlag
  479. void
  480. Usage( void )
  481. {
  482. fprintf( stderr, "\n" );
  483. fprintf( stderr, "Usage: flink [/acdmrvy?] [Path1] Path2 \n" );
  484. fprintf( stderr, " flink Path1 Path2 establishes at Path1 a symbolic link to Path2.\n" );
  485. fprintf( stderr, " The file at Path1 is created if it does not exist. \n" );
  486. fprintf( stderr, " /a sets the alternate default of creating a directory \n" );
  487. fprintf( stderr, " /c copies in Path2 the symbolic link in Path1 \n" );
  488. fprintf( stderr, " /d deletes the symbolic link in Path2 \n" );
  489. fprintf( stderr, " /m modifies the symbolic link Path1 to Path2 \n" );
  490. fprintf( stderr, " /r renames the symbolic link Path1 to Path2 \n" );
  491. fprintf( stderr, " /s creates a SIS link rather than a symbolic link \n" );
  492. fprintf( stderr, " /v prints verbose output \n" );
  493. fprintf( stderr, " /w prints very verbose output \n" );
  494. fprintf( stderr, " /y displays the symbolic link in Path2 \n" );
  495. fprintf( stderr, " /? prints this message \n" );
  496. exit(1);
  497. } // Usage
  498. NTSTATUS
  499. CreateSymbolicLink(
  500. CHAR *SourceName,
  501. CHAR *DestinationName,
  502. ATTRIBUTE_TYPE FileAttributes,
  503. BOOLEAN VerboseFlag
  504. )
  505. /*++
  506. Routine Description:
  507. Builds a symbolic link between SourceName and DestinationName.
  508. Opens the file named by SourceName and sets a reparse point of type symbolic link
  509. that points to DestinationName. No checks whatsoever are made in regards to the
  510. destination.
  511. If the symbolic link already exists, this routine will overwrite it.
  512. Return Value:
  513. NTSTATUS - returns the appropriate NT return code.
  514. --*/
  515. {
  516. NTSTATUS Status = STATUS_SUCCESS;
  517. HANDLE FileHandle;
  518. ULONG OpenOptions;
  519. UNICODE_STRING uSourceName,
  520. uDestinationName,
  521. uNewName,
  522. uOldName;
  523. IO_STATUS_BLOCK IoStatusBlock;
  524. OBJECT_ATTRIBUTES ObjectAttributes;
  525. PREPARSE_DATA_BUFFER ReparseBufferHeader = NULL;
  526. UCHAR ReparseBuffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
  527. //
  528. // Allocate and initialize Unicode strings.
  529. //
  530. RtlCreateUnicodeStringFromAsciiz( &uSourceName, SourceName );
  531. RtlCreateUnicodeStringFromAsciiz( &uDestinationName, DestinationName );
  532. RtlDosPathNameToNtPathName_U(
  533. uSourceName.Buffer,
  534. &uOldName,
  535. NULL,
  536. NULL );
  537. //
  538. // Open the existing (SourceName) pathname.
  539. // Notice that symbolic links in the path they are traversed silently.
  540. //
  541. InitializeObjectAttributes(
  542. &ObjectAttributes,
  543. &uOldName,
  544. OBJ_CASE_INSENSITIVE,
  545. NULL,
  546. NULL );
  547. if (VerboseFlag) {
  548. fprintf( stdout, "Will set symbolic link from: %Z\n", &uOldName );
  549. }
  550. //
  551. // Make sure that we call open with the appropriate flags for:
  552. //
  553. // (1) directory versus non-directory
  554. //
  555. OpenOptions = FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT;
  556. if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  557. OpenOptions |= FILE_DIRECTORY_FILE;
  558. } else {
  559. OpenOptions |= FILE_NON_DIRECTORY_FILE;
  560. }
  561. Status = NtOpenFile(
  562. &FileHandle,
  563. FILE_READ_DATA | SYNCHRONIZE,
  564. &ObjectAttributes,
  565. &IoStatusBlock,
  566. SHARE_ALL,
  567. OpenOptions );
  568. if (!NT_SUCCESS( Status )) {
  569. RtlFreeUnicodeString( &uSourceName );
  570. RtlFreeUnicodeString( &uDestinationName );
  571. fprintf( stderr, "Open failed %s\n", SourceName );
  572. return Status;
  573. }
  574. //
  575. // Verify that this is an empty file object:
  576. // (a) If it is a file then it should not have data in the unnamed data stream
  577. // nor should it have any named data streams.
  578. // (b) If it is a directory, it has no entries.
  579. // This case does not require code as the NTFS reparse point mechanism
  580. // checks for it.
  581. //
  582. {
  583. FILE_STANDARD_INFORMATION StandardInformation;
  584. PFILE_STREAM_INFORMATION StreamInformation;
  585. CHAR Buffer[2048];
  586. Status = NtQueryInformationFile(
  587. FileHandle,
  588. &IoStatusBlock,
  589. &StandardInformation,
  590. sizeof ( FILE_STANDARD_INFORMATION ),
  591. FileStandardInformation );
  592. if (!NT_SUCCESS( Status )) {
  593. RtlFreeUnicodeString( &uSourceName );
  594. RtlFreeUnicodeString( &uDestinationName );
  595. fprintf( stderr, "NtQueryInformation for standard information to %Z failed %x\n", &uSourceName, Status );
  596. return Status;
  597. }
  598. if (StandardInformation.EndOfFile.LowPart > 0) {
  599. //
  600. // The unnamed data stream has bytes in it.
  601. //
  602. if (VerboseFlag) {
  603. fprintf( stdout, "The unnamed data stream of %Z has eof of %d\n",
  604. &uOldName, StandardInformation.EndOfFile.LowPart );
  605. }
  606. fprintf( stderr, "Symbolic link not created. File has data.\n" );
  607. return Status;
  608. }
  609. //
  610. // Go and get the stream information.
  611. //
  612. Status = NtQueryInformationFile(
  613. FileHandle,
  614. &IoStatusBlock,
  615. Buffer,
  616. 2048,
  617. FileStreamInformation );
  618. if (!NT_SUCCESS( Status )) {
  619. RtlFreeUnicodeString( &uSourceName );
  620. RtlFreeUnicodeString( &uDestinationName );
  621. fprintf( stderr, "NtQueryInformation for streams to %Z failed %x\n",
  622. &uSourceName, Status );
  623. return Status;
  624. }
  625. //
  626. // Process the Buffer of data.
  627. //
  628. if (VerboseFlag) {
  629. fprintf( stdout, "IoStatusBlock.Status %d IoStatusBlock.Information %d\n",
  630. IoStatusBlock.Status, IoStatusBlock.Information );
  631. }
  632. StreamInformation = (PFILE_STREAM_INFORMATION)Buffer;
  633. if (VerboseFlag) {
  634. fprintf( stdout, "StreamInformation->NextEntryOffset %d StreamInformation->StreamNameLength %d\n",
  635. StreamInformation->NextEntryOffset, StreamInformation->StreamNameLength );
  636. }
  637. //
  638. // There has to be exactly one data stream, the one called ::$DATA whose
  639. // StreamNameLength is 14. If this is not the case fail the request.
  640. //
  641. if (StreamInformation->NextEntryOffset > 0) {
  642. RtlFreeUnicodeString( &uSourceName );
  643. RtlFreeUnicodeString( &uDestinationName );
  644. fprintf( stderr, "Symbolic link not created. There are named streams.\n",
  645. &uSourceName, Status );
  646. return Status;
  647. }
  648. }
  649. //
  650. // Build the appropriate target (DestinationName) name.
  651. //
  652. RtlDosPathNameToNtPathName_U(
  653. uDestinationName.Buffer,
  654. &uNewName,
  655. NULL,
  656. NULL );
  657. //
  658. // SIS hack
  659. //
  660. uNewName = uDestinationName;
  661. if (VerboseFlag) {
  662. fprintf( stdout, "Will set symbolic link to: %Z (%Z)\n", &uNewName, &uDestinationName );
  663. }
  664. //
  665. // Verify that the name is not too long for the reparse point.
  666. //
  667. if (uNewName.Length > (MAXIMUM_REPARSE_DATA_BUFFER_SIZE - FIELD_OFFSET(REPARSE_DATA_BUFFER, RDB))) {
  668. RtlFreeUnicodeString( &uSourceName );
  669. RtlFreeUnicodeString( &uDestinationName );
  670. fprintf( stderr, "Input length too long %x\n", uNewName.Length );
  671. return STATUS_IO_REPARSE_DATA_INVALID;
  672. }
  673. //
  674. // Verify that the target name:
  675. //
  676. // (1) ends in a trailing backslash only for directories
  677. // (2) does not contain more than one colon (:), thus denoting a complex name
  678. //
  679. {
  680. USHORT Index = (uNewName.Length / 2) - 1;
  681. BOOLEAN SeenFirstColon = FALSE;
  682. if (!(FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  683. if (uNewName.Buffer[Index] == L'\\') {
  684. RtlFreeUnicodeString( &uSourceName );
  685. RtlFreeUnicodeString( &uDestinationName );
  686. fprintf( stderr, "Name ends in backslash %Z\n", &uNewName );
  687. return STATUS_OBJECT_NAME_INVALID;
  688. }
  689. //
  690. // We have the name of a directory to set a symbolic link.
  691. //
  692. } else {
  693. //
  694. // Preserve the backslash that represents the root directory of a
  695. // volume. We assume that the root of a volume is denoted by an
  696. // identifier (a traditional drive letter) followed by a colon (:).
  697. //
  698. // Silently avoid (delete for practical purposes) the trailing
  699. // backlash file delimiter in all other cases.
  700. // The backslash is two bytes long.
  701. //
  702. if ((uNewName.Buffer[Index - 1] != L':') &&
  703. (uNewName.Buffer[Index] == L'\\')) {
  704. uNewName.Length -= 2;
  705. Index = (uNewName.Length / 2) - 1;
  706. }
  707. if (fVVerbose) {
  708. fprintf( stdout, "Directory name shortened to: %Z\n", &uNewName );
  709. }
  710. }
  711. while (Index > 0) {
  712. if (uNewName.Buffer[Index] == L':') {
  713. if (SeenFirstColon) {
  714. RtlFreeUnicodeString( &uSourceName );
  715. RtlFreeUnicodeString( &uDestinationName );
  716. fprintf( stderr, "More than one colon in the name %Z\n", &uNewName );
  717. return STATUS_OBJECT_NAME_INVALID;
  718. } else {
  719. SeenFirstColon = TRUE;
  720. }
  721. }
  722. Index --;
  723. }
  724. }
  725. //
  726. // Build the reparse point buffer.
  727. //
  728. ReparseBufferHeader = (PREPARSE_DATA_BUFFER)ReparseBuffer;
  729. ReparseBufferHeader->ReparseTag = LinkType;
  730. ReparseBufferHeader->ReparseDataLength = uNewName.Length;
  731. ReparseBufferHeader->Reserved = 0xcaf;
  732. RtlCopyMemory( ReparseBufferHeader->RDB,
  733. uNewName.Buffer,
  734. ReparseBufferHeader->ReparseDataLength );
  735. //
  736. // Set a symbolic link reparse point.
  737. //
  738. Status = NtFsControlFile(
  739. FileHandle,
  740. NULL,
  741. NULL,
  742. NULL,
  743. &IoStatusBlock,
  744. FSCTL_SET_REPARSE_POINT,
  745. ReparseBuffer,
  746. FIELD_OFFSET(REPARSE_DATA_BUFFER, RDB) + ReparseBufferHeader->ReparseDataLength,
  747. NULL, // Output buffer
  748. 0 ); // Output buffer length
  749. if (!NT_SUCCESS( Status )) {
  750. fprintf( stderr, "NtFsControlFile set failed %s\n", DestinationName );
  751. //
  752. // And return after cleaning up.
  753. //
  754. }
  755. //
  756. // Clean up and return.
  757. //
  758. RtlFreeUnicodeString( &uSourceName );
  759. RtlFreeUnicodeString( &uDestinationName );
  760. NtClose( FileHandle );
  761. return Status;
  762. } // CreateSymbolicLink
  763. NTSTATUS
  764. DeleteSymbolicLink(
  765. CHAR *DestinationName,
  766. ATTRIBUTE_TYPE FileAttributes,
  767. BOOLEAN VerboseFlag
  768. )
  769. /*++
  770. Routine Description:
  771. Deletes a symbolic link existing at DestinationName.
  772. DestinationName needs to denote a symbolic link.
  773. Opens the file named by DestinationName and deletes a reparse point of type
  774. symbolic link and also deletes the underlying file.
  775. If the reparse point is not a symbolic link this routine will leave it undisturbed.
  776. Return Value:
  777. NTSTATUS - returns the appropriate NT return code.
  778. --*/
  779. {
  780. NTSTATUS Status = STATUS_SUCCESS;
  781. HANDLE FileHandle;
  782. ULONG OpenOptions;
  783. UNICODE_STRING uDestinationName,
  784. uNewName;
  785. IO_STATUS_BLOCK IoStatusBlock;
  786. OBJECT_ATTRIBUTES ObjectAttributes;
  787. FILE_DISPOSITION_INFORMATION DispositionInformation;
  788. BOOLEAN foo = TRUE;
  789. #define REPARSE_BUFFER_LENGTH 45 * sizeof(WCHAR) + sizeof(REPARSE_DATA_BUFFER)
  790. PREPARSE_DATA_BUFFER ReparseBufferHeader = NULL;
  791. UCHAR ReparseBuffer[REPARSE_BUFFER_LENGTH];
  792. //
  793. // Allocate and initialize Unicode strings.
  794. //
  795. RtlCreateUnicodeStringFromAsciiz( &uDestinationName, DestinationName );
  796. RtlDosPathNameToNtPathName_U(
  797. uDestinationName.Buffer,
  798. &uNewName,
  799. NULL,
  800. NULL );
  801. //
  802. // Open the existing (SourceName) pathname.
  803. // Notice that if there are symbolic links in the path they are
  804. // traversed silently.
  805. //
  806. InitializeObjectAttributes(
  807. &ObjectAttributes,
  808. &uNewName,
  809. OBJ_CASE_INSENSITIVE,
  810. NULL,
  811. NULL );
  812. if (VerboseFlag) {
  813. fprintf( stdout, "Will delete symbolic link in: %Z\n", &uNewName );
  814. }
  815. //
  816. // Make sure that we call open with the appropriate flags for:
  817. //
  818. // (1) directory versus non-directory
  819. // (2) reparse point
  820. //
  821. OpenOptions = FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT;
  822. if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  823. OpenOptions |= FILE_DIRECTORY_FILE;
  824. } else {
  825. OpenOptions |= FILE_NON_DIRECTORY_FILE;
  826. }
  827. Status = NtOpenFile(
  828. &FileHandle,
  829. (ACCESS_MASK)DELETE | FILE_READ_DATA | SYNCHRONIZE,
  830. &ObjectAttributes,
  831. &IoStatusBlock,
  832. SHARE_ALL,
  833. OpenOptions );
  834. if (!NT_SUCCESS( Status )) {
  835. RtlFreeUnicodeString( &uDestinationName );
  836. fprintf( stderr, "Open failed %s\n", DestinationName );
  837. return Status;
  838. }
  839. //
  840. // Build the reparse point buffer.
  841. //
  842. ReparseBufferHeader = (PREPARSE_DATA_BUFFER)ReparseBuffer;
  843. ReparseBufferHeader->ReparseTag = LinkType;
  844. ReparseBufferHeader->ReparseDataLength = 0;
  845. ReparseBufferHeader->Reserved = 0xcabd;
  846. //
  847. // Delete a symbolic link reparse point.
  848. //
  849. Status = NtFsControlFile(
  850. FileHandle,
  851. NULL,
  852. NULL,
  853. NULL,
  854. &IoStatusBlock,
  855. FSCTL_DELETE_REPARSE_POINT,
  856. ReparseBuffer,
  857. FIELD_OFFSET(REPARSE_DATA_BUFFER, RDB),
  858. NULL, // Output buffer
  859. 0 ); // Output buffer length
  860. if (!NT_SUCCESS( Status )) {
  861. RtlFreeUnicodeString( &uDestinationName );
  862. fprintf( stderr, "NtFsControlFile delete failed %s\n", DestinationName );
  863. NtClose( FileHandle );
  864. return Status;
  865. }
  866. //
  867. // Change the disposition of the file so as to delete it as well.
  868. //
  869. // Look in flinks.h for the kludge I needed to do to make the following line
  870. // of code work:
  871. // #define DeleteFileA DeleteFile
  872. //
  873. DispositionInformation.DeleteFile = TRUE;
  874. if (VerboseFlag) {
  875. fprintf( stdout, "Will set the delete flag for: %Z\n", &uNewName );
  876. }
  877. Status = NtSetInformationFile(
  878. FileHandle,
  879. &IoStatusBlock,
  880. &DispositionInformation,
  881. sizeof (FILE_DISPOSITION_INFORMATION),
  882. FileDispositionInformation );
  883. //
  884. // Clean up and return.
  885. //
  886. NtClose( FileHandle );
  887. RtlFreeUnicodeString( &uDestinationName );
  888. return Status;
  889. } // DeleteSymbolicLink
  890. NTSTATUS
  891. IntegerToBase36String(
  892. ULONG Value,
  893. char *String,
  894. ULONG MaxLength)
  895. /*++
  896. Routine Description:
  897. This does what RtlIntegerToUnicodeString(Value,36,String) would do if it
  898. handled base 36. We use the same rules for digits as are normally used
  899. in Hex: 0-9, followed by a-z. Note that we're intentionally using Arabic
  900. numerals and English letters here rather than something localized because
  901. this is intended to generate filenames that are never seen by users, and
  902. are constant regardless of the language used on the machine.
  903. Arguments:
  904. Value - The ULONG to be converted into a base36 string
  905. String - A pointer to a string to receive the result
  906. MaxLength - the total size of the area pointed to by String
  907. Return Value:
  908. success or buffer overflow
  909. --*/
  910. {
  911. ULONG numChars;
  912. ULONG ValueCopy = Value;
  913. ULONG currentCharacter;
  914. // First, figure out the length by seeing how many times we can divide 36 into the value
  915. for (numChars = 0; ValueCopy != 0; ValueCopy /= 36, numChars++) {
  916. // No loop body
  917. }
  918. // Special case the value 0.
  919. if (numChars == 0) {
  920. ASSERT(Value == 0);
  921. if (MaxLength < 2)
  922. return STATUS_BUFFER_OVERFLOW;
  923. String[0] = '0';
  924. String[1] = 0;
  925. return STATUS_SUCCESS;
  926. }
  927. // If the string is too short, quit now.
  928. if (numChars * sizeof(char) + 1 > MaxLength) { // The +1 is for the terminating null
  929. return STATUS_BUFFER_OVERFLOW;
  930. }
  931. // Convert the string character-by-character starting at the lowest order (and so rightmost) "digit"
  932. ValueCopy = Value;
  933. for (currentCharacter = 0 ; currentCharacter < numChars; currentCharacter++) {
  934. ULONG digit = ValueCopy % 36;
  935. ASSERT(ValueCopy != 0);
  936. if (digit < 10) {
  937. String[numChars - (currentCharacter + 1)] = (char)('0' + (ValueCopy % 36));
  938. } else {
  939. String[numChars - (currentCharacter + 1)] = (char)('a' + ((ValueCopy % 36) - 10));
  940. }
  941. ValueCopy /= 36;
  942. }
  943. ASSERT(ValueCopy == 0);
  944. //
  945. // Fill in the terminating null and we're done.
  946. //
  947. String[numChars] = 0;
  948. return STATUS_SUCCESS;
  949. }
  950. NTSTATUS
  951. IndexToFileName(
  952. IN PLARGE_INTEGER Index,
  953. OUT char *fileName,
  954. IN ULONG MaxLength
  955. )
  956. /*++
  957. Routine Description:
  958. Given an index, returns the corresponding fully qualified file name.
  959. Arguments:
  960. Index - The CSINDEX to convert
  961. fileName - A pointer to a string to receive the result
  962. MaxLength - The size of the string printed to by fileName
  963. Return Value:
  964. success or buffer overflow
  965. --*/
  966. {
  967. UNICODE_STRING substring;
  968. NTSTATUS status;
  969. ULONG fileNameLength;
  970. //
  971. // We generate the filename as low.high, where low.high is the
  972. // base 36 representation of the CSIndex. We use this bizarre format in order to
  973. // avoid (for as long as possible) filenames that are not unique 8.3 names. ULONGS
  974. // in base 36 have at most 7 characters, so we don't exceed 8.3 until we hit an index
  975. // value of just over 2 * 10^14, which takes over 6000 years at 1 index/millisecond.
  976. //
  977. status = IntegerToBase36String(Index->LowPart,fileName,MaxLength);
  978. if (status != STATUS_SUCCESS) {
  979. return status;
  980. }
  981. fileNameLength = strlen(fileName);
  982. MaxLength -= fileNameLength;
  983. // Stick in the dot in the middle.
  984. if (MaxLength == 0) {
  985. return STATUS_BUFFER_OVERFLOW;
  986. }
  987. *(fileName + strlen(fileName)) = '.';
  988. fileNameLength++;
  989. MaxLength--;
  990. return IntegerToBase36String(Index->HighPart,(fileName + fileNameLength),MaxLength);
  991. }
  992. NTSTATUS
  993. DisplaySymbolicLink(
  994. CHAR *DestinationName,
  995. ATTRIBUTE_TYPE FileAttributes,
  996. BOOLEAN VerboseFlag
  997. )
  998. /*++
  999. Routine Description:
  1000. Displays a symbolic link existing at DestinationName.
  1001. DestinationName needs to denote a symbolic link.
  1002. Opens the file named by DestinationName and gets a reparse point of type
  1003. symbolic link.
  1004. If the reparse point is not a symbolic link this routine will not display it.
  1005. Return Value:
  1006. NTSTATUS - returns the appropriate NT return code.
  1007. --*/
  1008. {
  1009. NTSTATUS Status = STATUS_SUCCESS;
  1010. HANDLE FileHandle;
  1011. ULONG OpenOptions;
  1012. UNICODE_STRING uDestinationName,
  1013. uNewName;
  1014. IO_STATUS_BLOCK IoStatusBlock;
  1015. OBJECT_ATTRIBUTES ObjectAttributes;
  1016. PREPARSE_DATA_BUFFER ReparseBufferHeader = NULL;
  1017. UCHAR ReparseBuffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
  1018. //
  1019. // Allocate and initialize Unicode string.
  1020. //
  1021. RtlCreateUnicodeStringFromAsciiz( &uDestinationName, DestinationName );
  1022. RtlDosPathNameToNtPathName_U(
  1023. uDestinationName.Buffer,
  1024. &uNewName,
  1025. NULL,
  1026. NULL );
  1027. //
  1028. // Open the existing (SourceName) pathname.
  1029. // Notice that if there are symbolic links in the path they are
  1030. // traversed silently.
  1031. //
  1032. InitializeObjectAttributes(
  1033. &ObjectAttributes,
  1034. &uNewName,
  1035. OBJ_CASE_INSENSITIVE,
  1036. NULL,
  1037. NULL );
  1038. if (VerboseFlag) {
  1039. fprintf( stdout, "Will display symbolic link in: %Z\n", &uNewName );
  1040. }
  1041. //
  1042. // Make sure that we call open with the appropriate flags for:
  1043. //
  1044. // (1) directory versus non-directory
  1045. // (2) reparse point
  1046. //
  1047. OpenOptions = FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT;
  1048. if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  1049. OpenOptions |= FILE_DIRECTORY_FILE;
  1050. } else {
  1051. OpenOptions |= FILE_NON_DIRECTORY_FILE;
  1052. }
  1053. Status = NtOpenFile(
  1054. &FileHandle,
  1055. FILE_READ_DATA | SYNCHRONIZE,
  1056. &ObjectAttributes,
  1057. &IoStatusBlock,
  1058. SHARE_ALL,
  1059. OpenOptions );
  1060. if (!NT_SUCCESS( Status )) {
  1061. RtlFreeUnicodeString( &uDestinationName );
  1062. fprintf( stderr, "Open failed %s\n", DestinationName );
  1063. return Status;
  1064. }
  1065. //
  1066. // Get the reparse point.
  1067. //
  1068. Status = NtFsControlFile(
  1069. FileHandle,
  1070. NULL,
  1071. NULL,
  1072. NULL,
  1073. &IoStatusBlock,
  1074. FSCTL_GET_REPARSE_POINT,
  1075. NULL, // Input buffer
  1076. 0, // Input buffer length
  1077. ReparseBuffer, // Output buffer
  1078. MAXIMUM_REPARSE_DATA_BUFFER_SIZE ); // Output buffer length
  1079. if (!NT_SUCCESS( Status )) {
  1080. RtlFreeUnicodeString( &uDestinationName );
  1081. fprintf( stderr, "NtFsControlFile get failed %x %s\n", IoStatusBlock.Information, DestinationName );
  1082. return Status;
  1083. }
  1084. //
  1085. // Decode the reparse point buffer to display the data.
  1086. //
  1087. ReparseBufferHeader = (PREPARSE_DATA_BUFFER)ReparseBuffer;
  1088. if (ReparseBufferHeader->ReparseTag == IO_REPARSE_TAG_SIS) {
  1089. PSI_REPARSE_BUFFER sisReparseBuffer = (PSI_REPARSE_BUFFER)ReparseBufferHeader->RDB;
  1090. char stringBuffer[100];
  1091. PCHAR guidString;
  1092. FILE_INTERNAL_INFORMATION internalInfo[1];
  1093. printf("SIS Reparse point, format version %d\n",sisReparseBuffer->ReparsePointFormatVersion);
  1094. if (RPC_S_OK != UuidToString(&sisReparseBuffer->CSid,&guidString)) {
  1095. printf("CSid unable to stringify\n");
  1096. } else {
  1097. printf("CSid %s\n",guidString);
  1098. }
  1099. if (STATUS_SUCCESS != IndexToFileName(&sisReparseBuffer->LinkIndex,stringBuffer,100)) {
  1100. printf("LinkIndex 0x%x.0x%x (unable to stringify)\n",sisReparseBuffer->LinkIndex.HighPart,
  1101. sisReparseBuffer->LinkIndex.LowPart);
  1102. } else {
  1103. printf("LinkIndex 0x%x.0x%x (%s)\n",sisReparseBuffer->LinkIndex.HighPart,
  1104. sisReparseBuffer->LinkIndex.LowPart,stringBuffer);
  1105. }
  1106. Status = NtQueryInformationFile(
  1107. FileHandle,
  1108. &IoStatusBlock,
  1109. internalInfo,
  1110. sizeof(FILE_INTERNAL_INFORMATION),
  1111. FileInternalInformation);
  1112. if (STATUS_SUCCESS != Status) {
  1113. printf("LinkFileNtfsId 0x%x.0x%x (unable to query internal info, 0x%x)\n",
  1114. sisReparseBuffer->LinkFileNtfsId.HighPart,sisReparseBuffer->LinkFileNtfsId.LowPart,
  1115. Status);
  1116. } else if (internalInfo->IndexNumber.QuadPart == sisReparseBuffer->LinkFileNtfsId.QuadPart) {
  1117. printf("LinkFileNtfsId 0x%x.0x%x (matches actual id)\n",
  1118. sisReparseBuffer->LinkFileNtfsId.HighPart,sisReparseBuffer->LinkFileNtfsId.LowPart);
  1119. } else {
  1120. printf("LinkFileNtfsId 0x%x.0x%x (!= actual Id 0x%x.0x%x)\n",
  1121. sisReparseBuffer->LinkFileNtfsId.HighPart,sisReparseBuffer->LinkFileNtfsId.LowPart,
  1122. internalInfo->IndexNumber.HighPart,internalInfo->IndexNumber.LowPart);
  1123. }
  1124. printf("CSFileNtfsId 0x%x.0x%x\n",sisReparseBuffer->CSFileNtfsId.HighPart,sisReparseBuffer->CSFileNtfsId.LowPart);
  1125. printf("CSFileChecksum 0x%x.0x%x\n",sisReparseBuffer->CSChecksum.HighPart,sisReparseBuffer->CSChecksum.LowPart);
  1126. } else if (ReparseBufferHeader->ReparseTag != LinkType) {
  1127. fprintf( stderr, "Reparse point is not a symbolic link: tag %x\n", ReparseBufferHeader->ReparseTag );
  1128. Status = STATUS_OBJECT_NAME_INVALID;
  1129. } else {
  1130. UNICODE_STRING UniString;
  1131. UniString.Length = ReparseBufferHeader->ReparseDataLength;
  1132. UniString.Buffer = (PWCHAR)&ReparseBufferHeader->RDB[0];
  1133. if (fVerbose) {
  1134. fprintf( stdout, "The symbolic link is: " );
  1135. }
  1136. fprintf( stdout, "%Z\n", &UniString );
  1137. }
  1138. //
  1139. // Clean up and return.
  1140. //
  1141. NtClose( FileHandle );
  1142. RtlFreeUnicodeString( &uDestinationName );
  1143. return Status;
  1144. } // DisplaySymbolicLink
  1145. NTSTATUS
  1146. CreateEmptyFile(
  1147. CHAR *DestinationName,
  1148. ATTRIBUTE_TYPE FileAttributes,
  1149. BOOLEAN VerboseFlag
  1150. )
  1151. /*++
  1152. Routine Description:
  1153. Creates an empty file or directory, according to fileAttributes.
  1154. Return Value:
  1155. NTSTATUS - returns the appropriate NT return code.
  1156. --*/
  1157. {
  1158. NTSTATUS Status = STATUS_SUCCESS;
  1159. OBJECT_ATTRIBUTES ObjectAttributes;
  1160. IO_STATUS_BLOCK IoStatusBlock;
  1161. HANDLE FileHandle;
  1162. ULONG DesiredAccess = FILE_READ_DATA | SYNCHRONIZE;
  1163. ULONG CreateDisposition = FILE_OPEN_IF | FILE_OPEN;
  1164. ULONG CreateOptions;
  1165. ULONG ShareAccess = SHARE_ALL;
  1166. UNICODE_STRING uDestinationName,
  1167. uFileName;
  1168. //
  1169. // Initialize CreateOptions correctly.
  1170. //
  1171. if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  1172. CreateOptions = FILE_DIRECTORY_FILE;
  1173. } else {
  1174. CreateOptions = FILE_NON_DIRECTORY_FILE;
  1175. }
  1176. //
  1177. // Allocate and initialize Unicode string.
  1178. //
  1179. RtlCreateUnicodeStringFromAsciiz( &uDestinationName, DestinationName );
  1180. RtlDosPathNameToNtPathName_U(
  1181. uDestinationName.Buffer,
  1182. &uFileName,
  1183. NULL,
  1184. NULL );
  1185. InitializeObjectAttributes(
  1186. &ObjectAttributes,
  1187. &uFileName,
  1188. OBJ_CASE_INSENSITIVE,
  1189. NULL,
  1190. NULL );
  1191. if (VerboseFlag) {
  1192. if (CreateOptions & FILE_DIRECTORY_FILE) {
  1193. fprintf( stdout, "Will create the empty directory: %Z\n", &uFileName );
  1194. } else {
  1195. fprintf( stdout, "Will create the empty file: %Z\n", &uFileName );
  1196. }
  1197. }
  1198. Status = NtCreateFile(
  1199. &FileHandle,
  1200. DesiredAccess,
  1201. &ObjectAttributes,
  1202. &IoStatusBlock,
  1203. NULL, // pallocationsize (none!)
  1204. FILE_ATTRIBUTE_NORMAL,
  1205. ShareAccess,
  1206. CreateDisposition,
  1207. CreateOptions,
  1208. NULL, // EA buffer (none!)
  1209. 0 );
  1210. NtClose( FileHandle );
  1211. return Status;
  1212. } // CreateEmptyFile
  1213. NTSTATUS
  1214. CopySymbolicLink(
  1215. CHAR *SourceName,
  1216. CHAR *DestinationName,
  1217. ATTRIBUTE_TYPE FileAttributes,
  1218. BOOLEAN VerboseFlag
  1219. )
  1220. /*++
  1221. Routine Description:
  1222. Copies the symbolic link existing at SourceName in DestinationName.
  1223. SourceName needs to denote a symbolic link.
  1224. DestinationName exists, and may or not be a symbolic link.
  1225. Return Value:
  1226. NTSTATUS - returns the appropriate NT return code.
  1227. --*/
  1228. {
  1229. NTSTATUS Status = STATUS_SUCCESS;
  1230. HANDLE FileHandle;
  1231. ULONG OpenOptions;
  1232. UNICODE_STRING uName,
  1233. uFinalName;
  1234. IO_STATUS_BLOCK IoStatusBlock;
  1235. OBJECT_ATTRIBUTES ObjectAttributes;
  1236. PREPARSE_DATA_BUFFER ReparseBufferHeader = NULL;
  1237. UCHAR ReparseBuffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
  1238. //
  1239. // Allocate and initialize Unicode string for SourceName. We will open it
  1240. // and retrieve the symbolic link it stores.
  1241. //
  1242. RtlCreateUnicodeStringFromAsciiz( &uName, SourceName );
  1243. RtlDosPathNameToNtPathName_U(
  1244. uName.Buffer,
  1245. &uFinalName,
  1246. NULL,
  1247. NULL );
  1248. //
  1249. // Open Path1 assuming that it is a reparse point (as it should be).
  1250. // Notice that if there are symbolic links in the path they are
  1251. // traversed silently.
  1252. //
  1253. InitializeObjectAttributes(
  1254. &ObjectAttributes,
  1255. &uFinalName,
  1256. OBJ_CASE_INSENSITIVE,
  1257. NULL,
  1258. NULL );
  1259. if (VerboseFlag) {
  1260. fprintf( stdout, "Will retrieve symbolic link in: %Z\n", &uFinalName );
  1261. }
  1262. //
  1263. // Make sure that we call open with the appropriate flags for:
  1264. //
  1265. // (1) directory versus non-directory
  1266. // (2) reparse point
  1267. //
  1268. OpenOptions = FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT;
  1269. if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  1270. OpenOptions |= FILE_DIRECTORY_FILE;
  1271. } else {
  1272. OpenOptions |= FILE_NON_DIRECTORY_FILE;
  1273. }
  1274. Status = NtOpenFile(
  1275. &FileHandle,
  1276. FILE_READ_DATA | SYNCHRONIZE,
  1277. &ObjectAttributes,
  1278. &IoStatusBlock,
  1279. SHARE_ALL,
  1280. OpenOptions );
  1281. if (!NT_SUCCESS( Status )) {
  1282. RtlFreeUnicodeString( &uName );
  1283. fprintf( stderr, "Open as reparse point failed %s\n", SourceName );
  1284. return Status;
  1285. }
  1286. //
  1287. // Get the reparse point.
  1288. //
  1289. Status = NtFsControlFile(
  1290. FileHandle,
  1291. NULL,
  1292. NULL,
  1293. NULL,
  1294. &IoStatusBlock,
  1295. FSCTL_GET_REPARSE_POINT,
  1296. NULL, // Input buffer
  1297. 0, // Input buffer length
  1298. ReparseBuffer, // Output buffer
  1299. MAXIMUM_REPARSE_DATA_BUFFER_SIZE ); // Output buffer length
  1300. if (!NT_SUCCESS( Status )) {
  1301. RtlFreeUnicodeString( &uName );
  1302. fprintf( stderr, "NtFsControlFile get failed %x %s\n", IoStatusBlock.Information, SourceName );
  1303. return Status;
  1304. }
  1305. //
  1306. // Free the name buffer.
  1307. //
  1308. RtlFreeUnicodeString( &uName );
  1309. //
  1310. // Decode the reparse point buffer to display the data.
  1311. //
  1312. ReparseBufferHeader = (PREPARSE_DATA_BUFFER)ReparseBuffer;
  1313. if (ReparseBufferHeader->ReparseTag != LinkType) {
  1314. fprintf( stderr, "Reparse point is not a symbolic link: tag %x\n", ReparseBufferHeader->ReparseTag );
  1315. NtClose( FileHandle );
  1316. return STATUS_OBJECT_NAME_INVALID;
  1317. } else {
  1318. UNICODE_STRING UniString;
  1319. UniString.Length = ReparseBufferHeader->ReparseDataLength;
  1320. UniString.Buffer = (PWCHAR)&ReparseBufferHeader->RDB[0];
  1321. if (fVerbose) {
  1322. fprintf( stdout, "The symbolic link is: %Z\n", &UniString );
  1323. }
  1324. }
  1325. //
  1326. // Close Path1.
  1327. //
  1328. NtClose( FileHandle );
  1329. //
  1330. // We now deal with Path2.
  1331. // Allocate and initialize Unicode string for DestinationName. We will open it
  1332. // and set a reparse point in it.
  1333. //
  1334. RtlCreateUnicodeStringFromAsciiz( &uName, DestinationName );
  1335. RtlDosPathNameToNtPathName_U(
  1336. uName.Buffer,
  1337. &uFinalName,
  1338. NULL,
  1339. NULL );
  1340. //
  1341. // We fail if this file is not created.
  1342. //
  1343. InitializeObjectAttributes(
  1344. &ObjectAttributes,
  1345. &uFinalName,
  1346. OBJ_CASE_INSENSITIVE,
  1347. NULL,
  1348. NULL );
  1349. if (VerboseFlag) {
  1350. fprintf( stdout, "Will set symbolic link in: %Z\n", &uFinalName );
  1351. }
  1352. //
  1353. // Make sure that we open with the same options as Path1.
  1354. // We first try the reparse point case and trap the corresponsing error code.
  1355. //
  1356. Status = NtOpenFile(
  1357. &FileHandle,
  1358. FILE_READ_DATA | SYNCHRONIZE,
  1359. &ObjectAttributes,
  1360. &IoStatusBlock,
  1361. SHARE_ALL,
  1362. OpenOptions );
  1363. if (!NT_SUCCESS( Status )) {
  1364. RtlFreeUnicodeString( &uName );
  1365. fprintf( stderr, "Open failed %s\n", DestinationName );
  1366. return Status;
  1367. }
  1368. //
  1369. // The file in Path2 is open. We set the reparse point of type symbolic link.
  1370. //
  1371. Status = NtFsControlFile(
  1372. FileHandle,
  1373. NULL,
  1374. NULL,
  1375. NULL,
  1376. &IoStatusBlock,
  1377. FSCTL_SET_REPARSE_POINT,
  1378. ReparseBuffer,
  1379. FIELD_OFFSET(REPARSE_DATA_BUFFER, RDB) + ReparseBufferHeader->ReparseDataLength,
  1380. NULL, // Output buffer
  1381. 0 ); // Output buffer length
  1382. if (!NT_SUCCESS( Status )) {
  1383. fprintf( stderr, "NtFsControlFile set failed %s\n", DestinationName );
  1384. //
  1385. // And return after cleaning up.
  1386. //
  1387. }
  1388. //
  1389. // Free the name buffer and close Path2.
  1390. //
  1391. RtlFreeUnicodeString( &uName );
  1392. NtClose( FileHandle );
  1393. return Status;
  1394. } // CopySymbolicLink
  1395. NTSTATUS
  1396. RenameSymbolicLink(
  1397. CHAR *SourceName,
  1398. CHAR *DestinationName,
  1399. ATTRIBUTE_TYPE FileAttributes,
  1400. BOOLEAN VerboseFlag
  1401. )
  1402. {
  1403. NTSTATUS Status = STATUS_SUCCESS;
  1404. WCHAR *pch,
  1405. ch;
  1406. BOOLEAN LoopCondition = TRUE,
  1407. TranslationStatus = TRUE;
  1408. HANDLE FileHandle,
  1409. RootDirHandle;
  1410. ULONG OpenOptions;
  1411. USHORT Index = 0,
  1412. LastIndex = 0;
  1413. UNICODE_STRING uName,
  1414. uRelative,
  1415. uFinalName;
  1416. RTL_RELATIVE_NAME_U RelativeName;
  1417. IO_STATUS_BLOCK IoStatusBlock;
  1418. OBJECT_ATTRIBUTES ObjectAttributes;
  1419. FILE_RENAME_INFORMATION *RenameInformation = NULL;
  1420. //
  1421. // Allocate and initialize Unicode string for SourceName (Path1).
  1422. //
  1423. RtlCreateUnicodeStringFromAsciiz( &uName, SourceName );
  1424. TranslationStatus = RtlDosPathNameToRelativeNtPathName_U( uName.Buffer,
  1425. &uFinalName,
  1426. NULL,
  1427. &RelativeName );
  1428. if (!TranslationStatus) {
  1429. RtlFreeUnicodeString( &uName );
  1430. fprintf( stderr, "Path not translated: %s\n", SourceName );
  1431. SetLastError(ERROR_PATH_NOT_FOUND);
  1432. return STATUS_OBJECT_NAME_INVALID;
  1433. }
  1434. //
  1435. // Open Path1 as a reparse point; as it needs to be.
  1436. // Notice that if there are symbolic links in the path they are
  1437. // traversed silently.
  1438. //
  1439. if (RelativeName.RelativeName.Length) {
  1440. uFinalName = *(PUNICODE_STRING)&RelativeName.RelativeName;
  1441. if (VerboseFlag) {
  1442. fprintf( stdout, "Relative name is: %Z\n", &uFinalName );
  1443. }
  1444. } else {
  1445. RelativeName.ContainingDirectory = NULL;
  1446. }
  1447. InitializeObjectAttributes(
  1448. &ObjectAttributes,
  1449. &uFinalName,
  1450. OBJ_CASE_INSENSITIVE,
  1451. RelativeName.ContainingDirectory,
  1452. NULL );
  1453. if (VerboseFlag) {
  1454. fprintf( stdout, "Will rename symbolic link in: %Z\n", &uFinalName );
  1455. }
  1456. //
  1457. // Make sure that we call open with the appropriate flags for:
  1458. //
  1459. // (1) directory versus non-directory
  1460. // (2) reparse point
  1461. //
  1462. OpenOptions = FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT;
  1463. if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  1464. OpenOptions |= FILE_DIRECTORY_FILE;
  1465. } else {
  1466. OpenOptions |= FILE_NON_DIRECTORY_FILE;
  1467. }
  1468. Status = NtOpenFile(
  1469. &FileHandle,
  1470. (ACCESS_MASK)DELETE | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES| SYNCHRONIZE,
  1471. &ObjectAttributes,
  1472. &IoStatusBlock,
  1473. SHARE_ALL,
  1474. OpenOptions );
  1475. RtlReleaseRelativeName(&RelativeName);
  1476. if (!NT_SUCCESS( Status )) {
  1477. RtlFreeUnicodeString( &uName );
  1478. fprintf( stderr, "Open as reparse point failed %Z\n", &uFinalName );
  1479. return Status;
  1480. }
  1481. //
  1482. // Free the name for Path1.
  1483. //
  1484. RtlFreeUnicodeString( &uName );
  1485. //
  1486. // We now build the appropriate Unicode name for Path2.
  1487. //
  1488. RtlCreateUnicodeStringFromAsciiz( &uName, DestinationName );
  1489. TranslationStatus = RtlDosPathNameToNtPathName_U(
  1490. uName.Buffer,
  1491. &uFinalName,
  1492. NULL,
  1493. NULL );
  1494. if (!TranslationStatus) {
  1495. RtlFreeUnicodeString( &uName );
  1496. fprintf( stderr, "Path not translated: %s\n", DestinationName );
  1497. SetLastError(ERROR_PATH_NOT_FOUND);
  1498. return STATUS_OBJECT_NAME_INVALID;
  1499. }
  1500. if (VerboseFlag) {
  1501. fprintf( stdout, "The complete destination is: %Z\n", &uFinalName );
  1502. }
  1503. //
  1504. // We use the uFinalName to build the name for the directory where
  1505. // the target file resides.
  1506. // We will pass the handle in the link information.
  1507. // The rest of the path will be given relative to this root.
  1508. // We depend on paths looking like "\DosDevices\X:\path".
  1509. //
  1510. Index = uFinalName.Length / 2; // to account for the Unicode widths
  1511. Index -= 1; // as arrays begin from zero
  1512. if ((uFinalName.Buffer[Index] == L'\\') || (Index <= 4)) {
  1513. //
  1514. // Last character is a backslash or the full name is too short;
  1515. // this is not a valid name.
  1516. //
  1517. NtClose( FileHandle );
  1518. RtlFreeUnicodeString( &uName );
  1519. fprintf( stderr, "Bad Path2, ends in backslash or is too short (Index %d) %s\n", Index, DestinationName );
  1520. return STATUS_OBJECT_NAME_INVALID;
  1521. }
  1522. while ((Index > 0) && LoopCondition) {
  1523. if (uFinalName.Buffer[Index] == L'\\') {
  1524. LoopCondition = FALSE;
  1525. LastIndex = Index;
  1526. } else {
  1527. Index --;
  1528. }
  1529. }
  1530. uFinalName.Length = 2 * LastIndex;
  1531. if (VerboseFlag) {
  1532. fprintf( stdout, "The root directory is: %Z\n", &uFinalName );
  1533. }
  1534. InitializeObjectAttributes(
  1535. &ObjectAttributes,
  1536. &uFinalName,
  1537. OBJ_CASE_INSENSITIVE,
  1538. NULL,
  1539. NULL );
  1540. Status = NtCreateFile(
  1541. &RootDirHandle,
  1542. FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
  1543. &ObjectAttributes,
  1544. &IoStatusBlock,
  1545. NULL, // pallocationsize (none!)
  1546. FILE_ATTRIBUTE_NORMAL,
  1547. SHARE_ALL,
  1548. FILE_OPEN_IF | FILE_OPEN,
  1549. FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT,
  1550. NULL, // EA buffer (none!)
  1551. 0 );
  1552. if (!NT_SUCCESS( Status )) {
  1553. NtClose( FileHandle );
  1554. RtlFreeUnicodeString( &uName );
  1555. fprintf( stderr, "Could not get RootDirHandle %s\n", DestinationName );
  1556. return Status;
  1557. }
  1558. //
  1559. // Now get the path relative to the root.
  1560. //
  1561. RtlInitUnicodeString( &uRelative, &uFinalName.Buffer[LastIndex + 1] );
  1562. RenameInformation = malloc( sizeof(*RenameInformation) + uRelative.Length );
  1563. if (NULL == RenameInformation) {
  1564. NtClose( FileHandle );
  1565. NtClose( RootDirHandle );
  1566. RtlFreeUnicodeString( &uName );
  1567. return STATUS_NO_MEMORY;
  1568. }
  1569. RenameInformation->ReplaceIfExists = TRUE;
  1570. RenameInformation->RootDirectory = RootDirHandle;
  1571. RenameInformation->FileNameLength = uRelative.Length;
  1572. RtlMoveMemory( RenameInformation->FileName,
  1573. uRelative.Buffer,
  1574. uRelative.Length );
  1575. //
  1576. // Do the rename.
  1577. //
  1578. if (VerboseFlag) {
  1579. fprintf( stdout, "Will rename symbolic link to: %Z\n", &uRelative );
  1580. }
  1581. Status = NtSetInformationFile(
  1582. FileHandle,
  1583. &IoStatusBlock,
  1584. RenameInformation,
  1585. sizeof (FILE_RENAME_INFORMATION) + RenameInformation->FileNameLength,
  1586. FileRenameInformation );
  1587. if (Status == STATUS_NOT_SAME_DEVICE) {
  1588. fprintf( stderr, "Rename directed to a different device.\n" );
  1589. }
  1590. if (!NT_SUCCESS( Status )) {
  1591. fprintf( stderr, "NtSetInformationFile failed (Status %X) %Z\n", Status, &uRelative );
  1592. }
  1593. //
  1594. // Close Path1 and the root of Path2, free the buffer and return.
  1595. //
  1596. NtClose( FileHandle );
  1597. NtClose( RootDirHandle );
  1598. RtlFreeUnicodeString( &uName );
  1599. free( RenameInformation );
  1600. return Status;
  1601. } // RenameSymbolicLink