/*++ © 1998 Seagate Software, Inc. All rights reserved. Module Name: fsaitem.cpp Abstract: This class contains represents a scan item (i.e. file or directory) for NTFS 5.0. Author: Chuck Bardeen [cbardeen] 1-Dec-1996 Revision History: --*/ #include "stdafx.h" #define WSB_TRACE_IS WSB_TRACE_BIT_FSA #include "wsb.h" #include "wsbtrak.h" #include "fsa.h" #include "mover.h" #include "fsaitem.h" #include "fsaprem.h" static USHORT iCountItem = 0; // Count of existing objects HRESULT CFsaScanItem::CompareTo( IN IUnknown* pUnknown, OUT SHORT* pResult ) /*++ Implements: IWsbCollectable::CompareTo(). --*/ { HRESULT hr = S_OK; CComPtr pScanItem; WsbTraceIn(OLESTR("CFsaScanItem::CompareTo"), OLESTR("")); try { // Did they give us a valid item to compare to? WsbAssert(0 != pUnknown, E_POINTER); // We need the IWsbBool interface to get the value of the object. WsbAffirmHr(pUnknown->QueryInterface(IID_IFsaScanItem, (void**) &pScanItem)); // Compare the rules. hr = CompareToIScanItem(pScanItem, pResult); } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaScanItem::CompareTo"), OLESTR("hr = <%ls>, result = <%ls>"), WsbHrAsString(hr), WsbPtrToShortAsString(pResult)); return(hr); } HRESULT CFsaScanItem::CompareToIScanItem( IN IFsaScanItem* pScanItem, OUT SHORT* pResult ) /*++ Implements: IFsaScanItem::CompareToIScanItem(). --*/ { HRESULT hr = S_OK; CWsbStringPtr path; CWsbStringPtr name; WsbTraceIn(OLESTR("CFsaScanItem::CompareToIScanItem"), OLESTR("")); try { // Did they give us a valid item to compare to? WsbAssert(0 != pScanItem, E_POINTER); // Either compare the name or the id. WsbAffirmHr(pScanItem->GetPath(&path, 0)); WsbAffirmHr(pScanItem->GetName(&name, 0)); hr = CompareToPathAndName(path, name, pResult); } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaScanItem::CompareToIScanItem"), OLESTR("hr = <%ls>, result = <%ls>"), WsbHrAsString(hr), WsbPtrToShortAsString(pResult)); return(hr); } HRESULT CFsaScanItem::CompareToPathAndName( IN OLECHAR* path, IN OLECHAR* name, OUT SHORT* pResult ) /*++ Implements: IFsaScanItem::CompareToPathAndName(). --*/ { HRESULT hr = S_OK; SHORT aResult = 0; WsbTraceIn(OLESTR("CFsaScanItem::CompareToPathAndName"), OLESTR("")); try { // Compare the path. aResult = (SHORT) _wcsicmp(m_path, path); // Compare the name. if (0 == aResult) { aResult = (SHORT) _wcsicmp(m_findData.cFileName, name); } if (0 != aResult) { hr = S_FALSE; } if (0 != pResult) { *pResult = aResult; } } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaScanItem::CompareToPathAndName"), OLESTR("hr = <%ls>, result = <%u>"), WsbHrAsString(hr), aResult); return(hr); } HRESULT CFsaScanItem::Copy( IN OLECHAR* dest, IN BOOL /*retainHierarcy*/, IN BOOL /*expandPlaceholders*/, IN BOOL overwriteExisting ) /*++ Implements: IFsaScanItem::Copy(). --*/ { HRESULT hr = S_OK; try { // NOTE : This default behavior causes placeholders // to be expanded and probably doesn't retain the heirarchy. WsbAssert(0 != dest, E_POINTER); WsbAssert(CopyFile(m_findData.cFileName, dest, overwriteExisting), E_FAIL); } WsbCatch(hr); return(hr); } HRESULT CFsaScanItem::CreateLocalStream( OUT IStream **ppStream ) /*++ Implements: IFsaScanItem::CreateLocalStream(). --*/ { HRESULT hr = S_OK; LARGE_INTEGER fileSize; CWsbStringPtr volName; WsbTraceIn(OLESTR("CFsaScanItem::CreateLocalStream"), OLESTR("")); try { CWsbStringPtr localName; if ( !m_gotPlaceholder) { // // Get the placeholder info // fileSize.LowPart = m_findData.nFileSizeLow; fileSize.HighPart = m_findData.nFileSizeHigh; WsbAffirmHr(IsManaged(0, fileSize.QuadPart)); } WsbAssert( 0 != ppStream, E_POINTER); WsbAffirmHr( CoCreateInstance( CLSID_CNtFileIo, 0, CLSCTX_SERVER, IID_IDataMover, (void **)&m_pDataMover ) ); // // Set the device name for the mover so it can set the source infor for the USN journal. // WsbAffirmHr(m_pResource->GetPath(&volName, 0)); WsbAffirmHr( m_pDataMover->SetDeviceName(volName)); //WsbAffirmHr(GetFullPathAndName( NULL, 0, &localName, 0)); WsbAffirmHr(GetFullPathAndName( OLESTR("\\\\?\\"), 0, &localName, 0)); WsbAffirmHr( m_pDataMover->CreateLocalStream( localName, MVR_MODE_WRITE | MVR_FLAG_HSM_SEMANTICS | MVR_FLAG_POSIX_SEMANTICS, &m_pStream ) ); LARGE_INTEGER seekTo; ULARGE_INTEGER pos; seekTo.QuadPart = m_placeholder.dataStreamStart; WsbAffirmHr( m_pStream->Seek( seekTo, STREAM_SEEK_SET, &pos ) ); *ppStream = m_pStream; m_pStream.p->AddRef(); } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaScanItem::CreateLocalStream"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaScanItem::Delete( void ) /*++ Implements: IFsaScanItem::Delete(). --*/ { HRESULT hr = S_OK; CWsbStringPtr tmpString; HANDLE fileHandle; try { // This is the name of the file we want to delete. WsbAffirmHr(GetFullPathAndName(OLESTR("\\\\?\\"), 0, &tmpString, 0)); // Since we want to be POSIX compliant, we can't use DeleteFile() and instead will // open with the delete on close flag. This doesn't handle read-only files, so we // have to change that ourselves. WsbAffirmHr(MakeReadWrite()); fileHandle = CreateFile(tmpString, GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS | FILE_FLAG_DELETE_ON_CLOSE, 0); if (INVALID_HANDLE_VALUE == fileHandle) { WsbThrow(HRESULT_FROM_WIN32(GetLastError())); } else { if (!CloseHandle(fileHandle)) { WsbThrow(HRESULT_FROM_WIN32(GetLastError())); } } } WsbCatch(hr); return(hr); } #pragma optimize("g", off) HRESULT CFsaScanItem::FinalConstruct( void ) /*++ Implements: CComObjectRoot::FinalConstruct(). --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CFsaScanItem::FinalConstruct"), OLESTR("")); try { WsbAffirmHr(CComObjectRoot::FinalConstruct()); m_handle = INVALID_HANDLE_VALUE; m_gotPhysicalSize = FALSE; m_physicalSize.QuadPart = 0; m_gotPlaceholder = FALSE; m_changedAttributes = FALSE; m_handleRPI = 0; // Add class to object table WSB_OBJECT_ADD(CLSID_CFsaScanItemNTFS, this); } WsbCatch(hr); iCountItem++; WsbTraceOut(OLESTR("CFsaScanItem::FinalConstruct"), OLESTR("hr = <%ls>, Count is <%d>"), WsbHrAsString(hr), iCountItem); return(hr); } #pragma optimize("", on) void CFsaScanItem::FinalRelease( void ) /*++ Implements: CComObjectRoot::FinalRelease(). --*/ { WsbTraceIn(OLESTR("CFsaScanItem::FinalRelease"), OLESTR("")); // Subtract class from object table WSB_OBJECT_SUB(CLSID_CFsaScanItemNTFS, this); // Terminate the scan and free the path memory. if (INVALID_HANDLE_VALUE != m_handle) { FindClose(m_handle); m_handle = INVALID_HANDLE_VALUE; } if (0 != m_handleRPI) { CloseHandle(m_handleRPI); m_handleRPI = 0; } if (m_pUnmanageDb != NULL) { // Db must be open (void)m_pUnmanageDb->Close(m_pDbSession); m_pDbSession = 0; m_pUnmanageRec = 0; } if (TRUE == m_changedAttributes) { // // We changed it from read only to read/write - put it back. // RestoreAttributes(); } // // Detach the data mover stream if (m_pDataMover != 0) { WsbAffirmHr( m_pDataMover->CloseStream() ); } // Let the parent class do his thing. CComObjectRoot::FinalRelease(); iCountItem--; WsbTraceOut(OLESTR("CFsaScanItem::FinalRelease"), OLESTR("Count is <%d>"), iCountItem); } HRESULT CFsaScanItem::FindFirst( IN IFsaResource* pResource, IN OLECHAR* path, IN IHsmSession* pSession ) /*++ Implements: IFsaScanItem::FindFirst(). --*/ { HRESULT hr = S_OK; CWsbStringPtr findPath; CWsbStringPtr searchName; OLECHAR* slashPtr; DWORD lErr; WsbTraceIn(OLESTR("CFsaScanItem::FindFirst"), OLESTR("path = <%ls>"), path); try { WsbAssert(0 != pResource, E_POINTER); WsbAssert(0 != path, E_POINTER); // Store off some of the scan information. m_pResource = pResource; m_pSession = pSession; // Break up the incoming path into a path and a name. m_path = path; slashPtr = wcsrchr(m_path, L'\\'); // We could try to support relative path stuff (i.e. current // directory, but I am not going to do it for now. WsbAffirm(slashPtr != 0, E_FAIL); searchName = &(slashPtr[1]); slashPtr[1] = 0; // Get a path that can be used by the find function. WsbAffirmHr(GetPathForFind(searchName, &findPath, 0)); // Scan starting at the specified path. m_handle = FindFirstFileEx(findPath, FindExInfoStandard, &m_findData, FindExSearchNameMatch, 0, FIND_FIRST_EX_CASE_SENSITIVE); lErr = GetLastError(); // If we found a file, then remember the scan handle and // return the scan item. WsbAffirm(INVALID_HANDLE_VALUE != m_handle, WSB_E_NOTFOUND); m_gotPhysicalSize = FALSE; m_physicalSize.QuadPart = 0; m_gotPlaceholder = FALSE; } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaScanItem::FindFirst"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaScanItem::FindNext( void ) /*++ Implements: IFsaScanItem::FindNext(). --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CFsaScanItem::FindNext"), OLESTR("")); try { WsbAssert(INVALID_HANDLE_VALUE != m_handle, E_FAIL); if (TRUE == m_changedAttributes) { // // We changed it from read only to read/write - put it back. // RestoreAttributes(); } // Continue the scan. WsbAffirm(FindNextFile(m_handle, &m_findData), WSB_E_NOTFOUND); m_gotPhysicalSize = FALSE; m_physicalSize.QuadPart = 0; m_gotPlaceholder = FALSE; } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaScanItem::FindNext"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaScanItem::GetAccessTime( OUT FILETIME* pTime ) /*++ Implements: IFsaScanItem::GetAccessTime(). --*/ { HRESULT hr = S_OK; try { WsbAssert(0 != pTime, E_POINTER); *pTime = m_findData.ftLastAccessTime; } WsbCatch(hr); return(hr); } HRESULT CFsaScanItem::GetGroup( OUT OLECHAR** /*pGroup*/, IN ULONG /*bufferSize*/ ) /*++ Implements: IFsaScanItem::GetGroup(). --*/ { HRESULT hr = S_OK; try { hr = E_NOTIMPL; } WsbCatch(hr); return(hr); } HRESULT CFsaScanItem::GetLogicalSize( OUT LONGLONG* pSize ) /*++ Implements: IFsaScanItem::GetLogicalSize(). --*/ { HRESULT hr = S_OK; LARGE_INTEGER logSize; try { WsbAssert(0 != pSize, E_POINTER); logSize.LowPart = m_findData.nFileSizeLow; logSize.HighPart = m_findData.nFileSizeHigh; *pSize = logSize.QuadPart; } WsbCatch(hr); return(hr); } HRESULT CFsaScanItem::GetModifyTime( OUT FILETIME* pTime ) /*++ Implements: IFsaScanItem::GetModifyTime(). --*/ { HRESULT hr = S_OK; try { WsbAssert(0 != pTime, E_POINTER); *pTime = m_findData.ftLastWriteTime; } WsbCatch(hr); return(hr); } HRESULT CFsaScanItem::GetName( OUT OLECHAR** pName, IN ULONG bufferSize ) /*++ Implements: IFsaScanItem::GetName(). --*/ { HRESULT hr = S_OK; CWsbStringPtr tmpString = m_findData.cFileName; try { WsbAssert(0 != pName, E_POINTER); WsbAffirmHr(tmpString.CopyTo(pName, bufferSize)); } WsbCatch(hr); return(hr); } HRESULT CFsaScanItem::GetOwner( OUT OLECHAR** /*pOwner*/, IN ULONG /*bufferSize*/ ) /*++ Implements: IFsaScanItem::GetOwner(). --*/ { HRESULT hr = S_OK; try { hr = E_NOTIMPL; } WsbCatch(hr); return(hr); } HRESULT CFsaScanItem::GetPath( OUT OLECHAR** pPath, IN ULONG bufferSize ) /*++ Implements: IFsaScanItem::GetPath(). --*/ { HRESULT hr = S_OK; try { WsbAssert(0 != pPath, E_POINTER); WsbAffirmHr(m_path.CopyTo(pPath, bufferSize)); } WsbCatch(hr); return(hr); } HRESULT CFsaScanItem::GetPathForFind( IN OLECHAR* searchName, OUT OLECHAR** pPath, IN ULONG bufferSize ) /*++ Implements: IFsaScanItem::GetPathForFind(). --*/ { HRESULT hr = S_OK; CWsbStringPtr tmpString; try { WsbAssert(0 != pPath, E_POINTER); // Get a buffer. WsbAffirmHr(tmpString.TakeFrom(*pPath, bufferSize)); try { // Get the path to the resource of the resource. // WsbAffirmHr(m_pResource->GetPath(&tmpString, 0)); WsbAffirmHr(tmpString.Prepend(OLESTR("\\\\?\\"))); //WsbAffirmHr(tmpString.Append(OLESTR("\\"))); // Copy in the path. //WsbAffirmHr(tmpString.Prepend(OLESTR("\\\\?\\"))); WsbAffirmHr(tmpString.Append(&(m_path[1]))); WsbAffirmHr(tmpString.Append(searchName)); } WsbCatch(hr); WsbAffirmHr(tmpString.GiveTo(pPath)); } WsbCatch(hr); return(hr); } HRESULT CFsaScanItem::GetPathAndName( IN OLECHAR* appendix, OUT OLECHAR** pPath, IN ULONG bufferSize ) /*++ Implements: IFsaScanItem::GetPathAndName(). --*/ { HRESULT hr = S_OK; CWsbStringPtr tmpString; try { WsbAssert(0 != pPath, E_POINTER); // Get a buffer. WsbAffirmHr(tmpString.TakeFrom(*pPath, bufferSize)); try { tmpString = m_path; tmpString.Append(m_findData.cFileName); if (0 != appendix) { tmpString.Append(appendix); } } WsbCatch(hr); // Give responsibility for freeing the memory back to the caller. WsbAffirmHr(tmpString.GiveTo(pPath)); } WsbCatch(hr); return(hr); } HRESULT CFsaScanItem::GetFullPathAndName( IN OLECHAR* prependix, IN OLECHAR* appendix, OUT OLECHAR** pPath, IN ULONG bufferSize ) /*++ Implements: IFsaScanItem::GetFullPathAndName(). --*/ { HRESULT hr = S_OK; CWsbStringPtr tmpString; CWsbStringPtr tmpString2; try { WsbAssert(0 != pPath, E_POINTER); // Get a buffer. WsbAffirmHr(tmpString.TakeFrom(*pPath, bufferSize)); try { if (0 != prependix) { tmpString = prependix; // Get the path to the resource of the resource. WsbAffirmHr(m_pResource->GetPath(&tmpString2, 0)); WsbAffirmHr(tmpString.Append(tmpString2)); } else { WsbAffirmHr(m_pResource->GetPath(&tmpString, 0)); } // Copy in the path. WsbAffirmHr(tmpString.Append(&(m_path[1]))); WsbAffirmHr(tmpString.Append(m_findData.cFileName)); if (0 != appendix) { WsbAffirmHr(tmpString.Append(appendix)); } } WsbCatch(hr); // Give responsibility for freeing the memory back to the caller. WsbAffirmHr(tmpString.GiveTo(pPath)); } WsbCatch(hr); return(hr); } HRESULT CFsaScanItem::GetPhysicalSize( OUT LONGLONG* pSize ) /*++ Implements: IFsaScanItem::GetPhysicalSize(). --*/ { HRESULT hr = S_OK; CWsbStringPtr path; try { WsbAssert(0 != pSize, E_POINTER); //WsbAssertHr(GetFullPathAndName(NULL, 0, &path, 0)); WsbAssertHr(GetFullPathAndName(OLESTR("\\\\?\\"), 0, &path, 0)); // Only read this value in once, but wait until it is asked for // before reading it in (since this call takes time and many scans // won't need the information. if (!m_gotPhysicalSize) { m_physicalSize.LowPart = GetCompressedFileSize(path, &m_physicalSize.HighPart); if (MAXULONG == m_physicalSize.LowPart) { // Have to check last error since MAXULONG could be a valid // value for the low part of the size. DWORD err = GetLastError(); if (err != NO_ERROR) { WsbTrace(OLESTR("CFsaScanItem::GetPhysicalSize of %ws Last error = %u\n"), (WCHAR *) path, err); } WsbAffirm(NO_ERROR == err, E_FAIL); } m_gotPhysicalSize = TRUE; } *pSize = m_physicalSize.QuadPart; } WsbCatch(hr); return(hr); } HRESULT CFsaScanItem::GetPremigratedUsn( OUT LONGLONG* pFileUsn ) /*++ Implements: Routine Description: Get the USN Journal number for this file from the premigrated list. Arguments: pFileUsn - Pointer to File USN to be returned. Return Value: S_OK - success --*/ { HRESULT hr = S_OK; try { CComPtr pDbSession; CComPtr pPremDb; CComPtr pResourcePriv; WsbAssert(pFileUsn, E_POINTER); // Get the premigrated list DB WsbAffirmHr(m_pResource->QueryInterface(IID_IFsaResourcePriv, (void**) &pResourcePriv)); WsbAffirmHr(pResourcePriv->GetPremigrated(IID_IFsaPremigratedDb, (void**) &pPremDb)); // Open the premigration list WsbAffirmHr(pPremDb->Open(&pDbSession)); try { FSA_PLACEHOLDER PlaceHolder; CComPtr pPremRec; LONGLONG usn; // Get a DB entity for the search WsbAffirmHr(pPremDb->GetEntity(pDbSession, PREMIGRATED_REC_TYPE, IID_IFsaPremigratedRec, (void**) &pPremRec)); WsbAffirmHr(pPremRec->UseKey(PREMIGRATED_BAGID_OFFSETS_KEY_TYPE)); // Find the record WsbAffirmHr(GetPlaceholder(0, 0, &PlaceHolder)); WsbAffirmHr(pPremRec->SetBagId(PlaceHolder.bagId)); WsbAffirmHr(pPremRec->SetBagOffset(PlaceHolder.fileStart)); WsbAffirmHr(pPremRec->SetOffset(PlaceHolder.dataStreamStart)); WsbAffirmHr(pPremRec->FindEQ()); // Get the stored USN WsbAffirmHr(pPremRec->GetFileUSN(&usn)); *pFileUsn = usn; } WsbCatch(hr); // Close the DB pPremDb->Close(pDbSession); } WsbCatch(hr); return(hr); } HRESULT CFsaScanItem::GetSession( OUT IHsmSession** ppSession ) /*++ Implements: IFsaScanItem::GetSession(). --*/ { HRESULT hr = S_OK; try { WsbAssert(0 != ppSession, E_POINTER); *ppSession = m_pSession; m_pSession.p->AddRef(); } WsbCatch(hr); return(hr); } HRESULT CFsaScanItem::GetUncPathAndName( IN OLECHAR* prependix, IN OLECHAR* appendix, OUT OLECHAR** pPath, IN ULONG bufferSize ) /*++ Implements: IFsaScanItem::GetUncPathAndName(). --*/ { HRESULT hr = S_OK; CWsbStringPtr tmpString; CWsbStringPtr tmpString2; try { WsbAssert(0 != pPath, E_POINTER); // Get a buffer. WsbAffirmHr(tmpString.TakeFrom(*pPath, bufferSize)); try { if (0 != prependix) { tmpString = prependix; // Get the path to the resource of the resource. WsbAffirmHr(m_pResource->GetUncPath(&tmpString2, 0)); WsbAffirmHr(tmpString.Append(tmpString2)); } else { WsbAffirmHr(m_pResource->GetPath(&tmpString, 0)); } // Copy in the path. WsbAffirmHr(tmpString.Append(&(m_path[1]))); WsbAffirmHr(tmpString.Append(m_findData.cFileName)); if (0 != appendix) { WsbAffirmHr(tmpString.Append(appendix)); } } WsbCatch(hr); // Give responsibility for freeing the memory back to the caller. WsbAffirmHr(tmpString.GiveTo(pPath)); } WsbCatch(hr); return(hr); } HRESULT CFsaScanItem::IsAParent( void ) /*++ Implements: IFsaScanItem::IsAParent(). --*/ { HRESULT hr = S_FALSE; if ((m_findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { hr = S_OK; } return(hr); } HRESULT CFsaScanItem::IsARelativeParent( void ) /*++ Implements: IFsaScanItem::IsARelativeParent(). --*/ { HRESULT hr = S_FALSE; if ((m_findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { // looking for "." if (m_findData.cFileName[0] == L'.') { if (m_findData.cFileName[1] == 0) { hr = S_OK; } // looking for "." else if (m_findData.cFileName[1] == L'.') { if (m_findData.cFileName[2] == 0) { hr = S_OK; } } } } return(hr); } HRESULT CFsaScanItem::IsCompressed( void ) /*++ Implements: IFsaScanItem::IsCompressed(). --*/ { HRESULT hr = S_FALSE; if ((m_findData.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0) { hr = S_OK; } return(hr); } HRESULT CFsaScanItem::IsEncrypted( void ) /*++ Implements: IFsaScanItem::IsEncrypted(). --*/ { HRESULT hr = S_FALSE; if ((m_findData.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) != 0) { hr = S_OK; } return(hr); } HRESULT CFsaScanItem::IsDeleteOK( IN IFsaPostIt *pPostIt ) /*++ Implements: IFsaScanItem::IsDeleteOK(). --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CFsaScanItem::IsDeleteOK"), OLESTR("")); try { // // Get the version ID from the FSA Post it. This is the // version of the file at the time of the migrate request // LONGLONG workVersionId; WsbAffirmHr(pPostIt->GetFileVersionId(&workVersionId)); // // Get the version of the file at the time of this scan // LONGLONG scanVersionId; WsbAffirmHr(GetVersionId(&scanVersionId)); // // See if the versions match // WsbTrace(OLESTR("CFsaScanItem::IsDeleteOK: workVersionId:<%I64u> scanVersionId:<%I64u>\n"), workVersionId, scanVersionId); if (workVersionId != scanVersionId) { WsbTrace(OLESTR("CFsaScanItem::IsDeleteOK: File version has changed!\n")); WsbThrow(FSA_E_FILE_CHANGED); } } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaScanItem::IsDeleteOk"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaScanItem::IsGroupMemberOf( OLECHAR* /*group*/ ) /*++ Implements: IFsaScanItem::IsGroupMemberOf(). --*/ { HRESULT hr = S_FALSE; hr = E_NOTIMPL; return(hr); } HRESULT CFsaScanItem::IsHidden( void ) /*++ Implements: IFsaScanItem::IsHidden(). --*/ { HRESULT hr = S_FALSE; if ((m_findData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0) { hr = S_OK; } return(hr); } HRESULT CFsaScanItem::IsManageable( IN LONGLONG offset, IN LONGLONG size ) /*++ Implements: IFsaScanItem::IsManageable(). --*/ { HRESULT hr = S_FALSE; HRESULT hr2; LONGLONG logicalSize; LONGLONG managableSize; LONGLONG maxFileSize; FILETIME time; FILETIME managableTime; BOOL isRelative; // // Get some strings for logging and tracing // CWsbStringPtr fileName; CWsbStringPtr jobName; try { WsbAffirmHr(GetFullPathAndName( 0, 0, &fileName, 0)); WsbAffirmHr(m_pSession->GetName(&jobName, 0)); } WsbCatch( hr ); WsbTraceIn(OLESTR("CFsaScanItem::IsManageable"), OLESTR("<%ls>"), (OLECHAR *)fileName); try { // To be managable the item: // - can't already be managed (premigratted or truncated) // - can't be a link // - can't be encrypted // - can't be sparse // - can't have extended attributes (reparse point limitation) // - must have a size bigger than the resource's default size // - must have a last access time older than the resource's default time // Managed? hr2 = IsManaged(offset, size); if (S_FALSE == hr2) { // A link? hr2 = IsALink(); if (S_FALSE == hr2) { // Encrypted? hr2 = IsEncrypted(); if (S_FALSE == hr2) { // A sparse? hr2 = IsSparse(); if (S_FALSE == hr2) { // A sparse? hr2 = HasExtendedAttributes(); if (S_FALSE == hr2) { // Big enough? WsbAffirmHr(GetLogicalSize(&logicalSize)); WsbAffirmHr(m_pResource->GetManageableItemLogicalSize(&managableSize)); if (logicalSize >= managableSize) { // Old enough? WsbAffirmHr(GetAccessTime(&time)); WsbAffirmHr(m_pResource->GetManageableItemAccessTime(&isRelative, &managableTime)); if (WsbCompareFileTimes(time, managableTime, isRelative, FALSE) >= 0) { // Small enough? (This is according to media size limit !) CComPtr pResourcePriv; WsbAffirmHr(m_pResource->QueryInterface(IID_IFsaResourcePriv, (void**) &pResourcePriv)); WsbAffirmHr(pResourcePriv->GetMaxFileLogicalSize(&maxFileSize)); if ((logicalSize <= maxFileSize) || (0 == maxFileSize)) { // It can be managed!! hr = S_OK; } else { WsbLogEvent(FSA_MESSAGE_FILESKIPPED_ISTOOLARGE, 0, NULL, (OLECHAR*) jobName, WsbAbbreviatePath(fileName, 120), WsbHrAsString(hr), NULL); WsbTrace(OLESTR("CFsaScanItem::IsManageable: file not manageable: Logical size = %I64d; Max file size = %I64d\n"), logicalSize, maxFileSize); } } else { WsbLogEvent(FSA_MESSAGE_FILESKIPPED_ISACCESSED, 0, NULL, (OLECHAR*) jobName, WsbAbbreviatePath(fileName, 120), WsbHrAsString(hr), NULL); } } else { WsbLogEvent(FSA_MESSAGE_FILESKIPPED_ISTOOSMALL, 0, NULL, (OLECHAR*) jobName, WsbAbbreviatePath(fileName, 120), WsbHrAsString(hr), NULL); } } else { WsbLogEvent(FSA_MESSAGE_FILESKIPPED_HASEA, 0, NULL, (OLECHAR*) jobName, WsbAbbreviatePath(fileName, 120), WsbHrAsString(hr), NULL); } } else { WsbLogEvent(FSA_MESSAGE_FILESKIPPED_ISSPARSE, 0, NULL, (OLECHAR*) jobName, WsbAbbreviatePath(fileName, 120), WsbHrAsString(hr), NULL); } } else { WsbLogEvent(FSA_MESSAGE_FILESKIPPED_ISENCRYPTED, 0, NULL, (OLECHAR*) jobName, WsbAbbreviatePath(fileName, 120), WsbHrAsString(hr), NULL); } } else { WsbLogEvent(FSA_MESSAGE_FILESKIPPED_ISALINK, 0, NULL, (OLECHAR*) jobName, WsbAbbreviatePath(fileName, 120), WsbHrAsString(hr), NULL); } } else { WsbLogEvent(FSA_MESSAGE_FILESKIPPED_ISMANAGED, 0, NULL, (OLECHAR*) jobName, WsbAbbreviatePath(fileName, 120), WsbHrAsString(hr), NULL); } } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaScanItem::IsManageable"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaScanItem::IsMigrateOK( IN IFsaPostIt *pPostIt ) /*++ Implements: IFsaScanItem::IsMigrateOK(). --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CFsaScanItem::IsMigrateOK"), OLESTR("")); try { // // Make sure the file isn't already managed. This could happen if two jobs were scanning // the same volume. // LONGLONG offset; LONGLONG size; WsbAffirmHr(pPostIt->GetRequestOffset(&offset)); WsbAffirmHr(pPostIt->GetRequestSize(&size)); if (IsManaged(offset, size) == S_OK) { // // The file is already managed so skip it // WsbTrace(OLESTR("A manage request for an already managed file - skip it!\n")); WsbThrow(FSA_E_FILE_ALREADY_MANAGED); } // // Get the version ID from the FSA Post it. This is the // version of the file at the time of the migrate request // LONGLONG workVersionId; WsbAffirmHr(pPostIt->GetFileVersionId(&workVersionId)); // // Get the version of the file at the time of this scan // LONGLONG scanVersionId; WsbAffirmHr(GetVersionId(&scanVersionId)); // // See if the versions match // WsbTrace(OLESTR("CFsaScanItem::IsMigrateOK: workVersionId:<%I64u> scanVersionId:<%I64u>\n"), workVersionId, scanVersionId); if (workVersionId != scanVersionId) { WsbTrace(OLESTR("CFsaScanItem::IsMigrateOK: File version has changed!\n")); WsbThrow(FSA_E_FILE_CHANGED); } } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaScanItem::IsMigrateOK"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaScanItem::IsMbit( void ) /*++ Implements: IFsaScanItem::IsMbit(). --*/ { HRESULT hr = S_FALSE; if ((m_findData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) != 0) { hr = S_OK; } return(hr); } HRESULT CFsaScanItem::IsOffline( void ) /*++ Implements: IFsaScanItem::IsOffline(). --*/ { HRESULT hr = S_FALSE; if ((m_findData.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) != 0) { hr = S_OK; } return(hr); } HRESULT CFsaScanItem::IsOwnerMemberOf( OLECHAR* /*group*/ ) /*++ Implements: IFsaScanItem::IsOwnerMemberOf(). --*/ { HRESULT hr = S_FALSE; hr = E_NOTIMPL; return(hr); } HRESULT CFsaScanItem::IsReadOnly( void ) /*++ Implements: IFsaScanItem::IsReadOnly(). --*/ { HRESULT hr = S_FALSE; if ((m_findData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0) { hr = S_OK; } return(hr); } HRESULT CFsaScanItem::IsRecallOK( IN IFsaPostIt *pPostIt ) /*++ Implements: IFsaScanItem::IsRecallOK(). --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CFsaScanItem::IsRecallOK"), OLESTR("")); try { LONGLONG offset; LONGLONG size; // // Make sure the file is still truncated // WsbAffirmHr(pPostIt->GetRequestOffset(&offset)); WsbAffirmHr(pPostIt->GetRequestSize(&size)); hr = IsTruncated(offset, size); if (S_OK != hr) { // // The file is not truncated, so skip it // WsbTrace(OLESTR("CFsaScanItem::IsRecallOK - file isn't truncated.\n")); WsbThrow(FSA_E_FILE_NOT_TRUNCATED); } // Get the version ID from the FSA Post it. This is the // version of the file at the time of the migrate request // LONGLONG workVersionId; WsbAffirmHr(pPostIt->GetFileVersionId(&workVersionId)); // // Get the version of the file // LONGLONG scanVersionId; WsbAffirmHr(GetVersionId(&scanVersionId)); // // See if the versions match // WsbTrace(OLESTR("CFsaScanItem::IsRecallOK: workVersionId:<%I64u> scanVersionId:<%I64u>\n"), workVersionId, scanVersionId); if (workVersionId != scanVersionId) { WsbTrace(OLESTR("CFsaScanItem::IsRecallOK: File version has changed!\n")); // // If the use has changed alternate data streams // the file version ID may have changed but it is // OK to recall the file. So if the version ID's // don't match, then check to see if the truncated // part of the file is OK. If so, allow the recall // to happen. // // // Check to see if the whole file is still sparse // if (IsTotallySparse() == S_OK) { // // The file is OK so far to recall but we need // to make the last modify dates match // FSA_PLACEHOLDER placeholder; WsbAffirmHr(pPostIt->GetPlaceholder(&placeholder));; placeholder.fileVersionId = scanVersionId; WsbAffirmHr(pPostIt->SetPlaceholder(&placeholder)); } else { // // The file has been changed, recalling data will // overwrite something that has been added since the // truncation occurred. So don't do anything. // WsbTrace(OLESTR("File is no longer sparse.!\n")); WsbThrow(FSA_E_FILE_CHANGED); } } } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaScanItem::IsRecallOK"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaScanItem::IsSparse( void ) /*++ Implements: IFsaScanItem::IsSparse(). --*/ { HRESULT hr = S_FALSE; LONGLONG size; WsbTraceIn(OLESTR("CFsaScanItem::IsSparse"), OLESTR("")); if ((m_findData.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) != 0) { hr = GetLogicalSize( &size ) ; if ( S_OK == hr ) { hr = CheckIfSparse(0, size ); if ( (FSA_E_FILE_IS_TOTALLY_SPARSE == hr) || (FSA_E_FILE_IS_PARTIALLY_SPARSE == hr) ) { hr = S_OK; } else { hr = S_FALSE; } } } WsbTraceOut(OLESTR("CFsaScanItem::IsSparse"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaScanItem::IsTotallySparse( void ) /*++ Implements: IFsaScanItem::IsTotallySparse(). --*/ { HRESULT hr = S_FALSE; LONGLONG size; WsbTraceIn(OLESTR("CFsaScanItem::IsTotallySparse"), OLESTR("")); if ((m_findData.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) != 0) { hr = GetLogicalSize( &size ) ; if ( S_OK == hr ) { hr = CheckIfSparse(0, size ); if (FSA_E_FILE_IS_TOTALLY_SPARSE == hr) { hr = S_OK; } else { hr = S_FALSE; } } } WsbTraceOut(OLESTR("CFsaScanItem::IsTotallySparse"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaScanItem::Manage( IN LONGLONG offset, IN LONGLONG size, IN GUID storagePoolId, IN BOOL truncate ) /*++ Implements: IFsaScanItem::Manage(). --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CFsaScanItem::Manage"), OLESTR("")); try { WsbAssert(GUID_NULL != storagePoolId, E_INVALIDARG); WsbAffirmHr(m_pResource->Manage((IFsaScanItem*) this, offset, size, storagePoolId, truncate)); } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaScanItem::Manage"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaScanItem::Move( OLECHAR* dest, BOOL /*retainHierarcy*/, BOOL /*expandPlaceholders*/, BOOL overwriteExisting ) /*++ Implements: IFsaScanItem::Move(). --*/ { HRESULT hr = S_OK; DWORD mode = MOVEFILE_COPY_ALLOWED; try { // NOTE : This default behavior causes placeholders // to be expanded when moving to another volume and probably doesn't // retain the heirarchy. WsbAssert(0 != dest, E_POINTER); if (overwriteExisting) { mode |= MOVEFILE_REPLACE_EXISTING; } WsbAssert(MoveFileEx(m_findData.cFileName, dest, mode), E_FAIL); } WsbCatch(hr); return(hr); } HRESULT CFsaScanItem::Recall( IN LONGLONG offset, IN LONGLONG size, IN BOOL deletePlaceholder ) /*++ Implements: IFsaScanItem::Recall(). --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CFsaScanItem::Recall"), OLESTR("")); try { WsbAffirmHr(m_pResource->Recall((IFsaScanItem*) this, offset, size, deletePlaceholder)); } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaScanItem::Recall"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaScanItem::Recycle( void ) /*++ Implements: IFsaScanItem::Recycle(). --*/ { HRESULT hr = S_OK; try { // Probably need to look at SHFileOperation(). hr = E_NOTIMPL; } WsbCatch(hr); return(hr); } HRESULT CFsaScanItem::IsSystem( void ) /*++ Implements: IFsaScanItem::IsSystem(). --*/ { HRESULT hr = S_FALSE; if ((m_findData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) != 0) { hr = S_OK; } return(hr); } HRESULT CFsaScanItem::Test( USHORT* passed, USHORT* failed ) /*++ Implements: IWsbTestable::Test(). --*/ { HRESULT hr = S_OK; try { WsbAssert(0 != passed, E_POINTER); WsbAssert(0 != failed, E_POINTER); *passed = 0; *failed = 0; } WsbCatch(hr); return(hr); } HRESULT CFsaScanItem::Unmanage( IN LONGLONG offset, IN LONGLONG size ) /*++ Implements: IFsaScanItem::Unmanage(). --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CFsaScanItem::Unmanage"), OLESTR("<%ls>"), WsbAbbreviatePath(m_path, 120)); try { // We only need to worry about files that have placeholder information. if (IsManaged(offset, size) == S_OK) { // If the file is truncated, then we need to recall the data // before deleting the placeholder information. // NOTE: We set a flag on the Recall so the placeholder will // be deleted after the file is recalled. if (IsTruncated(offset, size) == S_OK) { WsbAffirmHr(Recall(offset, size, TRUE)); } else { // For disaster recovery, it would be better to delete the placeholder // and THEN remove this file from the premigration list. Unfortunately, // after deleting the placeholder, the RemovePremigrated call fails // because it needs to get some information from the placeholder (which // is gone). So we do it in this order. hr = m_pResource->RemovePremigrated((IFsaScanItem*) this, offset, size); if (WSB_E_NOTFOUND == hr) { // It's no tragedy if this file wasn't in the list since we were // going to delete it anyway (although it shouldn't happen) so // let's continue anyway hr = S_OK; } WsbAffirmHr(hr); WsbAffirmHr(DeletePlaceholder(offset, size)); } } } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaScanItem::Unmanage"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaScanItem::Validate( IN LONGLONG offset, IN LONGLONG size ) /*++ Implements: IFsaScanItem::Validate(). --*/ { HRESULT hr = S_OK; BOOL fileIsTruncated = FALSE; LONGLONG usn = 0; WsbTraceIn(OLESTR("CFsaScanItem::Validate"), OLESTR("offset = <%I64u>, size = <%I64u>"), offset, size); try { // // Do some local validation before calling the engine. // // We only need to worry about files that have placeholder information. if (IsManaged(offset, size) == S_OK) { // // If the file is marked as truncated, make sure it is still truncated. // if (IsTruncated(offset, size) == S_OK) { // // Check to see if the file is totally sparse to see if it is truncated. // if (IsTotallySparse() != S_OK) { // // The file is marked as truncated but is not truncated // Make it truncated. // WsbAffirmHr(Truncate(offset,size)); WsbLogEvent(FSA_MESSAGE_VALIDATE_TRUNCATED_FILE, 0, NULL, WsbAbbreviatePath(m_path, 120), WsbHrAsString(hr), NULL); } fileIsTruncated = TRUE; } } // // The last modify date may be updated on a file if the named data streams // have been modified. So check to see if the dates match. If they don't, // if the file is trunctated, see if it is still truncated, if so, update the // modify date in the placeholder to the file's modify date. If the file is // premigrated and the modify dates don't match, delete the placeholder. // Get the version ID from the file LONGLONG scanVersionId; WsbAffirmHr(GetVersionId(&scanVersionId)); // Get the version ID from the placeholder FSA_PLACEHOLDER scanPlaceholder; WsbAffirmHr(GetPlaceholder(offset, size, &scanPlaceholder)); if (TRUE == fileIsTruncated) { // Check to see if the dates match if (scanPlaceholder.fileVersionId != scanVersionId) { WsbTrace(OLESTR("CFsaScanItem::Validate - placeholer version ID = <%I64u>, file version Id = <%I64u>"), scanPlaceholder.fileVersionId, scanVersionId); // // Update the placeholder information on the reparse point // LONGLONG afterPhUsn; scanPlaceholder.fileVersionId = scanVersionId; WsbAffirmHr(CreatePlaceholder(offset, size, scanPlaceholder, FALSE, 0, &afterPhUsn)); WsbLogEvent(FSA_MESSAGE_VALIDATE_RESET_PH_MODIFY_TIME, 0, NULL, WsbAbbreviatePath(m_path, 120), WsbHrAsString(hr), NULL); } } else { // The file is pre-migrated. Verify that it has not changed since we managed it and if it has then unmanage it. if (Verify(offset, size) != S_OK) { WsbAffirmHr(Unmanage(offset, size)); WsbLogEvent(FSA_MESSAGE_VALIDATE_UNMANAGED_FILE, 0, NULL, WsbAbbreviatePath(m_path, 120), WsbHrAsString(hr), NULL); } } // Now that all of this stuff is OK, call the engine if (IsManaged(offset, size) == S_OK) { WsbAffirmHr(m_pResource->Validate((IFsaScanItem*) this, offset, size, usn)); } } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaScanItem::Validate"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaScanItem::FindFirstInDbIndex( IN IFsaResource* pResource, IN IHsmSession* pSession ) /*++ Implements: IFsaScanItemPriv::FindFirstInDbIndex(). --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CFsaScanItem::FindFirstInDbIndex"), OLESTR("")); try { CComPtr pResourcePriv; WsbAssert(0 != pResource, E_POINTER); // Store off some of the scan information. m_pResource = pResource; m_pSession = pSession; // If Db is already present (could happen if somebody calls First() twice in a row), // we close the Db and reopen since we cannot be sure that the resource is the same! if (m_pUnmanageDb != NULL) { // Db must be open (void)m_pUnmanageDb->Close(m_pDbSession); m_pDbSession = 0; m_pUnmanageRec = 0; m_pUnmanageDb = 0; } // Get and open the Unmanage db // (Note: if this scanning is ever extended to use another DB, // this method should get additional parameter for which DB to scan) WsbAffirmHr(m_pResource->QueryInterface(IID_IFsaResourcePriv, (void**) &pResourcePriv)); hr = pResourcePriv->GetUnmanageDb(IID_IFsaUnmanageDb, (void**) &m_pUnmanageDb); if (WSB_E_RESOURCE_UNAVAILABLE == hr) { // Db was not created ==> no files to scan hr = WSB_E_NOTFOUND; } WsbAffirmHr(hr); hr = m_pUnmanageDb->Open(&m_pDbSession); if (S_OK != hr) { m_pUnmanageDb = NULL; WsbAffirmHr(hr); } // Get a record to traverse with and set for sequential traversing WsbAffirmHr(m_pUnmanageDb->GetEntity(m_pDbSession, UNMANAGE_REC_TYPE, IID_IFsaUnmanageRec, (void**)&m_pUnmanageRec)); WsbAffirmHr(m_pUnmanageRec->SetSequentialScan()); // Get file information WsbAffirmHr(GetFromDbIndex(TRUE)); } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaScanItem::FindFirstInDbIndex"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaScanItem::FindNextInDbIndex( void ) /*++ Implements: IFsaScanItemPriv::FindNextInDbIndex(). --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CFsaScanItem::FindNextInDbIndex"), OLESTR("")); try { WsbAssert(m_pUnmanageDb != NULL, E_FAIL); // Get file information WsbAffirmHr(GetFromDbIndex(FALSE)); } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaScanItem::FindNextInDbIndex"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaScanItem::GetFromDbIndex( BOOL first ) /* Implements: CFsaScanItem::GetFromDbIndex(). --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CFsaScanItem::GetFromDbIndex"), OLESTR("")); try { IFsaScanItem* pScanItem; HRESULT hrFindFileId = S_OK; LONGLONG fileId; BOOL bCont; WsbAssert(m_pUnmanageDb != NULL, E_FAIL); WsbAssert(m_pUnmanageRec != NULL, E_FAIL); do { bCont = FALSE; // Get first/next record if (first) { hr = m_pUnmanageRec->First(); } else { hr = m_pUnmanageRec->Next(); } WsbAffirm(S_OK == hr, WSB_E_NOTFOUND); // Get file id WsbAffirmHr(m_pUnmanageRec->GetFileId(&fileId)); // Reset some items in case this isn't the first call to FindFileId // (FindFileId actually "attach" the object to a different file) if (INVALID_HANDLE_VALUE != m_handle) { FindClose(m_handle); m_handle = INVALID_HANDLE_VALUE; } if (TRUE == m_changedAttributes) { RestoreAttributes(); } // Find the file from the ID pScanItem = this; hrFindFileId = m_pResource->FindFileId(fileId, m_pSession, &pScanItem); // If the FindFileId failed, we just skip that item and get the // next one. This is to keep the scan from just stopping on this // item. FindFileId could fail because the file has been deleted // or open exclusively by somebody else if (!SUCCEEDED(hrFindFileId)) { WsbTrace(OLESTR("CFsaScanItem::GetFromDbIndex: file id %I64d skipped since FindFileId failed with hr = <%ls>\n"), fileId, WsbHrAsString(hrFindFileId)); first = FALSE; bCont = TRUE; } } while (bCont); WsbAffirmHr(pScanItem->Release()); // Get rid of extra ref. count (we get extra ref count only when FindFileId succeeds) } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaScanItem::GetFromDbIndex"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); }