test: obdir.c Source File

Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

# obdir.c

Go to the documentation of this file.

    00001 /*++
    00002
    00003 Copyright (c) 1989  Microsoft Corporation
    00004
    00005 Module Name:
    00006
    00007     obdir.c
    00008
    00009 Abstract:
    00010
    00011     Directory Object routines
    00012
    00013 Author:
    00014
    00015     Steve Wood (stevewo) 31-Mar-1989
    00016
    00017 Revision History:
    00018
    00019 --*/
    00020
    00021 #include "obp.h"
    00022
    00023 #if defined(ALLOC_PRAGMA)
    00024 #pragma alloc_text(PAGE,NtCreateDirectoryObject)
    00025 #pragma alloc_text(PAGE,NtOpenDirectoryObject)
    00026 #pragma alloc_text(PAGE,NtQueryDirectoryObject)
    00027 #pragma alloc_text(PAGE,ObpLookupDirectoryEntry)
    00028 #endif
    00029
    00030
    00031 NTSTATUS
    00032 NtCreateDirectoryObject (
    00033     OUT PHANDLE DirectoryHandle,
    00034     IN ACCESS_MASK DesiredAccess,
    00035     IN POBJECT_ATTRIBUTES ObjectAttributes
    00036     )
    00037
    00038 /*++
    00039
    00040 Routine Description:
    00041
    00042     This routine creates a new directory object according to user
    00043     specified object attributes
    00044
    00045 Arguments:
    00046
    00047     DirectoryHandle - Receives the handle for the newly created
    00048         directory object
    00049
    00050     DesiredAccess - Supplies the access being requested for this
    00051         new directory object
    00052
    00053     ObjectAttributes - Supplies caller specified attributes for new
    00054         directory object
    00055
    00056 Return Value:
    00057
    00058     An appropriate status value.
    00059
    00060 --*/
    00061
    00062 {
    00063     POBJECT_DIRECTORY Directory;
    00064     HANDLE Handle;
    00065     KPROCESSOR_MODE PreviousMode;
    00066     NTSTATUS Status;
    00067
    00068     PAGED_CODE();
    00069
    00070     ObpValidateIrql( "NtCreateDirectoryObject" );
    00071
    00072     //
    00073     //  Get previous processor mode and probe output arguments if necessary.
    00074     //
    00075
    00076     PreviousMode = KeGetPreviousMode();
    00077
    00078     if (PreviousMode != KernelMode) {
    00079
    00080         try {
    00081
    00082             ProbeForWriteHandle( DirectoryHandle );
    00083
    00084         } except( EXCEPTION_EXECUTE_HANDLER ) {
    00085
    00086             return( GetExceptionCode() );
    00087         }
    00088     }
    00089
    00090     //
    00091     //  Allocate and initialize a new Directory Object.  We don't need
    00092     //  to specify a parse context or charge any quota.  The size of
    00093     //  the object body is simply a directory object.  This call gets
    00094     //  us a new referenced object.
    00095     //
    00096
    00097     Status = ObCreateObject( PreviousMode,
    00098                              ObpDirectoryObjectType,
    00099                              ObjectAttributes,
    00100                              PreviousMode,
    00101                              NULL,
    00102                              sizeof( *Directory ),
    00103                              0,
    00104                              0,
    00105                              (PVOID *)&Directory );
    00106
    00107     if (!NT_SUCCESS( Status )) {
    00108
    00109         return( Status );
    00110     }
    00111
    00112     RtlZeroMemory( Directory, sizeof( *Directory ) );
    00113
    00114     //
    00115     //  Insert directory object in the current processes handle table,
    00116     //  set directory handle value and return status.
    00117     //
    00118     //  **** If the insert fails should we backout the create?
    00119     //
    00120
    00121     Status = ObInsertObject( Directory,
    00122                              NULL,
    00123                              DesiredAccess,
    00124                              0,
    00125                              (PVOID *)NULL,
    00126                              &Handle );
    00127
    00128     try {
    00129
    00130         *DirectoryHandle = Handle;
    00131
    00132     } except( EXCEPTION_EXECUTE_HANDLER ) {
    00133
    00134         //
    00135         //  Fall through, since we do not want to undo what we have done.
    00136         //
    00137     }
    00138
    00139     return( Status );
    00140 }
    00141
    00142
    00143 NTSTATUS
    00144 NtOpenDirectoryObject (
    00145     OUT PHANDLE DirectoryHandle,
    00146     IN ACCESS_MASK DesiredAccess,
    00147     IN POBJECT_ATTRIBUTES ObjectAttributes
    00148     )
    00149
    00150 /*++
    00151
    00152 Routine Description:
    00153
    00154     This routine opens an existing directory object.
    00155
    00156 Arguments:
    00157
    00158     DirectoryHandle - Receives the handle for the newly opened directory
    00159         object
    00160
    00161     DesiredAccess - Supplies the access being requested for this
    00162         directory object
    00163
    00164     ObjectAttributes - Supplies caller specified attributes for the
    00165         directory object
    00166
    00167 Return Value:
    00168
    00169     An appropriate status value.
    00170
    00171 --*/
    00172
    00173 {
    00174     KPROCESSOR_MODE PreviousMode;
    00175     NTSTATUS Status;
    00176     HANDLE Handle;
    00177
    00178     PAGED_CODE();
    00179
    00180     ObpValidateIrql( "NtOpenDirectoryObject" );
    00181
    00182     //
    00183     //  Get previous processor mode and probe output arguments if necessary.
    00184     //
    00185
    00186     PreviousMode = KeGetPreviousMode();
    00187
    00188     if (PreviousMode != KernelMode) {
    00189
    00190         try {
    00191
    00192             ProbeForWriteHandle( DirectoryHandle );
    00193
    00194         } except( EXCEPTION_EXECUTE_HANDLER ) {
    00195
    00196             return( GetExceptionCode() );
    00197         }
    00198     }
    00199
    00200     //
    00201     //  Open handle to the directory object with the specified desired access,
    00202     //  set directory handle value, and return service completion status.
    00203     //
    00204
    00205     Status = ObOpenObjectByName( ObjectAttributes,
    00206                                  ObpDirectoryObjectType,
    00207                                  PreviousMode,
    00208                                  NULL,
    00209                                  DesiredAccess,
    00210                                  NULL,
    00211                                  &Handle );
    00212
    00213     try {
    00214
    00215         *DirectoryHandle = Handle;
    00216
    00217     } except( EXCEPTION_EXECUTE_HANDLER ) {
    00218
    00219         //
    00220         //  Fall through, since we do not want to undo what we have done.
    00221         //
    00222     }
    00223
    00224     return Status;
    00225 }
    00226
    00227
    00228 NTSTATUS
    00229 NtQueryDirectoryObject (
    00230     IN HANDLE DirectoryHandle,
    00231     OUT PVOID Buffer,
    00232     IN ULONG Length,
    00233     IN BOOLEAN ReturnSingleEntry,
    00234     IN BOOLEAN RestartScan,
    00235     IN OUT PULONG Context,
    00236     OUT PULONG ReturnLength OPTIONAL
    00237     )
    00238
    00239 /*++
    00240
    00241 Routine Description:
    00242
    00243     This function returns information regarding a specified object
    00244     directory.
    00245
    00246 Arguments:
    00247
    00248     DirectoryHandle - Supplies a handle to the directory being queried
    00249
    00250     Buffer - Supplies the output buffer to receive the directory
    00251         information.  On return this contains one or more OBJECT DIRECTORY
    00252         INFORMATION structures, the last one being null.  And then this is
    00253         followed by the string names for the directory entries.
    00254
    00255     Length - Supplies the length, in bytes, of the user supplied output
    00256         buffer
    00257
    00258     ReturnSingleEntry - Indicates if this routine should just return
    00259         one entry in the directory
    00260
    00261     RestartScan - Indicates if we are to restart the scan or continue
    00262         relative to the enumeration context passed in as the next
    00263         parameter
    00264
    00265     Context - Supplies an enumeration context that must be resupplied
    00266         to this routine on subsequent calls to keep the enumeration
    00267         in sync
    00268
    00269     ReturnLength - Optionally receives the length, in bytes, that this
    00270         routine has stuffed into the output buffer
    00271
    00272 Return Value:
    00273
    00274     An appropriate status value.
    00275
    00276 --*/
    00277
    00278 {
    00279     POBJECT_DIRECTORY Directory;
    00280     POBJECT_DIRECTORY_ENTRY DirectoryEntry;
    00281     POBJECT_HEADER ObjectHeader;
    00282     POBJECT_HEADER_NAME_INFO NameInfo;
    00283     UNICODE_STRING ObjectName;
    00284     POBJECT_DIRECTORY_INFORMATION DirInfo;
    00285     PWCH NameBuffer;
    00286     KPROCESSOR_MODE PreviousMode;
    00287     NTSTATUS Status;
    00288     ULONG Bucket, EntryNumber, CapturedContext;
    00289     ULONG TotalLengthNeeded, LengthNeeded, EntriesFound;
    00290     PCHAR TempBuffer;
    00291
    00292     PAGED_CODE();
    00293
    00294     ObpValidateIrql( "NtQueryDirectoryObject" );
    00295
    00296     //
    00297     //  Get previous processor mode and probe output arguments if necessary.
    00298     //
    00299
    00300     PreviousMode = KeGetPreviousMode();
    00301
    00302     if (PreviousMode != KernelMode) {
    00303
    00304         try {
    00305
    00306             ProbeForWrite( Buffer, Length, sizeof( WCHAR ) );
    00307             ProbeForWriteUlong( Context );
    00308
    00309             if (ARGUMENT_PRESENT( ReturnLength )) {
    00310
    00311                 ProbeForWriteUlong( ReturnLength );
    00312             }
    00313
    00314             if (RestartScan) {
    00315
    00316                 CapturedContext = 0;
    00317
    00318             } else {
    00319
    00320                 CapturedContext = *Context;
    00321             }
    00322
    00323         } except( EXCEPTION_EXECUTE_HANDLER ) {
    00324
    00325             return( GetExceptionCode() );
    00326         }
    00327
    00328     } else {
    00329
    00330         if (RestartScan) {
    00331
    00332             CapturedContext = 0;
    00333
    00334         } else {
    00335
    00336             CapturedContext = *Context;
    00337         }
    00338     }
    00339
    00340     //
    00341     //  Allocate space for a temporary work buffer, make sure we got it,
    00342     //  and then zero it out.  Make sure the buffer is large enough to
    00343     //  hold at least one dir info record.  This will make the logic work
    00344     //  better when the a bad length is passed in.
    00345     //
    00346
    00347
    00348     //
    00349     //  Test for 64 bit if Length + sizeof( OBJECT_DIRECTORY_INFORMATION ) is less than Length
    00350     //  Return STATUS_INVALID_PARAMETER if there is an overflow
    00351     //
    00352
    00353     if (ObpIsOverflow( Length, sizeof( OBJECT_DIRECTORY_INFORMATION ))) {
    00354
    00355         return( STATUS_INVALID_PARAMETER );
    00356     }
    00357
    00358     TempBuffer = ExAllocatePoolWithTag( PagedPool,
    00359                                         Length + sizeof( OBJECT_DIRECTORY_INFORMATION ),
    00360                                         'mNbO' );
    00361
    00362     if (TempBuffer == NULL) {
    00363
    00364         return( STATUS_INSUFFICIENT_RESOURCES );
    00365     }
    00366
    00367     RtlZeroMemory( TempBuffer, Length );
    00368
    00369     //
    00370     //  Reference the directory object
    00371     //
    00372
    00373     Status = ObReferenceObjectByHandle( DirectoryHandle,
    00374                                         DIRECTORY_QUERY,
    00375                                         ObpDirectoryObjectType,
    00376                                         PreviousMode,
    00377                                         (PVOID *)&Directory,
    00378                                         NULL );
    00379
    00380     if (!NT_SUCCESS( Status )) {
    00381
    00382         ExFreePool( TempBuffer );
    00383
    00384         return( Status );
    00385     }
    00386
    00387     //
    00388     //  Lock down the directory structures for the life of this
    00389     //  procedure
    00390     //
    00391
    00392     ObpEnterRootDirectoryMutex();
    00393
    00394     //
    00395     //  DirInfo is used to march through the output buffer filling
    00396     //  in directory information.  We'll start off by making sure
    00397     //  there is room for a NULL entry at end.
    00398     //
    00399
    00400     DirInfo = (POBJECT_DIRECTORY_INFORMATION)TempBuffer;
    00401
    00402     TotalLengthNeeded = sizeof( *DirInfo );
    00403
    00404     //
    00405     //  Keep track of the number of entries found and actual
    00406     //  entry that we are processing
    00407     //
    00408
    00409     EntryNumber = 0;
    00410     EntriesFound = 0;
    00411
    00412     //
    00413     //  By default we'll say there are no more entries until the
    00414     //  following loop put in some data
    00415     //
    00416
    00417     Status = STATUS_NO_MORE_ENTRIES;
    00418
    00419     //
    00420     //  Our outer loop processes each hash bucket in the directory object
    00421     //
    00422
    00423     for (Bucket=0; Bucket<NUMBER_HASH_BUCKETS; Bucket++) {
    00424
    00425         DirectoryEntry = Directory->HashBuckets[ Bucket ];
    00426
    00427         //
    00428         //  For this hash bucket we'll zip through its list of entries.
    00429         //  This is a singly linked list so when the next pointer is null
    00430         //  (i.e., false) we at the end of the hash list
    00431         //
    00432
    00433         while (DirectoryEntry) {
    00434
    00435             //
    00436             //  The captured context is simply the entry count unless the
    00437             //  user specified otherwise we start at zero, which means
    00438             //  the first entry is always returned in the enumeration.
    00439             //  If we have an match based on the entry index then we
    00440             //  process this entry.  We bump the captured context further
    00441             //  done in the code.
    00442             //
    00443
    00444             if (CapturedContext == EntryNumber++) {
    00445
    00446                 //
    00447                 //  For this directory entry we'll get a pointer to the
    00448                 //  object body and see if it has an object name.  If it
    00449                 //  doesn't have a name then we'll give it an empty name.
    00450                 //
    00451
    00452                 ObjectHeader = OBJECT_TO_OBJECT_HEADER( DirectoryEntry->Object );
    00453                 NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );
    00454
    00455                 if (NameInfo != NULL) {
    00456
    00457                     ObjectName = NameInfo->Name;
    00458
    00459                 } else {
    00460
    00461                     RtlInitUnicodeString( &ObjectName, NULL );
    00462                 }
    00463
    00464                 //
    00465                 //  Now compute the length needed for this entry.  This would
    00466                 //  be the size of the object directory information record,
    00467                 //  plus the size of the object name and object type name both
    00468                 //  null terminated.
    00469                 //
    00470
    00471                 LengthNeeded = sizeof( *DirInfo ) +
    00472                                ObjectName.Length + sizeof( UNICODE_NULL ) +
    00473                                ObjectHeader->Type->Name.Length + sizeof( UNICODE_NULL );
    00474
    00475                 //
    00476                 //  If there isn't enough room then take the following error
    00477                 //  path.   If the user wanted a single entry then tell the
    00478                 //  caller what length is really needed and say the buffer was
    00479                 //  too small.  Otherwise the user wanted multiple entries,
    00480                 //  so we'll just say there are more entries in the directory.
    00481                 //  In both cases we drop down the entry number because we
    00482                 //  weren't able to fit it in on this call
    00483                 //
    00484
    00485                 if ((TotalLengthNeeded + LengthNeeded) > Length) {
    00486
    00487                     if (ReturnSingleEntry) {
    00488
    00489                         TotalLengthNeeded += LengthNeeded;
    00490
    00491                         Status = STATUS_BUFFER_TOO_SMALL;
    00492
    00493                     } else {
    00494
    00495                         Status = STATUS_MORE_ENTRIES;
    00496                     }
    00497
    00498                     EntryNumber -= 1;
    00499                     goto querydone;
    00500                 }
    00501
    00502                 //
    00503                 //  The information will fit in the buffer.  So now fill
    00504                 //  in the output buffer.  We temporarily put in pointers
    00505                 //  to the name buffer as stored in the object and object
    00506                 //  type.  We copy the data buffer to the user buffer
    00507                 //  right before we return to the caller
    00508                 //
    00509
    00510                 try {
    00511
    00512                     DirInfo->Name.Length            = ObjectName.Length;
    00513                     DirInfo->Name.MaximumLength     = (USHORT)(ObjectName.Length+sizeof( UNICODE_NULL ));
    00514                     DirInfo->Name.Buffer            = ObjectName.Buffer;
    00515
    00516                     DirInfo->TypeName.Length        = ObjectHeader->Type->Name.Length;
    00517                     DirInfo->TypeName.MaximumLength = (USHORT)(ObjectHeader->Type->Name.Length+sizeof( UNICODE_NULL ));
    00518                     DirInfo->TypeName.Buffer        = ObjectHeader->Type->Name.Buffer;
    00519
    00520                     Status = STATUS_SUCCESS;
    00521
    00522                 } except( EXCEPTION_EXECUTE_HANDLER ) {
    00523
    00524                     Status = GetExceptionCode();
    00525                 }
    00526
    00527                 if (!NT_SUCCESS( Status )) {
    00528
    00529                     goto querydone;
    00530                 }
    00531
    00532                 //
    00533                 //  Update the total number of bytes needed in this query.
    00534                 //  Push the dir info pointer to the next output location,
    00535                 //  and indicate how many entries we've processed
    00536                 //
    00537                 //
    00538
    00539                 TotalLengthNeeded += LengthNeeded;
    00540
    00541                 DirInfo++;
    00542                 EntriesFound++;
    00543
    00544                 //
    00545                 //  If we are to return only one entry then move on to the
    00546                 //  post processing phase, otherwise indicate that we're
    00547                 //  processing the next entry and go back to the top of
    00548                 //  the inner loop
    00549                 //
    00550
    00551                 if (ReturnSingleEntry) {
    00552
    00553                     goto querydone;
    00554
    00555                 } else {
    00556
    00557                     //
    00558                     //  Bump the captured context by one entry.
    00559                     //
    00560
    00561                     CapturedContext++;
    00562                 }
    00563             }
    00564
    00565             //
    00566             //  Get the next directory entry from the singly linked hash
    00567             //  bucket chain
    00568             //
    00569
    00570             DirectoryEntry = DirectoryEntry->ChainLink;
    00571         }
    00572     }
    00573
    00574     //
    00575     //  At this point we've processed the directory entries and the first
    00576     //  part of the output buffer now contains a bunch of object directory
    00577     //  information records,  but the pointers in them refer to the wrong
    00578     //  copies.  So now we have some fixup to do.
    00579     //
    00580
    00581 querydone:
    00582
    00583     try {
    00584
    00585         //
    00586         //  We'll only do this post processing if we've been successful
    00587         //  so far.  Note that this means we could be returning in the
    00588         //  user's output buffer system address that are meaningless, but
    00589         //  then getting back an error status should tell the caller to
    00590         //  forget about everything in the output buffer.  Given back
    00591         //  a system address also isn't harmful because there is nothing
    00592         //  that the user can really do with it.
    00593         //
    00594
    00595         if (NT_SUCCESS( Status )) {
    00596
    00597             //
    00598             //  Null terminate the string of object directory information
    00599             //  records and point to where the actual names will go
    00600             //
    00601
    00602             RtlZeroMemory( DirInfo, sizeof( *DirInfo ));
    00603
    00604             DirInfo++;
    00605
    00606             NameBuffer = (PWCH)DirInfo;
    00607
    00608             //
    00609             //  Now for every entry that we've put in the output buffer
    00610             //  DirInfo will point to the entry and EntriesFound kept the
    00611             //  count.  Note that we are guaranteed space because of
    00612             //  the math we did earlier in computing TotalLengthNeeded.
    00613             //
    00614
    00615             DirInfo = (POBJECT_DIRECTORY_INFORMATION)TempBuffer;
    00616
    00617             while (EntriesFound--) {
    00618
    00619                 //
    00620                 //  Copy over the object name, set the dir info pointer into
    00621                 //  the user's buffer, then null terminate the string.  Note
    00622                 //  that we are really copying the data into our temp buffer
    00623                 //  but the pointer fix up is for the user's buffer which
    00624                 //  we'll copy into right after this loop.
    00625                 //
    00626
    00627                 RtlMoveMemory( NameBuffer,
    00628                                DirInfo->Name.Buffer,
    00629                                DirInfo->Name.Length );
    00630
    00631                 DirInfo->Name.Buffer = (PVOID)((ULONG_PTR)Buffer + ((ULONG_PTR)NameBuffer - (ULONG_PTR)TempBuffer));
    00632                 NameBuffer           = (PWCH)((ULONG_PTR)NameBuffer + DirInfo->Name.Length);
    00633                 *NameBuffer++        = UNICODE_NULL;
    00634
    00635                 //
    00636                 //  Do the same copy with the object type name
    00637                 //
    00638
    00639                 RtlMoveMemory( NameBuffer,
    00640                                DirInfo->TypeName.Buffer,
    00641                                DirInfo->TypeName.Length );
    00642
    00643                 DirInfo->TypeName.Buffer = (PVOID)((ULONG_PTR)Buffer + ((ULONG_PTR)NameBuffer - (ULONG_PTR)TempBuffer));
    00644                 NameBuffer               = (PWCH)((ULONG_PTR)NameBuffer + DirInfo->TypeName.Length);
    00645                 *NameBuffer++            = UNICODE_NULL;
    00646
    00647                 //
    00648                 //  Move on to the next dir info record
    00649                 //
    00650
    00651                 DirInfo++;
    00652             }
    00653
    00654             //
    00655             //  Set the enumeration context to the entry number of the next
    00656             //  entry to return.
    00657             //
    00658
    00659             *Context = EntryNumber;
    00660         }
    00661
    00662         //
    00663         //  Copy over the results from our temp buffer to the users buffer.
    00664         //  But adjust the amount copied just in case the total length needed
    00665         //  exceeds the length we allocated.
    00666         //
    00667
    00668         RtlMoveMemory( Buffer,
    00669                        TempBuffer,
    00670                        (TotalLengthNeeded <= Length ? TotalLengthNeeded : Length) );
    00671
    00672         //
    00673         //  In all cases we'll tell the caller how much space if really needed
    00674         //  provided the user asked for this information
    00675         //
    00676
    00677         if (ARGUMENT_PRESENT( ReturnLength )) {
    00678
    00679             *ReturnLength = TotalLengthNeeded;
    00680         }
    00681
    00682     } except( EXCEPTION_EXECUTE_HANDLER ) {
    00683
    00684         //
    00685         //  Fall through, since we do not want to undo what we have done.
    00686         //
    00687     }
    00688
    00689     //
    00690     //  Unlock the directroy structures, dereference the directory object,
    00691     //  free up our temp buffer, and return to our caller
    00692     //
    00693
    00694     ObpLeaveRootDirectoryMutex();
    00695
    00696     ObDereferenceObject( Directory );
    00697
    00698     ExFreePool( TempBuffer );
    00699
    00700     return( Status );
    00701 }
    00702
    00703
    00704 PVOID
    00705 ObpLookupDirectoryEntry (
    00706     IN POBJECT_DIRECTORY Directory,
    00707     IN PUNICODE_STRING Name,
    00708     IN ULONG Attributes
    00709     )
    00710
    00711 /*++
    00712
    00713 Routine Description:
    00714
    00715     This routine will lookup a single directory entry in a given directory.
    00716
    00717     I believe this routine assumes that it is called with the root directory
    00718     locked.
    00719
    00720     Also note that this routine does not reference the returned object
    00721
    00722 Arguments:
    00723
    00724     Directory - Supplies the directory being searched
    00725
    00726     Name - Supplies the name of entry we're looking for
    00727
    00728     Attributes - Indicates if the lookup should be case insensitive
    00729         or not
    00730
    00731 Return Value:
    00732
    00733     Returns a pointer to the corresponding object body if found and NULL
    00734     otherwise.
    00735
    00736 --*/
    00737
    00738 {
    00739     POBJECT_DIRECTORY_ENTRY *HeadDirectoryEntry;
    00740     POBJECT_DIRECTORY_ENTRY DirectoryEntry;
    00741     POBJECT_HEADER ObjectHeader;
    00742     POBJECT_HEADER_NAME_INFO NameInfo;
    00743     PWCH Buffer;
    00744     WCHAR Wchar;
    00745     ULONG HashIndex;
    00746     ULONG WcharLength;
    00747     BOOLEAN CaseInSensitive;
    00748
    00749     PAGED_CODE();
    00750
    00751     //
    00752     //  The caller needs to specify both a directory and a name otherwise
    00753     //  we can't process the request
    00754     //
    00755
    00756     if (!Directory || !Name) {
    00757
    00758         return( NULL ); // BUG BUG
    00759     }
    00760
    00761     //
    00762     //  Set a local variable to tell us if the search is case sensitive
    00763     //
    00764
    00765     if (Attributes & OBJ_CASE_INSENSITIVE) {
    00766
    00767         CaseInSensitive = TRUE;
    00768
    00769     } else {
    00770
    00771         CaseInSensitive = FALSE;
    00772     }
    00773
    00774     //
    00775     //  Establish our local pointer to the input name buffer and get the
    00776     //  number of unicode characters in the input name.  Also make sure
    00777     //  the caller gave us a non null name
    00778     //
    00779
    00780     Buffer = Name->Buffer;
    00781     WcharLength = Name->Length / sizeof( *Buffer );
    00782
    00783     if (!WcharLength || !Buffer) {
    00784
    00785         return( NULL ); // BUG BUG
    00786     }
    00787
    00788     //
    00789     //  Compute the address of the head of the bucket chain for this name.
    00790     //
    00791
    00792     HashIndex = 0;
    00793     while (WcharLength--) {
    00794
    00795         Wchar = *Buffer++;
    00796         HashIndex += (HashIndex << 1) + (HashIndex >> 1);
    00797
    00798         if (Wchar < 'a') {
    00799
    00800             HashIndex += Wchar;
    00801
    00802         } else if (Wchar > 'z') {
    00803
    00804             HashIndex += RtlUpcaseUnicodeChar( Wchar );
    00805
    00806         } else {
    00807
    00808             HashIndex += (Wchar - ('a'-'A'));
    00809         }
    00810     }
    00811
    00812     HashIndex %= NUMBER_HASH_BUCKETS;
    00813
    00814     HeadDirectoryEntry = (POBJECT_DIRECTORY_ENTRY *)&Directory->HashBuckets[ HashIndex ];
    00815
    00816     Directory->LookupBucket = HeadDirectoryEntry;
    00817
    00818     //
    00819     //  Walk the chain of directory entries for this hash bucket, looking
    00820     //  for either a match, or the insertion point if no match in the chain.
    00821     //
    00822
    00823     while ((DirectoryEntry = *HeadDirectoryEntry) != NULL) {
    00824
    00825         //
    00826         //  Get the object header and name from the object body
    00827         //
    00828         //  This function assumes the name must exist, otherwise it
    00829         //  wouldn't be in a directory
    00830         //
    00831
    00832         ObjectHeader = OBJECT_TO_OBJECT_HEADER( DirectoryEntry->Object );
    00833         NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );
    00834
    00835         //
    00836         //  Compare strings using appropriate function.
    00837         //
    00838
    00839         if ((Name->Length == NameInfo->Name.Length) &&
    00840             RtlEqualUnicodeString( Name,
    00841                                    &NameInfo->Name,
    00842                                    CaseInSensitive )) {
    00843
    00844             //
    00845             //  If name matches, then exit loop with DirectoryEntry
    00846             //  pointing to matching entry.
    00847             //
    00848
    00849             break;
    00850         }
    00851
    00852         HeadDirectoryEntry = &DirectoryEntry->ChainLink;
    00853     }
    00854
    00855     //
    00856     //  At this point, there are two possiblilities:
    00857     //
    00858     //   - we found an entry that matched and DirectoryEntry points to that
    00859     //     entry.  Update the bucket chain so that the entry found is at the
    00860     //     head of the bucket chain.  This is so the ObpDeleteDirectoryEntry
    00861     //     and ObpInsertDirectoryEntry functions will work.  Also repeated
    00862     //     lookups of the same name will succeed quickly.
    00863     //
    00864     //   - we did not find an entry that matched and DirectoryEntry is NULL.
    00865     //
    00866
    00867     if (DirectoryEntry) {
    00868
    00869         Directory->LookupFound = TRUE;
    00870
    00871         //
    00872         //  The following convoluted piece of code moves a directory entry
    00873         //  we've found to the front of the hash list.
    00874         //
    00875
    00876         if (HeadDirectoryEntry != Directory->LookupBucket) {
    00877
    00878             *HeadDirectoryEntry = DirectoryEntry->ChainLink;
    00879             DirectoryEntry->ChainLink = *(Directory->LookupBucket);
    00880             *(Directory->LookupBucket) = DirectoryEntry;
    00881         }
    00882
    00883         //
    00884         //  Now return the object to our caller
    00885         //
    00886
    00887         return( DirectoryEntry->Object );
    00888
    00889     } else {
    00890
    00891         //
    00892         //  Otherwise we didn't find anything so return null
    00893         //
    00894
    00895         Directory->LookupFound = FALSE;
    00896
    00897         return( NULL );
    00898     }
    00899 }
    00900
    00901
    00902 BOOLEAN
    00903 ObpInsertDirectoryEntry (
    00904     IN POBJECT_DIRECTORY Directory,
    00905     IN PVOID Object
    00906     )
    00907
    00908 /*++
    00909
    00910 Routine Description:
    00911
    00912     This routine will insert a new directory entry into a directory
    00913     object.  The directory must have already have been searched using
    00914     ObpLookupDirectoryEntry because that routine sets the LookupBucket
    00915
    00916 Arguments:
    00917
    00918     Directory - Supplies the directory object being modified.  This
    00919         function assumes that we earlier did a lookup on the name
    00920         that was successful or we just did an insertion
    00921
    00922     Object - Supplies the object to insert into the directory
    00923
    00924 Return Value:
    00925
    00926     TRUE if the object is inserted successfully and FALSE otherwise
    00927
    00928 --*/
    00929
    00930 {
    00931     POBJECT_DIRECTORY_ENTRY *HeadDirectoryEntry;
    00932     POBJECT_DIRECTORY_ENTRY NewDirectoryEntry;
    00933     POBJECT_HEADER_NAME_INFO NameInfo;
    00934
    00935     //
    00936     //  Make sure we have a directory and that the last search was
    00937     //  successful, meaning there is a lookupbuket
    00938     //
    00939
    00940     if (!Directory || Directory->LookupFound) {
    00941
    00942         return( FALSE );
    00943     }
    00944
    00945     //
    00946     //  Also verify that we have a good lookupbucket
    00947     //
    00948
    00949     HeadDirectoryEntry = Directory->LookupBucket;
    00950
    00951     if (!HeadDirectoryEntry) {
    00952
    00953         return( FALSE );
    00954     }
    00955
    00956     //
    00957     //  Translate the object into a name info record, and make sure
    00958     //  that the object has a name
    00959     //
    00960
    00961     NameInfo = OBJECT_HEADER_TO_NAME_INFO( OBJECT_TO_OBJECT_HEADER( Object ) );
    00962
    00963     if (NameInfo == NULL) {
    00964
    00965         return FALSE;
    00966     }
    00967
    00968     //
    00969     //  Allocate memory for a new entry, and fail if not enough memory.
    00970     //
    00971
    00972     NewDirectoryEntry = (POBJECT_DIRECTORY_ENTRY)ExAllocatePoolWithTag( PagedPool,
    00973                                                                         sizeof( OBJECT_DIRECTORY_ENTRY ),
    00974                                                                         'iDbO' );
    00975
    00976     if (NewDirectoryEntry == NULL) {
    00977
    00978         return( FALSE );
    00979     }
    00980
    00981     //
    00982     //  Link the new entry into the chain at the insertion point.
    00983     //  This puts the new object right at the head of the current
    00984     //  hash bucket chain
    00985     //
    00986
    00987     NewDirectoryEntry->ChainLink = *HeadDirectoryEntry;
    00988     *HeadDirectoryEntry = NewDirectoryEntry;
    00989     NewDirectoryEntry->Object = Object;
    00990
    00991     //
    00992     //  Point the object header back to the directory we just inserted
    00993     //  it into.
    00994     //
    00995
    00996     NameInfo->Directory = Directory;
    00997
    00998     //
    00999     //  Return success.
    01000     //
    01001
    01002     Directory->LookupFound = TRUE;
    01003
    01004     return( TRUE );
    01005 }
    01006
    01007
    01008 BOOLEAN
    01009 ObpDeleteDirectoryEntry (
    01010     IN POBJECT_DIRECTORY Directory
    01011     )
    01012
    01013 /*++
    01014
    01015 Routine Description:
    01016
    01017     This routine deletes the most recently found directory entry from
    01018     the specified directory object.  It will only succeed after a
    01019     successful ObpLookupDirectoryEntry call.
    01020
    01021 Arguments:
    01022
    01023     Directory - Supplies the directory being modified
    01024
    01025 Return Value:
    01026
    01027     TRUE if the deletion succeeded and FALSE otherwise
    01028
    01029 --*/
    01030
    01031 {
    01032     POBJECT_DIRECTORY_ENTRY *HeadDirectoryEntry;
    01033     POBJECT_DIRECTORY_ENTRY DirectoryEntry;
    01034
    01035     //
    01036     //  Make sure we have a directory and that it has a found entry
    01037     //
    01038
    01039     if (!Directory || !Directory->LookupFound) {
    01040
    01041         return( FALSE );
    01042     }
    01043
    01044     //
    01045     //  Also make sure that the lookup bucket is valid
    01046     //
    01047
    01048     HeadDirectoryEntry = Directory->LookupBucket;
    01049
    01050     if (!HeadDirectoryEntry) {
    01051
    01052         return( FALSE );
    01053     }
    01054
    01055     DirectoryEntry = *HeadDirectoryEntry;
    01056
    01057     if (!DirectoryEntry) {
    01058
    01059         return( FALSE );
    01060     }
    01061
    01062     //
    01063     //  Unlink the entry from the head of the bucket chain and free the
    01064     //  memory for the entry.
    01065     //
    01066
    01067     *HeadDirectoryEntry = DirectoryEntry->ChainLink;
    01068     DirectoryEntry->ChainLink = NULL;
    01069
    01070     ExFreePool( DirectoryEntry );
    01071
    01072     //
    01073     //  Return success
    01074     //
    01075
    01076     return( TRUE );
    01077 }
    01078
    01079
    01080 NTSTATUS
    01081 ObpLookupObjectName (
    01082     IN HANDLE RootDirectoryHandle OPTIONAL,
    01083     IN PUNICODE_STRING ObjectName,
    01084     IN ULONG Attributes,
    01085     IN POBJECT_TYPE ObjectType,
    01086     IN KPROCESSOR_MODE AccessMode,
    01087     IN PVOID ParseContext OPTIONAL,
    01088     IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
    01089     IN PVOID InsertObject OPTIONAL,
    01090     IN OUT PACCESS_STATE AccessState,
    01091     OUT PBOOLEAN DirectoryLocked,
    01092     OUT PVOID *FoundObject
    01093     )
    01094
    01095 /*++
    01096
    01097 Routine Description:
    01098
    01099     This function will search a given directoroy for a specified
    01100     object name.  It will also create a new object specified by
    01101     InsertObject.
    01102
    01103 Arguments:
    01104
    01105     RootDirectoryHandle - Optionally supplies the directory being
    01106         searched.  If not supplied then this routine searches
    01107         the root directory
    01108
    01109     ObjectName - Supplies the name of object to lookup
    01110
    01111     Attributes - Specifies the attributes for the lookup (e.g., case
    01112         insensitive)
    01113
    01114     ObjectType - Specifies the type of the object to lookup
    01115
    01116     AccessMode - Specifies the callers processor mode
    01117
    01118     ParseContext - Optionally supplies a parse context that is blindly
    01119         passed to the parse callback routines
    01120
    01121     SecurityQos - Optionally supplies a pointer to the passed Security
    01122         Quality of Service parameter that is blindly passed to the parse
    01123         callback routines
    01124
    01125     InsertObject - Optionally supplies the object we think will be found.
    01126         This is used if the caller did not give a root directory handle
    01127         and the object name is "\" and the root object directory hasn't
    01128         been created yet.  In other cases where we wind up creating
    01129         a new directory entry this is the object inserted.
    01130
    01131     AccessState - Current access state, describing already granted access
    01132         types, the privileges used to get them, and any access types yet to
    01133         be granted.  The access masks may not contain any generic access
    01134         types.
    01135
    01136     DirectoryLocked - Receives an indication if this routine has returned
    01137         with the input directory locked
    01138
    01139     FoundObject - Receives a pointer to the object body if found
    01140
    01141 Return Value:
    01142
    01143     An appropriate status value
    01144
    01145 --*/
    01146
    01147 {
    01148     POBJECT_DIRECTORY RootDirectory;
    01149     POBJECT_DIRECTORY Directory;
    01150     POBJECT_DIRECTORY ParentDirectory = NULL;
    01151     POBJECT_HEADER ObjectHeader;
    01152     POBJECT_HEADER_NAME_INFO NameInfo;
    01153     PDEVICE_MAP DeviceMap = NULL;
    01154     PVOID Object;
    01155     UNICODE_STRING RemainingName;
    01156     UNICODE_STRING ComponentName;
    01157     PWCH NewName;
    01158     NTSTATUS Status;
    01159     BOOLEAN Reparse;
    01160     ULONG MaxReparse = OBJ_MAX_REPARSE_ATTEMPTS;
    01161     OB_PARSE_METHOD ParseProcedure;
    01162     extern POBJECT_TYPE IoFileObjectType;
    01163
    01164     ObpValidateIrql( "ObpLookupObjectName" );
    01165
    01166     //
    01167     //  Initialize our output variables to say we haven't lock or found
    01168     //  anything but we were successful at it
    01169     //
    01170
    01171     *DirectoryLocked = FALSE;
    01172     *FoundObject = NULL;
    01173     Status = STATUS_SUCCESS;
    01174
    01175     Object = NULL;
    01176
    01177     //
    01178     //  Check if the caller has given us a directory to search.  Otherwise
    01179     //  we'll search the root object directory
    01180     //
    01181
    01182     if (ARGUMENT_PRESENT( RootDirectoryHandle )) {
    01183
    01184         //
    01185         //  Otherwise reference the directory object and make sure
    01186         //  that we successfully got the object
    01187         //
    01188
    01189         Status = ObReferenceObjectByHandle( RootDirectoryHandle,
    01190                                             0,
    01191                                             NULL,
    01192                                             AccessMode,
    01193                                             (PVOID *)&RootDirectory,
    01194                                             NULL );
    01195
    01196         if (!NT_SUCCESS( Status )) {
    01197
    01198             return( Status );
    01199         }
    01200
    01201         //
    01202         //  Translate the directory object to its object header
    01203         //
    01204
    01205         ObjectHeader = OBJECT_TO_OBJECT_HEADER( RootDirectory );
    01206
    01207         //
    01208         //  Now if the name we're looking up starts with a "\" and it
    01209         //  does not have a parse procedure then the syntax is bad
    01210         //
    01211
    01212         if ((ObjectName->Buffer != NULL) &&
    01213             (*(ObjectName->Buffer) == OBJ_NAME_PATH_SEPARATOR) &&
    01214             (ObjectHeader->Type != IoFileObjectType)) {
    01215
    01216             ObDereferenceObject( RootDirectory );
    01217
    01218             return( STATUS_OBJECT_PATH_SYNTAX_BAD );
    01219         }
    01220
    01221         //
    01222         //  Now make sure that we do not have the directory of the
    01223         //  object types
    01224         //
    01225
    01226         if (ObjectHeader->Type != ObpDirectoryObjectType) {
    01227
    01228             //
    01229             //  We have an object directory that is not the object type
    01230             //  directory.  So now if it doesn't have a parse routine
    01231             //  then there is nothing we can
    01232             //
    01233
    01234             if (ObjectHeader->Type->TypeInfo.ParseProcedure == NULL) {
    01235
    01236                 ObDereferenceObject( RootDirectory );
    01237
    01238                 return( STATUS_INVALID_HANDLE );
    01239
    01240             } else {
    01241
    01242                 MaxReparse = OBJ_MAX_REPARSE_ATTEMPTS;
    01243
    01244                 //
    01245                 //  The following loop cycles cycles through the various
    01246                 //  parse routine to we could encounter trying to resolve
    01247                 //  this name through symbolic links.
    01248                 //
    01249
    01250                 while (TRUE) {
    01251
    01252                     KIRQL SaveIrql;
    01253
    01254                     RemainingName = *ObjectName;
    01255
    01256                     //
    01257                     //  Invoke the callback routine to parse the remaining
    01258                     //  object name
    01259                     //
    01260
    01261                     ObpBeginTypeSpecificCallOut( SaveIrql );
    01262
    01263                     Status = (*ObjectHeader->Type->TypeInfo.ParseProcedure)( RootDirectory,
    01264                                                                              ObjectType,
    01265                                                                              AccessState,
    01266                                                                              AccessMode,
    01267                                                                              Attributes,
    01268                                                                              ObjectName,
    01269                                                                              &RemainingName,
    01270                                                                              ParseContext,
    01271                                                                              SecurityQos,
    01272                                                                              &Object );
    01273
    01274                     ObpEndTypeSpecificCallOut( SaveIrql, "Parse", ObjectHeader->Type, Object );
    01275
    01276                     //
    01277                     //  If the status was not to do a reparse and the lookup
    01278                     //  was not successful then we found nothing so we
    01279                     //  dereference the directory and return the status to
    01280                     //  our caller.  If the object we got back was null then
    01281                     //  we'll tell our caller that we couldn't find the name.
    01282                     //  Lastly if we did not get a reparse and we were
    01283                     //  successful and the object is not null then everything
    01284                     //  gets nicely returned to our caller
    01285                     //
    01286
    01287                     if ( ( Status != STATUS_REPARSE ) &&
    01288                          ( Status != STATUS_REPARSE_OBJECT )) {
    01289
    01290                         if (!NT_SUCCESS( Status )) {
    01291
    01292                             Object = NULL;
    01293
    01294                         } else if (Object == NULL) {
    01295
    01296                             Status = STATUS_OBJECT_NAME_NOT_FOUND;
    01297                         }
    01298
    01299                         ObDereferenceObject( RootDirectory );
    01300
    01301                         *FoundObject = Object;
    01302
    01303                         return( Status );
    01304
    01305                     //
    01306                     //  We got a status reparse, which means the object
    01307                     //  name has been modified to have use start all over
    01308                     //  again.  If the reparse target is now empty or it
    01309                     //  is a path separator then we start the parse at the
    01310                     //  root directory
    01311                     //
    01312
    01313                     } else if ((ObjectName->Length == 0) ||
    01314                                (ObjectName->Buffer == NULL) ||
    01315                                (*(ObjectName->Buffer) == OBJ_NAME_PATH_SEPARATOR)) {
    01316
    01317                         //
    01318                         //  Restart the parse relative to the root directory.
    01319                         //
    01320
    01321                         ObDereferenceObject( RootDirectory );
    01322
    01323                         RootDirectory = ObpRootDirectoryObject;
    01324                         RootDirectoryHandle = NULL;
    01325
    01326                         break;
    01327
    01328                     //
    01329                     //  We got a reparse and we actually have a new name to
    01330                     //  go to we if we haven't exhausted our reparse attempts
    01331                     //  yet then just continue to the top of this loop.
    01332                     //
    01333
    01334                     } else if (--MaxReparse) {
    01335
    01336                         continue;
    01337
    01338                     //
    01339                     //  We got a reparse and we've exhausted our times through
    01340                     //  the loop so we'll return what we found.
    01341                     //
    01342                     //  **** this doesn't seem right.  It probably should be
    01343                     //  an error
    01344                     //
    01345
    01346                     } else {
    01347
    01348                         ObDereferenceObject( RootDirectory );
    01349
    01350                         *FoundObject = Object;
    01351
    01352                         //
    01353                         //  At this point we were failing in stress by
    01354                         //  returning to the caller with a success status but
    01355                         //  a null object pointer.
    01356                         //
    01357
    01358                         if (Object == NULL) {
    01359
    01360                             Status = STATUS_OBJECT_NAME_NOT_FOUND;
    01361                         }
    01362
    01363                         return( Status );
    01364                     }
    01365                 }
    01366             }
    01367
    01368         //
    01369         //  At this point the caller has given us the directory of object
    01370         //  types.  If the caller didn't specify a name then we'll return
    01371         //  a pointer to the root object directory.
    01372         //
    01373
    01374         } else if ((ObjectName->Length == 0) ||
    01375                    (ObjectName->Buffer == NULL)) {
    01376
    01377             Status = ObReferenceObjectByPointer( RootDirectory,
    01378                                                  0,
    01379                                                  ObjectType,
    01380                                                  AccessMode );
    01381
    01382             if (NT_SUCCESS( Status )) {
    01383
    01384                 Object = RootDirectory;
    01385             }
    01386
    01387             ObDereferenceObject( RootDirectory );
    01388
    01389             *FoundObject = Object;
    01390
    01391             return( Status );
    01392         }
    01393
    01394     //
    01395     //  Otherwise the caller did not specify a directory to search so
    01396     //  we'll default to the object root directory
    01397     //
    01398
    01399     } else {
    01400
    01401         RootDirectory = ObpRootDirectoryObject;
    01402
    01403         //
    01404         //  If the name we're looking for is empty then it is illformed.
    01405         //  Also it has to start with a "\" or it is illformed.
    01406         //
    01407
    01408         if ((ObjectName->Length == 0) ||
    01409             (ObjectName->Buffer == NULL) ||
    01410             (*(ObjectName->Buffer) != OBJ_NAME_PATH_SEPARATOR)) {
    01411
    01412             return( STATUS_OBJECT_PATH_SYNTAX_BAD );
    01413         }
    01414
    01415         //
    01416         //  Check if the name is has only one character (that is the "\")
    01417         //  Which means that the caller really just wants to lookup the
    01418         //  root directory.
    01419         //
    01420
    01421         if (ObjectName->Length == sizeof( OBJ_NAME_PATH_SEPARATOR )) {
    01422
    01423             //
    01424             //  If there is not a root directory yet.  Then we really
    01425             //  can't return it, however if the caller specified
    01426             //  an insert object that is the one we'll reference and
    01427             //  return to our caller
    01428             //
    01429
    01430             if (!RootDirectory) {
    01431
    01432                 if (InsertObject) {
    01433
    01434                     Status = ObReferenceObjectByPointer( InsertObject,
    01435                                                          0,
    01436                                                          ObjectType,
    01437                                                          AccessMode );
    01438
    01439                     if (NT_SUCCESS( Status )) {
    01440
    01441                         *FoundObject = InsertObject;
    01442                     }
    01443
    01444                     return( Status );
    01445
    01446                 } else {
    01447
    01448                     return( STATUS_INVALID_PARAMETER );
    01449                 }
    01450
    01451             //
    01452             //  At this point the caller did not specify a root directory,
    01453             //  the name is "\" and the root object directory exists so
    01454             //  we'll simply return the real root directory object
    01455             //
    01456
    01457             } else {
    01458
    01459                 Status = ObReferenceObjectByPointer( RootDirectory,
    01460                                                      0,
    01461                                                      ObjectType,
    01462                                                      AccessMode );
    01463
    01464                 if (NT_SUCCESS( Status )) {
    01465
    01466                     *FoundObject = RootDirectory;
    01467                 }
    01468
    01469                 return( Status );
    01470             }
    01471
    01472         //
    01473         //  At this pointer the caller did not specify a root directory,
    01474         //  and the name is more than just a "\"
    01475         //
    01476         //  Now if the lookup is case insensitive, and the name buffer is a
    01477         //  legitimate pointer (meaning that is it quadword aligned), and
    01478         //  there is a dos device map for the process.  Then we'll handle
    01479         //  the situation here. First get the device map and make sure it
    01480         //  doesn't go away while we're using it.
    01481         //
    01482
    01483         } else {
    01484
    01485             KIRQL OldIrql;
    01486
    01487             ExAcquireSpinLock( &ObpDeviceMapLock, &OldIrql );
    01488
    01489             if ((DeviceMap = PsGetCurrentProcess()->DeviceMap) != NULL) {
    01490
    01491                 DeviceMap->ReferenceCount++;
    01492                 ExReleaseSpinLock( &ObpDeviceMapLock, OldIrql );
    01493
    01494                 if (!((ULONG_PTR)(ObjectName->Buffer) & (sizeof(ULONGLONG)-1))
    01495
    01496                             &&
    01497
    01498                     (DeviceMap->DosDevicesDirectory != NULL )) {
    01499
    01500                     //
    01501                     //  Check if the object name is actually equal to the
    01502                     //  global dos devices short name prefix "\??\"
    01503                     //
    01504
    01505                     if ((ObjectName->Length >= ObpDosDevicesShortName.Length)
    01506
    01507                             &&
    01508
    01509                         (*(PULONGLONG)(ObjectName->Buffer) == ObpDosDevicesShortNamePrefix.QuadPart)) {
    01510
    01511                         //
    01512                         //  The user gave us the dos short name prefix so we'll
    01513                         //  look down the directory, and start the search at the
    01514                         //  dos device directory
    01515                         //
    01516
    01517                         *DirectoryLocked = TRUE;
    01518
    01519                         ObpEnterRootDirectoryMutex();
    01520
    01521                         ParentDirectory = RootDirectory;
    01522
    01523                         Directory = DeviceMap->DosDevicesDirectory;
    01524
    01525                         RemainingName = *ObjectName;
    01526                         RemainingName.Buffer += (ObpDosDevicesShortName.Length / sizeof( WCHAR ));
    01527                         RemainingName.Length -= ObpDosDevicesShortName.Length;
    01528
    01529                         goto quickStart;
    01530
    01531                     //
    01532                     //  The name is not equal to "\??\" but check if it is
    01533                     //  equal to "\??"
    01534                     //
    01535
    01536                     } else if ((ObjectName->Length == ObpDosDevicesShortName.Length - sizeof( WCHAR ))
    01537
    01538                                     &&
    01539
    01540                                (*(PULONG)(ObjectName->Buffer) == ObpDosDevicesShortNameRoot.LowPart)
    01541
    01542                                     &&
    01543
    01544                                (*((PWCHAR)(ObjectName->Buffer)+2) == (WCHAR)(ObpDosDevicesShortNameRoot.HighPart))) {
    01545
    01546                         //
    01547                         //  The user specified "\??" so we return to dos devices
    01548                         //  directory to our caller
    01549                         //
    01550
    01551                         Status = ObReferenceObjectByPointer( DeviceMap->DosDevicesDirectory,
    01552                                                              0,
    01553                                                              ObjectType,
    01554                                                              AccessMode );
    01555
    01556                         if (NT_SUCCESS( Status )) {
    01557
    01558                             *FoundObject = DeviceMap->DosDevicesDirectory;
    01559                         }
    01560
    01561                         //
    01562                         //  Dereference the Device Map
    01563                         //
    01564
    01565                         {
    01566                             KIRQL OldIrql;
    01567
    01568                             ExAcquireSpinLock( &ObpDeviceMapLock, &OldIrql );
    01569
    01570                             DeviceMap->ReferenceCount--;
    01571
    01572                             if (DeviceMap->ReferenceCount == 0) {
    01573
    01574                                 ExReleaseSpinLock( &ObpDeviceMapLock, OldIrql );
    01575
    01576                                 DeviceMap->DosDevicesDirectory->DeviceMap = NULL;
    01577                                 ObDereferenceObject( DeviceMap->DosDevicesDirectory );
    01578
    01579                                 ExFreePool( DeviceMap );
    01580
    01581                             } else {
    01582
    01583                                 ExReleaseSpinLock( &ObpDeviceMapLock, OldIrql );
    01584                             }
    01585                         }
    01586
    01587                         return( Status );
    01588                     }
    01589                 }
    01590
    01591             } else {
    01592
    01593                 ExReleaseSpinLock( &ObpDeviceMapLock, OldIrql );
    01594             }
    01595         }
    01596     }
    01597
    01598     //
    01599     //  At this point either
    01600     //
    01601     //  the user specified a directory that is not the object
    01602     //  type directory and got repase back to the root directory
    01603     //
    01604     //  the user specified the object type directory and gave us
    01605     //  a name to actually look up
    01606     //
    01607     //  the user did not specify a search directory (default
    01608     //  to root object directory) and if the name did start off
    01609     //  with the dos device prefix we've munged outselves back to
    01610     //  it to the dos device directory for the process
    01611     //
    01612
    01613     Reparse = TRUE;
    01614     MaxReparse = OBJ_MAX_REPARSE_ATTEMPTS;
    01615
    01616     while (Reparse) {
    01617
    01618         RemainingName = *ObjectName;
    01619
    01620 quickStart:
    01621
    01622         Reparse = FALSE;
    01623
    01624         while (TRUE) {
    01625
    01626             Object = NULL;
    01627
    01628             //if (RemainingName.Length == 0) {
    01629             //    Status = STATUS_OBJECT_NAME_INVALID;
    01630             //    break;
    01631             //    }
    01632
    01633             //
    01634             //  If the remaining name for the object starts with a
    01635             //  "\" then just gobble up the "\"
    01636             //
    01637
    01638             if ( (RemainingName.Length != 0) &&
    01639                  (*(RemainingName.Buffer) == OBJ_NAME_PATH_SEPARATOR) ) {
    01640
    01641                 RemainingName.Buffer++;
    01642                 RemainingName.Length -= sizeof( OBJ_NAME_PATH_SEPARATOR );
    01643             }
    01644
    01645             //
    01646             //  The following piece of code will calculate the first
    01647             //  component of the remaining name.  If there is not
    01648             //  a remaining component then the object name is illformed
    01649             //
    01650
    01651             ComponentName = RemainingName;
    01652
    01653             while (RemainingName.Length != 0) {
    01654
    01655                 if (*(RemainingName.Buffer) == OBJ_NAME_PATH_SEPARATOR) {
    01656
    01657                     break;
    01658                 }
    01659
    01660                 RemainingName.Buffer++;
    01661                 RemainingName.Length -= sizeof( OBJ_NAME_PATH_SEPARATOR );
    01662             }
    01663
    01664             ComponentName.Length -= RemainingName.Length;
    01665
    01666             if (ComponentName.Length == 0) {
    01667
    01668                 Status = STATUS_OBJECT_NAME_INVALID;
    01669                 break;
    01670             }
    01671
    01672             //
    01673             //  Now we have the first component name to lookup so we'll
    01674             //  look the directory is necessary
    01675             //
    01676
    01677             if (!*DirectoryLocked) {
    01678
    01679                 *DirectoryLocked = TRUE;
    01680                 ObpEnterRootDirectoryMutex();
    01681                 Directory = RootDirectory;
    01682             }
    01683
    01684             //
    01685             //  Now if the caller does not have traverse privilege and
    01686             //  there is a parent directory then we must check if the
    01687             //  user has traverse access to the directory.  Our local
    01688             //  Reparse variable should be false at this point so we'll
    01689             //  drop out of both loops
    01690             //
    01691
    01692             if ( !(AccessState->Flags & TOKEN_HAS_TRAVERSE_PRIVILEGE) && (ParentDirectory != NULL) ) {
    01693
    01694                 if (!ObpCheckTraverseAccess( ParentDirectory,
    01695                                              DIRECTORY_TRAVERSE,
    01696                                              AccessState,
    01697                                              FALSE,
    01698                                              AccessMode,
    01699                                              &Status )) {
    01700
    01701                     break;
    01702                 }
    01703             }
    01704
    01705             //
    01706             //  If the object already exists in this directory, find it,
    01707             //  else return NULL.
    01708             //
    01709
    01710             Object = ObpLookupDirectoryEntry( Directory, &ComponentName, Attributes );
    01711
    01712             if (!Object) {
    01713
    01714                 //
    01715                 //  We didn't find the object.  If there is some remaining
    01716                 //  name left (meaning the component name is a directory in
    01717                 //  path we trying to break) or the caller didn't specify an
    01718                 //  insert object then we then we'll break out here with an
    01719                 //  error status
    01720                 //
    01721
    01722                 if (RemainingName.Length != 0) {
    01723
    01724                     Status = STATUS_OBJECT_PATH_NOT_FOUND;
    01725                     break;
    01726                 }
    01727
    01728                 if (!InsertObject) {
    01729
    01730                     Status = STATUS_OBJECT_NAME_NOT_FOUND;
    01731                     break;
    01732                 }
    01733
    01734                 //
    01735                 //  Check that the caller has the access to the directory
    01736                 //  to either create a subdirectory (in the object type
    01737                 //  directory) or to create an object of the given component
    01738                 //  name.  If the call fails then we'll break out of here
    01739                 //  with the status value set
    01740                 //
    01741
    01742                 if (!ObCheckCreateObjectAccess( Directory,
    01743                                                 ObjectType == ObpDirectoryObjectType ?
    01744                                                         DIRECTORY_CREATE_SUBDIRECTORY :
    01745                                                         DIRECTORY_CREATE_OBJECT,
    01746                                                 AccessState,
    01747                                                 &ComponentName,
    01748                                                 FALSE,
    01749                                                 AccessMode,
    01750                                                 &Status )) {
    01751
    01752                     break;
    01753                 }
    01754
    01755                 //
    01756                 //  The object does not exist in the directory and
    01757                 //  we are allowed to create one.  So allocate space
    01758                 //  for the name and insert the name into the directory
    01759                 //
    01760
    01761                 NewName = ExAllocatePoolWithTag( PagedPool, ComponentName.Length, 'mNbO' );
    01762
    01763                 if ((NewName == NULL) ||
    01764                     !ObpInsertDirectoryEntry( Directory, InsertObject )) {
    01765
    01766                     if (NewName != NULL) {
    01767
    01768                         ExFreePool( NewName );
    01769                     }
    01770
    01771                     Status = STATUS_INSUFFICIENT_RESOURCES;
    01772                     break;
    01773                 }
    01774
    01775                 //
    01776                 //  We have an insert object so now get its name info,
    01777                 //  because we are going to change its name and insert it
    01778                 //  into the directory
    01779                 //
    01780
    01781                 ObReferenceObject( InsertObject );
    01782
    01783                 ObjectHeader = OBJECT_TO_OBJECT_HEADER( InsertObject );
    01784
    01785                 NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );
    01786
    01787                 ObReferenceObject( Directory );
    01788
    01789                 RtlMoveMemory( NewName,
    01790                                ComponentName.Buffer,
    01791                                ComponentName.Length );
    01792
    01793                 if (NameInfo->Name.Buffer) {
    01794
    01795                     ExFreePool( NameInfo->Name.Buffer );
    01796                 }
    01797
    01798                 NameInfo->Name.Buffer = NewName;
    01799                 NameInfo->Name.Length = ComponentName.Length;
    01800                 NameInfo->Name.MaximumLength = ComponentName.Length;
    01801
    01802                 Object = InsertObject;
    01803
    01804                 Status = STATUS_SUCCESS;
    01805
    01806                 break;
    01807             }
    01808
    01809             //
    01810             //  At this point we've found the component name within
    01811             //  the directory.  So we'll now grab the components object
    01812             //  header, and get its parse routine
    01813             //
    01814
    01815 ReparseObject:
    01816
    01817             ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
    01818             ParseProcedure = ObjectHeader->Type->TypeInfo.ParseProcedure;
    01819
    01820             //
    01821             //  Now if there is a parse routine for the type and we are not
    01822             //  inserting a new object or the parse routine is for symbolic
    01823             //  links then we'll actually call the parse routine
    01824             //
    01825
    01826             if (ParseProcedure && (!InsertObject || (ParseProcedure == ObpParseSymbolicLink))) {
    01827
    01828                 KIRQL SaveIrql;
    01829
    01830                 //
    01831                 //  Reference the object and then free the directory lock
    01832                 //  This will keep the object from going away with the
    01833                 //  directory unlocked
    01834                 //
    01835
    01836                 ObpIncrPointerCount( ObjectHeader );
    01837
    01838                 ASSERT(*DirectoryLocked);
    01839
    01840                 ObpLeaveRootDirectoryMutex();
    01841
    01842                 *DirectoryLocked = FALSE;
    01843
    01844                 ObpBeginTypeSpecificCallOut( SaveIrql );
    01845
    01846                 //
    01847                 //  Call the objects parse routine
    01848                 //
    01849
    01850                 Status = (*ParseProcedure)( Object,
    01851                                             (PVOID)ObjectType,
    01852                                             AccessState,
    01853                                             AccessMode,
    01854                                             Attributes,
    01855                                             ObjectName,
    01856                                             &RemainingName,
    01857                                             ParseContext,
    01858                                             SecurityQos,
    01859                                             &Object );
    01860
    01861                 ObpEndTypeSpecificCallOut( SaveIrql, "Parse", ObjectHeader->Type, Object );
    01862
    01863                 //
    01864                 //  We can now decrement the object reference count
    01865                 //
    01866
    01867                 ObDereferenceObject( &ObjectHeader->Body );
    01868
    01869                 //
    01870                 //  Check if we have some reparsing to do
    01871                 //
    01872
    01873                 if ((Status == STATUS_REPARSE) || (Status == STATUS_REPARSE_OBJECT)) {
    01874
    01875                     //
    01876                     //  See if we've reparsed too many times already and if
    01877                     //  so we'll fail the request
    01878                     //
    01879
    01880                     if (--MaxReparse) {
    01881
    01882                         //
    01883                         //  Tell the outer loop to continue looping
    01884                         //
    01885
    01886                         Reparse = TRUE;
    01887
    01888                         //
    01889                         //  Check if we have a reparse object or the name
    01890                         //  starts with a "\"
    01891                         //
    01892
    01893                         if ((Status == STATUS_REPARSE_OBJECT) ||
    01894                             (*(ObjectName->Buffer) == OBJ_NAME_PATH_SEPARATOR)) {
    01895
    01896                             //
    01897                             //  If the user specified a start directory then
    01898                             //  remove this information because we're taking
    01899                             //  a reparse point to someplace else
    01900                              //
    01901
    01902                             if (ARGUMENT_PRESENT( RootDirectoryHandle )) {
    01903
    01904                                 ObDereferenceObject( RootDirectory );
    01905                                 RootDirectoryHandle = NULL;
    01906                             }
    01907
    01908                             //
    01909                             //  And where we start is the root directory
    01910                             //  object
    01911                             //
    01912
    01913                             ParentDirectory = NULL;
    01914                             RootDirectory = ObpRootDirectoryObject;
    01915
    01916                             //
    01917                             //  Now if this is a reparse object (means we have
    01918                             //  encountered a symbolic link that has already been
    01919                             //  snapped so we have an object and remaining
    01920                             //  name that need to be examined) and we didn't
    01921                             //  find an object from the parse routine object
    01922                             //  break out of both loops.
    01923                             //
    01924
    01925                             if (Status == STATUS_REPARSE_OBJECT) {
    01926
    01927                                 Reparse = FALSE;
    01928
    01929                                 if (Object == NULL) {
    01930
    01931                                     Status = STATUS_OBJECT_NAME_NOT_FOUND;
    01932
    01933                                 } else {
    01934
    01935                                     //
    01936                                     //  At this point we have a reparse object
    01937                                     //  so we'll look the directory down and
    01938                                     //  parse the new object
    01939                                     //
    01940
    01941                                     *DirectoryLocked = TRUE;
    01942                                     ObpEnterRootDirectoryMutex();
    01943
    01944                                     goto ReparseObject;
    01945                                 }
    01946                             }
    01947
    01948                         //
    01949                         //  We did not have a reparse object and the name
    01950                         //  does not start with a "\".  Meaning we got back
    01951                         //  STATUS_REPASE, so now check if the directory
    01952                         //  is the root object directory and if so then
    01953                         //  we didn't the name otherwise we'll drop out of
    01954                         //  the inner loop and reparse true to get back to
    01955                         //  outer loop
    01956                         //
    01957
    01958                         } else if (RootDirectory == ObpRootDirectoryObject) {
    01959
    01960                             Object = NULL;
    01961                             Status = STATUS_OBJECT_NAME_NOT_FOUND;
    01962
    01963                             Reparse = FALSE;
    01964                         }
    01965
    01966                     } else {
    01967
    01968                         //
    01969                         //  **** this should probably be a differnt error
    01970                         //  status related to too many reparse points
    01971                         //
    01972
    01973                         Object = NULL;
    01974                         Status = STATUS_OBJECT_NAME_NOT_FOUND;
    01975                     }
    01976
    01977                 //
    01978                 //  We are not reparsing and if we did not get success then
    01979                 //  the object is null and we'll break out of our loops
    01980                 //
    01981
    01982                 } else if (!NT_SUCCESS( Status )) {
    01983
    01984                     Object = NULL;
    01985
    01986                 //
    01987                 //  We are not reparsing and we got back success but check
    01988                 //  if the object is null because that means we really didn't
    01989                 //  find the object, and then break out of our loops
    01990                 //
    01991                 //  If the object is not null then we've been successful and
    01992                 //  prosperous so break out with the object set.
    01993                 //
    01994
    01995                 } else if (Object == NULL) {
    01996
    01997                     Status = STATUS_OBJECT_NAME_NOT_FOUND;
    01998                 }
    01999
    02000                 break;
    02001
    02002             } else {
    02003
    02004                 //
    02005                 //  At this point we do not have a parse routine or if there
    02006                 //  is a parse routine it is not for symbolic links or there
    02007                 //  may not be a specified insert object
    02008                 //
    02009                 //  Check to see if we have exhausted the remaining name
    02010                 //
    02011
    02012                 if (RemainingName.Length == 0) {
    02013
    02014                     //
    02015                     //  Check if the caller specified an object to insert.
    02016                     //  If specified then we'll break out of our loops with
    02017                     //  the object that we've found
    02018                     //
    02019
    02020                     if (!InsertObject) {
    02021
    02022                         //
    02023                         //  The user did not specify an insert object
    02024                         //  so we're opening an existing object.  Make sure
    02025                         //  we have traverse access to the container
    02026                         //  directory.
    02027                         //
    02028
    02029                         if ( !(AccessState->Flags & TOKEN_HAS_TRAVERSE_PRIVILEGE) ) {
    02030
    02031                             if (!ObpCheckTraverseAccess( Directory,
    02032                                                          DIRECTORY_TRAVERSE,
    02033                                                          AccessState,
    02034                                                          FALSE,
    02035                                                          AccessMode,
    02036                                                          &Status )) {
    02037
    02038                                 Object = NULL;
    02039                                 break;
    02040                             }
    02041                         }
    02042
    02043                         Status = ObReferenceObjectByPointer( Object,
    02044                                                              0,
    02045                                                              ObjectType,
    02046                                                              AccessMode );
    02047
    02048                         if (!NT_SUCCESS( Status )) {
    02049
    02050                             Object = NULL;
    02051                         }
    02052                     }
    02053
    02054                     break;
    02055
    02056                 } else {
    02057
    02058                     //
    02059                     //  There is some name remaining names to process
    02060                     //  if the directory we're looking at is the
    02061                     //  directory of object types and set ourselves
    02062                     //  up to parse it all over again.
    02063                     //
    02064
    02065                     if (ObjectHeader->Type == ObpDirectoryObjectType) {
    02066
    02067                         ParentDirectory = Directory;
    02068                         Directory = (POBJECT_DIRECTORY)Object;
    02069
    02070                     } else {
    02071
    02072                         //
    02073                         //  Otherwise there has been a mismatch so we'll
    02074                         //  set our error status and break out of the
    02075                         //  loops
    02076                         //
    02077
    02078                         Status = STATUS_OBJECT_TYPE_MISMATCH;
    02079                         Object = NULL;
    02080
    02081                         break;
    02082                     }
    02083                 }
    02084             }
    02085         }
    02086     }
    02087
    02088     //
    02089     //  If the device map has been referenced then dereference it
    02090     //
    02091
    02092     if (DeviceMap != NULL) {
    02093
    02094         KIRQL OldIrql;
    02095
    02096         ExAcquireSpinLock( &ObpDeviceMapLock, &OldIrql );
    02097
    02098         DeviceMap->ReferenceCount--;
    02099
    02100         if (DeviceMap->ReferenceCount == 0) {
    02101
    02102             ExReleaseSpinLock( &ObpDeviceMapLock, OldIrql );
    02103
    02104             DeviceMap->DosDevicesDirectory->DeviceMap = NULL;
    02105             ObDereferenceObject( DeviceMap->DosDevicesDirectory );
    02106
    02107             ExFreePool( DeviceMap );
    02108
    02109         } else {
    02110
    02111             ExReleaseSpinLock( &ObpDeviceMapLock, OldIrql );
    02112         }
    02113     }
    02114
    02115     //
    02116     //  At this point we've parsed the object name as much as possible
    02117     //  going through symbolic links as necessary.  So now set the
    02118     //  output object pointer, and if we really did not find an object
    02119     //  then we might need to modify the error status.  If the
    02120     //  status was repase or some success status then translate it
    02121     //  to name not found.
    02122     //
    02123
    02124     if (!(*FoundObject = Object)) {
    02125
    02126         if (Status == STATUS_REPARSE) {
    02127
    02128             Status = STATUS_OBJECT_NAME_NOT_FOUND;
    02129
    02130         } else if (NT_SUCCESS( Status )) {
    02131
    02132             Status = STATUS_OBJECT_NAME_NOT_FOUND;
    02133         }
    02134     }
    02135
    02136     //
    02137     //  If the caller gave us a root directory to search (and we didn't
    02138     //  zero out this value) then free up our reference
    02139     //
    02140
    02141     if (ARGUMENT_PRESENT( RootDirectoryHandle )) {
    02142
    02143         ObDereferenceObject( RootDirectory );
    02144         RootDirectoryHandle = NULL;
    02145     }
    02146
    02147     //
    02148     //  And return to our caller
    02149     //
    02150
    02151     return( Status );
    02152 }
    02153
    02154