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.

2719 lines
78 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. obdir.c
  5. Abstract:
  6. Directory Object routines
  7. Author:
  8. Steve Wood (stevewo) 31-Mar-1989
  9. Revision History:
  10. --*/
  11. #include "obp.h"
  12. #include <stdio.h>
  13. POBJECT_DIRECTORY
  14. ObpGetShadowDirectory(
  15. POBJECT_DIRECTORY Dir
  16. );
  17. //
  18. // Defined in ntos\se\rmlogon.c
  19. // private kernel function to obtain the LUID device map's directory
  20. // object
  21. // returns a kernel handle
  22. //
  23. NTSTATUS
  24. SeGetLogonIdDeviceMap(
  25. IN PLUID pLogonId,
  26. OUT PDEVICE_MAP* ppDevMap
  27. );
  28. NTSTATUS
  29. ObpSetCurrentProcessDeviceMap(
  30. );
  31. #if defined(ALLOC_PRAGMA)
  32. #pragma alloc_text(PAGE,NtCreateDirectoryObject)
  33. #pragma alloc_text(PAGE,NtOpenDirectoryObject)
  34. #pragma alloc_text(PAGE,NtQueryDirectoryObject)
  35. #pragma alloc_text(PAGE,ObpLookupDirectoryEntry)
  36. #pragma alloc_text(PAGE,ObpInsertDirectoryEntry)
  37. #pragma alloc_text(PAGE,ObpDeleteDirectoryEntry)
  38. #pragma alloc_text(PAGE,ObpLookupObjectName)
  39. #pragma alloc_text(PAGE,NtMakePermanentObject)
  40. #ifdef OBP_PAGEDPOOL_NAMESPACE
  41. #pragma alloc_text(PAGE,ObpGetShadowDirectory)
  42. #pragma alloc_text(PAGE,ObpSetCurrentProcessDeviceMap)
  43. #pragma alloc_text(PAGE,ObpReferenceDeviceMap)
  44. #pragma alloc_text(PAGE,ObfDereferenceDeviceMap)
  45. #endif // OBP_PAGEDPOOL_NAMESPACE
  46. #endif
  47. //
  48. // Global Object manager flags to control the case sensitivity lookup
  49. // and the LUID devicemap lookup
  50. //
  51. ULONG ObpCaseInsensitive = 1;
  52. extern ULONG ObpLUIDDeviceMapsEnabled;
  53. NTSTATUS
  54. NtCreateDirectoryObject (
  55. OUT PHANDLE DirectoryHandle,
  56. IN ACCESS_MASK DesiredAccess,
  57. IN POBJECT_ATTRIBUTES ObjectAttributes
  58. )
  59. /*++
  60. Routine Description:
  61. This routine creates a new directory object according to user
  62. specified object attributes
  63. Arguments:
  64. DirectoryHandle - Receives the handle for the newly created
  65. directory object
  66. DesiredAccess - Supplies the access being requested for this
  67. new directory object
  68. ObjectAttributes - Supplies caller specified attributes for new
  69. directory object
  70. Return Value:
  71. An appropriate status value.
  72. --*/
  73. {
  74. POBJECT_DIRECTORY Directory;
  75. HANDLE Handle;
  76. KPROCESSOR_MODE PreviousMode;
  77. NTSTATUS Status;
  78. PAGED_CODE();
  79. ObpValidateIrql( "NtCreateDirectoryObject" );
  80. //
  81. // Get previous processor mode and probe output arguments if necessary.
  82. //
  83. PreviousMode = KeGetPreviousMode();
  84. if (PreviousMode != KernelMode) {
  85. try {
  86. ProbeForWriteHandle( DirectoryHandle );
  87. } except( EXCEPTION_EXECUTE_HANDLER ) {
  88. return( GetExceptionCode() );
  89. }
  90. }
  91. //
  92. // Allocate and initialize a new Directory Object. We don't need
  93. // to specify a parse context or charge any quota. The size of
  94. // the object body is simply a directory object. This call gets
  95. // us a new referenced object.
  96. //
  97. Status = ObCreateObject( PreviousMode,
  98. ObpDirectoryObjectType,
  99. ObjectAttributes,
  100. PreviousMode,
  101. NULL,
  102. sizeof( *Directory ),
  103. 0,
  104. 0,
  105. (PVOID *)&Directory );
  106. if (!NT_SUCCESS( Status )) {
  107. return( Status );
  108. }
  109. RtlZeroMemory( Directory, sizeof( *Directory ) );
  110. ExInitializePushLock( &Directory->Lock );
  111. //
  112. // Insert directory object in the current processes handle table,
  113. // set directory handle value and return status.
  114. //
  115. // ObInsertObject will delete the object in the case of failure
  116. //
  117. Status = ObInsertObject( Directory,
  118. NULL,
  119. DesiredAccess,
  120. 0,
  121. (PVOID *)NULL,
  122. &Handle );
  123. try {
  124. *DirectoryHandle = Handle;
  125. } except( EXCEPTION_EXECUTE_HANDLER ) {
  126. //
  127. // Fall through, since we do not want to undo what we have done.
  128. //
  129. }
  130. return( Status );
  131. }
  132. NTSTATUS
  133. NtOpenDirectoryObject (
  134. OUT PHANDLE DirectoryHandle,
  135. IN ACCESS_MASK DesiredAccess,
  136. IN POBJECT_ATTRIBUTES ObjectAttributes
  137. )
  138. /*++
  139. Routine Description:
  140. This routine opens an existing directory object.
  141. Arguments:
  142. DirectoryHandle - Receives the handle for the newly opened directory
  143. object
  144. DesiredAccess - Supplies the access being requested for this
  145. directory object
  146. ObjectAttributes - Supplies caller specified attributes for the
  147. directory object
  148. Return Value:
  149. An appropriate status value.
  150. --*/
  151. {
  152. KPROCESSOR_MODE PreviousMode;
  153. NTSTATUS Status;
  154. HANDLE Handle;
  155. PAGED_CODE();
  156. ObpValidateIrql( "NtOpenDirectoryObject" );
  157. //
  158. // Get previous processor mode and probe output arguments if necessary.
  159. //
  160. PreviousMode = KeGetPreviousMode();
  161. if (PreviousMode != KernelMode) {
  162. try {
  163. ProbeForWriteHandle( DirectoryHandle );
  164. } except( EXCEPTION_EXECUTE_HANDLER ) {
  165. return( GetExceptionCode() );
  166. }
  167. }
  168. //
  169. // Open handle to the directory object with the specified desired access,
  170. // set directory handle value, and return service completion status.
  171. //
  172. Status = ObOpenObjectByName( ObjectAttributes,
  173. ObpDirectoryObjectType,
  174. PreviousMode,
  175. NULL,
  176. DesiredAccess,
  177. NULL,
  178. &Handle );
  179. try {
  180. *DirectoryHandle = Handle;
  181. } except( EXCEPTION_EXECUTE_HANDLER ) {
  182. //
  183. // Fall through, since we do not want to undo what we have done.
  184. //
  185. }
  186. return Status;
  187. }
  188. NTSTATUS
  189. NtQueryDirectoryObject (
  190. IN HANDLE DirectoryHandle,
  191. OUT PVOID Buffer,
  192. IN ULONG Length,
  193. IN BOOLEAN ReturnSingleEntry,
  194. IN BOOLEAN RestartScan,
  195. IN OUT PULONG Context,
  196. OUT PULONG ReturnLength OPTIONAL
  197. )
  198. /*++
  199. Routine Description:
  200. This function returns information regarding a specified object
  201. directory.
  202. Arguments:
  203. DirectoryHandle - Supplies a handle to the directory being queried
  204. Buffer - Supplies the output buffer to receive the directory
  205. information. On return this contains one or more OBJECT DIRECTORY
  206. INFORMATION structures, the last one being null. And then this is
  207. followed by the string names for the directory entries.
  208. Length - Supplies the length, in bytes, of the user supplied output
  209. buffer
  210. ReturnSingleEntry - Indicates if this routine should just return
  211. one entry in the directory
  212. RestartScan - Indicates if we are to restart the scan or continue
  213. relative to the enumeration context passed in as the next
  214. parameter
  215. Context - Supplies an enumeration context that must be resupplied
  216. to this routine on subsequent calls to keep the enumeration
  217. in sync
  218. ReturnLength - Optionally receives the length, in bytes, that this
  219. routine has stuffed into the output buffer
  220. Return Value:
  221. An appropriate status value.
  222. --*/
  223. {
  224. POBJECT_DIRECTORY Directory;
  225. POBJECT_DIRECTORY_ENTRY DirectoryEntry;
  226. POBJECT_HEADER ObjectHeader;
  227. POBJECT_HEADER_NAME_INFO NameInfo;
  228. UNICODE_STRING ObjectName;
  229. POBJECT_DIRECTORY_INFORMATION DirInfo;
  230. PWCH NameBuffer;
  231. KPROCESSOR_MODE PreviousMode;
  232. NTSTATUS Status;
  233. ULONG Bucket, EntryNumber, CapturedContext;
  234. ULONG TotalLengthNeeded, LengthNeeded, EntriesFound;
  235. PCHAR TempBuffer;
  236. OBP_LOOKUP_CONTEXT LookupContext;
  237. PAGED_CODE();
  238. ObpValidateIrql( "NtQueryDirectoryObject" );
  239. ObpInitializeLookupContext( &LookupContext );
  240. //
  241. // Get previous processor mode and probe output arguments if necessary.
  242. //
  243. PreviousMode = KeGetPreviousMode();
  244. if (PreviousMode != KernelMode) {
  245. try {
  246. ProbeForWrite( Buffer, Length, sizeof( WCHAR ) );
  247. ProbeForWriteUlong( Context );
  248. if (ARGUMENT_PRESENT( ReturnLength )) {
  249. ProbeForWriteUlong( ReturnLength );
  250. }
  251. if (RestartScan) {
  252. CapturedContext = 0;
  253. } else {
  254. CapturedContext = *Context;
  255. }
  256. } except( EXCEPTION_EXECUTE_HANDLER ) {
  257. return( GetExceptionCode() );
  258. }
  259. } else {
  260. if (RestartScan) {
  261. CapturedContext = 0;
  262. } else {
  263. CapturedContext = *Context;
  264. }
  265. }
  266. //
  267. // Test for 64 bit if Length + sizeof( OBJECT_DIRECTORY_INFORMATION ) is less than Length
  268. // Return STATUS_INVALID_PARAMETER if there is an overflow
  269. //
  270. if (ObpIsOverflow( Length, sizeof( OBJECT_DIRECTORY_INFORMATION ))) {
  271. return( STATUS_INVALID_PARAMETER );
  272. }
  273. //
  274. // Allocate space for a temporary work buffer, make sure we got it,
  275. // and then zero it out. Make sure the buffer is large enough to
  276. // hold at least one dir info record. This will make the logic work
  277. // better when the a bad length is passed in.
  278. //
  279. TempBuffer = ExAllocatePoolWithTag( PagedPool,
  280. Length + sizeof( OBJECT_DIRECTORY_INFORMATION ),
  281. 'mNbO' );
  282. if (TempBuffer == NULL) {
  283. return( STATUS_INSUFFICIENT_RESOURCES );
  284. }
  285. RtlZeroMemory( TempBuffer, Length );
  286. //
  287. // Reference the directory object
  288. //
  289. Status = ObReferenceObjectByHandle( DirectoryHandle,
  290. DIRECTORY_QUERY,
  291. ObpDirectoryObjectType,
  292. PreviousMode,
  293. (PVOID *)&Directory,
  294. NULL );
  295. if (!NT_SUCCESS( Status )) {
  296. ExFreePool( TempBuffer );
  297. return( Status );
  298. }
  299. //
  300. // Lock down the directory structures for the life of this
  301. // procedure
  302. //
  303. ObpLockDirectoryShared(Directory, &LookupContext);
  304. //
  305. // DirInfo is used to march through the output buffer filling
  306. // in directory information. We'll start off by making sure
  307. // there is room for a NULL entry at end.
  308. //
  309. DirInfo = (POBJECT_DIRECTORY_INFORMATION)TempBuffer;
  310. TotalLengthNeeded = sizeof( *DirInfo );
  311. //
  312. // Keep track of the number of entries found and actual
  313. // entry that we are processing
  314. //
  315. EntryNumber = 0;
  316. EntriesFound = 0;
  317. //
  318. // By default we'll say there are no more entries until the
  319. // following loop put in some data
  320. //
  321. Status = STATUS_NO_MORE_ENTRIES;
  322. //
  323. // Our outer loop processes each hash bucket in the directory object
  324. //
  325. for (Bucket=0; Bucket<NUMBER_HASH_BUCKETS; Bucket++) {
  326. DirectoryEntry = Directory->HashBuckets[ Bucket ];
  327. //
  328. // For this hash bucket we'll zip through its list of entries.
  329. // This is a singly linked list so when the next pointer is null
  330. // (i.e., false) we at the end of the hash list
  331. //
  332. while (DirectoryEntry) {
  333. //
  334. // The captured context is simply the entry count unless the
  335. // user specified otherwise we start at zero, which means
  336. // the first entry is always returned in the enumeration.
  337. // If we have an match based on the entry index then we
  338. // process this entry. We bump the captured context further
  339. // done in the code.
  340. //
  341. if (CapturedContext == EntryNumber++) {
  342. //
  343. // For this directory entry we'll get a pointer to the
  344. // object body and see if it has an object name. If it
  345. // doesn't have a name then we'll give it an empty name.
  346. //
  347. ObjectHeader = OBJECT_TO_OBJECT_HEADER( DirectoryEntry->Object );
  348. NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );
  349. if (NameInfo != NULL) {
  350. ObjectName = NameInfo->Name;
  351. } else {
  352. RtlInitUnicodeString( &ObjectName, NULL );
  353. }
  354. //
  355. // Now compute the length needed for this entry. This would
  356. // be the size of the object directory information record,
  357. // plus the size of the object name and object type name both
  358. // null terminated.
  359. //
  360. LengthNeeded = sizeof( *DirInfo ) +
  361. ObjectName.Length + sizeof( UNICODE_NULL ) +
  362. ObjectHeader->Type->Name.Length + sizeof( UNICODE_NULL );
  363. //
  364. // If there isn't enough room then take the following error
  365. // path. If the user wanted a single entry then tell the
  366. // caller what length is really needed and say the buffer was
  367. // too small. Otherwise the user wanted multiple entries,
  368. // so we'll just say there are more entries in the directory.
  369. // In both cases we drop down the entry number because we
  370. // weren't able to fit it in on this call
  371. //
  372. if ((TotalLengthNeeded + LengthNeeded) > Length) {
  373. if (ReturnSingleEntry) {
  374. TotalLengthNeeded += LengthNeeded;
  375. Status = STATUS_BUFFER_TOO_SMALL;
  376. } else {
  377. Status = STATUS_MORE_ENTRIES;
  378. }
  379. EntryNumber -= 1;
  380. goto querydone;
  381. }
  382. //
  383. // The information will fit in the buffer. So now fill
  384. // in the output buffer. We temporarily put in pointers
  385. // to the name buffer as stored in the object and object
  386. // type. We copy the data buffer to the user buffer
  387. // right before we return to the caller
  388. //
  389. try {
  390. DirInfo->Name.Length = ObjectName.Length;
  391. DirInfo->Name.MaximumLength = (USHORT)(ObjectName.Length+sizeof( UNICODE_NULL ));
  392. DirInfo->Name.Buffer = ObjectName.Buffer;
  393. DirInfo->TypeName.Length = ObjectHeader->Type->Name.Length;
  394. DirInfo->TypeName.MaximumLength = (USHORT)(ObjectHeader->Type->Name.Length+sizeof( UNICODE_NULL ));
  395. DirInfo->TypeName.Buffer = ObjectHeader->Type->Name.Buffer;
  396. Status = STATUS_SUCCESS;
  397. } except( EXCEPTION_EXECUTE_HANDLER ) {
  398. Status = GetExceptionCode();
  399. }
  400. if (!NT_SUCCESS( Status )) {
  401. goto querydone;
  402. }
  403. //
  404. // Update the total number of bytes needed in this query.
  405. // Push the dir info pointer to the next output location,
  406. // and indicate how many entries we've processed
  407. //
  408. //
  409. TotalLengthNeeded += LengthNeeded;
  410. DirInfo++;
  411. EntriesFound++;
  412. //
  413. // If we are to return only one entry then move on to the
  414. // post processing phase, otherwise indicate that we're
  415. // processing the next entry and go back to the top of
  416. // the inner loop
  417. //
  418. if (ReturnSingleEntry) {
  419. goto querydone;
  420. } else {
  421. //
  422. // Bump the captured context by one entry.
  423. //
  424. CapturedContext++;
  425. }
  426. }
  427. //
  428. // Get the next directory entry from the singly linked hash
  429. // bucket chain
  430. //
  431. DirectoryEntry = DirectoryEntry->ChainLink;
  432. }
  433. }
  434. //
  435. // At this point we've processed the directory entries and the first
  436. // part of the output buffer now contains a bunch of object directory
  437. // information records, but the pointers in them refer to the wrong
  438. // copies. So now we have some fixup to do.
  439. //
  440. querydone:
  441. try {
  442. //
  443. // We'll only do this post processing if we've been successful
  444. // so far. Note that this means we could be returning in the
  445. // user's output buffer system address that are meaningless, but
  446. // then getting back an error status should tell the caller to
  447. // forget about everything in the output buffer. Given back
  448. // a system address also isn't harmful because there is nothing
  449. // that the user can really do with it.
  450. //
  451. if (NT_SUCCESS( Status )) {
  452. //
  453. // Null terminate the string of object directory information
  454. // records and point to where the actual names will go
  455. //
  456. RtlZeroMemory( DirInfo, sizeof( *DirInfo ));
  457. DirInfo++;
  458. NameBuffer = (PWCH)DirInfo;
  459. //
  460. // Now for every entry that we've put in the output buffer
  461. // DirInfo will point to the entry and EntriesFound kept the
  462. // count. Note that we are guaranteed space because of
  463. // the math we did earlier in computing TotalLengthNeeded.
  464. //
  465. DirInfo = (POBJECT_DIRECTORY_INFORMATION)TempBuffer;
  466. while (EntriesFound--) {
  467. //
  468. // Copy over the object name, set the dir info pointer into
  469. // the user's buffer, then null terminate the string. Note
  470. // that we are really copying the data into our temp buffer
  471. // but the pointer fix up is for the user's buffer which
  472. // we'll copy into right after this loop.
  473. //
  474. RtlCopyMemory( NameBuffer,
  475. DirInfo->Name.Buffer,
  476. DirInfo->Name.Length );
  477. DirInfo->Name.Buffer = (PVOID)((ULONG_PTR)Buffer + ((ULONG_PTR)NameBuffer - (ULONG_PTR)TempBuffer));
  478. NameBuffer = (PWCH)((ULONG_PTR)NameBuffer + DirInfo->Name.Length);
  479. *NameBuffer++ = UNICODE_NULL;
  480. //
  481. // Do the same copy with the object type name
  482. //
  483. RtlCopyMemory( NameBuffer,
  484. DirInfo->TypeName.Buffer,
  485. DirInfo->TypeName.Length );
  486. DirInfo->TypeName.Buffer = (PVOID)((ULONG_PTR)Buffer + ((ULONG_PTR)NameBuffer - (ULONG_PTR)TempBuffer));
  487. NameBuffer = (PWCH)((ULONG_PTR)NameBuffer + DirInfo->TypeName.Length);
  488. *NameBuffer++ = UNICODE_NULL;
  489. //
  490. // Move on to the next dir info record
  491. //
  492. DirInfo++;
  493. }
  494. //
  495. // Set the enumeration context to the entry number of the next
  496. // entry to return.
  497. //
  498. *Context = EntryNumber;
  499. }
  500. //
  501. // Copy over the results from our temp buffer to the users buffer.
  502. // But adjust the amount copied just in case the total length needed
  503. // exceeds the length we allocated.
  504. //
  505. RtlCopyMemory( Buffer,
  506. TempBuffer,
  507. (TotalLengthNeeded <= Length ? TotalLengthNeeded : Length) );
  508. //
  509. // In all cases we'll tell the caller how much space if really needed
  510. // provided the user asked for this information
  511. //
  512. if (ARGUMENT_PRESENT( ReturnLength )) {
  513. *ReturnLength = TotalLengthNeeded;
  514. }
  515. } except( EXCEPTION_EXECUTE_HANDLER ) {
  516. //
  517. // Fall through, since we do not want to undo what we have done.
  518. //
  519. }
  520. //
  521. // Unlock the directroy structures, dereference the directory object,
  522. // free up our temp buffer, and return to our caller
  523. //
  524. ObpUnlockDirectory( Directory, &LookupContext);
  525. ObDereferenceObject( Directory );
  526. ExFreePool( TempBuffer );
  527. return( Status );
  528. }
  529. PVOID
  530. ObpLookupDirectoryEntry (
  531. IN POBJECT_DIRECTORY Directory,
  532. IN PUNICODE_STRING Name,
  533. IN ULONG Attributes,
  534. IN BOOLEAN SearchShadow,
  535. OUT POBP_LOOKUP_CONTEXT LookupContext
  536. )
  537. /*++
  538. Routine Description:
  539. This routine will lookup a single directory entry in a given directory.
  540. If it founds an object into that directory with the give name,
  541. that object will be referenced and the name will be referenced too, in order
  542. to prevent them going away when the directory is unlocked.
  543. The referenced object is saved in the LookupContext, and the references will be released
  544. at the next lookup, or when ObpReleaseLookupContext is called.
  545. Arguments:
  546. Directory - Supplies the directory being searched
  547. Name - Supplies the name of entry we're looking for
  548. Attributes - Indicates if the lookup should be case insensitive
  549. or not
  550. SearchShadow - If TRUE, and the object name is not found in the current directory,
  551. it will search the object into the shadow directory.
  552. LookupContext - The lookup context for this call. This structure must be initialized
  553. before calling first time ObpLookupDirectoryEntry.
  554. Return Value:
  555. Returns a pointer to the corresponding object body if found and NULL
  556. otherwise.
  557. --*/
  558. {
  559. POBJECT_DIRECTORY_ENTRY *HeadDirectoryEntry;
  560. POBJECT_DIRECTORY_ENTRY DirectoryEntry;
  561. POBJECT_HEADER ObjectHeader;
  562. POBJECT_HEADER_NAME_INFO NameInfo;
  563. PWCH Buffer;
  564. WCHAR Wchar;
  565. ULONG HashIndex;
  566. ULONG WcharLength;
  567. BOOLEAN CaseInSensitive;
  568. POBJECT_DIRECTORY_ENTRY *LookupBucket;
  569. PVOID Object = NULL;
  570. PAGED_CODE();
  571. if (ObpLUIDDeviceMapsEnabled == 0) {
  572. SearchShadow = FALSE; // Disable global devmap search
  573. }
  574. //
  575. // The caller needs to specify both a directory and a name otherwise
  576. // we can't process the request
  577. //
  578. if (!Directory || !Name) {
  579. goto UPDATECONTEXT;
  580. }
  581. //
  582. // Set a local variable to tell us if the search is case sensitive
  583. //
  584. if (Attributes & OBJ_CASE_INSENSITIVE) {
  585. CaseInSensitive = TRUE;
  586. } else {
  587. CaseInSensitive = FALSE;
  588. }
  589. //
  590. // Establish our local pointer to the input name buffer and get the
  591. // number of unicode characters in the input name. Also make sure
  592. // the caller gave us a non null name
  593. //
  594. Buffer = Name->Buffer;
  595. WcharLength = Name->Length / sizeof( *Buffer );
  596. if (!WcharLength || !Buffer) {
  597. goto UPDATECONTEXT;
  598. }
  599. //
  600. // Compute the address of the head of the bucket chain for this name.
  601. //
  602. HashIndex = 0;
  603. while (WcharLength--) {
  604. Wchar = *Buffer++;
  605. HashIndex += (HashIndex << 1) + (HashIndex >> 1);
  606. if (Wchar < 'a') {
  607. HashIndex += Wchar;
  608. } else if (Wchar > 'z') {
  609. HashIndex += RtlUpcaseUnicodeChar( Wchar );
  610. } else {
  611. HashIndex += (Wchar - ('a'-'A'));
  612. }
  613. }
  614. HashIndex %= NUMBER_HASH_BUCKETS;
  615. LookupContext->HashIndex = (USHORT)HashIndex;
  616. while (1) {
  617. HeadDirectoryEntry = (POBJECT_DIRECTORY_ENTRY *)&Directory->HashBuckets[ HashIndex ];
  618. LookupBucket = HeadDirectoryEntry;
  619. //
  620. // Lock the directory for read access, if the context was not previously locked
  621. // exclusively
  622. //
  623. if (!LookupContext->DirectoryLocked) {
  624. ObpLockDirectoryShared( Directory, LookupContext);
  625. }
  626. //
  627. // Walk the chain of directory entries for this hash bucket, looking
  628. // for either a match, or the insertion point if no match in the chain.
  629. //
  630. while ((DirectoryEntry = *HeadDirectoryEntry) != NULL) {
  631. //
  632. // Get the object header and name from the object body
  633. //
  634. // This function assumes the name must exist, otherwise it
  635. // wouldn't be in a directory
  636. //
  637. ObjectHeader = OBJECT_TO_OBJECT_HEADER( DirectoryEntry->Object );
  638. NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );
  639. //
  640. // Compare strings using appropriate function.
  641. //
  642. if ((Name->Length == NameInfo->Name.Length) &&
  643. RtlEqualUnicodeString( Name,
  644. &NameInfo->Name,
  645. CaseInSensitive )) {
  646. //
  647. // If name matches, then exit loop with DirectoryEntry
  648. // pointing to matching entry.
  649. //
  650. break;
  651. }
  652. HeadDirectoryEntry = &DirectoryEntry->ChainLink;
  653. }
  654. //
  655. // At this point, there are two possiblilities:
  656. //
  657. // - we found an entry that matched and DirectoryEntry points to that
  658. // entry. Update the bucket chain so that the entry found is at the
  659. // head of the bucket chain. This is so the ObpDeleteDirectoryEntry
  660. // and ObpInsertDirectoryEntry functions will work. Also repeated
  661. // lookups of the same name will succeed quickly.
  662. //
  663. // - we did not find an entry that matched and DirectoryEntry is NULL.
  664. //
  665. if (DirectoryEntry) {
  666. //
  667. // The following convoluted piece of code moves a directory entry
  668. // we've found to the front of the hash list.
  669. //
  670. if (HeadDirectoryEntry != LookupBucket) {
  671. if ( LookupContext->DirectoryLocked
  672. ||
  673. ExTryConvertPushLockSharedToExclusive(&Directory->Lock)) {
  674. *HeadDirectoryEntry = DirectoryEntry->ChainLink;
  675. DirectoryEntry->ChainLink = *LookupBucket;
  676. *LookupBucket = DirectoryEntry;
  677. }
  678. }
  679. //
  680. // Now return the object to our caller
  681. //
  682. Object = DirectoryEntry->Object;
  683. goto UPDATECONTEXT;
  684. } else {
  685. if (!LookupContext->DirectoryLocked) {
  686. ObpUnlockDirectory( Directory, LookupContext );
  687. }
  688. //
  689. // If this is a directory with a device map then search the second directory for an entry.
  690. //
  691. if (SearchShadow && Directory->DeviceMap != NULL) {
  692. POBJECT_DIRECTORY NewDirectory;
  693. NewDirectory = ObpGetShadowDirectory (Directory);
  694. if (NewDirectory != NULL) {
  695. Directory = NewDirectory;
  696. continue;
  697. }
  698. }
  699. goto UPDATECONTEXT;
  700. }
  701. }
  702. UPDATECONTEXT:
  703. if (Object) {
  704. //
  705. // Reference the name to keep it's directory alive and the object
  706. // before returning from the lookup
  707. //
  708. ObpReferenceNameInfo( OBJECT_TO_OBJECT_HEADER(Object) );
  709. ObReferenceObject( Object );
  710. //
  711. // We can safetly drop the lock now
  712. //
  713. if (!LookupContext->DirectoryLocked) {
  714. ObpUnlockDirectory( Directory, LookupContext );
  715. }
  716. }
  717. //
  718. // If we have a previously referenced object we can dereference it
  719. //
  720. if (LookupContext->Object) {
  721. POBJECT_HEADER_NAME_INFO PreviousNameInfo;
  722. PreviousNameInfo = OBJECT_HEADER_TO_NAME_INFO(OBJECT_TO_OBJECT_HEADER(LookupContext->Object));
  723. ObpDereferenceNameInfo(PreviousNameInfo);
  724. ObDereferenceObject(LookupContext->Object);
  725. }
  726. LookupContext->Object = Object;
  727. return Object;
  728. }
  729. BOOLEAN
  730. ObpInsertDirectoryEntry (
  731. IN POBJECT_DIRECTORY Directory,
  732. IN POBP_LOOKUP_CONTEXT LookupContext,
  733. IN POBJECT_HEADER ObjectHeader
  734. )
  735. /*++
  736. Routine Description:
  737. This routine will insert a new directory entry into a directory
  738. object. The directory must have already have been searched using
  739. ObpLookupDirectoryEntry because that routine sets the LookupContext.
  740. N.B. The ObpLookupDirectoryEntry before should be done with the LookupContext
  741. locked.
  742. Arguments:
  743. Directory - Supplies the directory object being modified. This
  744. function assumes that we earlier did a lookup on the name
  745. that was successful or we just did an insertion
  746. Object - Supplies the object to insert into the directory
  747. LookupContext - The lookupContext passed in previously to ObpLookupDirectoryEntry
  748. Return Value:
  749. TRUE if the object is inserted successfully and FALSE otherwise
  750. --*/
  751. {
  752. POBJECT_DIRECTORY_ENTRY *HeadDirectoryEntry;
  753. POBJECT_DIRECTORY_ENTRY NewDirectoryEntry;
  754. POBJECT_HEADER_NAME_INFO NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );
  755. #if DBG
  756. //
  757. // This function should be always called with a valid Directory
  758. // and LookupContext. Test this on checked builds
  759. //
  760. if ((LookupContext->Object != NULL) ||
  761. !LookupContext->DirectoryLocked ||
  762. (Directory != LookupContext->Directory)) {
  763. DbgPrint("OB: ObpInsertDirectoryEntry - invalid context %p %ld\n",
  764. LookupContext->Object,
  765. (ULONG)LookupContext->DirectoryLocked );
  766. DbgBreakPoint();
  767. return FALSE;
  768. }
  769. #endif // DBG
  770. //
  771. // Allocate memory for a new entry, and fail if not enough memory.
  772. //
  773. NewDirectoryEntry = (POBJECT_DIRECTORY_ENTRY)ExAllocatePoolWithTag( PagedPool,
  774. sizeof( OBJECT_DIRECTORY_ENTRY ),
  775. 'iDbO' );
  776. if (NewDirectoryEntry == NULL) {
  777. return( FALSE );
  778. }
  779. //
  780. // Get the right lookup bucket based on the HashIndex
  781. //
  782. HeadDirectoryEntry = (POBJECT_DIRECTORY_ENTRY *)&Directory->HashBuckets[ LookupContext->HashIndex ];
  783. //
  784. // Link the new entry into the chain at the insertion point.
  785. // This puts the new object right at the head of the current
  786. // hash bucket chain
  787. //
  788. NewDirectoryEntry->ChainLink = *HeadDirectoryEntry;
  789. *HeadDirectoryEntry = NewDirectoryEntry;
  790. NewDirectoryEntry->Object = &ObjectHeader->Body;
  791. //
  792. // Point the object header back to the directory we just inserted
  793. // it into.
  794. //
  795. NameInfo->Directory = Directory;
  796. //
  797. // Return success.
  798. //
  799. return( TRUE );
  800. }
  801. BOOLEAN
  802. ObpDeleteDirectoryEntry (
  803. IN POBP_LOOKUP_CONTEXT LookupContext
  804. )
  805. /*++
  806. Routine Description:
  807. This routine deletes the most recently found directory entry from
  808. the specified directory object. It will only succeed after a
  809. successful ObpLookupDirectoryEntry call.
  810. Arguments:
  811. Directory - Supplies the directory being modified
  812. Return Value:
  813. TRUE if the deletion succeeded and FALSE otherwise
  814. --*/
  815. {
  816. POBJECT_DIRECTORY_ENTRY *HeadDirectoryEntry;
  817. POBJECT_DIRECTORY_ENTRY DirectoryEntry;
  818. IN POBJECT_DIRECTORY Directory = LookupContext->Directory;
  819. //
  820. // Make sure we have a directory and that it has a found entry
  821. //
  822. if (!Directory ) {
  823. return( FALSE );
  824. }
  825. //
  826. // The lookup path places the object in the front of the list, so basically
  827. // we find the object immediately
  828. //
  829. HeadDirectoryEntry = (POBJECT_DIRECTORY_ENTRY *)&Directory->HashBuckets[ LookupContext->HashIndex ];
  830. DirectoryEntry = *HeadDirectoryEntry;
  831. //
  832. // Unlink the entry from the head of the bucket chain and free the
  833. // memory for the entry.
  834. //
  835. *HeadDirectoryEntry = DirectoryEntry->ChainLink;
  836. DirectoryEntry->ChainLink = NULL;
  837. ExFreePool( DirectoryEntry );
  838. return TRUE;
  839. }
  840. POBJECT_DIRECTORY
  841. ObpGetShadowDirectory(
  842. POBJECT_DIRECTORY Dir
  843. )
  844. {
  845. KIRQL OldIrql;
  846. PDEVICE_MAP DeviceMap;
  847. POBJECT_DIRECTORY NewDir;
  848. NewDir = NULL;
  849. ObpLockDeviceMap();
  850. if (Dir->DeviceMap != NULL) {
  851. NewDir = Dir->DeviceMap->GlobalDosDevicesDirectory;
  852. }
  853. ObpUnlockDeviceMap();
  854. return NewDir;
  855. }
  856. NTSTATUS
  857. ObpSetCurrentProcessDeviceMap(
  858. )
  859. /*++
  860. Routine Description:
  861. This function sets the process' device map to the device map associated
  862. with the process token's LUID.
  863. Arguments:
  864. none
  865. Return Values:
  866. STATUS_NO_TOKEN - process does not have a primary token
  867. STATUS_OBJECT_PATH_INVALID - could not obtain the device map associated
  868. with the process token's LUID
  869. An appropriate status - unexcepted error occurred
  870. --*/
  871. {
  872. PEPROCESS pProcess;
  873. PACCESS_TOKEN pToken = NULL;
  874. LUID userLuid;
  875. LUID SystemAuthenticationId = SYSTEM_LUID; // Local_System's LUID
  876. PDEVICE_MAP DeviceMap = NULL;
  877. PDEVICE_MAP DerefDeviceMap = NULL;
  878. KIRQL OldIrql;
  879. NTSTATUS Status = STATUS_SUCCESS;
  880. pProcess = PsGetCurrentProcess();
  881. pToken = PsReferencePrimaryToken( pProcess );
  882. if (pToken == NULL) {
  883. return (STATUS_NO_TOKEN);
  884. }
  885. Status = SeQueryAuthenticationIdToken( pToken, &userLuid );
  886. if (!NT_SUCCESS(Status)) {
  887. PsDereferencePrimaryToken( pToken );
  888. return (Status);
  889. }
  890. if (!RtlEqualLuid( &userLuid, &SystemAuthenticationId )) {
  891. PDEVICE_MAP pDevMap;
  892. //
  893. // Get a handle to the Device Map for the user's LUID
  894. //
  895. Status = SeGetLogonIdDeviceMap( &userLuid,
  896. &pDevMap );
  897. if (NT_SUCCESS(Status)) {
  898. DeviceMap = pDevMap;
  899. }
  900. }
  901. else {
  902. //
  903. // Process is Local_System, so use the System's device map
  904. //
  905. DeviceMap = ObSystemDeviceMap;
  906. }
  907. if (DeviceMap != NULL) {
  908. //
  909. // set the process' device map
  910. //
  911. ObpLockDeviceMap();
  912. DerefDeviceMap = pProcess->DeviceMap;
  913. pProcess->DeviceMap = DeviceMap;
  914. DeviceMap->ReferenceCount++;
  915. ObpUnlockDeviceMap();
  916. }
  917. else {
  918. Status = STATUS_OBJECT_PATH_INVALID;
  919. }
  920. PsDereferencePrimaryToken( pToken );
  921. //
  922. // If the process already had a device map, then deref it now
  923. //
  924. if (DerefDeviceMap != NULL) {
  925. ObfDereferenceDeviceMap (DerefDeviceMap);
  926. }
  927. return (Status);
  928. }
  929. PDEVICE_MAP
  930. ObpReferenceDeviceMap(
  931. )
  932. /*++
  933. Routine Description:
  934. This function obtains the correct device map associated with the
  935. caller.
  936. If LUID device maps are enabled,
  937. then we obtain the LUID's device map.
  938. We use the existing process device map field as a cache for the
  939. process token's LUID device map.
  940. If LUID device maps are disabled,
  941. then use the device map associated with the process
  942. Arguments:
  943. none
  944. Return Values:
  945. A pointer to the caller's device map
  946. NULL - could not obtain the user's device map
  947. --*/
  948. {
  949. KIRQL OldIrql;
  950. PDEVICE_MAP DeviceMap = NULL;
  951. BOOLEAN LocalSystemRequest = FALSE;
  952. BOOLEAN LUIDDeviceMapsEnabled;
  953. PACCESS_TOKEN pToken = NULL;
  954. NTSTATUS Status;
  955. LUIDDeviceMapsEnabled = (ObpLUIDDeviceMapsEnabled != 0);
  956. if (LUIDDeviceMapsEnabled == TRUE) {
  957. PETHREAD Thread = NULL;
  958. //
  959. // Separate device maps for each user LUID
  960. // if (thread is impersonating)
  961. // then get user's LUID to retrieve their device map
  962. // else use the process' device map
  963. // if (LUID is the Local_System)
  964. // then use the system's device map
  965. // if (unable to retrieve LUID's device map),
  966. // then use the process' device map
  967. //
  968. //
  969. // Get the current thread & check if the thread is impersonating
  970. //
  971. // if impersonating,
  972. // then take the long path
  973. // - get thread's access token
  974. // - read the caller's LUID from the token
  975. // - get the device map associate with this LUID
  976. // if not impersonating,
  977. // then use the device map associated with the process
  978. //
  979. Thread = PsGetCurrentThread();
  980. if ( PS_IS_THREAD_IMPERSONATING (Thread) ) {
  981. BOOLEAN fCopyOnOpen;
  982. BOOLEAN fEffectiveOnly;
  983. SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
  984. LUID userLuid;
  985. //
  986. // Get the caller's access token from the thread
  987. //
  988. pToken = PsReferenceImpersonationToken( Thread,
  989. &fCopyOnOpen,
  990. &fEffectiveOnly,
  991. &ImpersonationLevel);
  992. if (pToken != NULL) {
  993. //
  994. // query the token for the LUID
  995. //
  996. Status = SeQueryAuthenticationIdToken( pToken, &userLuid );
  997. }
  998. else {
  999. Status = STATUS_NO_TOKEN;
  1000. }
  1001. if (NT_SUCCESS(Status)) {
  1002. LUID SystemAuthenticationId = SYSTEM_LUID; // Local_System's LUID
  1003. //
  1004. // Verify the caller is not Local_System by
  1005. // comparing the LUID with Local_System's LUID
  1006. //
  1007. if (!RtlEqualLuid( &userLuid, &SystemAuthenticationId )) {
  1008. PDEVICE_MAP pDevMap;
  1009. //
  1010. // Get a handle to the Device Map for the LUID
  1011. //
  1012. Status = SeGetLogonIdDeviceMap( &userLuid,
  1013. &pDevMap );
  1014. if (NT_SUCCESS(Status)) {
  1015. //
  1016. // Use the device map associated with the LUID
  1017. //
  1018. DeviceMap = pDevMap;
  1019. ObpLockDeviceMap();
  1020. if (DeviceMap != NULL) {
  1021. DeviceMap->ReferenceCount++;
  1022. }
  1023. ObpUnlockDeviceMap();
  1024. }
  1025. }
  1026. else {
  1027. //
  1028. // Local_System will use the system's device map
  1029. //
  1030. LocalSystemRequest = TRUE;
  1031. }
  1032. }
  1033. }
  1034. }
  1035. if (DeviceMap == NULL) {
  1036. //
  1037. // if (going to reference the process' device map and the process'
  1038. // device map is not set),
  1039. // then set the process' device map
  1040. //
  1041. if ((LUIDDeviceMapsEnabled == TRUE) &&
  1042. (LocalSystemRequest == FALSE) &&
  1043. ((PsGetCurrentProcess()->DeviceMap) == NULL)) {
  1044. Status = ObpSetCurrentProcessDeviceMap();
  1045. if (!NT_SUCCESS(Status)) {
  1046. goto Error_Exit;
  1047. }
  1048. }
  1049. ObpLockDeviceMap();
  1050. if (LocalSystemRequest == TRUE) {
  1051. //
  1052. // Use the system's device map
  1053. //
  1054. DeviceMap = ObSystemDeviceMap;
  1055. }
  1056. else {
  1057. //
  1058. // Use the device map from the process
  1059. //
  1060. DeviceMap = PsGetCurrentProcess()->DeviceMap;
  1061. }
  1062. if (DeviceMap != NULL) {
  1063. DeviceMap->ReferenceCount++;
  1064. }
  1065. ObpUnlockDeviceMap();
  1066. }
  1067. Error_Exit:
  1068. if( pToken != NULL ) {
  1069. PsDereferenceImpersonationToken(pToken);
  1070. }
  1071. return DeviceMap;
  1072. }
  1073. VOID
  1074. FASTCALL
  1075. ObfDereferenceDeviceMap(
  1076. IN PDEVICE_MAP DeviceMap
  1077. )
  1078. {
  1079. KIRQL OldIrql;
  1080. ObpLockDeviceMap();
  1081. DeviceMap->ReferenceCount--;
  1082. if (DeviceMap->ReferenceCount == 0) {
  1083. DeviceMap->DosDevicesDirectory->DeviceMap = NULL;
  1084. ObpUnlockDeviceMap();
  1085. //
  1086. // This devmap is dead so mark the directory temporary so its name will go away and dereference it.
  1087. //
  1088. ObMakeTemporaryObject (DeviceMap->DosDevicesDirectory);
  1089. ObDereferenceObject( DeviceMap->DosDevicesDirectory );
  1090. ExFreePool( DeviceMap );
  1091. } else {
  1092. ObpUnlockDeviceMap();
  1093. }
  1094. }
  1095. NTSTATUS
  1096. ObpLookupObjectName (
  1097. IN HANDLE RootDirectoryHandle OPTIONAL,
  1098. IN PUNICODE_STRING ObjectName,
  1099. IN ULONG Attributes,
  1100. IN POBJECT_TYPE ObjectType,
  1101. IN KPROCESSOR_MODE AccessMode,
  1102. IN PVOID ParseContext OPTIONAL,
  1103. IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
  1104. IN PVOID InsertObject OPTIONAL,
  1105. IN OUT PACCESS_STATE AccessState,
  1106. OUT POBP_LOOKUP_CONTEXT LookupContext,
  1107. OUT PVOID *FoundObject
  1108. )
  1109. /*++
  1110. Routine Description:
  1111. This function will search a given directoroy for a specified
  1112. object name. It will also create a new object specified by
  1113. InsertObject.
  1114. Arguments:
  1115. RootDirectoryHandle - Optionally supplies the directory being
  1116. searched. If not supplied then this routine searches
  1117. the root directory
  1118. ObjectName - Supplies the name of object to lookup
  1119. Attributes - Specifies the attributes for the lookup (e.g., case
  1120. insensitive)
  1121. ObjectType - Specifies the type of the object to lookup
  1122. AccessMode - Specifies the callers processor mode
  1123. ParseContext - Optionally supplies a parse context that is blindly
  1124. passed to the parse callback routines
  1125. SecurityQos - Optionally supplies a pointer to the passed Security
  1126. Quality of Service parameter that is blindly passed to the parse
  1127. callback routines
  1128. InsertObject - Optionally supplies the object we think will be found.
  1129. This is used if the caller did not give a root directory handle
  1130. and the object name is "\" and the root object directory hasn't
  1131. been created yet. In other cases where we wind up creating
  1132. a new directory entry this is the object inserted.
  1133. AccessState - Current access state, describing already granted access
  1134. types, the privileges used to get them, and any access types yet to
  1135. be granted. The access masks may not contain any generic access
  1136. types.
  1137. DirectoryLocked - Receives an indication if this routine has returned
  1138. with the input directory locked
  1139. FoundObject - Receives a pointer to the object body if found
  1140. Return Value:
  1141. An appropriate status value.
  1142. N.B. If the status returned is SUCCESS the caller has the
  1143. responsability to release the lookup context
  1144. --*/
  1145. {
  1146. POBJECT_DIRECTORY RootDirectory;
  1147. POBJECT_DIRECTORY Directory = NULL;
  1148. POBJECT_DIRECTORY ParentDirectory = NULL;
  1149. POBJECT_HEADER ObjectHeader;
  1150. POBJECT_HEADER_NAME_INFO NameInfo;
  1151. PDEVICE_MAP DeviceMap = NULL;
  1152. PVOID Object;
  1153. UNICODE_STRING RemainingName;
  1154. UNICODE_STRING ComponentName;
  1155. PWCH NewName;
  1156. NTSTATUS Status;
  1157. BOOLEAN Reparse, ReparsedSymbolicLink = FALSE;
  1158. ULONG MaxReparse = OBJ_MAX_REPARSE_ATTEMPTS;
  1159. OB_PARSE_METHOD ParseProcedure;
  1160. extern POBJECT_TYPE IoFileObjectType;
  1161. KPROCESSOR_MODE AccessCheckMode;
  1162. ObpValidateIrql( "ObpLookupObjectName" );
  1163. //
  1164. // Initialize our output variables to say we haven't lock or found
  1165. // anything but we were successful at it
  1166. //
  1167. ObpInitializeLookupContext(LookupContext);
  1168. *FoundObject = NULL;
  1169. Status = STATUS_SUCCESS;
  1170. Object = NULL;
  1171. //
  1172. // If the global flag says that we need to perform a case-insensitive check
  1173. // we'll force the OBJ_CASE_INSENSITIVE flag in attributes at lookup
  1174. //
  1175. if ( ObpCaseInsensitive ) {
  1176. if ( (ObjectType == NULL) ||
  1177. (ObjectType->TypeInfo.CaseInsensitive)
  1178. ) {
  1179. Attributes |= OBJ_CASE_INSENSITIVE;
  1180. }
  1181. }
  1182. if (Attributes & OBJ_FORCE_ACCESS_CHECK) {
  1183. AccessCheckMode = UserMode;
  1184. } else {
  1185. AccessCheckMode = AccessMode;
  1186. }
  1187. //
  1188. // Check if the caller has given us a directory to search. Otherwise
  1189. // we'll search the root object directory
  1190. //
  1191. if (ARGUMENT_PRESENT( RootDirectoryHandle )) {
  1192. //
  1193. // Otherwise reference the directory object and make sure
  1194. // that we successfully got the object
  1195. //
  1196. Status = ObReferenceObjectByHandle( RootDirectoryHandle,
  1197. 0,
  1198. NULL,
  1199. AccessMode,
  1200. (PVOID *)&RootDirectory,
  1201. NULL );
  1202. if (!NT_SUCCESS( Status )) {
  1203. return( Status );
  1204. }
  1205. //
  1206. // Translate the directory object to its object header
  1207. //
  1208. ObjectHeader = OBJECT_TO_OBJECT_HEADER( RootDirectory );
  1209. //
  1210. // Now if the name we're looking up starts with a "\" and it
  1211. // does not have a parse procedure then the syntax is bad
  1212. //
  1213. if ((ObjectName->Buffer != NULL) &&
  1214. (*(ObjectName->Buffer) == OBJ_NAME_PATH_SEPARATOR) &&
  1215. (ObjectHeader->Type != IoFileObjectType)) {
  1216. ObDereferenceObject( RootDirectory );
  1217. return( STATUS_OBJECT_PATH_SYNTAX_BAD );
  1218. }
  1219. //
  1220. // Now make sure that we do not have the directory of the
  1221. // object types
  1222. //
  1223. if (ObjectHeader->Type != ObpDirectoryObjectType) {
  1224. //
  1225. // We have an object directory that is not the object type
  1226. // directory. So now if it doesn't have a parse routine
  1227. // then there is nothing we can
  1228. //
  1229. if (ObjectHeader->Type->TypeInfo.ParseProcedure == NULL) {
  1230. ObDereferenceObject( RootDirectory );
  1231. return( STATUS_INVALID_HANDLE );
  1232. } else {
  1233. MaxReparse = OBJ_MAX_REPARSE_ATTEMPTS;
  1234. //
  1235. // The following loop cycles cycles through the various
  1236. // parse routine to we could encounter trying to resolve
  1237. // this name through symbolic links.
  1238. //
  1239. while (TRUE) {
  1240. KIRQL SaveIrql;
  1241. RemainingName = *ObjectName;
  1242. //
  1243. // Invoke the callback routine to parse the remaining
  1244. // object name
  1245. //
  1246. ObpBeginTypeSpecificCallOut( SaveIrql );
  1247. Status = (*ObjectHeader->Type->TypeInfo.ParseProcedure)( RootDirectory,
  1248. ObjectType,
  1249. AccessState,
  1250. AccessCheckMode,
  1251. Attributes,
  1252. ObjectName,
  1253. &RemainingName,
  1254. ParseContext,
  1255. SecurityQos,
  1256. &Object );
  1257. ObpEndTypeSpecificCallOut( SaveIrql, "Parse", ObjectHeader->Type, Object );
  1258. //
  1259. // If the status was not to do a reparse and the lookup
  1260. // was not successful then we found nothing so we
  1261. // dereference the directory and return the status to
  1262. // our caller. If the object we got back was null then
  1263. // we'll tell our caller that we couldn't find the name.
  1264. // Lastly if we did not get a reparse and we were
  1265. // successful and the object is not null then everything
  1266. // gets nicely returned to our caller
  1267. //
  1268. if ( ( Status != STATUS_REPARSE ) &&
  1269. ( Status != STATUS_REPARSE_OBJECT )) {
  1270. if (!NT_SUCCESS( Status )) {
  1271. Object = NULL;
  1272. } else if (Object == NULL) {
  1273. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  1274. }
  1275. ObDereferenceObject( RootDirectory );
  1276. *FoundObject = Object;
  1277. return( Status );
  1278. //
  1279. // We got a status reparse, which means the object
  1280. // name has been modified to have use start all over
  1281. // again. If the reparse target is now empty or it
  1282. // is a path separator then we start the parse at the
  1283. // root directory
  1284. //
  1285. } else if ((ObjectName->Length == 0) ||
  1286. (ObjectName->Buffer == NULL) ||
  1287. (*(ObjectName->Buffer) == OBJ_NAME_PATH_SEPARATOR)) {
  1288. //
  1289. // Restart the parse relative to the root directory.
  1290. //
  1291. ObDereferenceObject( RootDirectory );
  1292. RootDirectory = ObpRootDirectoryObject;
  1293. RootDirectoryHandle = NULL;
  1294. goto ParseFromRoot;
  1295. //
  1296. // We got a reparse and we actually have a new name to
  1297. // go to we if we haven't exhausted our reparse attempts
  1298. // yet then just continue to the top of this loop.
  1299. //
  1300. } else if (--MaxReparse) {
  1301. continue;
  1302. //
  1303. // We got a reparse and we've exhausted our times through
  1304. // the loop so we'll return what we found.
  1305. //
  1306. } else {
  1307. ObDereferenceObject( RootDirectory );
  1308. *FoundObject = Object;
  1309. //
  1310. // At this point we were failing in stress by
  1311. // returning to the caller with a success status but
  1312. // a null object pointer.
  1313. //
  1314. if (Object == NULL) {
  1315. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  1316. }
  1317. return( Status );
  1318. }
  1319. }
  1320. }
  1321. //
  1322. // At this point the caller has given us the directory of object
  1323. // types. If the caller didn't specify a name then we'll return
  1324. // a pointer to the root object directory.
  1325. //
  1326. } else if ((ObjectName->Length == 0) ||
  1327. (ObjectName->Buffer == NULL)) {
  1328. Status = ObReferenceObjectByPointer( RootDirectory,
  1329. 0,
  1330. ObjectType,
  1331. AccessMode );
  1332. if (NT_SUCCESS( Status )) {
  1333. Object = RootDirectory;
  1334. }
  1335. ObDereferenceObject( RootDirectory );
  1336. *FoundObject = Object;
  1337. return( Status );
  1338. }
  1339. //
  1340. // Otherwise the caller did not specify a directory to search so
  1341. // we'll default to the object root directory
  1342. //
  1343. } else {
  1344. RootDirectory = ObpRootDirectoryObject;
  1345. //
  1346. // If the name we're looking for is empty then it is illformed.
  1347. // Also it has to start with a "\" or it is illformed.
  1348. //
  1349. if ((ObjectName->Length == 0) ||
  1350. (ObjectName->Buffer == NULL) ||
  1351. (*(ObjectName->Buffer) != OBJ_NAME_PATH_SEPARATOR)) {
  1352. return( STATUS_OBJECT_PATH_SYNTAX_BAD );
  1353. }
  1354. //
  1355. // Check if the name is has only one character (that is the "\")
  1356. // Which means that the caller really just wants to lookup the
  1357. // root directory.
  1358. //
  1359. if (ObjectName->Length == sizeof( OBJ_NAME_PATH_SEPARATOR )) {
  1360. //
  1361. // If there is not a root directory yet. Then we really
  1362. // can't return it, however if the caller specified
  1363. // an insert object that is the one we'll reference and
  1364. // return to our caller
  1365. //
  1366. if (!RootDirectory) {
  1367. if (InsertObject) {
  1368. Status = ObReferenceObjectByPointer( InsertObject,
  1369. 0,
  1370. ObjectType,
  1371. AccessMode );
  1372. if (NT_SUCCESS( Status )) {
  1373. *FoundObject = InsertObject;
  1374. }
  1375. return( Status );
  1376. } else {
  1377. return( STATUS_INVALID_PARAMETER );
  1378. }
  1379. //
  1380. // At this point the caller did not specify a root directory,
  1381. // the name is "\" and the root object directory exists so
  1382. // we'll simply return the real root directory object
  1383. //
  1384. } else {
  1385. Status = ObReferenceObjectByPointer( RootDirectory,
  1386. 0,
  1387. ObjectType,
  1388. AccessMode );
  1389. if (NT_SUCCESS( Status )) {
  1390. *FoundObject = RootDirectory;
  1391. }
  1392. return( Status );
  1393. }
  1394. //
  1395. // At this pointer the caller did not specify a root directory,
  1396. // and the name is more than just a "\"
  1397. //
  1398. // Now if the lookup is case insensitive, and the name buffer is a
  1399. // legitimate pointer (meaning that is it quadword aligned), and
  1400. // there is a dos device map for the process. Then we'll handle
  1401. // the situation here. First get the device map and make sure it
  1402. // doesn't go away while we're using it.
  1403. //
  1404. } else {
  1405. ParseFromRoot:
  1406. if (DeviceMap != NULL) {
  1407. ObfDereferenceDeviceMap(DeviceMap);
  1408. DeviceMap = NULL;
  1409. }
  1410. if (!((ULONG_PTR)(ObjectName->Buffer) & (sizeof(ULONGLONG)-1))) {
  1411. //
  1412. // Check if the object name is actually equal to the
  1413. // global dos devices short name prefix "\??\"
  1414. //
  1415. if ((ObjectName->Length >= ObpDosDevicesShortName.Length)
  1416. &&
  1417. (*(PULONGLONG)(ObjectName->Buffer) == ObpDosDevicesShortNamePrefix.Alignment.QuadPart)) {
  1418. if ((DeviceMap = ObpReferenceDeviceMap()) != NULL) {
  1419. if (DeviceMap->DosDevicesDirectory != NULL ) {
  1420. //
  1421. // The user gave us the dos short name prefix so we'll
  1422. // look down the directory, and start the search at the
  1423. // dos device directory
  1424. //
  1425. ParentDirectory = RootDirectory;
  1426. Directory = DeviceMap->DosDevicesDirectory;
  1427. RemainingName = *ObjectName;
  1428. RemainingName.Buffer += (ObpDosDevicesShortName.Length / sizeof( WCHAR ));
  1429. RemainingName.Length -= ObpDosDevicesShortName.Length;
  1430. goto quickStart;
  1431. }
  1432. }
  1433. //
  1434. // The name is not equal to "\??\" but check if it is
  1435. // equal to "\??"
  1436. //
  1437. } else if ((ObjectName->Length == ObpDosDevicesShortName.Length - sizeof( WCHAR ))
  1438. &&
  1439. (*(PULONG)(ObjectName->Buffer) == ObpDosDevicesShortNameRoot.Alignment.LowPart)
  1440. &&
  1441. (*((PWCHAR)(ObjectName->Buffer)+2) == (WCHAR)(ObpDosDevicesShortNameRoot.Alignment.HighPart))) {
  1442. //
  1443. // The user specified "\??" so we return to dos devices
  1444. // directory to our caller
  1445. //
  1446. if ((DeviceMap = ObpReferenceDeviceMap()) != NULL) {
  1447. if (DeviceMap->DosDevicesDirectory != NULL ) {
  1448. Status = ObReferenceObjectByPointer( DeviceMap->DosDevicesDirectory,
  1449. 0,
  1450. ObjectType,
  1451. AccessMode );
  1452. if (NT_SUCCESS( Status )) {
  1453. *FoundObject = DeviceMap->DosDevicesDirectory;
  1454. }
  1455. //
  1456. // Dereference the Device Map
  1457. //
  1458. ObfDereferenceDeviceMap(DeviceMap);
  1459. return( Status );
  1460. }
  1461. }
  1462. }
  1463. }
  1464. }
  1465. }
  1466. //
  1467. // At this point either
  1468. //
  1469. // the user specified a directory that is not the object
  1470. // type directory and got repase back to the root directory
  1471. //
  1472. // the user specified the object type directory and gave us
  1473. // a name to actually look up
  1474. //
  1475. // the user did not specify a search directory (default
  1476. // to root object directory) and if the name did start off
  1477. // with the dos device prefix we've munged outselves back to
  1478. // it to the dos device directory for the process
  1479. //
  1480. if( ReparsedSymbolicLink == FALSE ) {
  1481. Reparse = TRUE;
  1482. MaxReparse = OBJ_MAX_REPARSE_ATTEMPTS;
  1483. }
  1484. while (Reparse) {
  1485. RemainingName = *ObjectName;
  1486. quickStart:
  1487. Reparse = FALSE;
  1488. while (TRUE) {
  1489. Object = NULL;
  1490. //if (RemainingName.Length == 0) {
  1491. // Status = STATUS_OBJECT_NAME_INVALID;
  1492. // break;
  1493. // }
  1494. //
  1495. // If the remaining name for the object starts with a
  1496. // "\" then just gobble up the "\"
  1497. //
  1498. if ( (RemainingName.Length != 0) &&
  1499. (*(RemainingName.Buffer) == OBJ_NAME_PATH_SEPARATOR) ) {
  1500. RemainingName.Buffer++;
  1501. RemainingName.Length -= sizeof( OBJ_NAME_PATH_SEPARATOR );
  1502. }
  1503. //
  1504. // The following piece of code will calculate the first
  1505. // component of the remaining name. If there is not
  1506. // a remaining component then the object name is illformed
  1507. //
  1508. ComponentName = RemainingName;
  1509. while (RemainingName.Length != 0) {
  1510. if (*(RemainingName.Buffer) == OBJ_NAME_PATH_SEPARATOR) {
  1511. break;
  1512. }
  1513. RemainingName.Buffer++;
  1514. RemainingName.Length -= sizeof( OBJ_NAME_PATH_SEPARATOR );
  1515. }
  1516. ComponentName.Length -= RemainingName.Length;
  1517. if (ComponentName.Length == 0) {
  1518. Status = STATUS_OBJECT_NAME_INVALID;
  1519. break;
  1520. }
  1521. //
  1522. // Now we have the first component name to lookup so we'll
  1523. // look the directory is necessary
  1524. //
  1525. if ( Directory == NULL ) {
  1526. Directory = RootDirectory;
  1527. }
  1528. //
  1529. // Now if the caller does not have traverse privilege and
  1530. // there is a parent directory then we must check if the
  1531. // user has traverse access to the directory. Our local
  1532. // Reparse variable should be false at this point so we'll
  1533. // drop out of both loops
  1534. //
  1535. if ( !(AccessState->Flags & TOKEN_HAS_TRAVERSE_PRIVILEGE) && (ParentDirectory != NULL) ) {
  1536. if (!ObpCheckTraverseAccess( ParentDirectory,
  1537. DIRECTORY_TRAVERSE,
  1538. AccessState,
  1539. FALSE,
  1540. AccessCheckMode,
  1541. &Status )) {
  1542. break;
  1543. }
  1544. }
  1545. //
  1546. // If the object already exists in this directory, find it,
  1547. // else return NULL.
  1548. //
  1549. if ((RemainingName.Length == 0) && (InsertObject != NULL)) {
  1550. //
  1551. // If we are searching the last name, and we have an object
  1552. // to insert into that directory, we lock the context
  1553. // exclusively before the lookup. An insertion is likely
  1554. // to occur after this lookup if it fails, so we need to protect
  1555. // this directory to be changed until the ObpInsertDirectoryEntry call
  1556. //
  1557. ObpLockLookupContext( LookupContext, Directory );
  1558. }
  1559. Object = ObpLookupDirectoryEntry( Directory,
  1560. &ComponentName,
  1561. Attributes,
  1562. InsertObject == NULL,
  1563. LookupContext );
  1564. if (!Object) {
  1565. //
  1566. // We didn't find the object. If there is some remaining
  1567. // name left (meaning the component name is a directory in
  1568. // path we trying to break) or the caller didn't specify an
  1569. // insert object then we then we'll break out here with an
  1570. // error status
  1571. //
  1572. if (RemainingName.Length != 0) {
  1573. Status = STATUS_OBJECT_PATH_NOT_FOUND;
  1574. break;
  1575. }
  1576. if (!InsertObject) {
  1577. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  1578. break;
  1579. }
  1580. //
  1581. // Check that the caller has the access to the directory
  1582. // to either create a subdirectory (in the object type
  1583. // directory) or to create an object of the given component
  1584. // name. If the call fails then we'll break out of here
  1585. // with the status value set
  1586. //
  1587. if (!ObCheckCreateObjectAccess( Directory,
  1588. ObjectType == ObpDirectoryObjectType ?
  1589. DIRECTORY_CREATE_SUBDIRECTORY :
  1590. DIRECTORY_CREATE_OBJECT,
  1591. AccessState,
  1592. &ComponentName,
  1593. FALSE,
  1594. AccessCheckMode,
  1595. &Status )) {
  1596. break;
  1597. }
  1598. //
  1599. // The object does not exist in the directory and
  1600. // we are allowed to create one. So allocate space
  1601. // for the name and insert the name into the directory
  1602. //
  1603. NewName = ExAllocatePoolWithTag( PagedPool, ComponentName.Length, 'mNbO' );
  1604. ObjectHeader = OBJECT_TO_OBJECT_HEADER( InsertObject );
  1605. if ((NewName == NULL) ||
  1606. !ObpInsertDirectoryEntry( Directory, LookupContext, ObjectHeader )) {
  1607. if (NewName != NULL) {
  1608. ExFreePool( NewName );
  1609. }
  1610. Status = STATUS_INSUFFICIENT_RESOURCES;
  1611. break;
  1612. }
  1613. //
  1614. // We have an insert object so now get its name info,
  1615. // because we are going to change its name and insert it
  1616. // into the directory
  1617. //
  1618. ObReferenceObject( InsertObject );
  1619. NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );
  1620. ObReferenceObject( Directory );
  1621. RtlCopyMemory( NewName,
  1622. ComponentName.Buffer,
  1623. ComponentName.Length );
  1624. if (NameInfo->Name.Buffer) {
  1625. ExFreePool( NameInfo->Name.Buffer );
  1626. }
  1627. NameInfo->Name.Buffer = NewName;
  1628. NameInfo->Name.Length = ComponentName.Length;
  1629. NameInfo->Name.MaximumLength = ComponentName.Length;
  1630. Object = InsertObject;
  1631. Status = STATUS_SUCCESS;
  1632. break;
  1633. }
  1634. //
  1635. // At this point we've found the component name within
  1636. // the directory. So we'll now grab the components object
  1637. // header, and get its parse routine
  1638. //
  1639. ReparseObject:
  1640. ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
  1641. ParseProcedure = ObjectHeader->Type->TypeInfo.ParseProcedure;
  1642. //
  1643. // Now if there is a parse routine for the type and we are not
  1644. // inserting a new object or the parse routine is for symbolic
  1645. // links then we'll actually call the parse routine
  1646. //
  1647. if (ParseProcedure && (!InsertObject || (ParseProcedure == ObpParseSymbolicLink))) {
  1648. KIRQL SaveIrql;
  1649. //
  1650. // Reference the object and then free the directory lock
  1651. // This will keep the object from going away with the
  1652. // directory unlocked
  1653. //
  1654. ObpIncrPointerCount( ObjectHeader );
  1655. Directory = NULL;
  1656. ObpReleaseLookupContext(LookupContext);
  1657. ObpBeginTypeSpecificCallOut( SaveIrql );
  1658. //
  1659. // Call the objects parse routine
  1660. //
  1661. Status = (*ParseProcedure)( Object,
  1662. (PVOID)ObjectType,
  1663. AccessState,
  1664. AccessCheckMode,
  1665. Attributes,
  1666. ObjectName,
  1667. &RemainingName,
  1668. ParseContext,
  1669. SecurityQos,
  1670. &Object );
  1671. ObpEndTypeSpecificCallOut( SaveIrql, "Parse", ObjectHeader->Type, Object );
  1672. //
  1673. // We can now decrement the object reference count
  1674. //
  1675. ObDereferenceObject( &ObjectHeader->Body );
  1676. //
  1677. // Check if we have some reparsing to do
  1678. //
  1679. if ((Status == STATUS_REPARSE) || (Status == STATUS_REPARSE_OBJECT)) {
  1680. //
  1681. // See if we've reparsed too many times already and if
  1682. // so we'll fail the request
  1683. //
  1684. if (--MaxReparse) {
  1685. //
  1686. // Tell the outer loop to continue looping
  1687. //
  1688. Reparse = TRUE;
  1689. //
  1690. // Check if we have a reparse object or the name
  1691. // starts with a "\"
  1692. //
  1693. if ((Status == STATUS_REPARSE_OBJECT) ||
  1694. (*(ObjectName->Buffer) == OBJ_NAME_PATH_SEPARATOR)) {
  1695. //
  1696. // If the user specified a start directory then
  1697. // remove this information because we're taking
  1698. // a reparse point to someplace else
  1699. //
  1700. if (ARGUMENT_PRESENT( RootDirectoryHandle )) {
  1701. ObDereferenceObject( RootDirectory );
  1702. RootDirectoryHandle = NULL;
  1703. }
  1704. //
  1705. // And where we start is the root directory
  1706. // object
  1707. //
  1708. ParentDirectory = NULL;
  1709. RootDirectory = ObpRootDirectoryObject;
  1710. //
  1711. // Now if this is a reparse object (means we have
  1712. // encountered a symbolic link that has already been
  1713. // snapped so we have an object and remaining
  1714. // name that need to be examined) and we didn't
  1715. // find an object from the parse routine object
  1716. // break out of both loops.
  1717. //
  1718. if (Status == STATUS_REPARSE_OBJECT) {
  1719. Reparse = FALSE;
  1720. if (Object == NULL) {
  1721. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  1722. } else {
  1723. //
  1724. // At this point we have a reparse object
  1725. // so we'll look the directory down and
  1726. // parse the new object
  1727. //
  1728. goto ReparseObject;
  1729. }
  1730. } else {
  1731. //
  1732. // At this point Status must be equal to
  1733. // STATUS_REPARSE because [(Status equals
  1734. // (STATUS_REPARSE_OBJECT or STATUS_REPARSE))
  1735. // && (Status != STATUS_REPARSE_OBJECT)]
  1736. //
  1737. ReparsedSymbolicLink = TRUE;
  1738. goto ParseFromRoot;
  1739. }
  1740. //
  1741. // We did not have a reparse object and the name
  1742. // does not start with a "\". Meaning we got back
  1743. // STATUS_REPASE, so now check if the directory
  1744. // is the root object directory and if so then
  1745. // we didn't the name otherwise we'll drop out of
  1746. // the inner loop and reparse true to get back to
  1747. // outer loop
  1748. //
  1749. } else if (RootDirectory == ObpRootDirectoryObject) {
  1750. Object = NULL;
  1751. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  1752. Reparse = FALSE;
  1753. }
  1754. } else {
  1755. //
  1756. // We return object not found if we've exhausted
  1757. // the MaxReparse time
  1758. //
  1759. Object = NULL;
  1760. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  1761. }
  1762. //
  1763. // We are not reparsing and if we did not get success then
  1764. // the object is null and we'll break out of our loops
  1765. //
  1766. } else if (!NT_SUCCESS( Status )) {
  1767. Object = NULL;
  1768. //
  1769. // We are not reparsing and we got back success but check
  1770. // if the object is null because that means we really didn't
  1771. // find the object, and then break out of our loops
  1772. //
  1773. // If the object is not null then we've been successful and
  1774. // prosperous so break out with the object set.
  1775. //
  1776. } else if (Object == NULL) {
  1777. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  1778. }
  1779. break;
  1780. } else {
  1781. //
  1782. // At this point we do not have a parse routine or if there
  1783. // is a parse routine it is not for symbolic links or there
  1784. // may not be a specified insert object
  1785. //
  1786. // Check to see if we have exhausted the remaining name
  1787. //
  1788. if (RemainingName.Length == 0) {
  1789. //
  1790. // Check if the caller specified an object to insert.
  1791. // If specified then we'll break out of our loops with
  1792. // the object that we've found
  1793. //
  1794. if (!InsertObject) {
  1795. //
  1796. // The user did not specify an insert object
  1797. // so we're opening an existing object. Make sure
  1798. // we have traverse access to the container
  1799. // directory.
  1800. //
  1801. if ( !(AccessState->Flags & TOKEN_HAS_TRAVERSE_PRIVILEGE) ) {
  1802. if (!ObpCheckTraverseAccess( Directory,
  1803. DIRECTORY_TRAVERSE,
  1804. AccessState,
  1805. FALSE,
  1806. AccessCheckMode,
  1807. &Status )) {
  1808. Object = NULL;
  1809. break;
  1810. }
  1811. }
  1812. Status = ObReferenceObjectByPointer( Object,
  1813. 0,
  1814. ObjectType,
  1815. AccessMode );
  1816. if (!NT_SUCCESS( Status )) {
  1817. Object = NULL;
  1818. }
  1819. }
  1820. break;
  1821. } else {
  1822. //
  1823. // There is some name remaining names to process
  1824. // if the directory we're looking at is the
  1825. // directory of object types and set ourselves
  1826. // up to parse it all over again.
  1827. //
  1828. if (ObjectHeader->Type == ObpDirectoryObjectType) {
  1829. ParentDirectory = Directory;
  1830. Directory = (POBJECT_DIRECTORY)Object;
  1831. } else {
  1832. //
  1833. // Otherwise there has been a mismatch so we'll
  1834. // set our error status and break out of the
  1835. // loops
  1836. //
  1837. Status = STATUS_OBJECT_TYPE_MISMATCH;
  1838. Object = NULL;
  1839. break;
  1840. }
  1841. }
  1842. }
  1843. }
  1844. }
  1845. //
  1846. // We can release the context if our search was unsuccesful.
  1847. // We still need the directory to be locked if a new object is
  1848. // inserted into that directory, until we finish the initialization
  1849. // (i.e SD, handle, ...). Note the object is visible now and could be accessed
  1850. // via name. We need to hold the directory lock until we are done.
  1851. //
  1852. if ( !NT_SUCCESS(Status) ) {
  1853. ObpReleaseLookupContext(LookupContext);
  1854. }
  1855. //
  1856. // If the device map has been referenced then dereference it
  1857. //
  1858. if (DeviceMap != NULL) {
  1859. ObfDereferenceDeviceMap(DeviceMap);
  1860. }
  1861. //
  1862. // At this point we've parsed the object name as much as possible
  1863. // going through symbolic links as necessary. So now set the
  1864. // output object pointer, and if we really did not find an object
  1865. // then we might need to modify the error status. If the
  1866. // status was repase or some success status then translate it
  1867. // to name not found.
  1868. //
  1869. if (!(*FoundObject = Object)) {
  1870. if (Status == STATUS_REPARSE) {
  1871. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  1872. } else if (NT_SUCCESS( Status )) {
  1873. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  1874. }
  1875. }
  1876. //
  1877. // If the caller gave us a root directory to search (and we didn't
  1878. // zero out this value) then free up our reference
  1879. //
  1880. if (ARGUMENT_PRESENT( RootDirectoryHandle )) {
  1881. ObDereferenceObject( RootDirectory );
  1882. RootDirectoryHandle = NULL;
  1883. }
  1884. //
  1885. // And return to our caller
  1886. //
  1887. return( Status );
  1888. }
  1889. NTSTATUS
  1890. NtMakePermanentObject (
  1891. IN HANDLE Handle
  1892. )
  1893. /*++
  1894. Routine Description:
  1895. This routine makes the specified object permanent.
  1896. By default, only Local_System may make this call
  1897. Arguments:
  1898. Handle - Supplies a handle to the object being modified
  1899. Return Value:
  1900. An appropriate status value.
  1901. --*/
  1902. {
  1903. KPROCESSOR_MODE PreviousMode;
  1904. NTSTATUS Status;
  1905. PVOID Object;
  1906. OBJECT_HANDLE_INFORMATION HandleInformation;
  1907. POBJECT_HEADER ObjectHeader;
  1908. PAGED_CODE();
  1909. //
  1910. // Get previous processor mode and probe output argument if necessary.
  1911. //
  1912. PreviousMode = KeGetPreviousMode();
  1913. //
  1914. // The object is being changed to permanent, check if
  1915. // the caller has the appropriate privilege.
  1916. //
  1917. if (!SeSinglePrivilegeCheck( SeCreatePermanentPrivilege,
  1918. PreviousMode)) {
  1919. Status = STATUS_PRIVILEGE_NOT_HELD;
  1920. return( Status );
  1921. }
  1922. Status = ObReferenceObjectByHandle( Handle,
  1923. 0,
  1924. (POBJECT_TYPE)NULL,
  1925. PreviousMode,
  1926. &Object,
  1927. &HandleInformation );
  1928. if (!NT_SUCCESS( Status )) {
  1929. return( Status );
  1930. }
  1931. //
  1932. // Make the object permanant. Note that the object should still
  1933. // have a name and directory entry because its handle count is not
  1934. // zero
  1935. //
  1936. ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
  1937. //
  1938. // Other bits are set in this flags field by the handle database code. Synchronize with that.
  1939. //
  1940. ObpLockObject( ObjectHeader );
  1941. ObjectHeader->Flags |= OB_FLAG_PERMANENT_OBJECT;
  1942. //
  1943. // This routine releases the type mutex
  1944. //
  1945. ObpUnlockObject( ObjectHeader );
  1946. ObDereferenceObject( Object );
  1947. return( Status );
  1948. }