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.

824 lines
20 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. nt_obj.c
  5. Abstract:
  6. 1. Contains routines to access symbolic link objects
  7. 2. Contains routines to convert between the DOS and ARC Name space
  8. Author:
  9. Sunil Pai (sunilp) 20-Nov-1991
  10. --*/
  11. #include <nt.h>
  12. #include <ntrtl.h>
  13. #include <nturtl.h>
  14. #include <windows.h>
  15. #include <string.h>
  16. #include "rc_ids.h"
  17. #include "patchdll.h"
  18. #define BUFFER_SIZE 1024
  19. #define SYMLINKTYPE L"SymbolicLink"
  20. #define ARCNAMEOBJDIR L"\\ArcName"
  21. #define DOSDEVOBJDIR L"\\DosDevices"
  22. extern CHAR ReturnTextBuffer[1024];
  23. //
  24. // Args[0]: $(SystemPartition)
  25. // Args[1]: $(OsLoader)
  26. // Args[2]: $(OsLoadPartition)
  27. // Args[3]: $(OsLoadFilename)
  28. // Args[4]: $(VolumeList)
  29. BOOL
  30. GetOsLoaderDest(
  31. IN DWORD cArgs,
  32. IN LPSTR Args[],
  33. OUT LPSTR *TextOut
  34. )
  35. {
  36. #if i386
  37. extern WCHAR x86DetermineSystemPartition(VOID);
  38. WCHAR UnicodeDrive[4];
  39. CHAR AnsiDrive[8];
  40. *TextOut = ReturnTextBuffer;
  41. SetReturnText("C:");
  42. UnicodeDrive[0] = x86DetermineSystemPartition();
  43. UnicodeDrive[1] = L':';
  44. UnicodeDrive[2] = L'\\';
  45. UnicodeDrive[3] = L'\0';
  46. if (WideCharToMultiByte(
  47. CP_ACP,
  48. 0,
  49. UnicodeDrive,
  50. 4,
  51. AnsiDrive,
  52. 8,
  53. NULL,
  54. NULL) ) {
  55. SetReturnText(AnsiDrive);
  56. }
  57. return( TRUE );
  58. #else
  59. RGSZ rgszSystemPartition;
  60. RGSZ rgszOsLoader;
  61. RGSZ rgszOsLoadPartition;
  62. RGSZ rgszOsLoadFilename;
  63. SZ szOsLoader, szFileName, szDir;
  64. CHAR szDrive[] = "?:";
  65. DWORD Disk;
  66. DWORD dwSystemPartition = 0;
  67. DWORD dwOsLoader = 0;
  68. DWORD dwOsLoadPartition = 0;
  69. DWORD dwOsLoadFilename = 0;
  70. CHAR WindowsDirectory[MAX_PATH] , ArcWindowsDirectory[MAX_PATH];
  71. CHAR SystemPartition[MAX_PATH] , ProcessedArcSysPart[MAX_PATH];
  72. CHAR ArcForDosDrive[MAX_PATH] , ProcessedArcForDosDrive[MAX_PATH];
  73. CHAR OsLoaderDest[MAX_PATH];
  74. BOOL Status = TRUE, Done, Found;
  75. *TextOut = ReturnTextBuffer;
  76. if(cArgs != 4) {
  77. SetErrorText(IDS_ERROR_BADARGS);
  78. return(FALSE);
  79. }
  80. //
  81. // Find the windows directory
  82. //
  83. if( !GetWindowsDirectory( WindowsDirectory, MAX_PATH ) ) {
  84. SetErrorText(IDS_ERROR_GETWINDOWSDIR);
  85. return(FALSE);
  86. }
  87. //
  88. // Find the arcname for the windows dir
  89. //
  90. if (!DosPathToArcPathWorker( WindowsDirectory, ArcWindowsDirectory )) {
  91. return( FALSE );
  92. }
  93. //
  94. // Break all the lists into arrays
  95. //
  96. rgszSystemPartition = RgszFromSzListValue(Args[0]);
  97. rgszOsLoader = RgszFromSzListValue(Args[1]);
  98. rgszOsLoadPartition = RgszFromSzListValue(Args[2]);
  99. rgszOsLoadFilename = RgszFromSzListValue(Args[3]);
  100. if( !( rgszSystemPartition &&
  101. rgszOsLoader &&
  102. rgszOsLoadPartition &&
  103. rgszOsLoadFilename
  104. )
  105. ) {
  106. Status = FALSE;
  107. SetErrorText(IDS_ERROR_DLLOOM);
  108. goto r0;
  109. }
  110. //
  111. // Go through the NVRAM Variables in tandem. Note that the last one in
  112. // each list gets reused for the others.
  113. //
  114. // First ensure that atleast one component exists for each of the arc
  115. // boot vars
  116. if( rgszSystemPartition[0] == NULL ||
  117. rgszOsLoader[0] == NULL ||
  118. rgszOsLoadPartition[0] == NULL ||
  119. rgszOsLoadFilename[0] == NULL
  120. ) {
  121. Status = FALSE;
  122. SetErrorText(IDS_ERROR_NONVRAMVARS);
  123. goto r0;
  124. }
  125. //
  126. // Pick up set by set till done
  127. //
  128. Found = FALSE;
  129. while( TRUE ) {
  130. CHAR ArcOsLoadWindowsDir[MAX_PATH];
  131. BOOL AnyAdvanced = FALSE;
  132. //
  133. // Combine the current OsLoadPartition with the OsLoadFilename
  134. // and compare it with the windows directory arc
  135. //
  136. strcpy( ArcOsLoadWindowsDir, rgszOsLoadPartition[dwOsLoadPartition] );
  137. strcat( ArcOsLoadWindowsDir, rgszOsLoadFilename[dwOsLoadFilename] );
  138. if( ArcOsLoadWindowsDir[lstrlen(ArcOsLoadWindowsDir) - 1] == '\\' ) {
  139. ArcOsLoadWindowsDir[lstrlen(ArcOsLoadWindowsDir) - 1] = '\0';
  140. }
  141. if( CompareArcNames( ArcOsLoadWindowsDir, ArcWindowsDirectory ) ) {
  142. Found = TRUE;
  143. break;
  144. }
  145. //
  146. // Advance to next step
  147. //
  148. if ( rgszSystemPartition[dwSystemPartition+1] ) { dwSystemPartition++ ; AnyAdvanced = TRUE ; }
  149. if ( rgszOsLoader[dwOsLoader+1] ) { dwOsLoader++ ; AnyAdvanced = TRUE ; }
  150. if ( rgszOsLoadPartition[dwOsLoadPartition+1] ) { dwOsLoadPartition++ ; AnyAdvanced = TRUE ; }
  151. if ( rgszOsLoadFilename[dwOsLoadFilename+1] ) { dwOsLoadFilename++ ; AnyAdvanced = TRUE ; }
  152. if ( !AnyAdvanced ) {
  153. break;
  154. }
  155. }
  156. if( !Found ) {
  157. Status = FALSE;
  158. SetErrorText(IDS_ERROR_OSLOADNOTFND);
  159. goto r0;
  160. }
  161. //
  162. // Set has been found, extract the osloader destination and the system
  163. // partition arc variables.
  164. //
  165. szOsLoader = rgszOsLoader[dwOsLoader];
  166. szFileName = strrchr( szOsLoader, '\\' );
  167. szDir = strchr( szOsLoader, '\\' );
  168. if( !szFileName ) {
  169. Status = FALSE;
  170. SetErrorText(IDS_ERROR_BADOSLNVR);
  171. goto r0;
  172. }
  173. strncpy( SystemPartition, szOsLoader, (int)(szDir - szOsLoader) );
  174. SystemPartition[ szDir - szOsLoader ] = '\0';
  175. //
  176. // Process the names and run through the drive list to determine a
  177. // match
  178. //
  179. ProcessArcName( SystemPartition, ProcessedArcSysPart );
  180. Found = FALSE;
  181. for( Disk = 0; Disk < 26 ; Disk++ ) {
  182. *szDrive = (CHAR)(Disk) + (CHAR)'A';
  183. if( DosPathToArcPathWorker( szDrive, ArcForDosDrive ) ) {
  184. ProcessArcName( ArcForDosDrive, ProcessedArcForDosDrive );
  185. if( !lstrcmpi( ProcessedArcSysPart, ProcessedArcForDosDrive ) ) {
  186. Found = TRUE;
  187. strcpy( OsLoaderDest, szDrive );
  188. strncat( OsLoaderDest, szDir, (int)(szFileName - szDir) );
  189. break;
  190. }
  191. }
  192. }
  193. if( !Found ) {
  194. Status = FALSE;
  195. SetErrorText(IDS_ERROR_DOSNOTEXIST);
  196. goto r0;
  197. }
  198. Status = TRUE;
  199. SetReturnText( OsLoaderDest );
  200. r0:
  201. if( rgszSystemPartition ) { RgszFree( rgszSystemPartition ); }
  202. if( rgszOsLoader ) { RgszFree( rgszOsLoader ); }
  203. if( rgszOsLoadPartition ) { RgszFree( rgszOsLoadPartition ); }
  204. if( rgszOsLoadFilename ) { RgszFree( rgszOsLoadFilename ); }
  205. return( Status );
  206. #endif
  207. }
  208. VOID
  209. ProcessArcName(
  210. IN LPSTR NameIn,
  211. IN LPSTR NameOut
  212. )
  213. {
  214. DWORD i;
  215. for(i = 0; (NameOut[i] = *NameIn) != '\0'; i++, NameIn++) {
  216. if( *NameIn == '(' && *(NameIn + 1) == ')' ) {
  217. NameOut[++i] = '0';
  218. }
  219. }
  220. return;
  221. }
  222. BOOL
  223. CompareArcNames(
  224. IN LPSTR Name1,
  225. IN LPSTR Name2
  226. )
  227. {
  228. CHAR ProcessedName1[MAX_PATH];
  229. CHAR ProcessedName2[MAX_PATH];
  230. ProcessArcName( Name1, ProcessedName1 );
  231. ProcessArcName( Name2, ProcessedName2 );
  232. return( !lstrcmpi( ProcessedName1, ProcessedName2 ) );
  233. }
  234. BOOL
  235. DosPathToArcPathWorker(
  236. IN LPSTR DosPath,
  237. OUT LPSTR ArcPath
  238. )
  239. {
  240. CHAR Drive[] = "\\DosDevices\\?:";
  241. WCHAR NtNameDrive[MAX_PATH];
  242. WCHAR ArcNameDrive[MAX_PATH];
  243. ANSI_STRING AnsiString;
  244. UNICODE_STRING Drive_U, NtNameDrive_U, ObjDir_U, ArcNameDrive_U;
  245. ANSI_STRING ArcNameDrive_A;
  246. BOOL bStatus;
  247. NTSTATUS Status;
  248. //
  249. // Validate the DOS Path passed in
  250. //
  251. if (lstrlen(DosPath) < 2 || DosPath[1] != ':') {
  252. SetErrorText(IDS_ERROR_INVALIDDISK);
  253. return ( FALSE );
  254. }
  255. //
  256. // Extract the drive
  257. //
  258. Drive[12] = DosPath[0];
  259. //
  260. // Get Unicode string for the drive
  261. //
  262. RtlInitAnsiString(&AnsiString, Drive);
  263. Status = RtlAnsiStringToUnicodeString(
  264. &Drive_U,
  265. &AnsiString,
  266. TRUE
  267. );
  268. if (!NT_SUCCESS(Status)) {
  269. SetErrorText(IDS_ERROR_RTLOOM);
  270. return ( FALSE );
  271. }
  272. //
  273. // Initialise Unicode string to hold the Nt Name for the Drive
  274. //
  275. NtNameDrive_U.Buffer = NtNameDrive;
  276. NtNameDrive_U.Length = 0;
  277. NtNameDrive_U.MaximumLength = MAX_PATH * sizeof(WCHAR);
  278. //
  279. // Initialise Unicode string to hold the ArcName for the drive
  280. //
  281. ArcNameDrive_U.Buffer = ArcNameDrive;
  282. ArcNameDrive_U.Length = 0;
  283. ArcNameDrive_U.MaximumLength = MAX_PATH * sizeof(WCHAR);
  284. //
  285. // Initialise Unicode string to hold the \\ArcName objdir name
  286. //
  287. RtlInitUnicodeString(&ObjDir_U, ARCNAMEOBJDIR);
  288. //
  289. // Query symbolic link of the drive
  290. //
  291. bStatus = GetSymbolicLinkTarget(&Drive_U, &NtNameDrive_U);
  292. RtlFreeUnicodeString(&Drive_U);
  293. if (!bStatus) {
  294. return ( FALSE );
  295. }
  296. //
  297. // Find the object in the arcname directory
  298. //
  299. bStatus = GetSymbolicLinkSource(&ObjDir_U, &NtNameDrive_U, &ArcNameDrive_U);
  300. if (!bStatus) {
  301. return ( FALSE );
  302. }
  303. //
  304. // Convert the Unicode ArcName for drive to ansi
  305. //
  306. Status = RtlUnicodeStringToAnsiString(
  307. &ArcNameDrive_A,
  308. &ArcNameDrive_U,
  309. TRUE
  310. );
  311. if (!NT_SUCCESS(Status)) {
  312. SetErrorText(IDS_ERROR_RTLOOM);
  313. return ( FALSE );
  314. }
  315. //
  316. // Copy the drive to the output variable
  317. //
  318. ArcNameDrive_A.Buffer[ArcNameDrive_A.Length] = '\0'; //Null terminate
  319. lstrcpy(ArcPath, ArcNameDrive_A.Buffer);
  320. RtlFreeAnsiString(&ArcNameDrive_A);
  321. //
  322. // concatenate the rest of the DosPath to this ArcPath
  323. //
  324. lstrcat(ArcPath, DosPath+2);
  325. return(TRUE);
  326. }
  327. /*
  328. * OBJECT MANAGEMENT ROUTINES
  329. */
  330. BOOL
  331. GetSymbolicLinkSource(
  332. IN PUNICODE_STRING pObjDir_U,
  333. IN PUNICODE_STRING pTarget_U,
  334. OUT PUNICODE_STRING pSource_U
  335. )
  336. {
  337. PCHAR Buffer, FullNameObject, ObjectLink, SavedMatch;
  338. UNICODE_STRING ObjName_U, ObjLink_U;
  339. UNICODE_STRING SavedMatch_U;
  340. BOOLEAN IsMatchSaved = FALSE;
  341. OBJECT_ATTRIBUTES Attributes;
  342. HANDLE DirectoryHandle;
  343. ULONG Context = 0;
  344. ULONG ReturnedLength;
  345. BOOLEAN RestartScan = TRUE;
  346. BOOLEAN ReturnSingleEntry = TRUE; //LATER change this to FALSE
  347. POBJECT_DIRECTORY_INFORMATION DirInfo;
  348. NTSTATUS Status;
  349. BOOL bStatus;
  350. //
  351. // Open the object directory.
  352. //
  353. InitializeObjectAttributes(
  354. &Attributes,
  355. pObjDir_U,
  356. OBJ_CASE_INSENSITIVE,
  357. NULL,
  358. NULL
  359. );
  360. Status = NtOpenDirectoryObject(
  361. &DirectoryHandle,
  362. STANDARD_RIGHTS_READ | DIRECTORY_QUERY | DIRECTORY_TRAVERSE,
  363. &Attributes
  364. );
  365. if (!NT_SUCCESS( Status ) ) {
  366. SetErrorText(IDS_ERROR_OBJDIROPEN);
  367. return ( FALSE );
  368. }
  369. //
  370. // Find the symbolic link objects in the directory and query them for
  371. // a match with the string passed in
  372. //
  373. //
  374. // Allocate a buffer to query the directory objects
  375. //
  376. if ((Buffer = LocalAlloc(0, BUFFER_SIZE)) == NULL) {
  377. SetErrorText(IDS_ERROR_DLLOOM);
  378. NtClose(DirectoryHandle);
  379. return ( FALSE );
  380. }
  381. //
  382. // Form a Unicode string object to hold the symbolic link objects found
  383. // in the object directory
  384. //
  385. if ((FullNameObject = LocalAlloc(0, MAX_PATH * sizeof(WCHAR))) == NULL) {
  386. SetErrorText(IDS_ERROR_DLLOOM);
  387. LocalFree (Buffer);
  388. NtClose (DirectoryHandle);
  389. return ( FALSE );
  390. }
  391. ObjName_U.Buffer = (PWSTR)FullNameObject;
  392. ObjName_U.Length = 0;
  393. ObjName_U.MaximumLength = MAX_PATH * sizeof(WCHAR);
  394. //
  395. // Form a Unicode string object to hold the symbolic link objects found
  396. // in the object directory
  397. //
  398. if ((ObjectLink = LocalAlloc(0, MAX_PATH * sizeof(WCHAR))) == NULL) {
  399. SetErrorText(IDS_ERROR_DLLOOM);
  400. LocalFree (Buffer);
  401. LocalFree (FullNameObject);
  402. NtClose (DirectoryHandle);
  403. return ( FALSE );
  404. }
  405. ObjLink_U.Buffer = (PWSTR)ObjectLink;
  406. ObjLink_U.Length = 0;
  407. ObjLink_U.MaximumLength = MAX_PATH * sizeof(WCHAR);
  408. if ((SavedMatch = LocalAlloc(0, MAX_PATH * sizeof(WCHAR))) == NULL) {
  409. SetErrorText(IDS_ERROR_DLLOOM);
  410. LocalFree (Buffer);
  411. LocalFree (FullNameObject);
  412. LocalFree (ObjectLink);
  413. NtClose (DirectoryHandle);
  414. return ( FALSE );
  415. }
  416. SavedMatch_U.Buffer = (PWSTR)SavedMatch;
  417. SavedMatch_U.Length = 0;
  418. SavedMatch_U.MaximumLength = MAX_PATH * sizeof(WCHAR);
  419. while (TRUE) {
  420. //
  421. // Clear the buffer
  422. //
  423. RtlZeroMemory( Buffer, BUFFER_SIZE);
  424. //
  425. // repeatedly Query the directory objects till done
  426. //
  427. Status = NtQueryDirectoryObject(
  428. DirectoryHandle,
  429. Buffer,
  430. BUFFER_SIZE,
  431. ReturnSingleEntry, // fetch more than one entry
  432. RestartScan, // start rescan true on first go
  433. &Context,
  434. &ReturnedLength
  435. );
  436. //
  437. // Check the Status of the operation.
  438. //
  439. if (!NT_SUCCESS(Status) && (Status != STATUS_MORE_ENTRIES)) {
  440. if (Status == STATUS_NO_MORE_FILES || Status == STATUS_NO_MORE_ENTRIES) {
  441. SetErrorText(IDS_ERROR_INVALIDDISK);
  442. }
  443. else {
  444. SetErrorText(IDS_ERROR_OBJDIRREAD);
  445. }
  446. LocalFree(Buffer);
  447. LocalFree(FullNameObject);
  448. LocalFree(ObjectLink);
  449. if(IsMatchSaved) {
  450. RtlCopyUnicodeString (pSource_U, &SavedMatch_U);
  451. LocalFree(SavedMatch);
  452. return(TRUE);
  453. }
  454. LocalFree(SavedMatch);
  455. return(FALSE);
  456. }
  457. //
  458. // Make sure that restart scan is false for next go
  459. //
  460. RestartScan = FALSE;
  461. //
  462. // For every record in the buffer, see if the type of the object
  463. // is a symbolic link
  464. //
  465. //
  466. // Point to the first record in the buffer, we are guaranteed to have
  467. // one otherwise Status would have been No More Files
  468. //
  469. DirInfo = (POBJECT_DIRECTORY_INFORMATION) Buffer;
  470. while (TRUE) {
  471. //
  472. // Check if there is another record. If there isn't, break out of
  473. // the loop now
  474. //
  475. if (DirInfo->Name.Length == 0) {
  476. break;
  477. }
  478. //
  479. // See if the object type is a symbolic link
  480. //
  481. if (IsSymbolicLinkType(&(DirInfo->TypeName))) {
  482. //
  483. // get full pathname of object
  484. //
  485. //
  486. // Check if we will overflow our buffer
  487. //
  488. if ((
  489. pObjDir_U->Length +
  490. sizeof(WCHAR) +
  491. DirInfo->Name.Length +
  492. sizeof(WCHAR)
  493. ) > ObjName_U.MaximumLength ) {
  494. SetErrorText(IDS_ERROR_OBJNAMOVF);
  495. LocalFree(Buffer);
  496. LocalFree(FullNameObject);
  497. LocalFree(ObjectLink);
  498. if(IsMatchSaved) {
  499. RtlCopyUnicodeString (pSource_U, &SavedMatch_U);
  500. LocalFree(SavedMatch);
  501. return(TRUE);
  502. }
  503. LocalFree(SavedMatch);
  504. return( FALSE );
  505. }
  506. //
  507. // Copy the current object name over the the buffer prefixing
  508. // it with the \ArcName\ object directory name
  509. //
  510. RtlCopyUnicodeString ( &ObjName_U, pObjDir_U );
  511. RtlAppendUnicodeToString ( &ObjName_U, L"\\" );
  512. RtlAppendUnicodeStringToString ( &ObjName_U, &(DirInfo->Name));
  513. //
  514. // query the symbolic link target
  515. //
  516. ObjLink_U.Buffer = (PWSTR)ObjectLink;
  517. ObjLink_U.Length = 0;
  518. ObjLink_U.MaximumLength = MAX_PATH * sizeof(WCHAR);
  519. bStatus = GetSymbolicLinkTarget (&ObjName_U, &ObjLink_U);
  520. if (bStatus != TRUE) {
  521. LocalFree(Buffer);
  522. LocalFree(FullNameObject);
  523. LocalFree(ObjectLink);
  524. if(IsMatchSaved) {
  525. RtlCopyUnicodeString (pSource_U, &SavedMatch_U);
  526. LocalFree(SavedMatch);
  527. return(TRUE);
  528. }
  529. LocalFree(SavedMatch);
  530. return FALSE;
  531. }
  532. //
  533. // see if it compares to the name we are looking for
  534. //
  535. if (RtlEqualUnicodeString (pTarget_U, &ObjLink_U, TRUE)) {
  536. #if i386
  537. UNICODE_STRING Multi_U;
  538. RtlInitUnicodeString(&Multi_U,L"multi(");
  539. if(RtlPrefixUnicodeString(&Multi_U,&DirInfo->Name,TRUE)) {
  540. RtlCopyUnicodeString(&SavedMatch_U,&DirInfo->Name);
  541. IsMatchSaved = TRUE;
  542. } else // scsi, just return it. Favor scsi over multi.
  543. #endif
  544. {
  545. RtlCopyUnicodeString (pSource_U, &(DirInfo->Name));
  546. LocalFree(Buffer);
  547. LocalFree(FullNameObject);
  548. LocalFree(ObjectLink);
  549. LocalFree(SavedMatch);
  550. return TRUE;
  551. }
  552. }
  553. }
  554. //
  555. // There is another record so advance DirInfo to the next entry
  556. //
  557. DirInfo = (POBJECT_DIRECTORY_INFORMATION) (((PCHAR) DirInfo) +
  558. sizeof( OBJECT_DIRECTORY_INFORMATION ) );
  559. } // while
  560. } // while
  561. return ( FALSE );
  562. } // getarcname
  563. /* Checks to see if a given object type is a symbolic link type */
  564. BOOL
  565. IsSymbolicLinkType(
  566. IN PUNICODE_STRING Type
  567. )
  568. {
  569. UNICODE_STRING TypeName;
  570. //
  571. // Check the length of the string
  572. //
  573. if (Type->Length == 0) {
  574. return FALSE;
  575. }
  576. //
  577. // compare it to the symbolic link type name
  578. //
  579. RtlInitUnicodeString(&TypeName, SYMLINKTYPE);
  580. return (RtlEqualUnicodeString(Type, &TypeName, TRUE));
  581. }
  582. BOOL
  583. GetSymbolicLinkTarget(
  584. IN PUNICODE_STRING pSourceString_U,
  585. IN OUT PUNICODE_STRING pDestString_U
  586. )
  587. {
  588. NTSTATUS Status;
  589. OBJECT_ATTRIBUTES Attributes;
  590. HANDLE ObjectHandle;
  591. //
  592. // Initialise the object attributes structure for the symbolic
  593. // link object
  594. InitializeObjectAttributes(
  595. &Attributes,
  596. pSourceString_U,
  597. OBJ_CASE_INSENSITIVE,
  598. NULL,
  599. NULL
  600. );
  601. //
  602. // Open the symbolic link for link_query access
  603. //
  604. Status = NtOpenSymbolicLinkObject(
  605. &ObjectHandle,
  606. READ_CONTROL | SYMBOLIC_LINK_QUERY,
  607. &Attributes
  608. );
  609. if (!NT_SUCCESS(Status)) {
  610. SetErrorText(IDS_ERROR_SYMLNKOPEN);
  611. return ( FALSE );
  612. }
  613. //
  614. // query the symbolic link target
  615. //
  616. Status = NtQuerySymbolicLinkObject(
  617. ObjectHandle,
  618. pDestString_U,
  619. NULL
  620. );
  621. if (!NT_SUCCESS(Status)) {
  622. SetErrorText(IDS_ERROR_SYMLNKREAD);
  623. NtClose(ObjectHandle);
  624. return(FALSE);
  625. }
  626. //
  627. // close the link object
  628. //
  629. NtClose(ObjectHandle);
  630. //
  631. // return the link target
  632. //
  633. return ( TRUE );
  634. }