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.

1700 lines
48 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. oblink.c
  5. Abstract:
  6. Symbolic Link Object routines
  7. Author:
  8. Steve Wood (stevewo) 3-Aug-1989
  9. Revision History:
  10. --*/
  11. #include "obp.h"
  12. VOID
  13. ObpProcessDosDeviceSymbolicLink (
  14. POBJECT_SYMBOLIC_LINK SymbolicLink,
  15. ULONG Action
  16. );
  17. #if defined(ALLOC_PRAGMA)
  18. #pragma alloc_text(PAGE,NtCreateSymbolicLinkObject)
  19. #pragma alloc_text(PAGE,NtOpenSymbolicLinkObject)
  20. #pragma alloc_text(PAGE,NtQuerySymbolicLinkObject)
  21. #pragma alloc_text(PAGE,ObpParseSymbolicLink)
  22. #pragma alloc_text(PAGE,ObpDeleteSymbolicLink)
  23. #pragma alloc_text(PAGE,ObpDeleteSymbolicLinkName)
  24. #pragma alloc_text(PAGE,ObpCreateSymbolicLinkName)
  25. #pragma alloc_text(PAGE,ObpProcessDosDeviceSymbolicLink)
  26. #endif
  27. //
  28. // This is the object type for device objects.
  29. //
  30. extern POBJECT_TYPE IoDeviceObjectType;
  31. //
  32. // Global that enables/disables LUID device maps
  33. //
  34. extern ULONG ObpLUIDDeviceMapsEnabled;
  35. //
  36. // Local procedure prototypes
  37. //
  38. #define CREATE_SYMBOLIC_LINK 0
  39. #define DELETE_SYMBOLIC_LINK 1
  40. NTSTATUS
  41. NtCreateSymbolicLinkObject (
  42. OUT PHANDLE LinkHandle,
  43. IN ACCESS_MASK DesiredAccess,
  44. IN POBJECT_ATTRIBUTES ObjectAttributes,
  45. IN PUNICODE_STRING LinkTarget
  46. )
  47. /*++
  48. Routine Description:
  49. This function creates a symbolic link object, sets it initial value to
  50. value specified in the LinkTarget parameter, and opens a handle to the
  51. object with the specified desired access.
  52. Arguments:
  53. LinkHandle - Supplies a pointer to a variable that will receive the
  54. symbolic link object handle.
  55. DesiredAccess - Supplies the desired types of access for the symbolic link
  56. object.
  57. ObjectAttributes - Supplies a pointer to an object attributes structure.
  58. LinkTarget - Supplies the target name for the symbolic link object.
  59. Return Value:
  60. An appropriate status value
  61. --*/
  62. {
  63. KPROCESSOR_MODE PreviousMode;
  64. NTSTATUS Status;
  65. POBJECT_SYMBOLIC_LINK SymbolicLink;
  66. PVOID Object;
  67. HANDLE Handle;
  68. UNICODE_STRING CapturedLinkTarget;
  69. PAGED_CODE();
  70. //
  71. // Get previous processor mode and probe output arguments if necessary.
  72. //
  73. PreviousMode = KeGetPreviousMode();
  74. if (PreviousMode != KernelMode) {
  75. try {
  76. ProbeForReadSmallStructure( ObjectAttributes,
  77. sizeof( OBJECT_ATTRIBUTES ),
  78. sizeof( ULONG ));
  79. ProbeForReadSmallStructure( LinkTarget,
  80. sizeof( *LinkTarget ),
  81. sizeof( UCHAR ));
  82. CapturedLinkTarget = *LinkTarget;
  83. ProbeForRead( CapturedLinkTarget.Buffer,
  84. CapturedLinkTarget.MaximumLength,
  85. sizeof( UCHAR ));
  86. ProbeForWriteHandle( LinkHandle );
  87. } except( EXCEPTION_EXECUTE_HANDLER ) {
  88. return( GetExceptionCode() );
  89. }
  90. } else {
  91. CapturedLinkTarget = *LinkTarget;
  92. }
  93. //
  94. // Check if there is an odd MaximumLength
  95. //
  96. if (CapturedLinkTarget.MaximumLength % sizeof( WCHAR )) {
  97. //
  98. // Round down the MaximumLength to a valid even size
  99. //
  100. CapturedLinkTarget.MaximumLength = (CapturedLinkTarget.MaximumLength / sizeof( WCHAR )) * sizeof( WCHAR );
  101. }
  102. //
  103. // Error if link target name length is odd, the length is greater than
  104. // the maximum length, or zero and creating.
  105. //
  106. if ((CapturedLinkTarget.MaximumLength == 0) ||
  107. (CapturedLinkTarget.Length > CapturedLinkTarget.MaximumLength) ||
  108. (CapturedLinkTarget.Length % sizeof( WCHAR ))) {
  109. KdPrint(( "OB: Invalid symbolic link target - %wZ\n", &CapturedLinkTarget ));
  110. return( STATUS_INVALID_PARAMETER );
  111. }
  112. //
  113. // Create the symbolic link object
  114. //
  115. Status = ObCreateObject( PreviousMode,
  116. ObpSymbolicLinkObjectType,
  117. ObjectAttributes,
  118. PreviousMode,
  119. NULL,
  120. sizeof( *SymbolicLink ),
  121. 0,
  122. 0,
  123. (PVOID *)&SymbolicLink );
  124. if (!NT_SUCCESS( Status )) {
  125. return( Status );
  126. }
  127. //
  128. // Fill in symbolic link object with link target name string
  129. //
  130. KeQuerySystemTime( &SymbolicLink->CreationTime );
  131. SymbolicLink->DosDeviceDriveIndex = 0;
  132. SymbolicLink->LinkTargetObject = NULL;
  133. RtlInitUnicodeString( &SymbolicLink->LinkTargetRemaining, NULL );
  134. SymbolicLink->LinkTarget.MaximumLength = CapturedLinkTarget.MaximumLength;
  135. SymbolicLink->LinkTarget.Length = CapturedLinkTarget.Length;
  136. SymbolicLink->LinkTarget.Buffer = (PWCH)ExAllocatePoolWithTag( PagedPool,
  137. CapturedLinkTarget.MaximumLength,
  138. 'tmyS' );
  139. if (SymbolicLink->LinkTarget.Buffer == NULL) {
  140. ObDereferenceObject( SymbolicLink );
  141. return STATUS_NO_MEMORY;
  142. }
  143. try {
  144. RtlCopyMemory( SymbolicLink->LinkTarget.Buffer,
  145. CapturedLinkTarget.Buffer,
  146. CapturedLinkTarget.MaximumLength );
  147. } except( EXCEPTION_EXECUTE_HANDLER ) {
  148. ObDereferenceObject( SymbolicLink );
  149. return( GetExceptionCode() );
  150. }
  151. //
  152. // Insert symbolic link object in the current processes object table,
  153. // set symbolic link handle value and return status.
  154. //
  155. Status = ObInsertObject( SymbolicLink,
  156. NULL,
  157. DesiredAccess,
  158. 0,
  159. (PVOID *)&Object,
  160. &Handle );
  161. try {
  162. *LinkHandle = Handle;
  163. } except( EXCEPTION_EXECUTE_HANDLER ) {
  164. //
  165. // Fall through, since we do not want to undo what we have done.
  166. //
  167. }
  168. return( Status );
  169. }
  170. NTSTATUS
  171. NtOpenSymbolicLinkObject (
  172. OUT PHANDLE LinkHandle,
  173. IN ACCESS_MASK DesiredAccess,
  174. IN POBJECT_ATTRIBUTES ObjectAttributes
  175. )
  176. /*++
  177. Routine Description:
  178. This function opens a handle to an symbolic link object with the specified
  179. desired access.
  180. Arguments:
  181. LinkHandle - Supplies a pointer to a variable that will receive the
  182. symbolic link object handle.
  183. DesiredAccess - Supplies the desired types of access for the symbolic link
  184. object.
  185. ObjectAttributes - Supplies a pointer to an object attributes structure.
  186. Return Value:
  187. An appropriate status value
  188. --*/
  189. {
  190. KPROCESSOR_MODE PreviousMode;
  191. NTSTATUS Status;
  192. HANDLE Handle;
  193. PAGED_CODE();
  194. //
  195. // Get previous processor mode and probe output arguments if necessary.
  196. // The object attributes does not need to be probed because the
  197. // ObOpenObjectByName does the probe for us
  198. //
  199. PreviousMode = KeGetPreviousMode();
  200. if (PreviousMode != KernelMode) {
  201. try {
  202. ProbeForWriteHandle( LinkHandle );
  203. } except( EXCEPTION_EXECUTE_HANDLER ) {
  204. return( GetExceptionCode() );
  205. }
  206. }
  207. //
  208. // Open handle to the symbolic link object with the specified desired
  209. // access, set symbolic link handle value, and return service completion
  210. // status.
  211. //
  212. Status = ObOpenObjectByName( ObjectAttributes,
  213. ObpSymbolicLinkObjectType,
  214. PreviousMode,
  215. NULL,
  216. DesiredAccess,
  217. NULL,
  218. &Handle );
  219. try {
  220. *LinkHandle = Handle;
  221. } except( EXCEPTION_EXECUTE_HANDLER ) {
  222. //
  223. // Fall through, since we do not want to undo what we have done.
  224. //
  225. }
  226. return( Status );
  227. }
  228. NTSTATUS
  229. NtQuerySymbolicLinkObject (
  230. IN HANDLE LinkHandle,
  231. IN OUT PUNICODE_STRING LinkTarget,
  232. OUT PULONG ReturnedLength OPTIONAL
  233. )
  234. /*++
  235. Routine Description:
  236. This function queries the state of a symbolic link object and returns the
  237. requested information in the specified record structure.
  238. Arguments:
  239. LinkHandle - Supplies a handle to a symbolic link object. This handle
  240. must have SYMBOLIC_LINK_QUERY access granted.
  241. LinkTarget - Supplies a pointer to a record that is to receive the
  242. target name of the symbolic link object.
  243. ReturnedLength - Optionally receives the maximum length, in bytes, of
  244. the link target on return
  245. Return Value:
  246. An appropriate status value
  247. --*/
  248. {
  249. KPROCESSOR_MODE PreviousMode;
  250. NTSTATUS Status;
  251. POBJECT_SYMBOLIC_LINK SymbolicLink;
  252. UNICODE_STRING CapturedLinkTarget;
  253. //
  254. // Get previous processor mode and probe output arguments if necessary.
  255. //
  256. PAGED_CODE();
  257. PreviousMode = KeGetPreviousMode();
  258. if (PreviousMode != KernelMode) {
  259. try {
  260. ProbeForReadSmallStructure( LinkTarget,
  261. sizeof( *LinkTarget ),
  262. sizeof( WCHAR ) );
  263. ProbeForWriteUshort( &LinkTarget->Length );
  264. ProbeForWriteUshort( &LinkTarget->MaximumLength );
  265. CapturedLinkTarget = *LinkTarget;
  266. ProbeForWrite( CapturedLinkTarget.Buffer,
  267. CapturedLinkTarget.MaximumLength,
  268. sizeof( UCHAR ) );
  269. if (ARGUMENT_PRESENT( ReturnedLength )) {
  270. ProbeForWriteUlong( ReturnedLength );
  271. }
  272. } except( EXCEPTION_EXECUTE_HANDLER ) {
  273. return( GetExceptionCode() );
  274. }
  275. } else {
  276. CapturedLinkTarget = *LinkTarget;
  277. }
  278. //
  279. // Reference symbolic link object by handle, read current state, deference
  280. // symbolic link object, fill in target name structure and return service
  281. // status.
  282. //
  283. Status = ObReferenceObjectByHandle( LinkHandle,
  284. SYMBOLIC_LINK_QUERY,
  285. ObpSymbolicLinkObjectType,
  286. PreviousMode,
  287. (PVOID *)&SymbolicLink,
  288. NULL );
  289. if (NT_SUCCESS( Status )) {
  290. POBJECT_HEADER ObjectHeader;
  291. ObjectHeader = OBJECT_TO_OBJECT_HEADER( SymbolicLink );
  292. ObpLockObject( ObjectHeader );
  293. //
  294. // If the caller wants a return length and what we found can easily
  295. // fit in the output buffer then we copy into the output buffer all
  296. // the bytes from the link.
  297. //
  298. // If the caller did not want a return length and we found can still
  299. // easily fit in the output buffer then copy over the bytes that just
  300. // make up the string and nothing extra
  301. //
  302. if ((ARGUMENT_PRESENT( ReturnedLength ) &&
  303. (SymbolicLink->LinkTarget.MaximumLength <= CapturedLinkTarget.MaximumLength))
  304. ||
  305. (!ARGUMENT_PRESENT( ReturnedLength ) &&
  306. (SymbolicLink->LinkTarget.Length <= CapturedLinkTarget.MaximumLength)) ) {
  307. try {
  308. RtlCopyMemory( CapturedLinkTarget.Buffer,
  309. SymbolicLink->LinkTarget.Buffer,
  310. ARGUMENT_PRESENT( ReturnedLength ) ? SymbolicLink->LinkTarget.MaximumLength
  311. : SymbolicLink->LinkTarget.Length );
  312. LinkTarget->Length = SymbolicLink->LinkTarget.Length;
  313. if (ARGUMENT_PRESENT( ReturnedLength )) {
  314. *ReturnedLength = SymbolicLink->LinkTarget.MaximumLength;
  315. }
  316. } except( EXCEPTION_EXECUTE_HANDLER ) {
  317. //
  318. // Fall through, since we do cannot undo what we have done.
  319. //
  320. }
  321. } else {
  322. //
  323. // The output buffer is just too small for the link target, but
  324. // we'll tell the user how much is needed if they asked for that
  325. // return value
  326. //
  327. if (ARGUMENT_PRESENT( ReturnedLength )) {
  328. try {
  329. *ReturnedLength = SymbolicLink->LinkTarget.MaximumLength;
  330. } except( EXCEPTION_EXECUTE_HANDLER ) {
  331. //
  332. // Fall through, since we do cannot undo what we have done.
  333. //
  334. }
  335. }
  336. Status = STATUS_BUFFER_TOO_SMALL;
  337. }
  338. ObpUnlockObject( ObjectHeader );
  339. ObDereferenceObject( SymbolicLink );
  340. }
  341. return( Status );
  342. }
  343. NTSTATUS
  344. ObpParseSymbolicLink (
  345. IN PVOID ParseObject,
  346. IN PVOID ObjectType,
  347. IN PACCESS_STATE AccessState,
  348. IN KPROCESSOR_MODE AccessMode,
  349. IN ULONG Attributes,
  350. IN OUT PUNICODE_STRING CompleteName,
  351. IN OUT PUNICODE_STRING RemainingName,
  352. IN OUT PVOID Context OPTIONAL,
  353. IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
  354. OUT PVOID *Object
  355. )
  356. /*++
  357. Routine Description:
  358. This is the call back routine for parsing symbolic link objects. It is invoked
  359. as part of ObpLookupObjectName
  360. Arguments:
  361. ParseObject - This will actually be a symbolic link object
  362. ObjectType - Specifies the type of the object to lookup
  363. AccessState - Current access state, describing already granted access
  364. types, the privileges used to get them, and any access types yet to
  365. be granted. The access masks may not contain any generic access
  366. types.
  367. AccessMode - Specifies the callers processor mode
  368. Attributes - Specifies the attributes for the lookup (e.g., case
  369. insensitive)
  370. CompleteName - Supplies a pointer to the complete name that we are trying
  371. to open. On return this could be modified to fit the new reparse
  372. buffer
  373. RemainingName - Supplies a pointer to the remaining name that we are
  374. trying to open. On return this will point to what remains after
  375. we processed the symbolic link.
  376. Context - Unused
  377. SecurityQos - Unused
  378. Object - Receives a pointer to the symbolic link object that we
  379. resolve to
  380. Return Value:
  381. STATUS_REPARSE_OBJECT if the parse object is already a snapped
  382. symbolic link meaning we've modified the remaining name and
  383. and have returned the target object of the symbolic link
  384. STATUS_REPARSE if the parse object has not been snapped. In this
  385. case the Complete name has been modified with the link target
  386. name added in front of the remaining name. The parameters
  387. remaining name and object must now be ignored by the caller
  388. An appropriate error value
  389. --*/
  390. {
  391. ULONG NewLength;
  392. USHORT Length;
  393. USHORT MaximumLength;
  394. PWCHAR NewName, NewRemainingName;
  395. ULONG InsertAmount;
  396. NTSTATUS Status;
  397. POBJECT_SYMBOLIC_LINK SymbolicLink;
  398. PUNICODE_STRING LinkTargetName;
  399. UNREFERENCED_PARAMETER (Context);
  400. UNREFERENCED_PARAMETER (AccessState);
  401. UNREFERENCED_PARAMETER (SecurityQos);
  402. UNREFERENCED_PARAMETER (Attributes);
  403. PAGED_CODE();
  404. //
  405. // This routine needs to be synchonized with the delete symbolic link
  406. // operation. Which uses the root directory mutex.
  407. //
  408. *Object = NULL;
  409. //
  410. // If there isn't any name remaining and the caller gave us
  411. // an object type then we'll reference the parse object. If
  412. // this is successful then that's the object we return. Otherwise
  413. // if the status is anything but a type mismatch then we'll
  414. // return that error status
  415. //
  416. if (RemainingName->Length == 0) {
  417. if ( ObjectType ) {
  418. Status = ObReferenceObjectByPointer( ParseObject,
  419. 0,
  420. ObjectType,
  421. AccessMode );
  422. if (NT_SUCCESS( Status )) {
  423. *Object = ParseObject;
  424. return Status;
  425. } else if (Status != STATUS_OBJECT_TYPE_MISMATCH) {
  426. return Status;
  427. }
  428. }
  429. //
  430. // If the remaining name does not start with a "\" then
  431. // its is illformed and we'll call it a type mismatch
  432. //
  433. } else if (*(RemainingName->Buffer) != OBJ_NAME_PATH_SEPARATOR) {
  434. return STATUS_OBJECT_TYPE_MISMATCH;
  435. }
  436. //
  437. // A symbolic link has been encountered. See if this link has been snapped
  438. // to a particular object.
  439. //
  440. SymbolicLink = (POBJECT_SYMBOLIC_LINK)ParseObject;
  441. if (SymbolicLink->LinkTargetObject != NULL) {
  442. //
  443. // This is a snapped link. Get the remaining portion of the
  444. // symbolic link target, if any.
  445. //
  446. LinkTargetName = &SymbolicLink->LinkTargetRemaining;
  447. if (LinkTargetName->Length == 0) {
  448. //
  449. // Remaining link target string is zero, so return to caller
  450. // quickly with snapped object pointer and remaining object name
  451. // which we haven't touched yet.
  452. //
  453. *Object = SymbolicLink->LinkTargetObject;
  454. return STATUS_REPARSE_OBJECT;
  455. }
  456. //
  457. // We have a snapped symbolic link that has additional text.
  458. // Insert that in front of the current remaining name, preserving
  459. // and text between CompleteName and RemainingName
  460. //
  461. InsertAmount = LinkTargetName->Length;
  462. if ((LinkTargetName->Buffer[ (InsertAmount / sizeof( WCHAR )) - 1 ] == OBJ_NAME_PATH_SEPARATOR)
  463. &&
  464. (*(RemainingName->Buffer) == OBJ_NAME_PATH_SEPARATOR)) {
  465. //
  466. // Both the link target name ends in a "\" and the remaining
  467. // starts with a "\" but we only need one when we're done
  468. //
  469. InsertAmount -= sizeof( WCHAR );
  470. }
  471. //
  472. // We need to bias the differnce between two
  473. // pointers with * sizeof(wchar) because the difference is in wchar
  474. // and we need the length in bytes
  475. //
  476. NewLength = (ULONG)(((RemainingName->Buffer - CompleteName->Buffer) * sizeof( WCHAR )) +
  477. InsertAmount +
  478. RemainingName->Length);
  479. if (NewLength > 0xFFF0) {
  480. return STATUS_NAME_TOO_LONG;
  481. }
  482. Length = (USHORT)NewLength;
  483. //
  484. // Now check if the new computed length is too big for the input
  485. // buffer containing the complete name
  486. //
  487. if (CompleteName->MaximumLength <= Length) {
  488. //
  489. // The new concatentated name is larger than the buffer supplied for
  490. // the complete name. Allocate space for this new string
  491. //
  492. MaximumLength = Length + sizeof( UNICODE_NULL );
  493. NewName = ExAllocatePoolWithTag( OB_NAMESPACE_POOL_TYPE, MaximumLength, 'mNbO' );
  494. if (NewName == NULL) {
  495. return STATUS_INSUFFICIENT_RESOURCES;
  496. }
  497. //
  498. // Calculate the pointer within this buffer for the remaining
  499. // name. This value has not been biased by the new link
  500. // target name
  501. //
  502. NewRemainingName = NewName + (RemainingName->Buffer - CompleteName->Buffer);
  503. //
  504. // Copy over all the names that we've processed so far
  505. //
  506. RtlCopyMemory( NewName,
  507. CompleteName->Buffer,
  508. ((RemainingName->Buffer - CompleteName->Buffer) * sizeof( WCHAR )));
  509. //
  510. // If we have some remaining names then those over at the
  511. // the location offset to hold the link target name
  512. //
  513. if (RemainingName->Length != 0) {
  514. RtlCopyMemory( (PVOID)((PUCHAR)NewRemainingName + InsertAmount),
  515. RemainingName->Buffer,
  516. RemainingName->Length );
  517. }
  518. //
  519. // Now insert the link target name
  520. //
  521. RtlCopyMemory( NewRemainingName, LinkTargetName->Buffer, InsertAmount );
  522. //
  523. // Free the old complete name buffer and reset the input
  524. // strings to use the new buffer
  525. //
  526. ExFreePool( CompleteName->Buffer );
  527. CompleteName->Buffer = NewName;
  528. CompleteName->Length = Length;
  529. CompleteName->MaximumLength = MaximumLength;
  530. RemainingName->Buffer = NewRemainingName;
  531. RemainingName->Length = Length - (USHORT)((PCHAR)NewRemainingName - (PCHAR)NewName);
  532. RemainingName->MaximumLength = RemainingName->Length + sizeof( UNICODE_NULL );
  533. } else {
  534. //
  535. // Insert extra text associated with this symbolic link name before
  536. // existing remaining name, if any.
  537. //
  538. // First shove over the remaining name to make a hole for the
  539. // link target name
  540. //
  541. if (RemainingName->Length != 0) {
  542. RtlMoveMemory( (PVOID)((PUCHAR)RemainingName->Buffer + InsertAmount),
  543. RemainingName->Buffer,
  544. RemainingName->Length );
  545. }
  546. //
  547. // Now insert the link target name
  548. //
  549. RtlCopyMemory( RemainingName->Buffer, LinkTargetName->Buffer, InsertAmount );
  550. //
  551. // Adjust input strings to account for this inserted text
  552. //
  553. CompleteName->Length = (USHORT)(CompleteName->Length + LinkTargetName->Length);
  554. RemainingName->Length = (USHORT)(RemainingName->Length + LinkTargetName->Length);
  555. RemainingName->MaximumLength += RemainingName->Length + sizeof( UNICODE_NULL );
  556. CompleteName->Buffer[ CompleteName->Length / sizeof( WCHAR ) ] = UNICODE_NULL;
  557. }
  558. //
  559. // Return the object address associated with snapped symbolic link
  560. // and the reparse object status code.
  561. //
  562. *Object = SymbolicLink->LinkTargetObject;
  563. return STATUS_REPARSE_OBJECT;
  564. }
  565. //
  566. // The symbolic has not yet been snapped
  567. //
  568. // Compute the size of the new name and check if the name will
  569. // fit in the existing complete name buffer.
  570. //
  571. LinkTargetName = &SymbolicLink->LinkTarget;
  572. InsertAmount = LinkTargetName->Length;
  573. if ((InsertAmount != 0)
  574. &&
  575. (LinkTargetName->Buffer[ (InsertAmount / sizeof( WCHAR )) - 1 ] == OBJ_NAME_PATH_SEPARATOR)
  576. &&
  577. (RemainingName->Length != 0)
  578. &&
  579. (*(RemainingName->Buffer) == OBJ_NAME_PATH_SEPARATOR)) {
  580. //
  581. // Both the link target name ends in a "\" and the remaining
  582. // starts with a "\" but we only need one when we're done
  583. //
  584. InsertAmount -= sizeof( WCHAR );
  585. }
  586. NewLength = InsertAmount + RemainingName->Length;
  587. if (NewLength > 0xFFF0) {
  588. return STATUS_NAME_TOO_LONG;
  589. }
  590. Length = (USHORT)NewLength;
  591. if (CompleteName->MaximumLength <= Length) {
  592. //
  593. // The new concatentated name is larger than the buffer supplied for
  594. // the complete name.
  595. //
  596. MaximumLength = Length + sizeof( UNICODE_NULL );
  597. NewName = ExAllocatePoolWithTag( OB_NAMESPACE_POOL_TYPE, MaximumLength, 'mNbO' );
  598. if (NewName == NULL) {
  599. return STATUS_INSUFFICIENT_RESOURCES;
  600. }
  601. } else {
  602. MaximumLength = CompleteName->MaximumLength;
  603. NewName = CompleteName->Buffer;
  604. }
  605. //
  606. // Concatenate the symbolic link name with the remaining name,
  607. // if any. What this does is overwrite the front of the complete
  608. // name up to the remaining name with the links target name
  609. //
  610. if (RemainingName->Length != 0) {
  611. RtlMoveMemory( (PVOID)((PUCHAR)NewName + InsertAmount),
  612. RemainingName->Buffer,
  613. RemainingName->Length );
  614. }
  615. RtlCopyMemory( NewName, LinkTargetName->Buffer, InsertAmount );
  616. NewName[ Length / sizeof( WCHAR ) ] = UNICODE_NULL;
  617. //
  618. // If a new name buffer was allocated, then free the original complete
  619. // name buffer.
  620. //
  621. if (NewName != CompleteName->Buffer) {
  622. ExFreePool( CompleteName->Buffer );
  623. }
  624. //
  625. // Set the new complete name buffer parameters and return a reparse
  626. // status.
  627. //
  628. CompleteName->Buffer = NewName;
  629. CompleteName->Length = Length;
  630. CompleteName->MaximumLength = MaximumLength;
  631. return STATUS_REPARSE;
  632. }
  633. VOID
  634. ObpDeleteSymbolicLink (
  635. IN PVOID Object
  636. )
  637. /*++
  638. Routine Description:
  639. This routine is called when a reference to a symbolic link goes to zero.
  640. Its job is to clean up the memory used to the symbolic link string
  641. Arguments:
  642. Object - Supplies a pointer to the symbolic link object being deleted
  643. Return Value:
  644. None.
  645. --*/
  646. {
  647. POBJECT_SYMBOLIC_LINK SymbolicLink = (POBJECT_SYMBOLIC_LINK)Object;
  648. PAGED_CODE();
  649. //
  650. // The only cleaning up we need to do is to free the link target
  651. // buffer
  652. //
  653. if (SymbolicLink->LinkTarget.Buffer != NULL) {
  654. ExFreePool( SymbolicLink->LinkTarget.Buffer );
  655. }
  656. SymbolicLink->LinkTarget.Buffer = NULL;
  657. //
  658. // And return to our caller
  659. //
  660. return;
  661. }
  662. VOID
  663. ObpDeleteSymbolicLinkName (
  664. POBJECT_SYMBOLIC_LINK SymbolicLink
  665. )
  666. /*++
  667. Routine Description:
  668. This routine delete the symbolic from the system
  669. Arguments:
  670. SymbolicLink - Supplies a pointer to the object body for the symbolic
  671. link object to delete
  672. Return Value:
  673. None.
  674. This function must be called with the symbolic link object locked.
  675. That lock is protecting the symlink specific fields.
  676. --*/
  677. {
  678. ObpProcessDosDeviceSymbolicLink( SymbolicLink, DELETE_SYMBOLIC_LINK );
  679. return;
  680. }
  681. VOID
  682. ObpCreateSymbolicLinkName (
  683. POBJECT_SYMBOLIC_LINK SymbolicLink
  684. )
  685. /*++
  686. Routine Description:
  687. This routine does extra processing for symbolic links being created in
  688. object directories controlled by device map objects.
  689. This processing consists of:
  690. 1. Determine if the name of the symbolic link is a drive letter.
  691. If so, then we will need to update the drive type in the
  692. associated device map object.
  693. 2. Process the link target, trying to resolve it into a pointer to
  694. an object other than a object directory object. All object
  695. directories traversed must grant world traverse access other
  696. wise we bail out. If we successfully find a non object
  697. directory object, then reference the object pointer and store it
  698. in the symbolic link object, along with a remaining string if
  699. any. ObpLookupObjectName will used this cache object pointer to
  700. short circuit the name lookup directly to the cached object's
  701. parse routine. For any object directory objects traversed along
  702. the way, increment their symbolic link SymbolicLinkUsageCount
  703. field. This field is used whenever an object directory is
  704. deleted or its security is changed such that it no longer grants
  705. world traverse access. In either case, if the field is non-zero
  706. we walk all the symbolic links and resnap them.
  707. Arguments:
  708. SymbolicLink - pointer to symbolic link object being created.
  709. Return Value:
  710. None.
  711. --*/
  712. {
  713. POBJECT_HEADER ObjectHeader;
  714. POBJECT_HEADER_NAME_INFO NameInfo;
  715. WCHAR DosDeviceDriveLetter;
  716. ULONG DosDeviceDriveIndex;
  717. //
  718. // Now see if this symbolic link is being created in an object directory
  719. // controlled by a device map object. Since we are only called from
  720. // NtCreateSymbolicLinkObject, after the handle to this symbolic link
  721. // has been created but before it is returned to the caller the handle can't
  722. // be closed while we are executing, unless via a random close,
  723. // So no need to hold the type specific mutex while we look at the name.
  724. //
  725. ObjectHeader = OBJECT_TO_OBJECT_HEADER( SymbolicLink );
  726. NameInfo = ObpReferenceNameInfo( ObjectHeader );
  727. if ((NameInfo == NULL) ||
  728. (NameInfo->Directory == NULL) ||
  729. (NameInfo->Directory->DeviceMap == NULL)) {
  730. ObpDereferenceNameInfo( NameInfo );
  731. return;
  732. }
  733. //
  734. // Here if we are creating a symbolic link in an object directory controlled
  735. // by a device map object. See if this is a drive letter definition. If so
  736. // calculate the drive letter index and remember in the symbolic link object.
  737. //
  738. DosDeviceDriveIndex = 0;
  739. if ((NameInfo->Name.Length == (2 * sizeof( WCHAR ))) &&
  740. (NameInfo->Name.Buffer[ 1 ] == L':')) {
  741. DosDeviceDriveLetter = RtlUpcaseUnicodeChar( NameInfo->Name.Buffer[ 0 ] );
  742. if ((DosDeviceDriveLetter >= L'A') && (DosDeviceDriveLetter <= L'Z')) {
  743. DosDeviceDriveIndex = DosDeviceDriveLetter - L'A';
  744. DosDeviceDriveIndex += 1;
  745. SymbolicLink->DosDeviceDriveIndex = DosDeviceDriveIndex;
  746. }
  747. }
  748. //
  749. // Now traverse the target path seeing if we can snap the link now.
  750. //
  751. ObpProcessDosDeviceSymbolicLink( SymbolicLink, CREATE_SYMBOLIC_LINK );
  752. ObpDereferenceNameInfo( NameInfo );
  753. return;
  754. }
  755. //
  756. // Local support routine
  757. //
  758. #define MAX_DEPTH 16
  759. VOID
  760. ObpProcessDosDeviceSymbolicLink (
  761. POBJECT_SYMBOLIC_LINK SymbolicLink,
  762. ULONG Action
  763. )
  764. /*++
  765. Routine Description:
  766. This function is called whenever a symbolic link is created or deleted
  767. in an object directory controlled by a device map object.
  768. For creates, it attempts to snap the symbolic link to a non-object
  769. directory object. It does this by walking the symbolic link target
  770. string, until it sees a non-directory object or a directory object
  771. that does NOT allow World traverse access. It stores a referenced
  772. pointer to this object in the symbolic link object. It also
  773. increments a count in each of the object directory objects that it
  774. walked over. This count is used to disallow any attempt to remove
  775. World traverse access from a directory object after it has
  776. participated in a snapped symbolic link.
  777. For deletes, it repeats the walk of the target string, decrementing
  778. the count associated with each directory object walked over. It also
  779. dereferences the snapped object pointer.
  780. Arguments:
  781. SymbolicLink - pointer to symbolic link object being created or deleted.
  782. Action - describes whether this is a create or a delete action
  783. Return Value:
  784. None.
  785. --*/
  786. {
  787. PVOID Object;
  788. POBJECT_HEADER ObjectHeader;
  789. POBJECT_HEADER_NAME_INFO NameInfo;
  790. UNICODE_STRING RemainingName;
  791. UNICODE_STRING ComponentName;
  792. #if 0
  793. NTSTATUS Status;
  794. PSECURITY_DESCRIPTOR SecurityDescriptor;
  795. BOOLEAN MemoryAllocated;
  796. BOOLEAN HaveWorldTraverseAccess;
  797. ULONG Depth;
  798. POBJECT_DIRECTORY Directories[ MAX_DEPTH ];
  799. #endif
  800. POBJECT_DIRECTORY Directory, ParentDirectory;
  801. PDEVICE_OBJECT DeviceObject;
  802. PDEVICE_MAP DeviceMap = NULL;
  803. ULONG DosDeviceDriveType;
  804. BOOLEAN DeviceMapUsed = FALSE;
  805. UNICODE_STRING RemainingTarget;
  806. ULONG MaxReparse = OBJ_MAX_REPARSE_ATTEMPTS;
  807. OBP_LOOKUP_CONTEXT LookupContext;
  808. POBJECT_DIRECTORY SymLinkDirectory = NULL;
  809. BOOLEAN PreviousLockingState;
  810. ObjectHeader = OBJECT_TO_OBJECT_HEADER( SymbolicLink );
  811. NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );
  812. if (NameInfo != NULL) {
  813. SymLinkDirectory = NameInfo->Directory;
  814. }
  815. Object = NULL;
  816. RtlInitUnicodeString( &RemainingTarget, NULL );
  817. ObpInitializeLookupContext( &LookupContext );
  818. //
  819. // Check if we are creating a symbolic link or if the link has already
  820. // been snapped
  821. //
  822. if ((Action == CREATE_SYMBOLIC_LINK) ||
  823. (SymbolicLink->LinkTargetObject != NULL)) {
  824. ParentDirectory = NULL;
  825. #if 0
  826. Depth = 0;
  827. #endif
  828. Directory = ObpRootDirectoryObject;
  829. RemainingName = SymbolicLink->LinkTarget;
  830. //
  831. // If LUID device maps are enabled,
  832. // then use the Object Manager's pointer to the global
  833. // device map
  834. // With LUID device maps enabled, the process' device map pointer
  835. // may be NULL
  836. //
  837. if (ObpLUIDDeviceMapsEnabled != 0) {
  838. DeviceMap = ObSystemDeviceMap;
  839. }
  840. else {
  841. //
  842. // use the device map associated with the process
  843. //
  844. DeviceMap = PsGetCurrentProcess()->DeviceMap;
  845. }
  846. ReCalcDeviceMap:
  847. if (DeviceMap) {
  848. if (!((ULONG_PTR)(RemainingName.Buffer) & (sizeof(ULONGLONG)-1))
  849. &&
  850. (DeviceMap->DosDevicesDirectory != NULL )) {
  851. //
  852. // Check if the object name is actually equal to the
  853. // global dos devices short name prefix "\??\"
  854. //
  855. if ((RemainingName.Length >= ObpDosDevicesShortName.Length)
  856. &&
  857. (*(PULONGLONG)(RemainingName.Buffer) == ObpDosDevicesShortNamePrefix.Alignment.QuadPart)) {
  858. //
  859. // The user gave us the dos short name prefix so we'll
  860. // look down the directory, and start the search at the
  861. // dos device directory
  862. //
  863. Directory = DeviceMap->DosDevicesDirectory;
  864. RemainingName.Buffer += (ObpDosDevicesShortName.Length / sizeof( WCHAR ));
  865. RemainingName.Length = (USHORT)(RemainingName.Length - ObpDosDevicesShortName.Length);
  866. DeviceMapUsed = TRUE;
  867. }
  868. }
  869. }
  870. //
  871. // The following loop will dissect the link target checking
  872. // that each directory exists and that we have access to
  873. // the directory. When we pop out the local directories
  874. // array will contain a list of directories that we need
  875. // to traverse to process this action.
  876. //
  877. while (TRUE) {
  878. //
  879. // Gobble up the "\" in the remaining name
  880. //
  881. if (*(RemainingName.Buffer) == OBJ_NAME_PATH_SEPARATOR) {
  882. RemainingName.Buffer++;
  883. RemainingName.Length -= sizeof( OBJ_NAME_PATH_SEPARATOR );
  884. }
  885. //
  886. // And dissect the name into its first component and any
  887. // remaining part
  888. //
  889. ComponentName = RemainingName;
  890. while (RemainingName.Length != 0) {
  891. if (*(RemainingName.Buffer) == OBJ_NAME_PATH_SEPARATOR) {
  892. break;
  893. }
  894. RemainingName.Buffer++;
  895. RemainingName.Length -= sizeof( OBJ_NAME_PATH_SEPARATOR );
  896. }
  897. ComponentName.Length = (USHORT)(ComponentName.Length - RemainingName.Length);
  898. if (ComponentName.Length == 0) {
  899. ObpReleaseLookupContext(&LookupContext);
  900. return;
  901. }
  902. #if 0
  903. //
  904. // See if we have world traverse access to look this name up
  905. //
  906. if (ParentDirectory != NULL) {
  907. HaveWorldTraverseAccess = FALSE;
  908. //
  909. // Obtain the object's security descriptor
  910. //
  911. Status = ObGetObjectSecurity( ParentDirectory,
  912. &SecurityDescriptor,
  913. &MemoryAllocated );
  914. if (NT_SUCCESS( Status )) {
  915. //
  916. // Check to see if WORLD has TRAVERSE access and then release
  917. // the security descriptor
  918. //
  919. HaveWorldTraverseAccess = SeFastTraverseCheck( SecurityDescriptor,
  920. DIRECTORY_TRAVERSE,
  921. UserMode );
  922. ObReleaseObjectSecurity( SecurityDescriptor,
  923. MemoryAllocated );
  924. }
  925. if (!HaveWorldTraverseAccess) {
  926. Object = NULL;
  927. break;
  928. }
  929. if (Depth >= MAX_DEPTH) {
  930. Object = NULL;
  931. break;
  932. }
  933. Directories[ Depth++ ] = ParentDirectory;
  934. }
  935. #endif
  936. //
  937. // Look this component name up in this directory. If not found, then
  938. // bail.
  939. //
  940. //
  941. // If we are searching the same directory that contains the sym link
  942. // we have already the directory exclusively locked. We need to adjust
  943. // the lookupcontext state and avoid recursive locking
  944. //
  945. if (Directory == SymLinkDirectory) {
  946. PreviousLockingState = LookupContext.DirectoryLocked;
  947. LookupContext.DirectoryLocked = TRUE;
  948. }
  949. else {
  950. PreviousLockingState = FALSE;
  951. }
  952. Object = ObpLookupDirectoryEntry( Directory,
  953. &ComponentName,
  954. 0,
  955. FALSE ,
  956. &LookupContext);
  957. if (Directory == SymLinkDirectory) {
  958. LookupContext.DirectoryLocked = PreviousLockingState;
  959. }
  960. if (Object == NULL) {
  961. break;
  962. }
  963. //
  964. // See if this is a object directory object. If so, keep going
  965. //
  966. ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
  967. if (ObjectHeader->Type == ObpDirectoryObjectType) {
  968. ParentDirectory = Directory;
  969. Directory = (POBJECT_DIRECTORY)Object;
  970. } else if ((ObjectHeader->Type == ObpSymbolicLinkObjectType) &&
  971. (((POBJECT_SYMBOLIC_LINK)Object)->DosDeviceDriveIndex == 0)) {
  972. //
  973. // To prevent Denial of Service attacks from parsing
  974. // symbolic links infinitely.
  975. // Check the number of symbolic link parse attempts
  976. //
  977. if (MaxReparse == 0) {
  978. Object = NULL;
  979. break;
  980. }
  981. MaxReparse--;
  982. //
  983. // Found a symbolic link to another symbolic link that is
  984. // not already snapped. So switch to its target string
  985. // so we can chase down the real device object
  986. //
  987. ParentDirectory = NULL;
  988. #if 0
  989. Depth = 0;
  990. #endif
  991. Directory = ObpRootDirectoryObject;
  992. //
  993. // Save the remaining name
  994. //
  995. if (RemainingTarget.Length == 0) {
  996. RemainingTarget = RemainingName;
  997. }
  998. RemainingName = ((POBJECT_SYMBOLIC_LINK)Object)->LinkTarget;
  999. goto ReCalcDeviceMap;
  1000. } else {
  1001. //
  1002. // Not an object directory object, or a symbolic link to an
  1003. // unsnapped symbolic link, so all done. Exit the loop
  1004. //
  1005. break;
  1006. }
  1007. }
  1008. #if 0
  1009. //
  1010. // Done walking the target path. Now update the counts associated
  1011. // with each directory object walked over.
  1012. //
  1013. while (Depth--) {
  1014. Directory = Directories[ Depth ];
  1015. if (Action == CREATE_SYMBOLIC_LINK) {
  1016. if (Object != NULL) {
  1017. Directory->SymbolicLinkUsageCount += 1;
  1018. }
  1019. } else {
  1020. Directory->SymbolicLinkUsageCount -= 1;
  1021. }
  1022. }
  1023. #endif
  1024. }
  1025. //
  1026. // Done processing symbolic link target path. Update symbolic link
  1027. // object as appropriate for passed in reason
  1028. //
  1029. //
  1030. // If this is a drive letter symbolic link, get the address of the device
  1031. // map object that is controlling the containing object directory so we
  1032. // can update the drive type in the device map.
  1033. //
  1034. DeviceMap = NULL;
  1035. if (SymbolicLink->DosDeviceDriveIndex != 0) {
  1036. ObjectHeader = OBJECT_TO_OBJECT_HEADER( SymbolicLink );
  1037. NameInfo = ObpReferenceNameInfo( ObjectHeader );
  1038. if (NameInfo != NULL && NameInfo->Directory) {
  1039. DeviceMap = NameInfo->Directory->DeviceMap;
  1040. }
  1041. ObpDereferenceNameInfo( NameInfo );
  1042. }
  1043. //
  1044. // Check if we are creating a symbolic link
  1045. //
  1046. if (Action == CREATE_SYMBOLIC_LINK) {
  1047. DosDeviceDriveType = DOSDEVICE_DRIVE_CALCULATE;
  1048. if (Object != NULL) {
  1049. //
  1050. // We only want to do snapping for console session. When we create a
  1051. // remote session all the symbolic links stored in the console dos devices
  1052. // directory (\??) are copied into the per session DosDevices object directory
  1053. // (\Session\<id>\DosDevices). We don't want to do snapping for the copied
  1054. // symbolic links since for each copy we will increment the ref count on the
  1055. // target object. All these counts have to go to zero before the device can be
  1056. // deleted.
  1057. //
  1058. // Disable snapping until we come up with a delete scheme for it
  1059. //
  1060. if (FALSE /*( PsGetCurrentProcess()->SessionId == 0) || (DeviceMapUsed)*/) {
  1061. //
  1062. // Create action. Store a referenced pointer to the snapped object
  1063. // along with the description of any remaining name string. Also,
  1064. // for Dos drive letters, update the drive type in the appropriate
  1065. // device map object.
  1066. //
  1067. ObReferenceObject( Object );
  1068. SymbolicLink->LinkTargetObject = Object;
  1069. //
  1070. // If we have saved a remaining target string
  1071. // we'll set it to the symbolic link object
  1072. //
  1073. if ( RemainingTarget.Length ) {
  1074. RemainingName = RemainingTarget;
  1075. }
  1076. if ((*(RemainingName.Buffer) == OBJ_NAME_PATH_SEPARATOR) &&
  1077. (RemainingName.Length == sizeof( OBJ_NAME_PATH_SEPARATOR))) {
  1078. RtlInitUnicodeString( &SymbolicLink->LinkTargetRemaining, NULL );
  1079. } else {
  1080. SymbolicLink->LinkTargetRemaining = RemainingName;
  1081. }
  1082. }
  1083. if (SymbolicLink->DosDeviceDriveIndex != 0) {
  1084. //
  1085. // Default is to calculate the drive type in user mode if we are
  1086. // unable to snap the symbolic link or it does not resolve to a
  1087. // DEVICE_OBJECT we know about.
  1088. //
  1089. ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
  1090. if (ObjectHeader->Type == IoDeviceObjectType) {
  1091. DeviceObject = (PDEVICE_OBJECT)Object;
  1092. switch (DeviceObject->DeviceType) {
  1093. case FILE_DEVICE_CD_ROM:
  1094. case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
  1095. DosDeviceDriveType = DOSDEVICE_DRIVE_CDROM;
  1096. break;
  1097. case FILE_DEVICE_DISK:
  1098. case FILE_DEVICE_DISK_FILE_SYSTEM:
  1099. case FILE_DEVICE_FILE_SYSTEM:
  1100. if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) {
  1101. DosDeviceDriveType = DOSDEVICE_DRIVE_REMOVABLE;
  1102. } else {
  1103. DosDeviceDriveType = DOSDEVICE_DRIVE_FIXED;
  1104. }
  1105. break;
  1106. case FILE_DEVICE_MULTI_UNC_PROVIDER:
  1107. case FILE_DEVICE_NETWORK:
  1108. case FILE_DEVICE_NETWORK_BROWSER:
  1109. case FILE_DEVICE_NETWORK_REDIRECTOR:
  1110. DosDeviceDriveType = DOSDEVICE_DRIVE_REMOTE;
  1111. break;
  1112. case FILE_DEVICE_NETWORK_FILE_SYSTEM:
  1113. #if defined(REMOTE_BOOT)
  1114. //
  1115. // If this is a remote boot workstation, the X:
  1116. // drive is a redirected drive, but needs to look
  1117. // like a local drive.
  1118. //
  1119. if (IoRemoteBootClient &&
  1120. (SymbolicLink->DosDeviceDriveIndex == 24)) {
  1121. DosDeviceDriveType = DOSDEVICE_DRIVE_FIXED;
  1122. } else
  1123. #endif // defined(REMOTE_BOOT)
  1124. {
  1125. DosDeviceDriveType = DOSDEVICE_DRIVE_REMOTE;
  1126. }
  1127. break;
  1128. case FILE_DEVICE_VIRTUAL_DISK:
  1129. DosDeviceDriveType = DOSDEVICE_DRIVE_RAMDISK;
  1130. break;
  1131. default:
  1132. DosDeviceDriveType = DOSDEVICE_DRIVE_UNKNOWN;
  1133. break;
  1134. }
  1135. }
  1136. }
  1137. }
  1138. //
  1139. // If this is a drive letter symbolic link, update the drive type and
  1140. // and mark as valid drive letter.
  1141. //
  1142. if (DeviceMap != NULL) {
  1143. ObpLockDeviceMap();
  1144. DeviceMap->DriveType[ SymbolicLink->DosDeviceDriveIndex-1 ] = (UCHAR)DosDeviceDriveType;
  1145. DeviceMap->DriveMap |= 1 << (SymbolicLink->DosDeviceDriveIndex-1) ;
  1146. ObpUnlockDeviceMap();
  1147. }
  1148. } else {
  1149. //
  1150. // Deleting the symbolic link. Dereference the snapped object pointer if any
  1151. // and zero out the snapped object fields.
  1152. //
  1153. RtlInitUnicodeString( &SymbolicLink->LinkTargetRemaining, NULL );
  1154. Object = SymbolicLink->LinkTargetObject;
  1155. if (Object != NULL) {
  1156. SymbolicLink->LinkTargetObject = NULL;
  1157. ObDereferenceObject( Object );
  1158. }
  1159. //
  1160. // If this is a drive letter symbolic link, set the drive type to
  1161. // unknown and clear the bit in the drive letter bit map.
  1162. //
  1163. if (DeviceMap != NULL) {
  1164. ObpLockDeviceMap();
  1165. DeviceMap->DriveMap &= ~(1 << (SymbolicLink->DosDeviceDriveIndex-1));
  1166. DeviceMap->DriveType[ SymbolicLink->DosDeviceDriveIndex-1 ] = DOSDEVICE_DRIVE_UNKNOWN;
  1167. ObpUnlockDeviceMap();
  1168. SymbolicLink->DosDeviceDriveIndex = 0;
  1169. }
  1170. //
  1171. // N.B. The original code freed here the target buffer. This is
  1172. // illegal because the parse routine for symbolic links reads the buffer unsynchronized
  1173. // The buffer will be released in the delete procedure, when the sym link goes away
  1174. //
  1175. }
  1176. ObpReleaseLookupContext(&LookupContext);
  1177. return;
  1178. }