text: /*++
  
  Copyright (c) 1998-1999 Microsoft Corporation
  
  Module Name:
  
      lookup.c
  
  Abstract:
  
      this is the sr lookup functionlity implementation
  
  Author:
  
      Kanwaljit Marok (kmarok)     01-May-2000
  
  Revision History:
  
  --*/
  
  #include "precomp.h"
  
  //
  // Include hlist.c to use the inline funtions
  //
  
  #include "hlist.c"
  #include "ptree.c"
  
  //
  // Internal helper APIs
  //
  
  static
  NTSTATUS
  SrOpenLookupBlob(
      IN  PUNICODE_STRING pFileName,
      IN  PDEVICE_OBJECT  pTargetDevice,
      OUT PBLOB_INFO pBlobInfo
      );
  
  //
  // linker commands
  //
  
  #ifdef ALLOC_PRAGMA
  
  #pragma alloc_text( PAGE, SrOpenLookupBlob    )
  #pragma alloc_text( PAGE, SrLoadLookupBlob    )
  #pragma alloc_text( PAGE, SrReloadLookupBlob  )
  #pragma alloc_text( PAGE, SrFreeLookupBlob    )
  #pragma alloc_text( PAGE, SrIsExtInteresting  )
  #pragma alloc_text( PAGE, SrIsPathInteresting )
  
  #endif  // ALLOC_PRAGMA
  
  //++
  // Function:
  //		SrOpenLookupBlob
  //
  // Description:
  //		This function loads the lookup blob in memory and 
  //		sets the appropriate pointers for lookup.
  //
  // Arguments:
  //
  // Return Value:
  //		This function returns STATUS_XXX
  //--
  
  static
  NTSTATUS
  SrOpenLookupBlob(
      IN  PUNICODE_STRING pFileName,
      IN  PDEVICE_OBJECT  pTargetDevice,
      OUT PBLOB_INFO      pBlobInfo
      )
  {
      NTSTATUS           Status;
      OBJECT_ATTRIBUTES  oa;
      IO_STATUS_BLOCK    IoStatusBlock;
      HANDLE             Handle   = NULL;
      PLIST_ENTRY        pListEntry;
      PSR_DEVICE_EXTENSION pExtension;
      static char blobFailureMessage[] = "sr!System Restore's BLOB file \"%wZ\" is invalid.\n";
  
      PAGED_CODE();
      
      ASSERT(pFileName);
      ASSERT(pBlobInfo);
  
      ASSERT( IS_BLOB_LOCK_ACQUIRED() );
  
      try    
      {
          //
          // Zero out the pointers that get initialized when the 
          // blob is successfully read into the memory from disk
          //
      
          pBlobInfo->LookupBlob = NULL;
          pBlobInfo->LookupTree = NULL;
          pBlobInfo->LookupList = NULL;
          pBlobInfo->DefaultType= NODE_TYPE_UNKNOWN;
      
          //
          // open and read the file
          //
      
          InitializeObjectAttributes( &oa,
                                      pFileName,
                                      OBJ_KERNEL_HANDLE, 
                                      NULL,
                                      NULL );
      
          Status = SrIoCreateFile(
                       &Handle,
                       GENERIC_READ | SYNCHRONIZE,
                       &oa,
                       &IoStatusBlock,
                       0,
                       FILE_ATTRIBUTE_NORMAL,
                       FILE_SHARE_READ,
                       FILE_OPEN,
                       FILE_SYNCHRONOUS_IO_NONALERT,
                       NULL,
                       0,
                       0,
                       pTargetDevice );
      
          if (NT_SUCCESS(Status))
          {
              DWORD dwBytesRead = 0, dwBytes = 0;
              LARGE_INTEGER nOffset;
              BlobHeader blobHeader;
      
              //
              // Read the blob header
              //
      
              nOffset.QuadPart = 0;
              dwBytes          = sizeof(blobHeader);
      
              Status =  ZwReadFile(
                            Handle,
                            NULL,
                            NULL,
                            NULL,
                            &IoStatusBlock,
                            &blobHeader,
                            dwBytes,
                            &nOffset,
                            NULL
                        );
      
              if (NT_SUCCESS(Status))
              {
                  //
                  // need to do some sanity check on the header
                  //
      
                  if ( !VERIFY_BLOB_VERSION(&blobHeader) ||
                       !VERIFY_BLOB_MAGIC  (&blobHeader) )
                  {
                      SrTrace( BLOB_VERIFICATION, (blobFailureMessage, pFileName) );
  
                      Status = STATUS_FILE_CORRUPT_ERROR;
                      leave;
                  }
      
                  pBlobInfo->LookupBlob = SR_ALLOCATE_POOL( 
                                              NonPagedPool,
                                              blobHeader.m_dwMaxSize,
                                              SR_LOOKUP_TABLE_TAG );
      
                  if( pBlobInfo->LookupBlob )
                  {
                      //
                      // Read the entire file now
                      //
      
                      nOffset.QuadPart = 0;
                      dwBytes = blobHeader.m_dwMaxSize;
       
                      Status =  ZwReadFile(
                                    Handle,
                                    NULL,
                                    NULL,
                                    NULL,
                                    &IoStatusBlock,
                                    pBlobInfo->LookupBlob,
                                    dwBytes,
                                    &nOffset,
                                    NULL
                                    );
      
                      if (NT_SUCCESS(Status))
                      {
                          //
                          // TODO: verify that size of the file matched the
                          // size from the header
                          //
      
                          //
                          // Setup the lookup pointers properly in blobinfo
                          //
      
                          pBlobInfo->LookupTree = pBlobInfo->LookupBlob + 
                                                  sizeof(blobHeader);
      
                          pBlobInfo->LookupList = pBlobInfo->LookupTree + 
                                                  BLOB_MAXSIZE((pBlobInfo->LookupTree));
      
                          pBlobInfo->DefaultType = TREE_HEADER((pBlobInfo->LookupTree))->m_dwDefault;
      
                          //
                          // Verify the individual blobs
                          //
      
                          if (!SrVerifyBlob(pBlobInfo->LookupBlob)) {
  
                              SrTrace( BLOB_VERIFICATION, 
                                       (blobFailureMessage,pFileName) );
                              Status = STATUS_FILE_CORRUPT_ERROR;
                              leave;
                          }
                      }
                  }
                  else
                  {
                      Status = STATUS_INSUFFICIENT_RESOURCES;
                  }
              }
          }
          else
          {
              SrTrace( VERBOSE_ERRORS, 
                       ("sr!SrOpenLookupBlob:  Cannot Open Blob file \"%wZ\"\n",
                        pFileName) );
          }
  
          //
          //  The new blob was loaded successfully, perge all contexts on all
          //  volumes since what is interesting and what is not interesting
          //  may have changed.
          //
  
          ASSERT(!IS_DEVICE_EXTENSION_LIST_LOCK_ACQUIRED());
  
          try
          {
              SrAcquireDeviceExtensionListLockShared();
  
              for (pListEntry = _globals.DeviceExtensionListHead.Flink;
                   pListEntry != &_globals.DeviceExtensionListHead;
                   pListEntry = pListEntry->Flink)
              {
                  pExtension = CONTAINING_RECORD( pListEntry,
                                                  SR_DEVICE_EXTENSION,
                                                  ListEntry );
              
                  ASSERT(IS_VALID_SR_DEVICE_EXTENSION(pExtension));
  
                  //
                  //  Skip Control Device Objects.
                  //
  
                  if (!FlagOn(pExtension->FsType,SrFsControlDeviceObject))
                  {
                      SrDeleteAllContexts( pExtension );
                  }
              }
          }
          finally
          {
              SrReleaseDeviceExtensionListLock();
          }
      }
      finally
      {
          Status = FinallyUnwind(SrOpenLookupBlob, Status);
  
          //
          // close the blob file handle
          //
      
          if (Handle)
          {
              ZwClose( Handle );
          }
      
          //
          // incase of a failure free up the resources
          //
      
          if (!NT_SUCCESS(Status))
          {
              if( pBlobInfo->LookupBlob )
              {
                  SR_FREE_POOL( pBlobInfo->LookupBlob, SR_LOOKUP_TABLE_TAG );
              }
      
              pBlobInfo->LookupBlob = NULL;
              pBlobInfo->LookupTree = NULL;
              pBlobInfo->LookupList = NULL;
              pBlobInfo->DefaultType= NODE_TYPE_UNKNOWN;
          }
      }
  
      RETURN(Status);
  }
  
  //
  // Public APIs called by the filer
  //
  
  //++
  // Function:
  //		SrLoadLookupBlob
  //
  // Description:
  //		This function loads the lookup blob in memory and 
  //		sets the appropriate pointers for lookup.
  //
  // Arguments:
  //
  // Return Value:
  //		This function returns STATUS_XXX
  //--
  
  NTSTATUS
  SrLoadLookupBlob(
      IN  PUNICODE_STRING pFileName,
      IN  PDEVICE_OBJECT pTargetDevice,
      OUT PBLOB_INFO pBlobInfo
      )
  {
      NTSTATUS Status;
  
      PAGED_CODE();
      
      ASSERT( pFileName );
      ASSERT( pBlobInfo );
  
      try
      {
          SrAcquireBlobLockExclusive();
       
          //
          // if somebody else did it, bail out
          //
      
          if (global->BlobInfoLoaded)
          {
              Status = STATUS_SUCCESS;
              leave;
          }
      
          //
          // initialize return information
          //
      
          RtlZeroMemory( pBlobInfo, sizeof( BLOB_INFO ) );
      
          //
          // Try and open the lookup blob
          //
      
          Status = SrOpenLookupBlob( pFileName, 
                                     pTargetDevice,
                                     pBlobInfo );
      
          //
          // If we failed to read the file for some reason,
          // reinitlialize the return info
          //
      
          if ( NT_SUCCESS( Status ) )
          {
              SrTrace(LOOKUP, ("Loaded lookup blob :%wZ\n", pFileName) );
              global->BlobInfoLoaded = TRUE;
          }
          else
          {
              SrFreeLookupBlob( pBlobInfo );
          }
      }
      finally
      {
          SrReleaseBlobLock();
      }
  
      RETURN(Status);
  }
  
  //++
  // Function:
  //		SrReloadLookupBlob
  //
  // Description:
  //		This function loads the lookup blob in memory and 
  //		sets the appropriate pointers for lookup.
  //
  // Arguments:
  //		Pointer to LookupBlob 
  //		Pointer to BlobInfo structure 
  //
  // Return Value:
  //		This function returns STATUS_XXX
  //--
  
  NTSTATUS
  SrReloadLookupBlob(
      IN  PUNICODE_STRING pFileName,
      IN  PDEVICE_OBJECT pTargetDevice,
      OUT PBLOB_INFO pBlobInfo
      )
  {
      NTSTATUS   Status = STATUS_UNSUCCESSFUL;
      BLOB_INFO  OldBlobInfo;
  
      PAGED_CODE();
  
      ASSERT( pFileName != NULL );
      ASSERT( pBlobInfo != NULL );
  
      ASSERT( !IS_BLOB_LOCK_ACQUIRED() );
      
      try
      {
          SrAcquireBlobLockExclusive();
      
          if (global->BlobInfoLoaded == 0)
          {
              Status = SrLoadLookupBlob( pFileName, 
                                         pTargetDevice,
                                         pBlobInfo );
              leave;
          }
      
          //
          // Save the current blob info
          //
      
          RtlCopyMemory( &OldBlobInfo, pBlobInfo, sizeof( BLOB_INFO ) );
      
          //
          // Open the new blob file
          //
      
          Status = SrOpenLookupBlob( pFileName, 
                                     pTargetDevice,
                                     pBlobInfo );
      
          if(NT_SUCCESS(Status))
          {
              //
              // Free up the memory taken up by the old blob
              //
      
              if (OldBlobInfo.LookupBlob)
              {
                  SR_FREE_POOL( OldBlobInfo.LookupBlob, SR_LOOKUP_TABLE_TAG );
              }
      
              SrTrace(LOOKUP, ("Reloaded lookup blob :%wZ\n", pFileName) );
          }
          else
          {
              //
              // Copy the old information back in the original context
              //
      
              RtlCopyMemory( pBlobInfo, &OldBlobInfo, sizeof( BLOB_INFO ) );
      
              SrTrace(LOOKUP, (" Cannot reload blob :%wZ\n", pFileName) );
          }
      }
      finally
      {
          if (NT_SUCCESS_NO_DBGBREAK( Status ))
          {
              //
              //  The blob has been reload successfully, so make sure that the
              //  global BlobError flag is cleared.
              //
              //  We do this here because we are still holding the blob lock.
              //
  
              _globals.HitErrorLoadingBlob = FALSE;
          }
  
          SrReleaseBlobLock();
      }
  
      RETURN(Status);
  }
  
  
  //++
  // Function:
  //		SrFreeLookupBlob
  //
  // Description:
  //		This function Frees the lookup blob in memory
  //
  // Arguments:
  //		Pointer to BlobInfo structure 
  //
  // Return Value:
  //		This function returns STATUS_XXX
  //--
  
  NTSTATUS
  SrFreeLookupBlob(
      IN  PBLOB_INFO pBlobInfo
      )
  {
      NTSTATUS Status = STATUS_SUCCESS;
  
      PAGED_CODE();
  
      ASSERT( pBlobInfo );
  
      try
      {    
          SrAcquireBlobLockExclusive();
      
          if (_globals.BlobInfoLoaded == 0)
          {
              //
              //  Reset our error flag here.
              //
              
              _globals.HitErrorLoadingBlob = FALSE;
              leave;
          }
      
          if( pBlobInfo->LookupBlob )
          {
              SR_FREE_POOL( pBlobInfo->LookupBlob, SR_LOOKUP_TABLE_TAG );
              pBlobInfo->LookupBlob = NULL;
          }
      
          RtlZeroMemory( pBlobInfo, sizeof(BLOB_INFO) );
          pBlobInfo->DefaultType = NODE_TYPE_UNKNOWN;
      
          SrTrace(LOOKUP, ("Freed lookup blob\n") );
      
          global->BlobInfoLoaded = 0;
      }
      finally
      {
          SrReleaseBlobLock();
      }
   
      RETURN(Status);
  }
  
  
  //++
  // Function:
  //		SrIsExtInteresting
  //
  // Description:
  //		This function checks the file extension in the blob to
  //      see if we care about it
  //
  // Arguments:
  //		Pointer to BlobInfo structure  
  //		Pointer to Path 
  //		Pointer to boolean return value
  //
  // Return Value:
  //		This function returns TRUE/FALSE
  //--
  
  NTSTATUS
  SrIsExtInteresting(
      IN  PUNICODE_STRING pFileName,
      OUT PBOOLEAN        pInteresting
      )
  {
      BOOL     fRet   = FALSE;
      NTSTATUS Status = STATUS_SUCCESS;
      INT      iType  = 0;
      BOOL     fPathHasExt = FALSE;
      BOOL     fMatch = FALSE;
  
      PAGED_CODE();
  
      //
      // check parameters and lookup info
      //
  
      ASSERT(pFileName);
      ASSERT(pInteresting);
  
      //
      // Lookup code is enclosed in an exception handler to protect against
      // bad memroy accesses generated by corrupt lookup data
      //
  
      try
      {
  
          *pInteresting = FALSE;
      
          //
          // CODEWORK : put some blob verification code, 
          // magicnum, type etc
          //
      
          //
          // Take the blob lock so that other threads won't change 
          // the blob while we are looking up.  Note that the blob
          // can be gone after we get the lock.
          //
      
          SrAcquireBlobLockShared();
  
          if ( !global->BlobInfoLoaded ||
               !global->BlobInfo.LookupList )
          {
              Status = SR_STATUS_VOLUME_DISABLED;
              leave;
          }
      
          //
          // parse the filename for lookup in the mem blob
          //
          
          fMatch = MatchExtension(
                       global->BlobInfo.LookupList, 
                       pFileName,
                       &iType, 
                       &fPathHasExt );
  
          if ( !fMatch )
          {
              //
              // Extension didn't match, so setting to default type
              //
  
              iType = global->BlobInfo.DefaultType;
          }
  
          if ( !fPathHasExt )
          {
              //
              // If the path didn't contain an extension then we should
              // treat it as an exclude 
              //
  
              iType = NODE_TYPE_EXCLUDE;
          }
  
          //
          // If type is still unknown then set the type to the default.
          //
  
          if ( NODE_TYPE_UNKNOWN == iType )
          {
              iType = global->BlobInfo.DefaultType;
          }
  
          *pInteresting = (iType != NODE_TYPE_EXCLUDE);
      
          // SrTrace(LOOKUP, ("Extention Interest:%d\n", *pInteresting) );
      }
      finally
      {
          Status = FinallyUnwind(SrIsExtInteresting, Status);
          
          SrReleaseBlobLock();
  
          if (!NT_SUCCESS(Status))
          {
              *pInteresting = FALSE;
          }
              
      }
  
      RETURN(Status);
  }
  
  
  //++
  // Function:
  //		SrIsPathInteresting
  //
  // Description:
  //		This function checks the file name in the blob to
  //      see if we care about it
  //
  // Arguments:
  //		Pointer to BlobInfo structure  
  //		Pointer to Full Path 
  //		Pointer to Volume Prefix 
  //		Boolean to indicate if this path is a directory 
  //		Pointer to boolean return value
  //
  // Return Value:
  //		This function returns TRUE/FALSE
  //--
  
  NTSTATUS
  SrIsPathInteresting(
      IN  PUNICODE_STRING pFullPath,
      IN  PUNICODE_STRING pVolPrefix,
      IN  BOOLEAN         IsDirectory,
      OUT PBOOLEAN        pInteresting
      )
  {
      BOOL        fRet    = FALSE;
      NTSTATUS    Status  = STATUS_UNSUCCESSFUL;
      PBYTE       pFileName = NULL;
      WORD        FileNameSize = 0;
      UNICODE_STRING localName;
  
      PAGED_CODE();
      
      //
      // check parameters and lookup info
      //
  
      ASSERT(pFullPath);
      ASSERT(pVolPrefix);
      ASSERT(pFullPath->Length >= pVolPrefix->Length);
      ASSERT(pInteresting);
   
      try
      {
          *pInteresting = FALSE;
      
          //
          // Take the blob lock so that other threads won't change 
          //
      
          SrAcquireBlobLockShared();
  
          if ( !global->BlobInfoLoaded ||
               !global->BlobInfo.LookupList ||
               !global->BlobInfo.LookupTree )
          {
              Status = SR_STATUS_VOLUME_DISABLED;
              leave;
          }
      
          ASSERT(global->BlobInfo.DefaultType != NODE_TYPE_UNKNOWN );
          
          //
          // allocate space for a parsed path
          //
  
          FileNameSize = CALC_PPATH_SIZE( pFullPath->Length/sizeof(WCHAR) );
          pFileName = ExAllocatePoolWithTag( PagedPool,
                                             FileNameSize,
                                             SR_FILENAME_BUFFER_TAG );
                                             
          if (NULL == pFileName)
          {
              Status = STATUS_INSUFFICIENT_RESOURCES;
              leave;
          }
      
          //
          // parse the filename for lookup in the mem blob
          //
      
          fRet = ConvertToParsedPath(
                     pFullPath->Buffer,
                     pFullPath->Length/sizeof(WCHAR),
                     pFileName,
                     FileNameSize );
      
          if(fRet)
          {
              INT    iNode  = -1;
              INT    iType  = 0;
              INT    iLevel = 0;
              BOOL   fExactMatch = FALSE;
              BOOL   fMatch      = FALSE;
      
              //
              // Lookup the parsed path in the tree blob
              //
      
              fMatch = MatchPrefix( 
                           global->BlobInfo.LookupTree, 
                           TREE_ROOT_NODE, 
                           ((path_t)pFileName)->pp_elements, 
                           &iNode, 
                           &iLevel, 
                           &iType, 
                           NULL, 
                           &fExactMatch); 
              
              if (fMatch)
              {
                  SrTrace(LOOKUP, 
                          ("Found match in pathtree N: %d L:%d T:%d\n",
                            iNode, iLevel, iType));
              }
      
              //
              // Lookup in __ALLVOLUMES__ to see is there is a match
              //
      
              if ( NODE_TYPE_UNKNOWN == iType ||   
                   (!fExactMatch && NODE_TYPE_EXCLUDE != iType )       
                 )                                      
              {
                  PBYTE  pRelFileName   = NULL;
                  INT    RelFileNameLen = 0;
      
                  //
                  // Lookup only volume relative filename
                  //
  
                  RelFileNameLen = sizeof(L'\\' ) +
                                   sizeof(ALLVOLUMES_PATH_W) +
                                   (pFullPath->Length - pVolPrefix->Length);
  
                  pRelFileName = ExAllocatePoolWithTag( PagedPool,
                                                        RelFileNameLen,
                                                        SR_FILENAME_BUFFER_TAG );
                                                     
                  if (NULL == pRelFileName)
                  {
                      Status = STATUS_INSUFFICIENT_RESOURCES;
                      leave;
                  }
      
                  localName.Buffer = &pFullPath->Buffer[pVolPrefix->Length/sizeof(WCHAR)];
                  localName.Length = pFullPath->Length - pVolPrefix->Length;
                  localName.MaximumLength = localName.Length;
  
                  RelFileNameLen = swprintf( 
                                      (LPWSTR)pRelFileName, 
                                      L"\\%s%wZ",
                                      ALLVOLUMES_PATH_W,
                                      &localName );
  
                  fRet = ConvertToParsedPath(
                             (LPWSTR)pRelFileName,
                             (USHORT)RelFileNameLen,
                             pFileName,
                             FileNameSize );
      
                  if(fRet)
                  {
                      //
                      // Lookup the parsed path in the appropriate protion of 
                      // the tree blob NTROOT\\__ALLVOLUMES__
                      //
      
                      fMatch = MatchPrefix( 
                           global->BlobInfo.LookupTree, 
                           TREE_ROOT_NODE, 
                           ((path_t)pFileName)->pp_elements, 
                           &iNode, 
                           &iLevel, 
                           &iType, 
                           NULL, 
                           &fExactMatch); 
              
                      if (fMatch)
                      {
                          SrTrace(LOOKUP,
                                  ("Found match in pathtree N: %d L:%d T:%d\n",
                                  iNode, iLevel, iType));
                      }
                  }
                  else
                  {
                      CHECK_STATUS( Status );
                  }
      
                  ExFreePoolWithTag( pRelFileName, SR_FILENAME_BUFFER_TAG );
                  NULLPTR( pRelFileName );
              }
      
      
              if ( !IsDirectory )
              {
                  //
                  // If path didn't match or matched partially, we need to
                  // lookup the extension list also
                  //
          
                  if ( NODE_TYPE_UNKNOWN == iType ||   
                       (!fExactMatch && NODE_TYPE_EXCLUDE != iType )       
                     )                                      
                  {
                      BOOL fPathHasExt = FALSE;
          
                      fMatch = MatchExtension(
                                   global->BlobInfo.LookupList, 
                                   pFullPath, 
                                   &iType, 
                                   &fPathHasExt );
          
                      if ( !fMatch )
                      {
                          //
                          // Extension didn't match, setting to default type
                          //
          
                          iType = global->BlobInfo.DefaultType;
                      }
          
                      if ( !fPathHasExt )
                      {
                          //
                          // If path didn't contain an extension then 
                          // treat it as an exclude 
                          //
          
                          iType = NODE_TYPE_EXCLUDE;
                      }
                  }
          
                  //
                  // If still type is unknown then set type to the default.
                  //
          
                  if ( NODE_TYPE_UNKNOWN == iType )
                  {
                      iType = global->BlobInfo.DefaultType;
                  }
              }
              else
              {
      
                  //
                  // If this is directory operation and no match found in 
                  // tree then treat is as include.
                  //
          
                  if ( NODE_TYPE_UNKNOWN == iType )
                  {
                      iType = NODE_TYPE_INCLUDE;
          
                  }
              }
      
              *pInteresting = (iType != NODE_TYPE_EXCLUDE);
              Status = STATUS_SUCCESS;
          }
          else
          {
              SrTrace( LOOKUP,
                       ( "ConvertToParsedPath Failed : %wZ\n", pFullPath )
                     );
              CHECK_STATUS( Status );
          }
      
          // SrTrace(LOOKUP, ("Path Interest:%d\n", *pInteresting) );
      }
      finally
      {
          Status = FinallyUnwind(SrIsPathInteresting, Status);
          
          SrReleaseBlobLock();
  
          if (pFileName != NULL)
          {
              ExFreePoolWithTag( pFileName, SR_FILENAME_BUFFER_TAG );
              NULLPTR( pFileName );;
          }
  
          if (!NT_SUCCESS(Status))
          {
              *pInteresting = FALSE;
          }
      }
  
      RETURN(Status);
  }
  