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.

1053 lines
27 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. linkd.c
  5. Simple utility to manipulate name graftings at directories.
  6. Author:
  7. Felipe Cabrera (Cabrera) 271-Aug-1997
  8. Revision History:
  9. --*/
  10. #define UNICODE
  11. #define _UNICODE
  12. #include <stdio.h>
  13. #include <stdlib.h> // exit
  14. #include <io.h> // _get_osfhandle
  15. #include <nt.h>
  16. #include <ntrtl.h>
  17. #include <nturtl.h>
  18. #include <ntioapi.h>
  19. #include <windows.h>
  20. #include <locale.h> // setlocale
  21. //
  22. // Functions forward referenced.
  23. //
  24. void
  25. ScanArgs (
  26. int argc,
  27. char **argv
  28. );
  29. void
  30. __cdecl
  31. printmessage (
  32. DWORD messageID,
  33. ...
  34. );
  35. void
  36. SzToWsz (
  37. OUT WCHAR *Unicode,
  38. IN char *Ansi
  39. );
  40. BOOL
  41. MassageLinkValue (
  42. IN LPCWSTR lpLinkName,
  43. IN LPCWSTR lpLinkValue,
  44. OUT PUNICODE_STRING NtLinkName,
  45. OUT PUNICODE_STRING NtLinkValue,
  46. OUT PUNICODE_STRING DosLinkValue
  47. );
  48. void
  49. __cdecl
  50. printmessage (
  51. DWORD messageID,
  52. ...
  53. );
  54. void
  55. __cdecl
  56. DisplayMsg (
  57. DWORD MsgNum,
  58. ...
  59. );
  60. int
  61. FileIsConsole (
  62. int fh
  63. );
  64. //
  65. // I/O stream handles and variables.
  66. //
  67. HANDLE hInput;
  68. HANDLE hOutput;
  69. HANDLE hError;
  70. #define STDIN 0
  71. #define STDOUT 1
  72. #define STDERR 2
  73. BOOL ConsoleInput;
  74. BOOL ConsoleOutput;
  75. BOOL ConsoleError;
  76. //
  77. // Core control state vars
  78. //
  79. BOOL NeedHelp;
  80. BOOL DoCreate;
  81. BOOL DoDelete;
  82. BOOL DoQuery;
  83. BOOL DoEnumerate;
  84. #include "linkdmsg.h"
  85. TCHAR Buf[1024]; // for displaying stuff
  86. //
  87. // Main
  88. //
  89. void
  90. __cdecl
  91. main(
  92. int argc,
  93. char **argv
  94. )
  95. {
  96. CHAR lBuf[16];
  97. DWORD dwCodePage;
  98. LANGID LangId;
  99. NTSTATUS Status = STATUS_SUCCESS;
  100. OBJECT_ATTRIBUTES ObjectAttributes;
  101. HANDLE Handle;
  102. UNICODE_STRING UnicodeName;
  103. UNICODE_STRING NtLinkName;
  104. UNICODE_STRING NtLinkValue;
  105. UNICODE_STRING DosLinkValue;
  106. WCHAR FullPathLinkValue[ DOS_MAX_PATH_LENGTH+1 ];
  107. IO_STATUS_BLOCK IoStatusBlock;
  108. BOOL TranslationStatus;
  109. PVOID FreeBuffer;
  110. PVOID FreeBuffer2;
  111. FILE_DISPOSITION_INFORMATION Disposition;
  112. PREPARSE_DATA_BUFFER ReparseBufferHeader = NULL;
  113. PCHAR ReparseBuffer = NULL;
  114. ULONG ReparsePointTag = IO_REPARSE_TAG_RESERVED_ZERO;
  115. USHORT ReparseDataLength = 0;
  116. WCHAR WFileName[MAX_PATH + 2];
  117. WCHAR WFileNameTwo[MAX_PATH + 2];
  118. UCHAR Command[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
  119. ULONG FsControlCode = 0;
  120. ULONG CreateOptions = 0;
  121. ULONG CreateDisposition = 0;
  122. ULONG DesiredAccess = SYNCHRONIZE;
  123. //
  124. // Build up state vector in global booleans.
  125. //
  126. ScanArgs(argc, argv);
  127. //
  128. // printf( "argc = %d NeedHelp = %d DoCreate = %d DoDelete = %d DoQuery = %d DoEnumerate = %d\n",
  129. // argc, NeedHelp, DoCreate, DoDelete, DoQuery, DoEnumerate );
  130. //
  131. //
  132. // Since FormatMessage checks the current TEB's locale, and the Locale for
  133. // CHCP is initialized when the message class is initialized, the TEB has to
  134. // be updated after the code page is changed successfully.
  135. //
  136. // Why are we doing this, you ask. Well, the FE guys have plans to add
  137. // more than one set of language resources to this module, but not all
  138. // the possible resources. So this limited set is what they plan for.
  139. // If FormatMessage can't find the right language, it falls back to
  140. // something hopefully useful.
  141. //
  142. dwCodePage = GetConsoleOutputCP();
  143. sprintf(lBuf, ".%d", dwCodePage);
  144. switch( dwCodePage )
  145. {
  146. case 437:
  147. LangId = MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US );
  148. break;
  149. case 932:
  150. LangId = MAKELANGID( LANG_JAPANESE, SUBLANG_DEFAULT );
  151. break;
  152. case 949:
  153. LangId = MAKELANGID( LANG_KOREAN, SUBLANG_KOREAN );
  154. break;
  155. case 936:
  156. LangId = MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED );
  157. break;
  158. case 950:
  159. LangId = MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL );
  160. break;
  161. default:
  162. LangId = MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT );
  163. lBuf[0] = '\0';
  164. break;
  165. }
  166. SetThreadLocale( MAKELCID(LangId, SORT_DEFAULT) );
  167. setlocale(LC_ALL, lBuf);
  168. //
  169. // Set the appropriate handles.
  170. //
  171. hInput = GetStdHandle(STD_INPUT_HANDLE);
  172. ConsoleInput = FileIsConsole(STDIN) ? TRUE : FALSE;
  173. hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
  174. ConsoleOutput = FileIsConsole(STDOUT) ? TRUE : FALSE;
  175. hError = GetStdHandle(STD_ERROR_HANDLE);
  176. ConsoleError = FileIsConsole(STDERR) ? TRUE : FALSE;
  177. //
  178. // OK, we know the state of the command, do work
  179. //
  180. //
  181. // printf( "The parameters specified were: [0]%s [1]%s\n", argv[0], argv[1] );
  182. //
  183. //
  184. // If they asked for help, or did something that indicates they don't
  185. // understand how the program works, print help and exit.
  186. //
  187. if (NeedHelp) {
  188. printmessage( MSG_LINKD_HELP );
  189. exit(1);
  190. }
  191. //
  192. // The enumeration of all mount points is not yet supported.
  193. //
  194. if (DoEnumerate) {
  195. printmessage( MSG_LINKD_HELP );
  196. exit(1);
  197. }
  198. //
  199. // The following three calls require, at least, to have the SourceName of the operation.
  200. // Thus, we have one NT file name that we will operate on.
  201. // Change the string to Unicode and store it locally. Use it latter to open the file.
  202. //
  203. SzToWsz( WFileName, argv[1] );
  204. TranslationStatus = RtlDosPathNameToNtPathName_U(
  205. WFileName,
  206. &UnicodeName,
  207. NULL,
  208. NULL
  209. );
  210. if (!TranslationStatus) {
  211. printmessage( MSG_LINKD_WRONG_NAME );
  212. exit (1);
  213. }
  214. FreeBuffer = UnicodeName.Buffer;
  215. InitializeObjectAttributes(
  216. &ObjectAttributes,
  217. &UnicodeName,
  218. OBJ_CASE_INSENSITIVE,
  219. NULL,
  220. NULL
  221. );
  222. //
  223. // printf( "Transformed unicode str is %Z\n", &UnicodeName );
  224. //
  225. //
  226. // Now go do the appropriate actions.
  227. //
  228. if (DoCreate) {
  229. //
  230. // Set the code of the FSCTL operation.
  231. //
  232. FsControlCode = FSCTL_SET_REPARSE_POINT;
  233. //
  234. // Set the open/create options for a directory.
  235. //
  236. CreateOptions = FILE_OPEN_REPARSE_POINT;
  237. //
  238. // Set the tag to mount point.
  239. //
  240. ReparsePointTag = IO_REPARSE_TAG_MOUNT_POINT;
  241. //
  242. // Open to set the reparse point.
  243. //
  244. DesiredAccess |= FILE_WRITE_DATA;
  245. CreateDisposition = FILE_OPEN; // the file must be present
  246. Status = NtCreateFile(
  247. &Handle,
  248. DesiredAccess,
  249. &ObjectAttributes,
  250. &IoStatusBlock,
  251. NULL, // pallocationsize (none!)
  252. FILE_ATTRIBUTE_NORMAL, // attributes to be set if created
  253. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  254. CreateDisposition,
  255. CreateOptions,
  256. NULL, // EA buffer (none!)
  257. 0
  258. );
  259. //
  260. // Create a directory if you do not find it.
  261. //
  262. if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
  263. DesiredAccess = SYNCHRONIZE;
  264. CreateDisposition = FILE_CREATE;
  265. CreateOptions = FILE_DIRECTORY_FILE;
  266. Status = NtCreateFile(
  267. &Handle,
  268. DesiredAccess,
  269. &ObjectAttributes,
  270. &IoStatusBlock,
  271. NULL, // pallocationsize (none!)
  272. FILE_ATTRIBUTE_NORMAL, // attributes to be set if created
  273. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  274. CreateDisposition,
  275. CreateOptions,
  276. NULL, // EA buffer (none!)
  277. 0
  278. );
  279. if (!NT_SUCCESS(Status)) {
  280. printmessage( MSG_LINKD_CREATE_FAILED );
  281. exit (1);
  282. }
  283. //
  284. // Close the handle and re-open.
  285. //
  286. NtClose( Handle );
  287. CreateOptions = FILE_OPEN_REPARSE_POINT;
  288. DesiredAccess |= FILE_WRITE_DATA;
  289. CreateDisposition = FILE_OPEN; // the file must be present
  290. Status = NtCreateFile(
  291. &Handle,
  292. DesiredAccess,
  293. &ObjectAttributes,
  294. &IoStatusBlock,
  295. NULL, // pallocationsize (none!)
  296. FILE_ATTRIBUTE_NORMAL, // attributes to be set if created
  297. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  298. CreateDisposition,
  299. CreateOptions,
  300. NULL, // EA buffer (none!)
  301. 0
  302. );
  303. }
  304. RtlFreeHeap( RtlProcessHeap(), 0, FreeBuffer );
  305. if (!NT_SUCCESS(Status)) {
  306. SzToWsz( WFileName, argv[1] );
  307. swprintf(&Buf[0], TEXT("%s"), WFileName);
  308. DisplayMsg(MSG_LINKD_OPEN_FAILED, Buf);
  309. // printmessage( MSG_LINKD_OPEN_FAILED );
  310. exit (1);
  311. }
  312. //
  313. // Build the appropriate buffer for mount points and for symbolic links.
  314. //
  315. if ((ReparsePointTag == IO_REPARSE_TAG_MOUNT_POINT) ||
  316. (ReparsePointTag == IO_REPARSE_TAG_SYMBOLIC_LINK)) {
  317. //
  318. // The value of the mount point or of the symbolic link comes in argv[2].
  319. //
  320. SzToWsz( WFileName, argv[1] );
  321. SzToWsz( WFileNameTwo, argv[2] );
  322. //
  323. // Innitialize the DosName buffer.
  324. //
  325. DosLinkValue.Buffer = FullPathLinkValue;
  326. DosLinkValue.MaximumLength = sizeof( FullPathLinkValue );
  327. DosLinkValue.Length = 0;
  328. //
  329. // Massage all the names.
  330. //
  331. if (!MassageLinkValue( WFileName, WFileNameTwo, &NtLinkName, &NtLinkValue, &DosLinkValue )) {
  332. if (DosLinkValue.Length == 0) {
  333. printmessage( MSG_LINKD_WRONG_NAME );
  334. }
  335. else {
  336. printmessage( MSG_LINKD_PATH_NOT_FOUND );
  337. }
  338. RtlFreeUnicodeString( &NtLinkName );
  339. RtlFreeUnicodeString( &NtLinkValue );
  340. exit (1);
  341. }
  342. //
  343. // printf( "NtLinkName %Z\n", &NtLinkName );
  344. // printf( "NtLinkValue %Z\n", &NtLinkValue );
  345. // printf( "DosLinkValue is %Z\n", &DosLinkValue );
  346. // printf( "NtLinkValue.Length %d DosLinkValue.Length %d sizeof(UNICODE_NULL) %d\n",
  347. // NtLinkValue.Length, DosLinkValue.Length, sizeof(UNICODE_NULL) );
  348. //
  349. RtlFreeUnicodeString( &NtLinkName );
  350. //
  351. // Set the reparse point with mount point or symbolic link tag and determine
  352. // the appropriate length of the buffer.
  353. //
  354. //
  355. // printf( "FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) - REPARSE_DATA_BUFFER_HEADER_SIZE %d\n",
  356. // (FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) - REPARSE_DATA_BUFFER_HEADER_SIZE) );
  357. //
  358. ReparseDataLength = (USHORT)((FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) -
  359. REPARSE_DATA_BUFFER_HEADER_SIZE) +
  360. NtLinkValue.Length + sizeof(UNICODE_NULL) +
  361. DosLinkValue.Length + sizeof(UNICODE_NULL));
  362. //
  363. // printf( "ReparseDataLength %d\n", ReparseDataLength );
  364. //
  365. //
  366. // Allocate a buffer to set the reparse point.
  367. //
  368. ReparseBufferHeader = (PREPARSE_DATA_BUFFER)RtlAllocateHeap(
  369. RtlProcessHeap(),
  370. HEAP_ZERO_MEMORY,
  371. REPARSE_DATA_BUFFER_HEADER_SIZE + ReparseDataLength
  372. );
  373. if (ReparseBufferHeader == NULL) {
  374. NtClose( Handle );
  375. RtlFreeUnicodeString( &NtLinkValue );
  376. printmessage( MSG_LINKD_NO_MEMORY );
  377. exit (1);
  378. }
  379. //
  380. // Setting the buffer is common for both tags as their buffers have identical fields.
  381. //
  382. ReparseBufferHeader->ReparseDataLength = (USHORT)ReparseDataLength;
  383. ReparseBufferHeader->Reserved = 0;
  384. ReparseBufferHeader->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
  385. ReparseBufferHeader->SymbolicLinkReparseBuffer.SubstituteNameLength = NtLinkValue.Length;
  386. ReparseBufferHeader->SymbolicLinkReparseBuffer.PrintNameOffset = NtLinkValue.Length + sizeof( UNICODE_NULL );
  387. ReparseBufferHeader->SymbolicLinkReparseBuffer.PrintNameLength = DosLinkValue.Length;
  388. RtlCopyMemory(
  389. ReparseBufferHeader->SymbolicLinkReparseBuffer.PathBuffer,
  390. NtLinkValue.Buffer,
  391. NtLinkValue.Length
  392. );
  393. RtlCopyMemory(
  394. (PCHAR)(ReparseBufferHeader->SymbolicLinkReparseBuffer.PathBuffer)+
  395. NtLinkValue.Length + sizeof(UNICODE_NULL),
  396. DosLinkValue.Buffer,
  397. DosLinkValue.Length
  398. );
  399. RtlFreeUnicodeString( &NtLinkValue );
  400. }
  401. //
  402. // Set the tag in common, once for all possible cases.
  403. //
  404. ReparseBufferHeader->ReparseTag = ReparsePointTag;
  405. //
  406. // Set the reparse point.
  407. //
  408. Status = NtFsControlFile(
  409. Handle,
  410. NULL,
  411. NULL,
  412. NULL,
  413. &IoStatusBlock,
  414. FsControlCode,
  415. ReparseBufferHeader,
  416. REPARSE_DATA_BUFFER_HEADER_SIZE + ReparseBufferHeader->ReparseDataLength,
  417. NULL, // no output buffer
  418. 0 // output buffer length
  419. );
  420. //
  421. // Close the file.
  422. //
  423. NtClose( Handle );
  424. if (!NT_SUCCESS(Status)) {
  425. SzToWsz( WFileName, argv[1] );
  426. swprintf(&Buf[0], TEXT("%s"), WFileName);
  427. DisplayMsg(MSG_LINKD_SET_OPERATION_FAILED, Buf);
  428. // printmessage( MSG_LINKD_SET_OPERATION_FAILED );
  429. exit (1);
  430. }
  431. SzToWsz( WFileName, argv[1] );
  432. swprintf(&Buf[0], TEXT("%s"), WFileName);
  433. DisplayMsg(MSG_LINKD_CREATE_OPERATION_SUCCESS, Buf);
  434. // printmessage( MSG_LINKD_CREATE_OPERATION_SUCCESS );
  435. }
  436. if (DoDelete) {
  437. FILE_DISPOSITION_INFORMATION Disposition = {TRUE};
  438. //
  439. // Open the file for delete access.
  440. // Inhibit the reparse behavior using FILE_OPEN_REPARSE_POINT.
  441. // This will get a handle to the entity whether the appropriate filter is or not in place.
  442. //
  443. Status = NtOpenFile(
  444. &Handle,
  445. (ACCESS_MASK)DELETE,
  446. &ObjectAttributes,
  447. &IoStatusBlock,
  448. FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
  449. FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT
  450. );
  451. if (!NT_SUCCESS(Status)) {
  452. SzToWsz( WFileName, argv[1] );
  453. swprintf(&Buf[0], TEXT("%s"), WFileName);
  454. DisplayMsg(MSG_LINKD_OPEN_FAILED, Buf);
  455. // printmessage( MSG_LINKD_OPEN_FAILED );
  456. RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
  457. exit(1);
  458. }
  459. RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
  460. //
  461. // Delete the file
  462. //
  463. Status = NtSetInformationFile(
  464. Handle,
  465. &IoStatusBlock,
  466. &Disposition,
  467. sizeof(Disposition),
  468. FileDispositionInformation
  469. );
  470. NtClose(Handle);
  471. if (!NT_SUCCESS(Status)) {
  472. printmessage( MSG_LINKD_DELETE_OPERATION_FAILED );
  473. exit(1);
  474. }
  475. printmessage( MSG_LINKD_DELETE_OPERATION_SUCCESS );
  476. }
  477. if (DoQuery) {
  478. //
  479. // Set the code of the FSCTL operation.
  480. //
  481. FsControlCode = FSCTL_GET_REPARSE_POINT;
  482. //
  483. // We do not specify whether it is a file or a directory to get either.
  484. //
  485. DesiredAccess = FILE_READ_DATA | SYNCHRONIZE;
  486. CreateOptions = FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT;
  487. //
  488. // Open the reparse point for query.
  489. //
  490. Status = NtOpenFile(
  491. &Handle,
  492. DesiredAccess,
  493. &ObjectAttributes,
  494. &IoStatusBlock,
  495. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  496. CreateOptions
  497. );
  498. //
  499. // Free the name buffer as we are done with it.
  500. //
  501. RtlFreeHeap( RtlProcessHeap(), 0, FreeBuffer );
  502. if (!NT_SUCCESS(Status)) {
  503. SzToWsz( WFileName, argv[1] );
  504. swprintf(&Buf[0], TEXT("%s"), WFileName);
  505. DisplayMsg(MSG_LINKD_OPEN_FAILED, Buf);
  506. // printmessage( MSG_LINKD_OPEN_FAILED );
  507. exit (1);
  508. }
  509. //
  510. // Query the reparse point.
  511. //
  512. // We are use the approach of passing a buffer of well-known length:
  513. // MAXIMUM_REPARSE_DATA_BUFFER_SIZE
  514. //
  515. // Allocate a buffer and get the information.
  516. //
  517. ReparseDataLength = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
  518. ReparseBuffer = RtlAllocateHeap(
  519. RtlProcessHeap(),
  520. HEAP_ZERO_MEMORY,
  521. ReparseDataLength
  522. );
  523. if (ReparseBuffer == NULL) {
  524. printmessage( MSG_LINKD_NO_MEMORY );
  525. exit (1);
  526. }
  527. //
  528. // Now go and get the data.
  529. //
  530. Status = NtFsControlFile(
  531. Handle,
  532. NULL,
  533. NULL,
  534. NULL,
  535. &IoStatusBlock,
  536. FsControlCode, // no input buffer
  537. NULL, // input buffer length
  538. 0,
  539. (PVOID)ReparseBuffer,
  540. ReparseDataLength
  541. );
  542. //
  543. // printf( "Status %x IoStatusBlock.Status %x IoStatusBlock.Information %x\n", Status, IoStatusBlock.Status, IoStatusBlock.Information );
  544. //
  545. if (!NT_SUCCESS(Status)) {
  546. SzToWsz( WFileName, argv[1] );
  547. swprintf(&Buf[0], TEXT("%s"), WFileName);
  548. DisplayMsg(MSG_LINKD_GET_OPERATION_FAILED, Buf);
  549. NtClose( Handle );
  550. RtlFreeHeap( RtlProcessHeap(), 0, ReparseBufferHeader );
  551. exit (1);
  552. }
  553. //
  554. // Close the file and free the buffer.
  555. //
  556. NtClose( Handle );
  557. //
  558. // Display the buffer.
  559. //
  560. ReparseBufferHeader = (PREPARSE_DATA_BUFFER)ReparseBuffer;
  561. if ((ReparseBufferHeader->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) ||
  562. (ReparseBufferHeader->ReparseTag == IO_REPARSE_TAG_SYMBOLIC_LINK)) {
  563. USHORT Offset = 0;
  564. NtLinkValue.Buffer = &ReparseBufferHeader->SymbolicLinkReparseBuffer.PathBuffer[Offset];
  565. NtLinkValue.Length = ReparseBufferHeader->SymbolicLinkReparseBuffer.SubstituteNameLength;
  566. Offset = NtLinkValue.Length + sizeof(UNICODE_NULL);
  567. DosLinkValue.Buffer = &ReparseBufferHeader->SymbolicLinkReparseBuffer.PathBuffer[Offset/sizeof(WCHAR)];
  568. DosLinkValue.Length = ReparseBufferHeader->SymbolicLinkReparseBuffer.PrintNameLength;
  569. //
  570. // printf( "NtLinkValue.Length %d DosLinkValue.Length %d\n", NtLinkValue.Length, DosLinkValue.Length );
  571. // printf( " NtLinkValue: %Z\n", &NtLinkValue );
  572. // printf( "DosLinkValue: %Z\n", &DosLinkValue );
  573. //
  574. SzToWsz( WFileName, argv[1] );
  575. swprintf(&Buf[0], TEXT("%s"), WFileName);
  576. DisplayMsg(MSG_LINKD_DISPLAY_NL_A, Buf);
  577. swprintf(&Buf[0], TEXT("%s"), DosLinkValue.Buffer);
  578. DisplayMsg(MSG_LINKD_DISPLAY_NL, Buf);
  579. }
  580. else {
  581. SzToWsz( WFileName, argv[1] );
  582. swprintf(&Buf[0], TEXT("%s"), WFileName);
  583. DisplayMsg(MSG_LINKD_GET_OPERATION_FAILED, Buf);
  584. }
  585. //
  586. // Free the buffer.
  587. //
  588. RtlFreeHeap( RtlProcessHeap(), 0, ReparseBufferHeader );
  589. }
  590. //
  591. // Final exit point.
  592. //
  593. exit (0);
  594. } // main
  595. //
  596. // Changing a file name to wide characters.
  597. //
  598. void
  599. SzToWsz (
  600. OUT WCHAR *Unicode,
  601. IN char *Ansi
  602. )
  603. {
  604. while (*Unicode++ = *Ansi++)
  605. ;
  606. } // SzToWsz
  607. //
  608. // Name transformations to create a legitimate mount point or a symbolic link.
  609. //
  610. BOOL
  611. MassageLinkValue(
  612. IN LPCWSTR lpLinkName,
  613. IN LPCWSTR lpLinkValue,
  614. OUT PUNICODE_STRING NtLinkName,
  615. OUT PUNICODE_STRING NtLinkValue,
  616. OUT PUNICODE_STRING DosLinkValue
  617. )
  618. {
  619. PWSTR FilePart;
  620. PWSTR s, sBegin, sBackupLimit, sLinkName;
  621. NTSTATUS Status;
  622. USHORT nSaveNtNameLength;
  623. ULONG nLevels;
  624. //
  625. // Initialize output variables to NULL
  626. //
  627. RtlInitUnicodeString( NtLinkName, NULL );
  628. RtlInitUnicodeString( NtLinkValue, NULL );
  629. //
  630. // Translate link name into full NT path.
  631. //
  632. if (!RtlDosPathNameToNtPathName_U( lpLinkName,
  633. NtLinkName,
  634. &sLinkName,
  635. NULL
  636. )
  637. ) {
  638. return FALSE;
  639. }
  640. //
  641. // All done if no link value.
  642. //
  643. if (!ARGUMENT_PRESENT( lpLinkValue )) {
  644. return TRUE;
  645. }
  646. //
  647. // If the target is a device, do not allow the link.
  648. //
  649. if (RtlIsDosDeviceName_U( (PWSTR)lpLinkValue )) {
  650. return FALSE;
  651. }
  652. //
  653. // Convert to DOS path to full path, and get Nt representation
  654. // of DOS path.
  655. //
  656. if (!RtlGetFullPathName_U( lpLinkValue,
  657. DosLinkValue->MaximumLength,
  658. DosLinkValue->Buffer,
  659. NULL
  660. )
  661. ) {
  662. return FALSE;
  663. }
  664. DosLinkValue->Length = wcslen( DosLinkValue->Buffer ) * sizeof( WCHAR );
  665. //
  666. // Verify that the link value is a valid NT name.
  667. //
  668. if (!RtlDosPathNameToNtPathName_U( DosLinkValue->Buffer,
  669. NtLinkValue,
  670. NULL,
  671. NULL
  672. )
  673. ) {
  674. return FALSE;
  675. }
  676. return TRUE;
  677. } // MassageLinkValue
  678. VOID
  679. ScanArgs(
  680. int argc,
  681. char **argv
  682. )
  683. /*++
  684. Routine Description:
  685. ScanArgs - parse command line arguments, and set control flags
  686. to reflect what we find.
  687. Sets:
  688. NeedHelp;
  689. DoCreate;
  690. DoDelete;
  691. DoQuery;
  692. DoEnumerate;
  693. Arguments:
  694. argc - count of command line args
  695. argv - argument vector
  696. Return Value:
  697. --*/
  698. {
  699. int i;
  700. NeedHelp = FALSE;
  701. DoCreate = FALSE;
  702. DoDelete = FALSE;
  703. DoQuery = FALSE;
  704. DoEnumerate = FALSE;
  705. //
  706. // The valid calls are:
  707. //
  708. // linkd sourceName valueName -- create a directory name grafting
  709. // linkd sourceName /d -- delete a directory name grafting
  710. // linkd sourceName -- to query the value of the name grafting
  711. // linkd /? -- print help
  712. // linkd -- to enumerate all the name graftings
  713. //
  714. if (argc > 3) {
  715. NeedHelp = TRUE;
  716. goto done;
  717. }
  718. if (argc == 1) {
  719. DoEnumerate = TRUE;
  720. goto done;
  721. }
  722. if (argc == 2) {
  723. if ( (argv[1][0] == '/') &&
  724. (argv[1][1] == '?') &&
  725. (strlen(argv[1]) == 2) ) {
  726. NeedHelp = TRUE;
  727. goto done;
  728. }
  729. DoQuery = TRUE;
  730. goto done;
  731. }
  732. if (argc == 3) {
  733. if ( (argv[2][0] == '/') &&
  734. ((argv[2][1] == 'd') || (argv[2][1] == 'D')) &&
  735. (strlen(argv[2]) == 2) ) {
  736. DoDelete = TRUE;
  737. goto done;
  738. }
  739. DoCreate = TRUE;
  740. goto done;
  741. }
  742. done:
  743. return;
  744. } // ScanArgs
  745. //
  746. // Call FormatMessage and dump the result. All messages to Stdout
  747. //
  748. void
  749. __cdecl
  750. printmessage (
  751. DWORD messageID,
  752. ...
  753. )
  754. {
  755. unsigned short messagebuffer[4096];
  756. va_list ap;
  757. va_start(ap, messageID);
  758. FormatMessage(FORMAT_MESSAGE_FROM_HMODULE, NULL, messageID, 0,
  759. messagebuffer, 4095, &ap);
  760. wprintf(messagebuffer);
  761. va_end(ap);
  762. } // printmessage
  763. TCHAR DisplayBuffer[4096];
  764. CHAR DisplayBuffer2[4096];
  765. void
  766. __cdecl
  767. DisplayMsg (
  768. DWORD MsgNum,
  769. ...
  770. )
  771. {
  772. DWORD len, bytes_written;
  773. BOOL success;
  774. DWORD status;
  775. va_list ap;
  776. va_start(ap, MsgNum);
  777. len = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE, NULL, MsgNum, 0,
  778. DisplayBuffer, 4096, &ap);
  779. if (ConsoleOutput) {
  780. success = WriteConsole(hOutput, (LPVOID)DisplayBuffer, len,
  781. &bytes_written, NULL);
  782. } else {
  783. CharToOem(DisplayBuffer, DisplayBuffer2);
  784. success = WriteFile(hOutput, (LPVOID)DisplayBuffer2, len,
  785. &bytes_written, NULL);
  786. }
  787. if (!success || bytes_written != len) {
  788. status = GetLastError();
  789. }
  790. va_end(ap);
  791. } // DisplayMsg
  792. int
  793. FileIsConsole(int fh)
  794. {
  795. unsigned htype;
  796. DWORD dwMode;
  797. HANDLE hFile;
  798. hFile = (HANDLE)_get_osfhandle(fh);
  799. htype = GetFileType(hFile);
  800. htype &= ~FILE_TYPE_REMOTE;
  801. if (FILE_TYPE_CHAR == htype) {
  802. switch (fh) {
  803. case STDIN:
  804. hFile = GetStdHandle(STD_INPUT_HANDLE);
  805. break;
  806. case STDOUT:
  807. hFile = GetStdHandle(STD_OUTPUT_HANDLE);
  808. break;
  809. case STDERR:
  810. hFile = GetStdHandle(STD_ERROR_HANDLE);
  811. break;
  812. }
  813. if (GetConsoleMode(hFile, &dwMode)) {
  814. return TRUE;
  815. }
  816. }
  817. return FALSE;
  818. } // FileIsConsole