test: dirctrl.c Source File

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

# dirctrl.c

Go to the documentation of this file.

    00001 /*++
    00002
    00003 Copyright (c) 1989  Microsoft Corporation
    00004
    00005 Module Name:
    00006
    00007     DirCtrl.c
    00008
    00009 Abstract:
    00010
    00011     This module implements the File Directory Control routines for Udfs called
    00012     by the Fsd/Fsp dispatch drivers.
    00013
    00014 Author:
    00015
    00016     Dan Lovinger    [DanLo]     27-Nov-1996
    00017
    00018 Revision History:
    00019
    00020 --*/
    00021
    00022 #include "UdfProcs.h"
    00023
    00024 //
    00025 //  The Bug check file id for this module
    00026 //
    00027
    00028 #define BugCheckFileId                   (UDFS_BUG_CHECK_DIRCTRL)
    00029
    00030 //
    00031 //  The local debug trace level
    00032 //
    00033
    00034 #define Dbg                              (UDFS_DEBUG_LEVEL_DIRCTRL)
    00035
    00036 //
    00037 //  Local structures
    00038 //
    00039
    00040 //
    00041 //  The following is used for the more complete enumeration required in the DirectoryControl path
    00042 //  and encapsulates the structures for enumerating both directories and ICBs, as well as converted
    00043 //  data from the ICB.
    00044 //
    00045
    00046 typedef struct _COMPOUND_DIR_ENUM_CONTEXT {
    00047
    00048     //
    00049     //  Standard enumeration contexts.  For this enumeration we walk the directory and lift the
    00050     //  associated ICB for each entry.
    00051     //
    00052
    00053     DIR_ENUM_CONTEXT DirContext;
    00054     ICB_SEARCH_CONTEXT IcbContext;
    00055
    00056     //
    00057     //  Timestamps converted from the ICB into NT-native form.
    00058     //
    00059
    00060     TIMESTAMP_BUNDLE Timestamps;
    00061
    00062     //
    00063     //  File index corresponding to the current position in the enumeration.
    00064     //
    00065
    00066     LARGE_INTEGER FileIndex;
    00067
    00068 } COMPOUND_DIR_ENUM_CONTEXT, *PCOMPOUND_DIR_ENUM_CONTEXT;
    00069
    00070 //
    00071 //  Local macros
    00072 //
    00073
    00074 //
    00075 //  Constants defining the space of FileIndices for directory enumeration.
    00076 //
    00077
    00078 //
    00079 //  The virtual (synthesized) file indices
    00080 //
    00081
    00082 #define UDF_FILE_INDEX_VIRTUAL_SELF         0
    00083
    00084 //
    00085 //  The file index where the physical directory entries begin
    00086 //
    00087
    00088 #define UDF_FILE_INDEX_PHYSICAL             1
    00089
    00090 //
    00091 //  Provide initialization and cleanup for compound enumeration contexts.
    00092 //
    00093
    00094 INLINE
    00095 VOID
    00096 UdfInitializeCompoundDirContext (
    00097     IN PIRP_CONTEXT IrpContext,
    00098     IN PCOMPOUND_DIR_ENUM_CONTEXT CompoundDirContext
    00099     )
    00100 {
    00101
    00102     UdfInitializeDirContext( IrpContext, &CompoundDirContext->DirContext );
    00103     UdfFastInitializeIcbContext( IrpContext, &CompoundDirContext->IcbContext );
    00104
    00105     RtlZeroMemory( &CompoundDirContext->Timestamps, sizeof( TIMESTAMP_BUNDLE ));
    00106
    00107     CompoundDirContext->FileIndex.QuadPart = 0;
    00108 }
    00109
    00110 INLINE
    00111 VOID
    00112 UdfCleanupCompoundDirContext (
    00113     IN PIRP_CONTEXT IrpContext,
    00114     IN PCOMPOUND_DIR_ENUM_CONTEXT CompoundDirContext
    00115     )
    00116 {
    00117
    00118     UdfCleanupDirContext( IrpContext, &CompoundDirContext->DirContext );
    00119     UdfCleanupIcbContext( IrpContext, &CompoundDirContext->IcbContext );
    00120 }
    00121
    00122 //
    00123 //  UDF directories are unsorted (UDF 1.0.1 2.3.5.3) and do not contain self
    00124 //  entries.  For directory enumeration we must provide a way for a restart to
    00125 //  occur at a random entry (SL_INDEX_SPECIFIED), but the key used is only
    00126 //  32bits.  Since the directory is unsorted, the filename is unsuitable for
    00127 //  quickly finding a restart point (even assuming that it was sorted,
    00128 //  discovering a directory entry is still not fast).  Additionally, we must
    00129 //  synthesize the self-entry.  So, here is how we map the space of file
    00130 //  indices to directory entries:
    00131 //
    00132 //    File Index              Directory Entry
    00133 //
    00134 //    0                       self ('.')
    00135 //    1                       at byte offset 0 in the stream
    00136 //    N                       at byte offset N-1 in the stream
    00137 //
    00138 //  The highest 32bit FileIndex returned will be stashed in the Ccb.
    00139 //
    00140 //  For FileIndex > 2^32, we will return FileIndex 0 in the query structure.
    00141 //  On a restart, we will notice a FileIndex of zero and use the saved high
    00142 //  32bit FileIndex as the starting point for a linear scan to find the named
    00143 //  directory entry in the restart request.  In this way we only penalize the
    00144 //  improbable case of a directory stream > 2^32 bytes.
    00145 //
    00146 //  The following inline routines assist with this mapping.
    00147 //
    00148
    00149 INLINE
    00150 LONGLONG
    00151 UdfFileIndexToPhysicalOffset(
    00152     LONGLONG FileIndex
    00153     )
    00154 {
    00155
    00156     return FileIndex - UDF_FILE_INDEX_PHYSICAL;
    00157 }
    00158
    00159 INLINE
    00160 LONGLONG
    00161 UdfPhysicalOffsetToFileIndex(
    00162     LONGLONG PhysicalOffset
    00163     )
    00164 {
    00165
    00166     return PhysicalOffset + UDF_FILE_INDEX_PHYSICAL;
    00167 }
    00168
    00169 INLINE
    00170 BOOLEAN
    00171 UdfIsFileIndexVirtual(
    00172    LONGLONG FileIndex
    00173    )
    00174 {
    00175
    00176     return FileIndex < UDF_FILE_INDEX_PHYSICAL;
    00177 }
    00178
    00179 //
    00180 //  Local support routines
    00181 //
    00182
    00183 NTSTATUS
    00184 UdfQueryDirectory (
    00185     IN PIRP_CONTEXT IrpContext,
    00186     IN PIRP Irp,
    00187     IN PIO_STACK_LOCATION IrpSp,
    00188     IN PFCB Fcb,
    00189     IN PCCB Ccb
    00190     );
    00191
    00192 NTSTATUS
    00193 UdfNotifyChangeDirectory (
    00194     IN PIRP_CONTEXT IrpContext,
    00195     IN PIRP Irp,
    00196     IN PIO_STACK_LOCATION IrpSp,
    00197     IN PCCB Ccb
    00198     );
    00199
    00200 VOID
    00201 UdfInitializeEnumeration (
    00202     IN PIRP_CONTEXT IrpContext,
    00203     IN PIO_STACK_LOCATION IrpSp,
    00204     IN PFCB Fcb,
    00205     IN OUT PCCB Ccb,
    00206     IN OUT PCOMPOUND_DIR_ENUM_CONTEXT CompoundDirContext,
    00207     OUT PBOOLEAN ReturnNextEntry,
    00208     OUT PBOOLEAN ReturnSingleEntry,
    00209     OUT PBOOLEAN InitialQuery
    00210     );
    00211
    00212 BOOLEAN
    00213 UdfEnumerateIndex (
    00214     IN PIRP_CONTEXT IrpContext,
    00215     IN PCCB Ccb,
    00216     IN OUT PCOMPOUND_DIR_ENUM_CONTEXT CompoundDirContext,
    00217     IN BOOLEAN ReturnNextEntry
    00218     );
    00219
    00220 VOID
    00221 UdfLookupFileEntryInEnumeration (
    00222     IN PIRP_CONTEXT IrpContext,
    00223     IN PFCB Fcb,
    00224     IN PCOMPOUND_DIR_ENUM_CONTEXT CompoundDirContext
    00225     );
    00226
    00227 BOOLEAN
    00228 UdfLookupInitialFileIndex (
    00229     IN PIRP_CONTEXT IrpContext,
    00230     IN PFCB Fcb,
    00231     IN PCOMPOUND_DIR_ENUM_CONTEXT CompoundDirContext,
    00232     IN PLONGLONG InitialIndex
    00233     );
    00234
    00235 BOOLEAN
    00236 UdfLookupNextFileIndex (
    00237     IN PIRP_CONTEXT IrpContext,
    00238     IN PFCB Fcb,
    00239     IN PCOMPOUND_DIR_ENUM_CONTEXT CompoundDirContext
    00240     );
    00241
    00242 #ifdef ALLOC_PRAGMA
    00243 #pragma alloc_text(PAGE, UdfCommonDirControl)
    00244 #pragma alloc_text(PAGE, UdfEnumerateIndex)
    00245 #pragma alloc_text(PAGE, UdfInitializeEnumeration)
    00246 #pragma alloc_text(PAGE, UdfLookupFileEntryInEnumeration)
    00247 #pragma alloc_text(PAGE, UdfLookupInitialFileIndex)
    00248 #pragma alloc_text(PAGE, UdfLookupNextFileIndex)
    00249 #pragma alloc_text(PAGE, UdfNotifyChangeDirectory)
    00250 #pragma alloc_text(PAGE, UdfQueryDirectory)
    00251 #endif
    00252
    00253
    00254 NTSTATUS
    00255 UdfCommonDirControl (
    00256     IN PIRP_CONTEXT IrpContext,
    00257     IN PIRP Irp
    00258     )
    00259
    00260 /*++
    00261
    00262 Routine Description:
    00263
    00264     This routine is the entry point for the directory control operations.  These
    00265     are directory enumerations and directory notify calls.  We verify the
    00266     user's handle is for a directory and then call the appropriate routine.
    00267
    00268 Arguments:
    00269
    00270     Irp - Irp for this request.
    00271
    00272 Return Value:
    00273
    00274     NTSTATUS - Status returned from the lower level routines.
    00275
    00276 --*/
    00277
    00278 {
    00279     NTSTATUS Status;
    00280     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
    00281
    00282     PFCB Fcb;
    00283     PCCB Ccb;
    00284
    00285     PAGED_CODE();
    00286
    00287     //
    00288     //  Decode the user file object and fail this request if it is not
    00289     //  a user directory.
    00290     //
    00291
    00292     if (UdfDecodeFileObject( IrpSp->FileObject,
    00293                              &Fcb,
    00294                              &Ccb ) != UserDirectoryOpen) {
    00295
    00296         UdfCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
    00297         return STATUS_INVALID_PARAMETER;
    00298     }
    00299
    00300     //
    00301     //  We know this is a directory control so we'll case on the
    00302     //  minor function, and call a internal worker routine to complete
    00303     //  the irp.
    00304     //
    00305
    00306     switch (IrpSp->MinorFunction) {
    00307
    00308     case IRP_MN_QUERY_DIRECTORY:
    00309
    00310         Status = UdfQueryDirectory( IrpContext, Irp, IrpSp, Fcb, Ccb );
    00311         break;
    00312
    00313     case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
    00314
    00315         Status = UdfNotifyChangeDirectory( IrpContext, Irp, IrpSp, Ccb );
    00316         break;
    00317
    00318     default:
    00319
    00320         UdfCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
    00321         Status = STATUS_INVALID_DEVICE_REQUEST;
    00322         break;
    00323     }
    00324
    00325     return Status;
    00326 }
    00327
    00328
    00329 //
    00330 //  Local support routines
    00331 //
    00332
    00333 NTSTATUS
    00334 UdfQueryDirectory (
    00335     IN PIRP_CONTEXT IrpContext,
    00336     IN PIRP Irp,
    00337     IN PIO_STACK_LOCATION IrpSp,
    00338     IN PFCB Fcb,
    00339     IN PCCB Ccb
    00340     )
    00341
    00342 /*++
    00343
    00344 Routine Description:
    00345
    00346     This routine performs the query directory operation.  It is responsible
    00347     for either completing of enqueuing the input Irp.  We store the state of the
    00348     search in the Ccb.
    00349
    00350 Arguments:
    00351
    00352     Irp - Supplies the Irp to process
    00353
    00354     IrpSp - Stack location for this Irp.
    00355
    00356     Fcb - Fcb for this directory.
    00357
    00358     Ccb - Ccb for this directory open.
    00359
    00360 Return Value:
    00361
    00362     NTSTATUS - The return status for the operation
    00363
    00364 --*/
    00365
    00366 {
    00367     NTSTATUS Status = STATUS_SUCCESS;
    00368     ULONG Information = 0;
    00369
    00370     ULONG LastEntry = 0;
    00371     ULONG NextEntry = 0;
    00372
    00373     ULONG FileNameBytes;
    00374     ULONG BytesConverted;
    00375
    00376     COMPOUND_DIR_ENUM_CONTEXT CompoundDirContext;
    00377
    00378     PNSR_FID ThisFid;
    00379     PICBFILE ThisFe;
    00380
    00381     BOOLEAN InitialQuery;
    00382     BOOLEAN ReturnNextEntry;
    00383     BOOLEAN ReturnSingleEntry;
    00384     BOOLEAN Found;
    00385
    00386     PCHAR UserBuffer;
    00387     ULONG BytesRemainingInBuffer;
    00388
    00389     ULONG BaseLength;
    00390
    00391     PFILE_BOTH_DIR_INFORMATION DirInfo;
    00392     PFILE_NAMES_INFORMATION NamesInfo;
    00393
    00394     PAGED_CODE();
    00395
    00396     //
    00397     //  Check if we support this search mode.  Also remember the size of the base part of
    00398     //  each of these structures.
    00399     //
    00400
    00401     switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) {
    00402
    00403     case FileDirectoryInformation:
    00404
    00405         BaseLength = FIELD_OFFSET( FILE_DIRECTORY_INFORMATION,
    00406                                    FileName[0] );
    00407         break;
    00408
    00409     case FileFullDirectoryInformation:
    00410
    00411         BaseLength = FIELD_OFFSET( FILE_FULL_DIR_INFORMATION,
    00412                                    FileName[0] );
    00413         break;
    00414
    00415     case FileNamesInformation:
    00416
    00417         BaseLength = FIELD_OFFSET( FILE_NAMES_INFORMATION,
    00418                                    FileName[0] );
    00419         break;
    00420
    00421     case FileBothDirectoryInformation:
    00422
    00423         BaseLength = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION,
    00424                                    FileName[0] );
    00425         break;
    00426
    00427     default:
    00428
    00429         UdfCompleteRequest( IrpContext, Irp, STATUS_INVALID_INFO_CLASS );
    00430         return STATUS_INVALID_INFO_CLASS;
    00431     }
    00432
    00433     //
    00434     //  Get the user buffer.
    00435     //
    00436
    00437     UdfMapUserBuffer( IrpContext, &UserBuffer);
    00438
    00439     //
    00440     //  Initialize our search context.
    00441     //
    00442
    00443     UdfInitializeCompoundDirContext( IrpContext, &CompoundDirContext );
    00444
    00445     //
    00446     //  Acquire the directory.
    00447     //
    00448
    00449     UdfAcquireFileShared( IrpContext, Fcb );
    00450
    00451     //
    00452     //  Use a try-finally to facilitate cleanup.
    00453     //
    00454
    00455     try {
    00456
    00457         //
    00458         //  Verify the Fcb is still good.
    00459         //
    00460
    00461         UdfVerifyFcbOperation( IrpContext, Fcb );
    00462
    00463         //
    00464         //  Start by getting the initial state for the enumeration.  This will set up the Ccb with
    00465         //  the initial search parameters and let us know the starting offset in the directory
    00466         //  to search.
    00467         //
    00468
    00469         UdfInitializeEnumeration( IrpContext,
    00470                                   IrpSp,
    00471                                   Fcb,
    00472                                   Ccb,
    00473                                   &CompoundDirContext,
    00474                                   &ReturnNextEntry,
    00475                                   &ReturnSingleEntry,
    00476                                   &InitialQuery );
    00477
    00478         //
    00479         //  At this point we are about to enter our query loop.  We have
    00480         //  determined the index into the directory file to begin the
    00481         //  search.  LastEntry and NextEntry are used to index into the user
    00482         //  buffer.  LastEntry is the last entry we've added, NextEntry is
    00483         //  current one we're working on.  If NextEntry is non-zero, then
    00484         //  at least one entry was added.
    00485         //
    00486
    00487         while (TRUE) {
    00488
    00489             //
    00490             //  If the user had requested only a single match and we have
    00491             //  returned that, then we stop at this point.  We update the Ccb with
    00492             //  the status based on the last entry returned.
    00493             //
    00494
    00495             if ((NextEntry != 0) && ReturnSingleEntry) {
    00496
    00497                 try_leave( Status );
    00498             }
    00499
    00500             //
    00501             //  We try to locate the next matching dirent.  Our search if based on a starting
    00502             //  dirent offset, whether we should return the current or next entry, whether
    00503             //  we should be doing a short name search and finally whether we should be
    00504             //  checking for a version match.
    00505             //
    00506
    00507             Found = UdfEnumerateIndex( IrpContext, Ccb, &CompoundDirContext, ReturnNextEntry );
    00508
    00509             //
    00510             //  Initialize the value for the next search.
    00511             //
    00512
    00513             ReturnNextEntry = TRUE;
    00514
    00515             //
    00516             //  If we didn't receive a dirent, then we are at the end of the
    00517             //  directory.  If we have returned any files, we exit with
    00518             //  success, otherwise we return STATUS_NO_MORE_FILES.
    00519             //
    00520
    00521             if (!Found) {
    00522
    00523                 if (NextEntry == 0) {
    00524
    00525                     Status = STATUS_NO_MORE_FILES;
    00526
    00527                     if (InitialQuery) {
    00528
    00529                         Status = STATUS_NO_SUCH_FILE;
    00530                     }
    00531                 }
    00532
    00533                 try_leave( Status );
    00534             }
    00535
    00536             //
    00537             //  Remember the dirent/file entry for the file we just found.
    00538             //
    00539
    00540             ThisFid = CompoundDirContext.DirContext.Fid;
    00541
    00542             //
    00543             //  Here are the rules concerning filling up the buffer:
    00544             //
    00545             //  1.  The Io system garentees that there will always be
    00546             //      enough room for at least one base record.
    00547             //
    00548             //  2.  If the full first record (including file name) cannot
    00549             //      fit, as much of the name as possible is copied and
    00550             //      STATUS_BUFFER_OVERFLOW is returned.
    00551             //
    00552             //  3.  If a subsequent record cannot completely fit into the
    00553             //      buffer, none of it (as in 0 bytes) is copied, and
    00554             //      STATUS_SUCCESS is returned.  A subsequent query will
    00555             //      pick up with this record.
    00556             //
    00557
    00558             //
    00559             //  We can look directly at the dirent that we found.
    00560             //
    00561
    00562             FileNameBytes = CompoundDirContext.DirContext.CaseObjectName.Length;
    00563
    00564             //
    00565             //  If the slot for the next entry would be beyond the length of the
    00566             //  user's buffer just exit (we know we've returned at least one entry
    00567             //  already). This will happen when we align the pointer past the end.
    00568             //
    00569
    00570             if (NextEntry > IrpSp->Parameters.QueryDirectory.Length) {
    00571
    00572                 ReturnNextEntry = FALSE;
    00573                 try_leave( Status = STATUS_SUCCESS );
    00574             }
    00575
    00576             //
    00577             //  Compute the number of bytes remaining in the buffer.  Round this
    00578             //  down to a WCHAR boundary so we can copy full characters.
    00579             //
    00580
    00581             BytesRemainingInBuffer = IrpSp->Parameters.QueryDirectory.Length - NextEntry;
    00582             ClearFlag( BytesRemainingInBuffer, 1 );
    00583
    00584             //
    00585             //  If this won't fit and we have returned a previous entry then just
    00586             //  return STATUS_SUCCESS.
    00587             //
    00588
    00589             if ((BaseLength + FileNameBytes) > BytesRemainingInBuffer) {
    00590
    00591                 //
    00592                 //  If we already found an entry then just exit.
    00593                 //
    00594
    00595                 if (NextEntry != 0) {
    00596
    00597                     ReturnNextEntry = FALSE;
    00598                     try_leave( Status = STATUS_SUCCESS );
    00599                 }
    00600
    00601                 //
    00602                 //  Reduce the FileNameBytes to just fit in the buffer.
    00603                 //
    00604
    00605                 FileNameBytes = BytesRemainingInBuffer - BaseLength;
    00606
    00607                 //
    00608                 //  Use a status code of STATUS_BUFFER_OVERFLOW.  Also set
    00609                 //  ReturnSingleEntry so that we will exit the loop at the top.
    00610                 //
    00611
    00612                 Status = STATUS_BUFFER_OVERFLOW;
    00613                 ReturnSingleEntry = TRUE;
    00614             }
    00615
    00616             //
    00617             //  Protect access to the user buffer with an exception handler.
    00618             //  Since (at our request) IO doesn't buffer these requests, we have
    00619             //  to guard against a user messing with the page protection and other
    00620             //  such trickery.
    00621             //
    00622
    00623             try {
    00624
    00625                 //
    00626                 //  Zero and initialize the base part of the current entry.
    00627                 //
    00628
    00629                 RtlZeroMemory( Add2Ptr( UserBuffer, NextEntry, PVOID ),
    00630                                BaseLength );
    00631
    00632                 //
    00633                 //  Now we have an entry to return to our caller.
    00634                 //  We'll case on the type of information requested and fill up
    00635                 //  the user buffer if everything fits.
    00636                 //
    00637
    00638                 switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) {
    00639
    00640                 case FileBothDirectoryInformation:
    00641                 case FileFullDirectoryInformation:
    00642                 case FileDirectoryInformation:
    00643
    00644                     DirInfo = Add2Ptr( UserBuffer, NextEntry, PFILE_BOTH_DIR_INFORMATION );
    00645
    00646                     //
    00647                     //  These information types require we look up the file entry.
    00648                     //
    00649
    00650                     UdfLookupFileEntryInEnumeration( IrpContext,
    00651                                                      Fcb,
    00652                                                      &CompoundDirContext );
    00653                     //
    00654                     //  Directly reference the file entry we just looked up.
    00655                     //
    00656
    00657                     ThisFe = (PICBFILE) CompoundDirContext.IcbContext.Active.View;
    00658
    00659                     //
    00660                     //  Now go gather all of the timestamps for this guy.
    00661                     //
    00662
    00663                     UdfUpdateTimestampsFromIcbContext ( IrpContext,
    00664                                                         &CompoundDirContext.IcbContext,
    00665                                                         &CompoundDirContext.Timestamps );
    00666
    00667                     DirInfo->CreationTime = CompoundDirContext.Timestamps.CreationTime;
    00668
    00669                     DirInfo->LastWriteTime =
    00670                     DirInfo->ChangeTime = CompoundDirContext.Timestamps.ModificationTime;
    00671
    00672                     DirInfo->LastAccessTime = CompoundDirContext.Timestamps.AccessTime;
    00673
    00674                     //
    00675                     //  Set the attributes and sizes separately for directories and
    00676                     //  files.
    00677                     //
    00678
    00679                     if (ThisFe->Icbtag.FileType == ICBTAG_FILE_T_DIRECTORY) {
    00680
    00681                         DirInfo->EndOfFile.QuadPart = DirInfo->AllocationSize.QuadPart = 0;
    00682
    00683                         SetFlag( DirInfo->FileAttributes, FILE_ATTRIBUTE_DIRECTORY );
    00684
    00685                     } else {
    00686
    00687                         DirInfo->EndOfFile.QuadPart = ThisFe->InfoLength;
    00688                         DirInfo->AllocationSize.QuadPart = LlBlockAlign( Fcb->Vcb, ThisFe->InfoLength );
    00689                     }
    00690
    00691                     //
    00692                     //  All Cdrom files are readonly.  We also copy the existence
    00693                     //  bit to the hidden attribute, assuming that synthesized FIDs
    00694                     //  are never hidden.
    00695                     //
    00696
    00697                     SetFlag( DirInfo->FileAttributes, FILE_ATTRIBUTE_READONLY );
    00698
    00699                     if (ThisFid && FlagOn( ThisFid->Flags, NSR_FID_F_HIDDEN )) {
    00700
    00701                         SetFlag( DirInfo->FileAttributes, FILE_ATTRIBUTE_HIDDEN );
    00702                     }
    00703
    00704                     //
    00705                     //  The file index for real file indices > 2^32 is zero.  When asked to
    00706                     //  restart at an index of zero, we will know to use a stashed starting
    00707                     //  point to beging to search, by name, for the correct restart point.
    00708                     //
    00709
    00710                     if (CompoundDirContext.FileIndex.HighPart == 0) {
    00711
    00712                         DirInfo->FileIndex = CompoundDirContext.FileIndex.LowPart;
    00713
    00714                     } else {
    00715
    00716                         DirInfo->FileIndex = 0;
    00717                     }
    00718
    00719                     DirInfo->FileNameLength = FileNameBytes;
    00720
    00721                     break;
    00722
    00723                 case FileNamesInformation:
    00724
    00725                     NamesInfo = Add2Ptr( UserBuffer, NextEntry, PFILE_NAMES_INFORMATION );
    00726
    00727                     if (CompoundDirContext.FileIndex.HighPart == 0) {
    00728
    00729                         NamesInfo->FileIndex = CompoundDirContext.FileIndex.LowPart;
    00730
    00731                     } else {
    00732
    00733                         NamesInfo->FileIndex = 0;
    00734                     }
    00735
    00736                     NamesInfo->FileNameLength = FileNameBytes;
    00737
    00738                     break;
    00739                 }
    00740
    00741                 //
    00742                 //  Now copy as much of the name as possible.
    00743                 //
    00744
    00745                 if (FileNameBytes != 0) {
    00746
    00747                     //
    00748                     //  This is a Unicode name, we can copy the bytes directly.
    00749                     //
    00750
    00751                     RtlCopyMemory( Add2Ptr( UserBuffer, NextEntry + BaseLength, PVOID ),
    00752                                    CompoundDirContext.DirContext.ObjectName.Buffer,
    00753                                    FileNameBytes );
    00754                 }
    00755
    00756                 //
    00757                 //  Fill in the short name if we got STATUS_SUCCESS.  The short name
    00758                 //  may already be in the file context, otherwise we will check
    00759                 //  whether the long name is 8.3.  Special case the self and parent
    00760                 //  directory names.
    00761                 //
    00762
    00763                 if ((Status == STATUS_SUCCESS) &&
    00764                     (IrpSp->Parameters.QueryDirectory.FileInformationClass == FileBothDirectoryInformation) &&
    00765                     FlagOn( CompoundDirContext.DirContext.Flags, DIR_CONTEXT_FLAG_SEEN_NONCONSTANT )) {
    00766
    00767                     //
    00768                     //  If we already have the short name then copy into the user's buffer.
    00769                     //
    00770
    00771                     if (CompoundDirContext.DirContext.ShortObjectName.Length != 0) {
    00772
    00773                         RtlCopyMemory( DirInfo->ShortName,
    00774                                        CompoundDirContext.DirContext.ShortObjectName.Buffer,
    00775                                        CompoundDirContext.DirContext.ShortObjectName.Length );
    00776
    00777                         DirInfo->ShortNameLength = (CCHAR) CompoundDirContext.DirContext.ShortObjectName.Length;
    00778
    00779                     //
    00780                     //  If the short name length is currently zero then check if
    00781                     //  the long name is not 8.3.  We can copy the short name in
    00782                     //  unicode form directly into the caller's buffer.
    00783                     //
    00784
    00785                     } else {
    00786
    00787                         if (!UdfIs8dot3Name( IrpContext,
    00788                                              CompoundDirContext.DirContext.ObjectName )) {
    00789
    00790                             UNICODE_STRING ShortName;
    00791
    00792                             ShortName.Buffer = DirInfo->ShortName;
    00793                             ShortName.MaximumLength = BYTE_COUNT_8_DOT_3;
    00794
    00795                             UdfGenerate8dot3Name( IrpContext,
    00796                                                   &CompoundDirContext.DirContext.PureObjectName,
    00797                                                   &ShortName );
    00798
    00799                             DirInfo->ShortNameLength = (CCHAR) ShortName.Length;
    00800                         }
    00801                     }
    00802                 }
    00803
    00804                 //
    00805                 //  Update the information with the number of bytes stored in the
    00806                 //  buffer.  We quad-align the existing buffer to add any necessary
    00807                 //  pad bytes.
    00808                 //
    00809
    00810                 Information = NextEntry + BaseLength + FileNameBytes;
    00811
    00812                 //
    00813                 //  Go back to the previous entry and fill in the update to this entry.
    00814                 //
    00815
    00816                 *(Add2Ptr( UserBuffer, LastEntry, PULONG )) = NextEntry - LastEntry;
    00817
    00818                 //
    00819                 //  Set up our variables for the next dirent.
    00820                 //
    00821
    00822                 InitialQuery = FALSE;
    00823
    00824                 LastEntry = NextEntry;
    00825                 NextEntry = QuadAlign( Information );
    00826
    00827             } except (!FsRtlIsNtstatusExpected(GetExceptionCode()) ?
    00828                       EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
    00829
    00830                   //
    00831                   //  We must have had a problem filling in the user's buffer, so stop
    00832                   //  and fail this request.
    00833                   //
    00834
    00835                   Information = 0;
    00836                   try_leave( Status = GetExceptionCode());
    00837             }
    00838         }
    00839
    00840     } finally {
    00841
    00842         if (!AbnormalTermination() && !NT_ERROR( Status )) {
    00843
    00844             //
    00845             //  Update the Ccb to show the current state of the enumeration.
    00846             //
    00847
    00848             UdfLockFcb( IrpContext, Fcb );
    00849
    00850             Ccb->CurrentFileIndex = CompoundDirContext.FileIndex.QuadPart;
    00851
    00852             //
    00853             //  Update our notion of a high 32bit file index.  We only do this once to avoid the hit
    00854             //  of thrashing the Fcb mutex to do this for every entry.  If it is ever neccesary to use
    00855             //  this information, the difference of a few dozen entries from the optimal pick-up point
    00856             //  will be trivial.
    00857             //
    00858
    00859             if (CompoundDirContext.FileIndex.HighPart == 0 &&
    00860                 CompoundDirContext.FileIndex.LowPart > Ccb->HighestReturnableFileIndex) {
    00861
    00862                     Ccb->HighestReturnableFileIndex = CompoundDirContext.FileIndex.LowPart;
    00863             }
    00864
    00865             ClearFlag( Ccb->Flags, CCB_FLAG_ENUM_RETURN_NEXT );
    00866
    00867             if (ReturnNextEntry) {
    00868
    00869                 SetFlag( Ccb->Flags, CCB_FLAG_ENUM_RETURN_NEXT );
    00870             }
    00871
    00872             UdfUnlockFcb( IrpContext, Fcb );
    00873         }
    00874
    00875         //
    00876         //  Cleanup our search context.
    00877         //
    00878
    00879         UdfCleanupCompoundDirContext( IrpContext, &CompoundDirContext );
    00880
    00881         //
    00882         //  Release the Fcb.
    00883         //
    00884
    00885         UdfReleaseFile( IrpContext, Fcb );
    00886     }
    00887
    00888     //
    00889     //  Complete the request here.
    00890     //
    00891
    00892     Irp->IoStatus.Information = Information;
    00893
    00894     UdfCompleteRequest( IrpContext, Irp, Status );
    00895     return Status;
    00896 }
    00897
    00898
    00899 //
    00900 //  Local support routines
    00901 //
    00902
    00903 NTSTATUS
    00904 UdfNotifyChangeDirectory (
    00905     IN PIRP_CONTEXT IrpContext,
    00906     IN PIRP Irp,
    00907     IN PIO_STACK_LOCATION IrpSp,
    00908     IN PCCB Ccb
    00909     )
    00910
    00911 /*++
    00912
    00913 Routine Description:
    00914
    00915     This routine performs the notify change directory operation.  It is
    00916     responsible for either completing of enqueuing the input Irp.  Although there
    00917     will never be a notify signalled on a readonly disk we still support this call.
    00918
    00919     We have already checked that this is not an OpenById handle.
    00920
    00921 Arguments:
    00922
    00923     Irp - Supplies the Irp to process
    00924
    00925     IrpSp - Io stack location for this request.
    00926
    00927     Ccb - Handle to the directory being watched.
    00928
    00929 Return Value:
    00930
    00931     NTSTATUS - STATUS_PENDING, any other error will raise.
    00932
    00933 --*/
    00934
    00935 {
    00936     PAGED_CODE();
    00937
    00938     //
    00939     //  Always set the wait bit in the IrpContext so the initial wait can't fail.
    00940     //
    00941
    00942     SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
    00943
    00944     //
    00945     //  Acquire the Vcb shared.
    00946     //
    00947
    00948     UdfAcquireVcbShared( IrpContext, IrpContext->Vcb, FALSE );
    00949
    00950     //
    00951     //  Use a try-finally to facilitate cleanup.
    00952     //
    00953
    00954     try {
    00955
    00956         //
    00957         //  Verify the Vcb.
    00958         //
    00959
    00960         UdfVerifyVcb( IrpContext, IrpContext->Vcb );
    00961
    00962         //
    00963         //  Call the Fsrtl package to process the request.  We cast the
    00964         //  unicode strings to ansi strings as the dir notify package
    00965         //  only deals with memory matching.
    00966         //
    00967
    00968         FsRtlNotifyFullChangeDirectory( IrpContext->Vcb->NotifySync,
    00969                                         &IrpContext->Vcb->DirNotifyList,
    00970                                         Ccb,
    00971                                         (PSTRING) &IrpSp->FileObject->FileName,
    00972                                         BooleanFlagOn( IrpSp->Flags, SL_WATCH_TREE ),
    00973                                         FALSE,
    00974                                         IrpSp->Parameters.NotifyDirectory.CompletionFilter,
    00975                                         Irp,
    00976                                         NULL,
    00977                                         NULL );
    00978
    00979     } finally {
    00980
    00981         //
    00982         //  Release the Vcb.
    00983         //
    00984
    00985         UdfReleaseVcb( IrpContext, IrpContext->Vcb );
    00986     }
    00987
    00988     //
    00989     //  Cleanup the IrpContext.
    00990     //
    00991
    00992     UdfCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
    00993
    00994     return STATUS_PENDING;
    00995 }
    00996
    00997
    00998 //
    00999 //  Local support routine
    01000 //
    01001
    01002 VOID
    01003 UdfInitializeEnumeration (
    01004     IN PIRP_CONTEXT IrpContext,
    01005     IN PIO_STACK_LOCATION IrpSp,
    01006     IN PFCB Fcb,
    01007     IN OUT PCCB Ccb,
    01008     IN OUT PCOMPOUND_DIR_ENUM_CONTEXT CompoundDirContext,
    01009     OUT PBOOLEAN ReturnNextEntry,
    01010     OUT PBOOLEAN ReturnSingleEntry,
    01011     OUT PBOOLEAN InitialQuery
    01012     )
    01013
    01014 /*++
    01015
    01016 Routine Description:
    01017
    01018     This routine is called to initialize the enumeration variables and structures.
    01019     We look at the state of a previous enumeration from the Ccb as well as any
    01020     input values from the user.  On exit we will position the DirContext at
    01021     a file in the directory and let the caller know whether this entry or the
    01022     next entry should be returned.
    01023
    01024 Arguments:
    01025
    01026     IrpSp - Irp stack location for this request.
    01027
    01028     Fcb - Fcb for this directory.
    01029
    01030     Ccb - Ccb for the directory handle.
    01031
    01032     CompoundDirContext - Context to use for this enumeration.
    01033
    01034     ReturnNextEntry - Address to store whether we should return the entry at
    01035         the context position or the next entry.
    01036
    01037     ReturnSingleEntry - Address to store whether we should only return
    01038         a single entry.
    01039
    01040     InitialQuery - Address to store whether this is the first enumeration
    01041         query on this handle.
    01042
    01043 Return Value:
    01044
    01045     None.
    01046
    01047 --*/
    01048
    01049 {
    01050     NTSTATUS Status;
    01051
    01052     PUNICODE_STRING FileName;
    01053     UNICODE_STRING SearchExpression;
    01054
    01055     PUNICODE_STRING RestartName = NULL;
    01056
    01057     ULONG CcbFlags;
    01058
    01059     LONGLONG FileIndex;
    01060     ULONG HighFileIndex;
    01061     BOOLEAN KnownIndex;
    01062
    01063     BOOLEAN Found;
    01064
    01065     PAGED_CODE();
    01066
    01067     //
    01068     //  Check inputs.
    01069     //
    01070
    01071     ASSERT_IRP_CONTEXT( IrpContext );
    01072     ASSERT_FCB_INDEX( Fcb );
    01073     ASSERT_CCB( Ccb );
    01074
    01075     //
    01076     //  If this is the initial query then build a search expression from the input
    01077     //  file name.
    01078     //
    01079
    01080     if (!FlagOn( Ccb->Flags, CCB_FLAG_ENUM_INITIALIZED )) {
    01081
    01082         FileName = (PUNICODE_STRING) IrpSp->Parameters.QueryDirectory.FileName;
    01083
    01084         CcbFlags = 0;
    01085
    01086         //
    01087         //  If the filename is not specified or is a single '*' then we will
    01088         //  match all names.
    01089         //
    01090
    01091         if ((FileName == NULL) ||
    01092             (FileName->Buffer == NULL) ||
    01093             (FileName->Length == 0) ||
    01094             ((FileName->Length == sizeof( WCHAR )) &&
    01095              (FileName->Buffer[0] == L'*'))) {
    01096
    01097             SetFlag( CcbFlags, CCB_FLAG_ENUM_MATCH_ALL );
    01098
    01099             SearchExpression.Length =
    01100             SearchExpression.MaximumLength = 0;
    01101             SearchExpression.Buffer = NULL;
    01102
    01103         //
    01104         //  Otherwise build the name from the name in the stack location.
    01105         //  This involves checking for wild card characters and upcasing the
    01106         //  string if this is a case-insensitive search.
    01107         //
    01108
    01109         } else {
    01110
    01111             //
    01112             //  The name better have at least one character.
    01113             //
    01114
    01115             if (FileName->Length == 0) {
    01116
    01117                 UdfRaiseStatus( IrpContext, STATUS_INVALID_PARAMETER );
    01118             }
    01119
    01120             //
    01121             //  Check for wildcards in the separate components.
    01122             //
    01123
    01124             if (FsRtlDoesNameContainWildCards( FileName)) {
    01125
    01126                 SetFlag( CcbFlags, CCB_FLAG_ENUM_NAME_EXP_HAS_WILD );
    01127             }
    01128
    01129             //
    01130             //  Now create the search expression to store in the Ccb.
    01131             //
    01132
    01133             SearchExpression.Buffer = FsRtlAllocatePoolWithTag( UdfPagedPool,
    01134                                                                 FileName->Length,
    01135                                                                 TAG_ENUM_EXPRESSION );
    01136
    01137             SearchExpression.MaximumLength = FileName->Length;
    01138
    01139             //
    01140             //  Either copy the name directly or perform the upcase.
    01141             //
    01142
    01143             if (FlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE )) {
    01144
    01145                 Status = RtlUpcaseUnicodeString( &SearchExpression,
    01146                                                  FileName,
    01147                                                  FALSE );
    01148
    01149                 //
    01150                 //  This should never fail.
    01151                 //
    01152
    01153                 ASSERT( Status == STATUS_SUCCESS );
    01154
    01155             } else {
    01156
    01157                 RtlCopyMemory( SearchExpression.Buffer,
    01158                                FileName->Buffer,
    01159                                FileName->Length );
    01160             }
    01161
    01162             SearchExpression.Length = FileName->Length;
    01163         }
    01164
    01165         //
    01166         //  But we do not want to return the constant "." and ".." entries for
    01167         //  the root directory, for consistency with the rest of Microsoft's
    01168         //  filesystems.
    01169         //
    01170
    01171         if (Fcb == Fcb->Vcb->RootIndexFcb) {
    01172
    01173             SetFlag( CcbFlags, CCB_FLAG_ENUM_NOMATCH_CONSTANT_ENTRY );
    01174         }
    01175
    01176         //
    01177         //  Now lock the Fcb in order to update the Ccb with the inital
    01178         //  enumeration values.
    01179         //
    01180
    01181         UdfLockFcb( IrpContext, Fcb );
    01182
    01183         //
    01184         //  Check again that this is the initial search.
    01185         //
    01186
    01187         if (!FlagOn( Ccb->Flags, CCB_FLAG_ENUM_INITIALIZED )) {
    01188
    01189             //
    01190             //  Update the values in the Ccb.
    01191             //
    01192
    01193             Ccb->CurrentFileIndex = 0;
    01194             Ccb->SearchExpression = SearchExpression;
    01195
    01196             //
    01197             //  Set the appropriate flags in the Ccb.
    01198             //
    01199
    01200             SetFlag( Ccb->Flags, CcbFlags | CCB_FLAG_ENUM_INITIALIZED );
    01201
    01202         //
    01203         //  Otherwise cleanup any buffer allocated here.
    01204         //
    01205
    01206         } else {
    01207
    01208             if (!FlagOn( CcbFlags, CCB_FLAG_ENUM_MATCH_ALL )) {
    01209
    01210                 UdfFreePool( &SearchExpression.Buffer );
    01211             }
    01212         }
    01213
    01214     //
    01215     //  Otherwise lock the Fcb so we can read the current enumeration values.
    01216     //
    01217
    01218     } else {
    01219
    01220         UdfLockFcb( IrpContext, Fcb );
    01221     }
    01222
    01223     //
    01224     //  Capture the current state of the enumeration.
    01225     //
    01226     //  If the user specified an index then use his offset.  We always
    01227     //  return the next entry in this case.  If  no name is specified,
    01228     //  then we can't perform the restart.
    01229     //
    01230
    01231     if (FlagOn( IrpSp->Flags, SL_INDEX_SPECIFIED ) &&
    01232         IrpSp->Parameters.QueryDirectory.FileName != NULL) {
    01233
    01234         KnownIndex = FALSE;
    01235         FileIndex = IrpSp->Parameters.QueryDirectory.FileIndex;
    01236         RestartName = (PUNICODE_STRING) IrpSp->Parameters.QueryDirectory.FileName;
    01237         *ReturnNextEntry = TRUE;
    01238
    01239         //
    01240         //  We will use the highest file index reportable to the caller as a
    01241         //  starting point as required if we cannot directly land at the
    01242         //  specified location.
    01243         //
    01244
    01245         HighFileIndex = Ccb->HighestReturnableFileIndex;
    01246
    01247     //
    01248     //  If we are restarting the scan then go from the self entry.
    01249     //
    01250
    01251     } else if (FlagOn( IrpSp->Flags, SL_RESTART_SCAN )) {
    01252
    01253         KnownIndex = TRUE;
    01254         FileIndex = 0;
    01255         *ReturnNextEntry = FALSE;
    01256
    01257     //
    01258     //  Otherwise use the values from the Ccb.
    01259     //
    01260
    01261     } else {
    01262
    01263         KnownIndex = TRUE;
    01264         FileIndex = Ccb->CurrentFileIndex;
    01265         *ReturnNextEntry = BooleanFlagOn( Ccb->Flags, CCB_FLAG_ENUM_RETURN_NEXT );
    01266     }
    01267
    01268     //
    01269     //  Unlock the Fcb.
    01270     //
    01271
    01272     UdfUnlockFcb( IrpContext, Fcb );
    01273
    01274     //
    01275     //  We have the starting offset in the directory and whether to return
    01276     //  that entry or the next.  If we are at the beginning of the directory
    01277     //  and are returning that entry, then tell our caller this is the
    01278     //  initial query.
    01279     //
    01280
    01281     *InitialQuery = FALSE;
    01282
    01283     if ((FileIndex == 0) &&
    01284         !(*ReturnNextEntry)) {
    01285
    01286         *InitialQuery = TRUE;
    01287     }
    01288
    01289     //
    01290     //  Determine the offset in the stream to position the context and
    01291     //  whether this offset is known to be a file offset.
    01292     //
    01293     //  If this offset is known to be safe then go ahead and position the
    01294     //  context.  This handles the cases where the offset is the beginning
    01295     //  of the stream, the offset is from a previous search or this is the
    01296     //  initial query.
    01297     //
    01298
    01299     if (KnownIndex) {
    01300
    01301         Found = UdfLookupInitialFileIndex( IrpContext, Fcb, CompoundDirContext, &FileIndex );
    01302
    01303         ASSERT( Found );
    01304
    01305     //
    01306     //  Try to directly jump to the specified file index.  Otherwise we walk through
    01307     //  the directory from the beginning (or the saved highest known offset if that is
    01308     //  useful) until we reach the entry which contains this offset.
    01309     //
    01310
    01311     } else {
    01312
    01313         //
    01314         //  We need to handle the special case of a restart from a synthesized
    01315         //  entry - this is the one time where the restart index can be zero
    01316         //  without requiring us to search above the 2^32 byte mark.
    01317         //
    01318
    01319         if (UdfFullCompareNames( IrpContext,
    01320                                  RestartName,
    01321                                  &UdfUnicodeDirectoryNames[SELF_ENTRY] ) == EqualTo) {
    01322
    01323             FileIndex = UDF_FILE_INDEX_VIRTUAL_SELF;
    01324
    01325             Found = UdfLookupInitialFileIndex( IrpContext, Fcb, CompoundDirContext, &FileIndex );
    01326
    01327             ASSERT( Found );
    01328
    01329         //
    01330         //  We are restarting from a physical entry.  If the restart index is zero, we were
    01331         //  unable to inform the caller as to the "real" file index due to the dispartity
    01332         //  between the ULONG FileIndex in the return structures and the LONGLONG offsets
    01333         //  possible in directory streams.  In this case, we will go as high as we were able
    01334         //  to inform the caller of and search linearly from that point forward.
    01335         //
    01336         //  It is also possible (realistic? unknown) that the restart index is somewhere in the
    01337         //  middle of an entry and we won't find anything useable.  In this case we try to find
    01338         //  the entry which contains this index, using it as the real restart point.
    01339         //
    01340
    01341         } else {
    01342
    01343             //
    01344             //  See if we need the high water mark.
    01345             //
    01346
    01347             if (FileIndex == 0) {
    01348
    01349                 //
    01350                 //  We know that this is good.
    01351                 //
    01352
    01353                 FileIndex = Max( Ccb->HighestReturnableFileIndex, UDF_FILE_INDEX_PHYSICAL );;
    01354                 KnownIndex = TRUE;
    01355
    01356             }
    01357
    01358             //
    01359             //  The file index is now useful, falling into two cases
    01360             //
    01361             //      1) KnownIndex == FALSE - searching by index
    01362             //      2) KnownIndex == TRUE  - searching by name
    01363             //
    01364             //  Go set up our inquiry.
    01365             //
    01366
    01367             Found = UdfLookupInitialFileIndex( IrpContext, Fcb, CompoundDirContext, &FileIndex );
    01368
    01369             if (KnownIndex) {
    01370
    01371                 //
    01372                 //  Walk forward to discover an entry named per the caller's expectation.
    01373                 //
    01374
    01375                 do {
    01376
    01377                     UdfUpdateDirNames( IrpContext,
    01378                                        &CompoundDirContext->DirContext,
    01379                                        BooleanFlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE ));
    01380
    01381                     if (UdfFullCompareNames( IrpContext,
    01382                                              &CompoundDirContext->DirContext.CaseObjectName,
    01383                                              RestartName ) == EqualTo) {
    01384
    01385                         break;
    01386                     }
    01387
    01388                     Found = UdfLookupNextFileIndex( IrpContext, Fcb, CompoundDirContext );
    01389
    01390                 } while (Found);
    01391
    01392             } else if (!Found) {
    01393
    01394                 LONGLONG LastFileIndex;
    01395
    01396                 //
    01397                 //  Perform the search for the entry by index from the beginning of the physical directory.
    01398                 //
    01399
    01400                 LastFileIndex = UDF_FILE_INDEX_PHYSICAL;
    01401
    01402                 Found = UdfLookupInitialFileIndex( IrpContext, Fcb, CompoundDirContext, &LastFileIndex );
    01403
    01404                 ASSERT( Found );
    01405
    01406                 //
    01407                 //  Keep walking through the directory until we run out of
    01408                 //  entries or we find an entry which ends beyond the input
    01409                 //  index value (index search case) or corresponds to the
    01410                 //  name we are looking for (name search case).
    01411                 //
    01412
    01413                 do {
    01414
    01415                     //
    01416                     //  If we have passed the index value then exit.
    01417                     //
    01418
    01419                     if (CompoundDirContext->FileIndex.QuadPart > FileIndex) {
    01420
    01421                         Found = FALSE;
    01422                         break;
    01423                     }
    01424
    01425                     //
    01426                     //  Remember the current position in case we need to go back.
    01427                     //
    01428
    01429                     LastFileIndex = CompoundDirContext->FileIndex.QuadPart;
    01430
    01431                     //
    01432                     //  Exit if the next entry is beyond the desired index value.
    01433                     //
    01434
    01435                     if (LastFileIndex + ISONsrFidSize( CompoundDirContext->DirContext.Fid ) > FileIndex) {
    01436
    01437                         break;
    01438                     }
    01439
    01440                     Found = UdfLookupNextFileIndex( IrpContext, Fcb, CompoundDirContext );
    01441
    01442                 } while (Found);
    01443
    01444                 //
    01445                 //  If we didn't find the entry then go back to the last known entry.
    01446                 //
    01447
    01448                 if (!Found) {
    01449
    01450                     UdfCleanupDirContext( IrpContext, &CompoundDirContext->DirContext );
    01451                     UdfInitializeDirContext( IrpContext, &CompoundDirContext->DirContext );
    01452
    01453                     Found = UdfLookupInitialFileIndex( IrpContext, Fcb, CompoundDirContext, &LastFileIndex );
    01454
    01455                     ASSERT( Found );
    01456                 }
    01457             }
    01458         }
    01459     }
    01460
    01461     //
    01462     //  Only update the dirent name if we will need it for some reason.
    01463     //  Don't update this name if we are returning the next entry, and
    01464     //  don't update it if it was already done.
    01465     //
    01466
    01467     if (!(*ReturnNextEntry) &&
    01468         CompoundDirContext->DirContext.PureObjectName.Buffer == NULL) {
    01469
    01470         //
    01471         //  Update the names in the dirent.
    01472         //
    01473
    01474         UdfUpdateDirNames( IrpContext,
    01475                            &CompoundDirContext->DirContext,
    01476                            BooleanFlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE ));
    01477     }
    01478
    01479     //
    01480     //  Look at the flag in the IrpSp indicating whether to return just
    01481     //  one entry.
    01482     //
    01483
    01484     *ReturnSingleEntry = FALSE;
    01485
    01486     if (FlagOn( IrpSp->Flags, SL_RETURN_SINGLE_ENTRY )) {
    01487
    01488         *ReturnSingleEntry = TRUE;
    01489     }
    01490
    01491     return;
    01492 }
    01493
    01494
    01495 //
    01496 //  Local support routine
    01497 //
    01498
    01499 BOOLEAN
    01500 UdfEnumerateIndex (
    01501     IN PIRP_CONTEXT IrpContext,
    01502     IN PCCB Ccb,
    01503     IN OUT PCOMPOUND_DIR_ENUM_CONTEXT CompoundDirContext,
    01504     IN BOOLEAN ReturnNextEntry
    01505     )
    01506
    01507 /*++
    01508
    01509 Routine Description:
    01510
    01511     This routine is the worker routine for index enumeration.  We are positioned
    01512     at some dirent in the directory and will either return the first match
    01513     at that point or look to the next entry.  The Ccb contains details about
    01514     the type of matching to do.
    01515
    01516 Arguments:
    01517
    01518     Ccb - Ccb for this directory handle.
    01519
    01520     CompoundDirContext - context already positioned at some entry in the directory.
    01521
    01522     ReturnNextEntry - Indicates if we are returning this entry or should start
    01523         with the next entry.
    01524
    01525 Return Value:
    01526
    01527     BOOLEAN - TRUE if next entry is found, FALSE otherwise.
    01528
    01529 --*/
    01530
    01531 {
    01532     BOOLEAN Found = FALSE;
    01533     PNSR_FID Fid;
    01534     PDIR_ENUM_CONTEXT DirContext;
    01535
    01536     PAGED_CODE();
    01537
    01538     //
    01539     //  Check inputs.
    01540     //
    01541
    01542     ASSERT_IRP_CONTEXT( IrpContext );
    01543     ASSERT_CCB( Ccb );
    01544
    01545     //
    01546     //  Directly reference the directory enumeration context for convenience.
    01547     //
    01548
    01549     DirContext = &CompoundDirContext->DirContext;
    01550
    01551     //
    01552     //  Loop until we find a match or exaust the directory.
    01553     //
    01554
    01555     while (TRUE) {
    01556
    01557         //
    01558         //  Move to the next entry unless we want to consider the current
    01559         //  entry.
    01560         //
    01561
    01562         if (ReturnNextEntry) {
    01563
    01564             if (!UdfLookupNextFileIndex( IrpContext, Ccb->Fcb, CompoundDirContext )) {
    01565
    01566                 break;
    01567             }
    01568
    01569             UdfUpdateDirNames( IrpContext,
    01570                                DirContext,
    01571                                BooleanFlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE ));
    01572         } else {
    01573
    01574             ReturnNextEntry = TRUE;
    01575         }
    01576
    01577         //
    01578         //  Don't bother if we have a constant entry and are ignoring them.
    01579         //
    01580
    01581         if (!FlagOn( DirContext->Flags, DIR_CONTEXT_FLAG_SEEN_NONCONSTANT ) &&
    01582             FlagOn( Ccb->Flags, CCB_FLAG_ENUM_NOMATCH_CONSTANT_ENTRY )) {
    01583
    01584             continue;
    01585         }
    01586
    01587         //
    01588         //  Directly reference the Fid for convenience.
    01589         //
    01590
    01591         Fid = DirContext->Fid;
    01592
    01593         //
    01594         //  Look at the current entry if it is pointing at real space on the disk.  If the Fid is NULL, this
    01595         //  means it is to be synthesized (and thus interesting to look at).
    01596         //
    01597
    01598         if (Fid == NULL || !FlagOn( Fid->Flags, NSR_FID_F_DELETED )) {
    01599
    01600             //
    01601             //  If we match all names then return to our caller.
    01602             //
    01603
    01604             if (FlagOn( Ccb->Flags, CCB_FLAG_ENUM_MATCH_ALL )) {
    01605
    01606                 DirContext->ShortObjectName.Length = 0;
    01607                 Found = TRUE;
    01608
    01609                 break;
    01610             }
    01611
    01612             //
    01613             //  Check if the long name matches the search expression.
    01614             //
    01615
    01616             if (UdfIsNameInExpression( IrpContext,
    01617                                        &DirContext->CaseObjectName,
    01618                                        &Ccb->SearchExpression,
    01619                                        BooleanFlagOn( Ccb->Flags, CCB_FLAG_ENUM_NAME_EXP_HAS_WILD ))) {
    01620
    01621                 //
    01622                 //  Let our caller know we found an entry.
    01623                 //
    01624
    01625                 DirContext->ShortObjectName.Length = 0;
    01626                 Found = TRUE;
    01627
    01628                 break;
    01629             }
    01630
    01631             //
    01632             //  The long name didn't match so we need to check for a
    01633             //  possible short name match.  There is no match if the
    01634             //  long name is one of the constant entries or already
    01635             //  is 8dot3.
    01636             //
    01637
    01638             if (!(!FlagOn( DirContext->Flags, DIR_CONTEXT_FLAG_SEEN_NONCONSTANT ) ||
    01639                   UdfIs8dot3Name( IrpContext,
    01640                                   DirContext->CaseObjectName ))) {
    01641
    01642                 //
    01643                 //  Allocate the shortname if it isn't already done.
    01644                 //
    01645
    01646                 if (DirContext->ShortObjectName.Buffer == NULL) {
    01647
    01648                     DirContext->ShortObjectName.Buffer = FsRtlAllocatePoolWithTag( UdfPagedPool,
    01649                                                                                    BYTE_COUNT_8_DOT_3,
    01650                                                                                    TAG_SHORT_FILE_NAME );
    01651                     DirContext->ShortObjectName.MaximumLength = BYTE_COUNT_8_DOT_3;
    01652                 }
    01653
    01654                 UdfGenerate8dot3Name( IrpContext,
    01655                                       &DirContext->PureObjectName,
    01656                                       &DirContext->ShortObjectName );
    01657
    01658                 //
    01659                 //  Check if this name matches.
    01660                 //
    01661
    01662                 if (UdfIsNameInExpression( IrpContext,
    01663                                            &DirContext->ShortObjectName,
    01664                                            &Ccb->SearchExpression,
    01665                                            BooleanFlagOn( Ccb->Flags, CCB_FLAG_ENUM_NAME_EXP_HAS_WILD ))) {
    01666
    01667                     //
    01668                     //  Let our caller know we found an entry.
    01669                     //
    01670
    01671                     Found = TRUE;
    01672
    01673                     break;
    01674                 }
    01675             }
    01676         }
    01677     }
    01678
    01679     return Found;
    01680 }
    01681
    01682
    01683 //
    01684 //  Local support routine
    01685 //
    01686
    01687 VOID
    01688 UdfLookupFileEntryInEnumeration (
    01689     IN PIRP_CONTEXT IrpContext,
    01690     IN PFCB Fcb,
    01691     IN PCOMPOUND_DIR_ENUM_CONTEXT CompoundDirContext
    01692     )
    01693
    01694 /*++
    01695
    01696 Routine Description:
    01697
    01698     This routine retrieves the file entry associated with the current location in
    01699     the enumeration of a compound directory context.
    01700
    01701 Arguments:
    01702
    01703     Fcb - the directory being enumerated.
    01704
    01705     CompoundDirContext - a corresponding context for the enumeration.
    01706
    01707 Return Value:
    01708
    01709     None.  Status may be raised on discovery of corruption.
    01710
    01711 --*/
    01712
    01713 {
    01714     PNSR_FID Fid;
    01715     PICBFILE Fe;
    01716
    01717     Fid = CompoundDirContext->DirContext.Fid;
    01718
    01719     //
    01720     //  Figure out where the ICB we want is.
    01721     //
    01722
    01723     if (UdfIsFileIndexVirtual( CompoundDirContext->FileIndex.QuadPart )) {
    01724
    01725         //
    01726         //  Synthesize!  We only have to synthesize the self entry.  The name is already done,
    01727         //  so the remaining work is trivial.
    01728         //
    01729
    01730         ASSERT( Fid == NULL );
    01731
    01732         //
    01733         //  Lift the FE corresponding to this directory
    01734         //
    01735
    01736         UdfCleanupIcbContext( IrpContext, &CompoundDirContext->IcbContext );
    01737
    01738         UdfInitializeIcbContextFromFcb( IrpContext,
    01739                                         &CompoundDirContext->IcbContext,
    01740                                         Fcb );
    01741
    01742     } else {
    01743
    01744         //
    01745         //  Lift the FE corresponding to this FID.
    01746         //
    01747
    01748         ASSERT( Fid != NULL );
    01749
    01750         UdfCleanupIcbContext( IrpContext, &CompoundDirContext->IcbContext );
    01751
    01752         UdfInitializeIcbContext( IrpContext,
    01753                                  &CompoundDirContext->IcbContext,
    01754                                  Fcb->Vcb,
    01755                                  DESTAG_ID_NSR_FILE,
    01756                                  Fid->Icb.Start.Partition,
    01757                                  Fid->Icb.Start.Lbn,
    01758                                  Fid->Icb.Length.Length );
    01759     }
    01760
    01761     //
    01762     //  Retrieve the ICB for inspection.
    01763     //
    01764
    01765     UdfLookupActiveIcb( IrpContext, &CompoundDirContext->IcbContext );
    01766
    01767     Fe = (PICBFILE) CompoundDirContext->IcbContext.Active.View;
    01768
    01769     //
    01770     //  Perform some basic verification that the FE is of the proper type and that
    01771     //  FID and FE agree as to the type of the object.  We explicitly check that
    01772     //  a legal filesystem-level FE type is discovered, even though we don't support
    01773     //  them in other paths.
    01774     //
    01775
    01776     if (Fe->Destag.Ident != DESTAG_ID_NSR_FILE ||
    01777
    01778         (((Fid && FlagOn( Fid->Flags, NSR_FID_F_DIRECTORY )) ||
    01779           Fid == NULL) &&
    01780          Fe->Icbtag.FileType != ICBTAG_FILE_T_DIRECTORY) ||
    01781
    01782         (Fe->Icbtag.FileType != ICBTAG_FILE_T_FILE &&
    01783          Fe->Icbtag.FileType != ICBTAG_FILE_T_DIRECTORY &&
    01784          Fe->Icbtag.FileType != ICBTAG_FILE_T_BLOCK_DEV &&
    01785          Fe->Icbtag.FileType != ICBTAG_FILE_T_CHAR_DEV &&
    01786          Fe->Icbtag.FileType != ICBTAG_FILE_T_FIFO &&
    01787          Fe->Icbtag.FileType != ICBTAG_FILE_T_C_ISSOCK &&
    01788          Fe->Icbtag.FileType != ICBTAG_FILE_T_PATHLINK)) {
    01789
    01790         UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
    01791     }
    01792 }
    01793
    01794
    01795 //
    01796 //  Local support routine
    01797 //
    01798
    01799 BOOLEAN
    01800 UdfLookupInitialFileIndex (
    01801     IN PIRP_CONTEXT IrpContext,
    01802     IN PFCB Fcb,
    01803     IN PCOMPOUND_DIR_ENUM_CONTEXT CompoundDirContext,
    01804     IN PLONGLONG InitialIndex
    01805     )
    01806
    01807 /*++
    01808
    01809 Routine Description:
    01810
    01811     This routine begins the enumeration of a directory by setting the context
    01812     at the first avaliable virtual directory entry.
    01813
    01814 Arguments:
    01815
    01816     Fcb - the directory being enumerated.
    01817
    01818     CompoundDirContext - a corresponding context for the enumeration.
    01819
    01820     InitialIndex - an optional starting file index to base the enumeration.
    01821
    01822 Return Value:
    01823
    01824    TRUE will be returned if a valid entry is found at this offset, FALSE otherwise.
    01825
    01826 --*/
    01827
    01828 {
    01829     LONGLONG DirOffset;
    01830
    01831     if (UdfIsFileIndexVirtual( *InitialIndex )) {
    01832
    01833         //
    01834         //  We only synthesize a single virtual directory entry.  Position the context
    01835         //  at the virtual self entry.
    01836         //
    01837
    01838         CompoundDirContext->FileIndex.QuadPart = UDF_FILE_INDEX_VIRTUAL_SELF;
    01839
    01840         return TRUE;
    01841     }
    01842
    01843     CompoundDirContext->FileIndex.QuadPart = *InitialIndex;
    01844
    01845     //
    01846     //  Find the base offset in the directory and look it up.
    01847     //
    01848
    01849     DirOffset = UdfFileIndexToPhysicalOffset( *InitialIndex );
    01850
    01851     return UdfLookupInitialDirEntry( IrpContext,
    01852                                      Fcb,
    01853                                      &CompoundDirContext->DirContext,
    01854                                      &DirOffset );
    01855 }
    01856
    01857
    01858 //
    01859 //  Local support routine
    01860 //
    01861
    01862 BOOLEAN
    01863 UdfLookupNextFileIndex (
    01864     IN PIRP_CONTEXT IrpContext,
    01865     IN PFCB Fcb,
    01866     IN PCOMPOUND_DIR_ENUM_CONTEXT CompoundDirContext
    01867     )
    01868
    01869 /*++
    01870
    01871 Routine Description:
    01872
    01873     This routine advances the enumeration of a virtual directory by one entry.
    01874
    01875 Arguments:
    01876
    01877     Fcb - the directory being enumerated.
    01878
    01879     CompoundDirContext - a corresponding context for the enumeration.
    01880
    01881 Return Value:
    01882
    01883     BOOLEAN True if another Fid is avaliable, False if we are at the end.
    01884
    01885 --*/
    01886
    01887 {
    01888     ULONG Advance;
    01889     BOOLEAN Result;
    01890
    01891     //
    01892     //  Advance from the synthesized to the physical directory.
    01893     //
    01894
    01895     if (UdfIsFileIndexVirtual( CompoundDirContext->FileIndex.QuadPart )) {
    01896
    01897         Result = UdfLookupInitialDirEntry( IrpContext,
    01898                                            Fcb,
    01899                                            &CompoundDirContext->DirContext,
    01900                                            NULL );
    01901
    01902         if (Result) {
    01903
    01904             CompoundDirContext->FileIndex.QuadPart = UDF_FILE_INDEX_PHYSICAL;
    01905         }
    01906
    01907         return Result;
    01908     }
    01909
    01910     Advance = ISONsrFidSize( CompoundDirContext->DirContext.Fid );
    01911
    01912     //
    01913     //  Advance to the next entry in this directory.
    01914     //
    01915
    01916     Result = UdfLookupNextDirEntry( IrpContext, Fcb, &CompoundDirContext->DirContext );
    01917
    01918     if (Result) {
    01919
    01920         CompoundDirContext->FileIndex.QuadPart += Advance;
    01921     }
    01922
    01923     return Result;
    01924 }
    01925