/*++ Copyright (c) 1998-1999 Microsoft Corporation Module Name: sisbackup.cpp Abstract: The SIS Backup dll. Author: Bill Bolosky [bolosky] March 1998 Revision History: --*/ #include "sibp.h" BOOLEAN NonSISEnabledVolume( PSIB_RESTORE_VOLUME_STRUCTURE restoreStructure) /*++ Routine Description: Figure out if restoreStructure represents a SIS enabled volume. First, we check to see if we've already made the check, in which case we return the value we already stored. If not, then we open a root handle, and send down a mal-formed SIS_COPYFILE request. If we get back ERROR_INVALID_FUNCTION then it's not SIS enabled. If we get back ERROR_INVALID_PARAMETER, then it's a SIS-enabled volume. If we get back anything else, then we can't prove it's not SIS enabled, and we just retry the next time we're asked. Caller must hold the mutex in the restore volume structure. Arguments: restoreStructure - A pointer to the restore structure representing the volume to check. Return Value: Returns TRUE if this is not a SIS-enabled volume, FALSE if it is or if it can't be determined. --*/ { if (restoreStructure->checkedForSISEnabledVolume) { return !restoreStructure->isSISEnabledVolume; } HANDLE volumeRootHandle; PWCHAR volumeRootName; // // Allocate space for a string containing the volume root name including the trailing // backslash. It will be two (wide) characters longer than restoreStructure->volumeRoot // because of the backslash and null terminator. // volumeRootName = (PWCHAR) malloc ((wcslen(restoreStructure->volumeRoot) + 2) * sizeof(WCHAR)); if (NULL == volumeRootName) { // // Guess we can't check, just assume it's OK. // #if DBG DbgPrint("SISBkup: NonSISEnabledVolume: unable to allocate space for volume root name\n"); #endif // DBG return FALSE; } wcscpy(volumeRootName,restoreStructure->volumeRoot); wcscat(volumeRootName,L"\\"); volumeRootHandle = CreateFileW( volumeRootName, 0, // don't need any access for this check FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, // security attributes OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, // needed to open a directory NULL); // hTemplateFile free(volumeRootName); volumeRootName = NULL; if (INVALID_HANDLE_VALUE == volumeRootHandle) { return FALSE; } // // Send a malformed FSCTL_SIS_COPYFILE down on the handle we just opened. // DWORD bytesReturned; BOOL worked = DeviceIoControl( volumeRootHandle, FSCTL_SIS_COPYFILE, NULL, // input buffer (this is a malformed request, after all) 0, // i.b. size NULL, // output buffer 0, // o.b. size &bytesReturned, NULL); // lap CloseHandle(volumeRootHandle); if (worked) { // // This is bizarre! // #if DBG DbgPrint("SISBkup: malformed FSCTL_SIS_COPYFILE worked!\n"); #endif // DBG return FALSE; } if (GetLastError() == ERROR_INVALID_FUNCTION) { // // No one recognized the copyfile request, or SIS decided that // this isn't a SIS enabled volume. Say no. // restoreStructure->checkedForSISEnabledVolume = TRUE; restoreStructure->isSISEnabledVolume = FALSE; return TRUE; } if (GetLastError() == ERROR_INVALID_PARAMETER) { // // This means that SIS saw the request and thinks this is // a SIS enabled volume. Say so. // restoreStructure->checkedForSISEnabledVolume = TRUE; restoreStructure->isSISEnabledVolume = TRUE; return FALSE; } // // Else, it's some weird error. We can't prove it's not a SIS volume. // #if DBG DbgPrint("SISBkup: got unexpected error from SIS_FSCTL_COPYFILE, %d\n",GetLastError()); #endif // DBG return FALSE; } VOID SipComputeChecksum( IN PVOID buffer, IN ULONG size, IN OUT PLONGLONG checksum) /*++ Routine Description: Compute a checksum for a buffer. We use the "131 hash," which work by keeping a 64 bit running total, and for each 32 bits of data multiplying the 64 bits by 131 and adding in the next 32 bits. Must be called at PASSIVE_LEVEL, and all aruments may be pagable. Arguments: buffer - pointer to the data to be checksummed size - size of the data to be checksummed checksum - pointer to large integer to receive the checksum. This may be within the buffer, and SipComputeChecksum guarantees that the initial value will be used in computing the checksum. Return Value: Returns STATUS_SUCCESS or an error returned from the actual disk write. --*/ { LONGLONG runningTotal; PULONG ptr = (PULONG)buffer; ULONG bytesRemaining = size; runningTotal = *checksum; while (bytesRemaining >= sizeof(*ptr)) { runningTotal = runningTotal * 131 + *ptr; bytesRemaining -= sizeof(*ptr); ptr++; } if (bytesRemaining > 0) { ULONG extra; ASSERT(bytesRemaining < sizeof (ULONG)); extra = 0; RtlMoveMemory(&extra, ptr, bytesRemaining); runningTotal = runningTotal * 131 + extra; } *checksum = runningTotal; } void TryOpeningFile( PWCHAR fileName) { HANDLE fileHandle; fileHandle = CreateFileW( fileName, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, // security attributes OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); // template file if (INVALID_HANDLE_VALUE != fileHandle) { CloseHandle(fileHandle); } } LONG CsidCompare( IN PCSID id1, IN PCSID id2) { PLONGLONG keyValue1 = (PLONGLONG)id1; PLONGLONG keyValue2 = keyValue1 + 1; PLONGLONG nodeValue1 = (PLONGLONG)id2; PLONGLONG nodeValue2 = nodeValue1 + 1; if (*keyValue1 < *nodeValue1) { return -1; } else if (*keyValue1 > *nodeValue1) { return 1; } else { if (*keyValue2 < *nodeValue2) { return -1; } else if (*keyValue2 > *nodeValue2) { return 1; } else { return 0; } } } NTSTATUS FilenameFromCSid( IN PCSID CSid, IN PWCHAR volumeRoot, OUT PWCHAR *fileName) { PWCHAR uuidString; RPC_STATUS status; *fileName = (PWCHAR)malloc( wcslen(volumeRoot) * sizeof(WCHAR) + SIS_CSDIR_STRING_SIZE + INDEX_MAX_NUMERIC_STRING_LENGTH + sizeof(WCHAR)); if (NULL == *fileName) { return STATUS_INSUFFICIENT_RESOURCES; } wcscpy(*fileName,volumeRoot); wcscat(*fileName,SIS_CSDIR_STRING); status = UuidToStringW(CSid,(unsigned short **)&uuidString); if (RPC_S_OK != status) { free(*fileName); *fileName = NULL; return STATUS_INSUFFICIENT_RESOURCES; } wcscat(*fileName,uuidString); wcscat(*fileName,L".sis"); RpcStringFreeW((unsigned short **)&uuidString); return STATUS_SUCCESS; } NTSTATUS CSidFromFilename( IN PWCHAR FileName, OUT PCSID CSid) { #define UUID_STRING_MAX_LENGTH 100// Should get this length from somewhere better... PWCHAR trailingSlash; PWCHAR dot; WCHAR uuid[UUID_STRING_MAX_LENGTH]; DWORD uuidChars = 0; trailingSlash = wcsrchr(FileName, '\\'); if (NULL == trailingSlash) { // // Assume that it's just the CS file without the directory name, etc. // trailingSlash = FileName - 1; } dot = wcsrchr(FileName, '.'); if (NULL != dot) { uuidChars = (DWORD)(dot - (trailingSlash + 1)); } if ((uuidChars <= 0) || (uuidChars >= UUID_STRING_MAX_LENGTH)) { // // Something's bogus about the filename. Give up. // return STATUS_OBJECT_NAME_INVALID; } memcpy(uuid,trailingSlash+1,uuidChars * sizeof(WCHAR)); uuid[uuidChars] = 0; if (RPC_S_OK != UuidFromStringW((unsigned short *)uuid,CSid)) { return STATUS_OBJECT_NAME_INVALID; } return STATUS_SUCCESS; } NTSTATUS SisCreateBackupStructureI( IN PWCHAR volumeRoot, OUT PVOID *sisBackupStructure, OUT PWCHAR *commonStoreRootPathname, OUT PULONG countOfCommonStoreFilesToBackup, OUT PWCHAR **commonStoreFilesToBackup) { PSIB_BACKUP_VOLUME_STRUCTURE backupVolumeStructure; backupVolumeStructure = (PSIB_BACKUP_VOLUME_STRUCTURE)malloc(sizeof(SIB_BACKUP_VOLUME_STRUCTURE)); if (NULL == backupVolumeStructure) { return STATUS_INSUFFICIENT_RESOURCES; } backupVolumeStructure->linkTree = new AVLTree; if (NULL == backupVolumeStructure->linkTree) { free(backupVolumeStructure); return STATUS_INSUFFICIENT_RESOURCES; } // // Allocate space for our private copy of the volume root name, being sure to leave space for // the terminating NULL. // backupVolumeStructure->volumeRoot = (PWCHAR)malloc((wcslen(volumeRoot) + 1) * sizeof(WCHAR)); if (NULL == backupVolumeStructure->volumeRoot) { delete backupVolumeStructure->linkTree; free(backupVolumeStructure); return STATUS_INSUFFICIENT_RESOURCES; } wcscpy(backupVolumeStructure->volumeRoot, volumeRoot); // // Allocate space for the common store root pathname that we return, being sure // to leave room for the terminating NULL. // *commonStoreRootPathname = (PWCHAR) malloc(SIS_CSDIR_STRING_SIZE + (wcslen(volumeRoot) + 1) * sizeof(WCHAR)); if (NULL == *commonStoreRootPathname) { free(backupVolumeStructure->volumeRoot); delete backupVolumeStructure->linkTree; free(backupVolumeStructure); return STATUS_INSUFFICIENT_RESOURCES; } wcscpy(*commonStoreRootPathname,volumeRoot); wcscat(*commonStoreRootPathname,SIS_CSDIR_STRING); InitializeCriticalSection(backupVolumeStructure->criticalSection); *countOfCommonStoreFilesToBackup = 0; *commonStoreFilesToBackup = NULL; *sisBackupStructure = backupVolumeStructure; return STATUS_SUCCESS; } NTSTATUS SisCSFilesToBackupForLinkI( IN PVOID sisBackupStructure, IN PVOID reparseData, IN ULONG reparseDataSize, IN PVOID thisFileContext OPTIONAL, OUT PVOID *matchingFileContext OPTIONAL, OUT PULONG countOfCommonStoreFilesToBackup, OUT PWCHAR **commonStoreFilesToBackup) { PREPARSE_DATA_BUFFER reparseDataBuffer = (PREPARSE_DATA_BUFFER)reparseData; PSI_REPARSE_BUFFER sisReparseBuffer = (PSI_REPARSE_BUFFER)reparseDataBuffer->GenericReparseBuffer.DataBuffer; BackupFileEntry entry[1]; BackupFileEntry *foundEntry, *newEntry; PSIB_BACKUP_VOLUME_STRUCTURE backupVolumeStructure = (PSIB_BACKUP_VOLUME_STRUCTURE)sisBackupStructure; PVOID matchedContext = NULL; PWCHAR CSFileName[MAX_PATH]; NTSTATUS status; EnterCriticalSection(backupVolumeStructure->criticalSection); if (reparseDataSize != SIS_REPARSE_DATA_SIZE) { // // It's the wrong size to contain a SIS reparse buffer, so we don't // want to add any CS files based on it. // status = STATUS_INVALID_PARAMETER; goto Error; } if (IO_REPARSE_TAG_SIS != reparseDataBuffer->ReparseTag || sizeof(SI_REPARSE_BUFFER) != reparseDataBuffer->ReparseDataLength) { // // The size or tag is wrong. Ignore it. // status = STATUS_INVALID_PARAMETER; goto Error; } if ((SIS_REPARSE_BUFFER_FORMAT_VERSION != sisReparseBuffer->ReparsePointFormatVersion) && (4 != sisReparseBuffer->ReparsePointFormatVersion)) { // // We don't understand this format SIS reparse point. This is probably an // old dll version. // status = STATUS_INVALID_PARAMETER; goto Error; } // // The only thing we really care about is the CSIndex of the file. See if we've // already backed up a file with a matching CSIndex by looking in the tree. // entry->CSid = sisReparseBuffer->CSid; foundEntry = backupVolumeStructure->linkTree->findFirstLessThanOrEqualTo(entry); if ((NULL != foundEntry) && (*foundEntry == entry)) { // // We already returned the CS file that backs this link. Return the caller's // context for that link. // matchedContext = foundEntry->callerContext; goto BackupNoCSFiles; } // // This is the first time we've seen this particular CS file, so back it up. // newEntry = new BackupFileEntry; if (NULL == newEntry) { LeaveCriticalSection(backupVolumeStructure->criticalSection); return STATUS_INSUFFICIENT_RESOURCES; } newEntry->callerContext = thisFileContext; newEntry->CSid = sisReparseBuffer->CSid; if (!backupVolumeStructure->linkTree->insert(newEntry)) { delete newEntry; LeaveCriticalSection(backupVolumeStructure->criticalSection); return STATUS_INSUFFICIENT_RESOURCES; } if (NULL != matchingFileContext) { *matchingFileContext = NULL; } *countOfCommonStoreFilesToBackup = 1; *commonStoreFilesToBackup = (PWCHAR *)malloc(sizeof(PWCHAR) * *countOfCommonStoreFilesToBackup); if (NULL == *commonStoreFilesToBackup) { backupVolumeStructure->linkTree->remove(newEntry); delete newEntry; *countOfCommonStoreFilesToBackup = 0; LeaveCriticalSection(backupVolumeStructure->criticalSection); return STATUS_INSUFFICIENT_RESOURCES; } status = FilenameFromCSid( &sisReparseBuffer->CSid, backupVolumeStructure->volumeRoot, &(*commonStoreFilesToBackup)[0]); LeaveCriticalSection(backupVolumeStructure->criticalSection); return STATUS_SUCCESS; BackupNoCSFiles: if (NULL != matchingFileContext) { *matchingFileContext = matchedContext; } *countOfCommonStoreFilesToBackup = 0; *commonStoreFilesToBackup = NULL; LeaveCriticalSection(backupVolumeStructure->criticalSection); return STATUS_SUCCESS; Error: if (NULL != matchingFileContext) { *matchingFileContext = matchedContext; } *countOfCommonStoreFilesToBackup = 0; *commonStoreFilesToBackup = NULL; LeaveCriticalSection(backupVolumeStructure->criticalSection); return status; } NTSTATUS SisFreeBackupStructureI( IN PVOID sisBackupStructure) { PSIB_BACKUP_VOLUME_STRUCTURE backupVolumeStructure = (PSIB_BACKUP_VOLUME_STRUCTURE)sisBackupStructure; BackupFileEntry *entry; while (!backupVolumeStructure->linkTree->empty()) { entry = backupVolumeStructure->linkTree->findMin(); assert(NULL != entry); backupVolumeStructure->linkTree->remove(entry); delete entry; } free(backupVolumeStructure->volumeRoot); delete backupVolumeStructure->linkTree; DeleteCriticalSection(backupVolumeStructure->criticalSection); free(backupVolumeStructure); return STATUS_SUCCESS; } NTSTATUS SisCreateRestoreStructureI( IN PWCHAR volumeRoot, OUT PVOID *sisRestoreStructure, OUT PWCHAR *commonStoreRootPathname, OUT PULONG countOfCommonStoreFilesToRestore, OUT PWCHAR **commonStoreFilesToRestore) { PSIB_RESTORE_VOLUME_STRUCTURE restoreVolumeStructure; DWORD sectorsPerCluster, freeClusters, totalClusters; restoreVolumeStructure = (PSIB_RESTORE_VOLUME_STRUCTURE)malloc(sizeof(SIB_RESTORE_VOLUME_STRUCTURE)); if (NULL == restoreVolumeStructure) { return STATUS_INSUFFICIENT_RESOURCES; } restoreVolumeStructure->linkTree = new AVLTree; if (NULL == restoreVolumeStructure->linkTree) { free(restoreVolumeStructure); return STATUS_INSUFFICIENT_RESOURCES; } // // Allocate space for our private copy of the volume root name, being sure to leave space for // the terminating NULL. // restoreVolumeStructure->volumeRoot = (PWCHAR)malloc((wcslen(volumeRoot) + 1) * sizeof(WCHAR)); if (NULL == restoreVolumeStructure->volumeRoot) { delete restoreVolumeStructure->linkTree; free(restoreVolumeStructure); return STATUS_INSUFFICIENT_RESOURCES; } wcscpy(restoreVolumeStructure->volumeRoot, volumeRoot); // // Allocate space for the common store root pathname that we return, being sure // to leave room for the terminating NULL. // *commonStoreRootPathname = (PWCHAR) malloc(SIS_CSDIR_STRING_SIZE + (wcslen(volumeRoot) + 1) * sizeof(WCHAR)); if (NULL == *commonStoreRootPathname) { free(restoreVolumeStructure->volumeRoot); delete restoreVolumeStructure->linkTree; free(restoreVolumeStructure); return STATUS_INSUFFICIENT_RESOURCES; } wcscpy(*commonStoreRootPathname,volumeRoot); wcscat(*commonStoreRootPathname,SIS_CSDIR_STRING); InitializeCriticalSection(restoreVolumeStructure->criticalSection); *countOfCommonStoreFilesToRestore = 0; *commonStoreFilesToRestore = NULL; if (!GetDiskFreeSpaceW( volumeRoot, §orsPerCluster, &restoreVolumeStructure->VolumeSectorSize, &freeClusters, &totalClusters)) { // // The call failed. Just assume it's 512 bytes. // restoreVolumeStructure->VolumeSectorSize = 512; } restoreVolumeStructure->sector = (PSIS_BACKPOINTER)malloc(restoreVolumeStructure->VolumeSectorSize); if (NULL == restoreVolumeStructure->sector) { free(restoreVolumeStructure->volumeRoot); delete restoreVolumeStructure->linkTree; free(restoreVolumeStructure); free(*commonStoreRootPathname); *commonStoreRootPathname = NULL; return STATUS_INSUFFICIENT_RESOURCES; } restoreVolumeStructure->alignedSectorBuffer = (PVOID)malloc(restoreVolumeStructure->VolumeSectorSize * 2); if (NULL == restoreVolumeStructure->alignedSectorBuffer) { free(restoreVolumeStructure->sector); free(restoreVolumeStructure->volumeRoot); delete restoreVolumeStructure->linkTree; free(restoreVolumeStructure); free(*commonStoreRootPathname); *commonStoreRootPathname = NULL; return STATUS_INSUFFICIENT_RESOURCES; } restoreVolumeStructure->alignedSector = (PVOID)(( ((UINT_PTR)restoreVolumeStructure->alignedSectorBuffer + restoreVolumeStructure->VolumeSectorSize) / restoreVolumeStructure->VolumeSectorSize) * restoreVolumeStructure->VolumeSectorSize); ASSERT(restoreVolumeStructure->alignedSector >= restoreVolumeStructure->alignedSectorBuffer); ASSERT((PCHAR)restoreVolumeStructure->alignedSectorBuffer + restoreVolumeStructure->VolumeSectorSize >= (PCHAR)restoreVolumeStructure->alignedSector); memset(restoreVolumeStructure->alignedSector,255,restoreVolumeStructure->VolumeSectorSize); *sisRestoreStructure = restoreVolumeStructure; return STATUS_SUCCESS; } NTSTATUS SisFixValidDataLengthI( PSIB_RESTORE_VOLUME_STRUCTURE restoreVolumeStructure, IN HANDLE restoredFileHandle) { #define BIGGER_THAN_AN_ALLOCATION_REGION (128 * 1024) // should get this from somewhere else // // Figure out if we need to extend ValidDataLength. We need to do this // if the final range of the file is unallocated. // FILE_STANDARD_INFORMATION standardInfo[1]; FILE_END_OF_FILE_INFORMATION endOfFileInfo[1]; FILE_ALLOCATED_RANGE_BUFFER inArb[1]; const unsigned outArbSize = 10; FILE_ALLOCATED_RANGE_BUFFER outArb[outArbSize]; NTSTATUS status; IO_STATUS_BLOCK Iosb[1]; DWORD bytesReturned; LARGE_INTEGER rangeToZero; FILE_BASIC_INFORMATION basicInfo[1]; BOOLEAN basicInfoValid = FALSE; DWORD nBytesWritten; unsigned i; FILE_ZERO_DATA_INFORMATION zeroInfo[1]; LARGE_INTEGER WriteOffset; status = NtQueryInformationFile( restoredFileHandle, Iosb, standardInfo, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation); if (!NT_SUCCESS(status)) { #if DBG DbgPrint("SisFixValidDataLength: unable to query standard info on link file, 0x%x\n",status); #endif // DBG return status; } ASSERT(STATUS_PENDING != status); endOfFileInfo->EndOfFile = standardInfo->EndOfFile; if (standardInfo->EndOfFile.QuadPart > BIGGER_THAN_AN_ALLOCATION_REGION) { rangeToZero.QuadPart = inArb->FileOffset.QuadPart = standardInfo->EndOfFile.QuadPart - BIGGER_THAN_AN_ALLOCATION_REGION; rangeToZero.QuadPart -= rangeToZero.QuadPart % BIGGER_THAN_AN_ALLOCATION_REGION; // round it down. } else { rangeToZero.QuadPart = inArb->FileOffset.QuadPart = 0; } inArb->Length.QuadPart = MAXLONGLONG - inArb->FileOffset.QuadPart; if (!DeviceIoControl( restoredFileHandle, FSCTL_QUERY_ALLOCATED_RANGES, inArb, sizeof(FILE_ALLOCATED_RANGE_BUFFER), outArb, sizeof(FILE_ALLOCATED_RANGE_BUFFER) * outArbSize, &bytesReturned, NULL)) { // lap #if DBG DbgPrint("SisFixValidDataLength: unable to query allocated ranges on link file, %d\n",GetLastError()); #endif // DBG return STATUS_UNSUCCESSFUL; } ASSERT(bytesReturned / sizeof(FILE_ALLOCATED_RANGE_BUFFER) < outArbSize); // this relies on knowledge about the minimum allocated range size ASSERT(bytesReturned % sizeof(FILE_ALLOCATED_RANGE_BUFFER) == 0); if (bytesReturned > 0) { unsigned lastElement = bytesReturned/sizeof(FILE_ALLOCATED_RANGE_BUFFER) - 1; ASSERT(lastElement < outArbSize); rangeToZero.QuadPart = outArb[lastElement].FileOffset.QuadPart + outArb[lastElement].Length.QuadPart; } status = NtQueryInformationFile( restoredFileHandle, Iosb, basicInfo, sizeof(FILE_BASIC_INFORMATION), FileBasicInformation); if (NT_SUCCESS(status)) { ASSERT(STATUS_PENDING != status); // because we didn't open the file for overlapped. basicInfoValid = TRUE; } else { #if DBG DbgPrint("SisFixValidDataLength: unable to query basic info on link file, 0x%x\n",status); #endif // DBG } WriteOffset.QuadPart = ((standardInfo->EndOfFile.QuadPart + restoreVolumeStructure->VolumeSectorSize + BIGGER_THAN_AN_ALLOCATION_REGION) / restoreVolumeStructure->VolumeSectorSize) * restoreVolumeStructure->VolumeSectorSize; ASSERT(WriteOffset.QuadPart >= standardInfo->EndOfFile.QuadPart); ASSERT(standardInfo->EndOfFile.QuadPart + restoreVolumeStructure->VolumeSectorSize < WriteOffset.QuadPart); if ((WriteOffset.LowPart != SetFilePointer( restoredFileHandle, WriteOffset.LowPart, &WriteOffset.HighPart, FILE_BEGIN)) || (NO_ERROR != GetLastError())) { #if DBG DbgPrint("SisFixValidDataLength: unable to SetFilePointer, %d\n",GetLastError()); #endif // DBG return STATUS_UNSUCCESSFUL; } if (!WriteFile(restoredFileHandle, restoreVolumeStructure->alignedSectorBuffer, restoreVolumeStructure->VolumeSectorSize, // bytes to write &nBytesWritten, NULL)) { // overlapped #if DBG DbgPrint("SisFixValidDataLength: unable to append a byte to advance ValidDataLength, %d\n",GetLastError()); #endif // DBG } // // Truncate the file, erasing the sector we just wrote. // status = NtSetInformationFile( restoredFileHandle, Iosb, endOfFileInfo, sizeof(FILE_END_OF_FILE_INFORMATION), FileEndOfFileInformation); if (rangeToZero.QuadPart < standardInfo->EndOfFile.QuadPart) { // // Re-zero the end of the file in order to deallocate it. // zeroInfo->FileOffset = rangeToZero; zeroInfo->BeyondFinalZero.QuadPart = MAXLONGLONG; if (!DeviceIoControl( restoredFileHandle, FSCTL_SET_ZERO_DATA, zeroInfo, sizeof(FILE_ZERO_DATA_INFORMATION), NULL, // output buffer 0, // o.b. size &bytesReturned, NULL)) { // overlapped #if DBG DbgPrint("SisFixValidDataLength: unable to zero trailing portion of file, %d\n",GetLastError()); #endif // DBG } } #if DBG if (!NT_SUCCESS(status)) { DbgPrint("SisFixValidDataLength: unable to truncate file after extending it to advance ValidDataLength, 0x%x\n",status); } #endif // DBG // // Reset the dates on the file. // status = NtSetInformationFile( restoredFileHandle, Iosb, basicInfo, sizeof(FILE_BASIC_INFORMATION), FileBasicInformation); #if DBG if (!NT_SUCCESS(status)) { DbgPrint("SisFixValidDataLength: unable to reset times after extending file to advance ValidDataLength, 0x%x\n",status); } #endif // DBG return status; } NTSTATUS SisRestoredLinkI( IN PVOID sisRestoreStructure, IN PWCHAR restoredFileName, IN PVOID reparseData, IN ULONG reparseDataSize, OUT PULONG countOfCommonStoreFilesToRestore, OUT PWCHAR **commonStoreFilesToRestore) { PSIB_RESTORE_VOLUME_STRUCTURE restoreVolumeStructure = (PSIB_RESTORE_VOLUME_STRUCTURE)sisRestoreStructure; PREPARSE_DATA_BUFFER reparseDataBuffer = (PREPARSE_DATA_BUFFER)reparseData; PSI_REPARSE_BUFFER sisReparseBuffer = (PSI_REPARSE_BUFFER)reparseDataBuffer->GenericReparseBuffer.DataBuffer; RestoreFileEntry entry[1]; RestoreFileEntry *foundEntry, *newEntry; PWCHAR CSFileName = NULL; BOOLEAN foundCSFile; HANDLE fileHandle; BOOLEAN openFile = TRUE; NTSTATUS status; DWORD bytesReturned; DWORD fileAttributes; BOOLEAN readonlyAttributeCleared = FALSE; EnterCriticalSection(restoreVolumeStructure->criticalSection); if (NonSISEnabledVolume(restoreVolumeStructure)) { // // This isn't a SIS enabled volume, so tell the user that. // There's no NT status code corresponding to ERROR_VOLUME_NOT_SIS_ENABLED, // so we set the win32 code and return STATUS_UNSUCCESSFUL, which makes // the wrapper function not change the win32 error. // SetLastError(ERROR_VOLUME_NOT_SIS_ENABLED); status = STATUS_UNSUCCESSFUL; goto Error; } // // Do consistency checks on the reparse point to see if we can understand it. // if (reparseDataSize != SIS_REPARSE_DATA_SIZE) { // // It's the wrong size to contain a SIS reparse buffer, so we don't // want to restore any CS files based on it. // status = STATUS_INVALID_PARAMETER; goto Error; } if (IO_REPARSE_TAG_SIS != reparseDataBuffer->ReparseTag || sizeof(SI_REPARSE_BUFFER) != reparseDataBuffer->ReparseDataLength) { // // The size or tag is wrong. Ignore it. // status = STATUS_INVALID_PARAMETER; goto Error; } if ((SIS_REPARSE_BUFFER_FORMAT_VERSION != sisReparseBuffer->ReparsePointFormatVersion) && (4 != sisReparseBuffer->ReparsePointFormatVersion)) { // // We don't understand this format SIS reparse point. This is probably an // old dll version. // status = STATUS_INVALID_PARAMETER; goto Error; } // // The only thing we really care about is the CSid and checksum of the file. See if we've // already returned a file with a matching CSid by looking in the tree. // entry->CSid = sisReparseBuffer->CSid; foundEntry = restoreVolumeStructure->linkTree->findFirstLessThanOrEqualTo(entry); if ((NULL != foundEntry) && (*foundEntry == entry)) { // // We already returned the CS file that backs this link. Enter the name of this file // on the linked list for this CS file. // PendingRestoredFile *restoredFile = new PendingRestoredFile; if (NULL == restoredFile) { LeaveCriticalSection(restoreVolumeStructure->criticalSection); #if DBG DbgPrint("couldn't allocate restored file\n"); #endif // DBG return STATUS_INSUFFICIENT_RESOURCES; } restoredFile->fileName = (PWCHAR) malloc((wcslen(restoredFileName) + 1) * sizeof(WCHAR) ); if (NULL == restoredFile->fileName) { delete restoredFile; #if DBG DbgPrint("couldn't allocate restored file filename\n"); #endif // DBG LeaveCriticalSection(restoreVolumeStructure->criticalSection); return STATUS_INSUFFICIENT_RESOURCES; } wcscpy(restoredFile->fileName,restoredFileName); restoredFile->CSFileChecksum = sisReparseBuffer->CSChecksum; restoredFile->next = foundEntry->files; foundEntry->files = restoredFile; goto RestoreNoCSFiles; } // // This is the first time we've seen this particular CS file. See if it still // exists in the \SIS Common Store directory. // status = FilenameFromCSid(&sisReparseBuffer->CSid,restoreVolumeStructure->volumeRoot,&CSFileName); if (!NT_SUCCESS(status)) { if (NULL != CSFileName) { free(CSFileName); } LeaveCriticalSection(restoreVolumeStructure->criticalSection); return status; } fileHandle = CreateFileW( CSFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == fileHandle) { if (GetLastError() == ERROR_SHARING_VIOLATION) { // // The file exists, we just couldn't open it. // foundCSFile = TRUE; } else { foundCSFile = FALSE; } } else { foundCSFile = TRUE; CloseHandle(fileHandle); } if (foundCSFile) { // // We don't add it to the tree here, even though that might speed up things somewhat. // The reason is that someone could come along and delete all of the references to the // file (including the one that we just created) and then the backing file would go away. // If we'd entered it in the tree, and we try to restore a subsequent link to the file, // we'd not notice that the backing file was gone and would restore a dangling link. // openFile = FALSE; // There's no need to open this file, since it's a good link. HANDLE restoredFileHandle = CreateFileW( restoredFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_NO_BUFFERING|FILE_OPEN_REPARSE_POINT, NULL); if (INVALID_HANDLE_VALUE == restoredFileHandle) { fileAttributes = GetFileAttributesW(restoredFileName); if (fileAttributes & FILE_ATTRIBUTE_READONLY) { DWORD newFileAttributes = fileAttributes & ~FILE_ATTRIBUTE_READONLY; if (0 == newFileAttributes) { newFileAttributes = FILE_ATTRIBUTE_NORMAL; } if (!SetFileAttributesW(restoredFileName,newFileAttributes)) { #if DBG DbgPrint("sisbkup: SisRestoredLinkI: unable to reset read only attribute on link, %d\n",GetLastError()); #endif DBG } else { readonlyAttributeCleared = TRUE; } // // Now that we've (tried to) cleared the read only attribute, re-try the file open. // restoredFileHandle = CreateFileW( restoredFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_NO_BUFFERING|FILE_OPEN_REPARSE_POINT, NULL); } } if (INVALID_HANDLE_VALUE != restoredFileHandle) { } else { #if DBG DbgPrint("SisRestoredLinkI: Unable to open link file in order to fix ValidDataLength, %d\n",::GetLastError()); #endif // DBG status = STATUS_UNSUCCESSFUL; // This will leave the win32 error code undisturbed goto Error; } CHAR reparseBuffer[SIS_REPARSE_DATA_SIZE]; if (!DeviceIoControl( restoredFileHandle, FSCTL_GET_REPARSE_POINT, 0, NULL, reparseBuffer, SIS_REPARSE_DATA_SIZE, &bytesReturned, NULL)) { #if DBG DbgPrint("SisRestoredLinkI: Unable to get reparse point, %d\n",::GetLastError()); #endif // DBG status = STATUS_UNSUCCESSFUL; // This will leave the win32 error code undisturbed goto Error; } status = SisFixValidDataLengthI(restoreVolumeStructure,restoredFileHandle); if (!NT_SUCCESS(status)) { #if DBG DbgPrint("SisRestoredLink: unable to fix up valid data length, 0x%x, %d\n",status,::GetLastError()); #endif // DBG CloseHandle(restoredFileHandle); goto Error; } // // Reset the reparse point, which has been destroyed by the last operation. // if (!DeviceIoControl( restoredFileHandle, FSCTL_SET_REPARSE_POINT, reparseData, reparseDataSize, NULL, 0, &bytesReturned, NULL)) { #if DBG DbgPrint("SisRestoredLink: unable to reset reparse point, %d\n",::GetLastError()); #endif // DBG CloseHandle(restoredFileHandle); status = STATUS_UNSUCCESSFUL; // This will leave the win32 error code undisturbed goto Error; } CloseHandle(restoredFileHandle); if (readonlyAttributeCleared) { SetFileAttributesW(restoredFileName,fileAttributes); readonlyAttributeCleared = FALSE; } goto RestoreNoCSFiles; } // // It's not already in the common store directory. Enter it in the tree and return it to // the user. // newEntry = new RestoreFileEntry; if (NULL == newEntry) { LeaveCriticalSection(restoreVolumeStructure->criticalSection); return STATUS_INSUFFICIENT_RESOURCES; } newEntry->CSid = sisReparseBuffer->CSid; newEntry->files = new PendingRestoredFile; if (NULL == newEntry->files) { LeaveCriticalSection(restoreVolumeStructure->criticalSection); delete newEntry; return STATUS_INSUFFICIENT_RESOURCES; } newEntry->files->next = NULL; newEntry->files->fileName = (PWCHAR) malloc((wcslen(restoredFileName) + 1) * sizeof(WCHAR)); newEntry->files->CSFileChecksum = sisReparseBuffer->CSChecksum; if (NULL == newEntry->files->fileName) { LeaveCriticalSection(restoreVolumeStructure->criticalSection); delete newEntry->files; delete newEntry; return STATUS_INSUFFICIENT_RESOURCES; } wcscpy(newEntry->files->fileName,restoredFileName); if (!restoreVolumeStructure->linkTree->insert(newEntry)) { *countOfCommonStoreFilesToRestore = 0; LeaveCriticalSection(restoreVolumeStructure->criticalSection); delete newEntry->files; delete newEntry; return STATUS_INSUFFICIENT_RESOURCES; } *countOfCommonStoreFilesToRestore = 1; *commonStoreFilesToRestore = (PWCHAR *)malloc(sizeof(PWCHAR) * *countOfCommonStoreFilesToRestore); if (NULL == *commonStoreFilesToRestore) { restoreVolumeStructure->linkTree->remove(newEntry); *countOfCommonStoreFilesToRestore = 0; LeaveCriticalSection(restoreVolumeStructure->criticalSection); delete newEntry->files; delete newEntry; return STATUS_INSUFFICIENT_RESOURCES; } status = FilenameFromCSid( &sisReparseBuffer->CSid, restoreVolumeStructure->volumeRoot, &(*commonStoreFilesToRestore)[0]); if (!NT_SUCCESS(status)) { restoreVolumeStructure->linkTree->remove(newEntry); *countOfCommonStoreFilesToRestore = 0; LeaveCriticalSection(restoreVolumeStructure->criticalSection); free(*commonStoreFilesToRestore); delete newEntry->files; delete newEntry; return status; } if (openFile) { TryOpeningFile(restoredFileName); } LeaveCriticalSection(restoreVolumeStructure->criticalSection); return STATUS_SUCCESS; RestoreNoCSFiles: *countOfCommonStoreFilesToRestore = 0; *commonStoreFilesToRestore = NULL; if (openFile) { TryOpeningFile(restoredFileName); } LeaveCriticalSection(restoreVolumeStructure->criticalSection); return STATUS_SUCCESS; Error: *countOfCommonStoreFilesToRestore = 0; *commonStoreFilesToRestore = NULL; if (readonlyAttributeCleared) { SetFileAttributesW(restoredFileName,fileAttributes); } if (openFile) { TryOpeningFile(restoredFileName); } LeaveCriticalSection(restoreVolumeStructure->criticalSection); return status; } NTSTATUS SisRestoredCommonStoreFileI( IN PVOID sisRestoreStructure, IN PWCHAR commonStoreFileName) { PSIB_RESTORE_VOLUME_STRUCTURE restoreVolumeStructure = (PSIB_RESTORE_VOLUME_STRUCTURE)sisRestoreStructure; HANDLE fileHandle = INVALID_HANDLE_VALUE; NTSTATUS status; DWORD bytesRead, bytesWritten; LONGLONG checksum; CSID CSid; RestoreFileEntry entry[1]; RestoreFileEntry *foundEntry, *newEntry; PWCHAR BPStreamName = NULL; status = CSidFromFilename(commonStoreFileName,&CSid); if (!NT_SUCCESS(status)) { // // It was a bogus filename. Punt. // return status; } BPStreamName = (PWCHAR) malloc((wcslen(commonStoreFileName) + 1) * sizeof(WCHAR) + BACKPOINTER_STREAM_NAME_SIZE); if (NULL == BPStreamName) { return STATUS_INSUFFICIENT_RESOURCES; } wcscpy(BPStreamName, commonStoreFileName); wcscat(BPStreamName, BACKPOINTER_STREAM_NAME); // // We just need to reinitialize the backpointer stream for this file so that it looks like // it has no references. // EnterCriticalSection(restoreVolumeStructure->criticalSection); if (NonSISEnabledVolume(restoreVolumeStructure)) { // // This isn't a SIS enabled volume, so tell the user that. // There's no NT status code corresponding to ERROR_VOLUME_NOT_SIS_ENABLED, // so we set the win32 code and return STATUS_UNSUCCESSFUL, which makes // the wrapper function not change the win32 error. // SetLastError(ERROR_VOLUME_NOT_SIS_ENABLED); status = STATUS_UNSUCCESSFUL; goto Error; } // // Now open the file. // fileHandle = CreateFileW( BPStreamName, GENERIC_READ | GENERIC_WRITE, 0, // exclusive NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL); free(BPStreamName); BPStreamName = NULL; if (INVALID_HANDLE_VALUE == fileHandle) { status = STATUS_UNSUCCESSFUL; // This will cause the C wrapper to not call SetLastError #if DBG DbgPrint("SisRestoredCommonStoreFile: unable to open common store file, %d\n",GetLastError()); #endif // DBG goto Error; } // // Read in the first sector. // if (!ReadFile( fileHandle, restoreVolumeStructure->sector, restoreVolumeStructure->VolumeSectorSize, &bytesRead, NULL)) { status = STATUS_UNSUCCESSFUL; // This will cause the C wrapper to not call SetLastError #if DBG DbgPrint("SisRestoredCommonStoreFile: Unable to read in first BP sector, %d\n",GetLastError()); #endif // DBG goto Error; } if (bytesRead < sizeof(SIS_BACKPOINTER_STREAM_HEADER)) { status = STATUS_UNSUCCESSFUL; // This will cause the C wrapper to not call SetLastError goto Error; } #define Header ((PSIS_BACKPOINTER_STREAM_HEADER)restoreVolumeStructure->sector) if ((BACKPOINTER_STREAM_FORMAT_VERSION != Header->FormatVersion) || (BACKPOINTER_MAGIC != Header->Magic)) { #undef Header #if DBG DbgPrint("SisRectoredCommonStoreFile: restored CS file has bogus header format version/Magic\n"); #endif // DBG } else { // // Fill in the backpointer portion of the sector with // null entries. // for (unsigned i = SIS_BACKPOINTER_RESERVED_ENTRIES; i < (restoreVolumeStructure->VolumeSectorSize / sizeof(SIS_BACKPOINTER)); i++) { restoreVolumeStructure->sector[i].LinkFileIndex.QuadPart = MAXLONGLONG; restoreVolumeStructure->sector[i].LinkFileNtfsId.QuadPart = MAXLONGLONG; } // // Write out the new sector. // SetFilePointer(fileHandle,0,NULL,FILE_BEGIN); if (!WriteFile( fileHandle, restoreVolumeStructure->sector, restoreVolumeStructure->VolumeSectorSize, &bytesWritten, NULL)) { #if DBG DbgPrint("SisRestoredCommonStoreFile: write failed %d\n",GetLastError()); #endif // DBG } } // // Make the stream be exactly one sector long. // SetFilePointer(fileHandle,restoreVolumeStructure->VolumeSectorSize,NULL,FILE_BEGIN); SetEndOfFile(fileHandle); CloseHandle(fileHandle); fileHandle = INVALID_HANDLE_VALUE; // // Look up in the tree and find the files that we restored to this link. // Open them and rewrite their reparse points. // entry->CSid = CSid; foundEntry = restoreVolumeStructure->linkTree->findFirstLessThanOrEqualTo(entry); if ((NULL != foundEntry) && (*foundEntry == entry)) { // // We've got a match. Cruise the list and set the reparse points on all of the // files. // while (NULL != foundEntry->files) { HANDLE restoredFileHandle; PendingRestoredFile *thisFile = foundEntry->files; DWORD bytesReturned; DWORD fileAttributes; BOOLEAN readOnlyAttributeCleared = FALSE; restoredFileHandle = CreateFileW( thisFile->fileName, GENERIC_READ | GENERIC_WRITE, 0, // exclusive NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_NO_BUFFERING, NULL); if (INVALID_HANDLE_VALUE == restoredFileHandle) { // // Check the read only file attribute, and reset it if necessary. // fileAttributes = GetFileAttributesW(thisFile->fileName); if (fileAttributes & FILE_ATTRIBUTE_READONLY) { DWORD newFileAttributes = fileAttributes & ~FILE_ATTRIBUTE_READONLY; if (0 == newFileAttributes) { newFileAttributes = FILE_ATTRIBUTE_NORMAL; } if (!SetFileAttributesW(thisFile->fileName,newFileAttributes)) { #if DBG DbgPrint("sisbkup: unable to clear read only attribute on file %ws\n",thisFile->fileName); #endif // DBG } readOnlyAttributeCleared = TRUE; restoredFileHandle = CreateFileW( thisFile->fileName, GENERIC_READ | GENERIC_WRITE, 0, // exclusive NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_NO_BUFFERING, NULL); } } if (INVALID_HANDLE_VALUE != restoredFileHandle) { SisFixValidDataLengthI(restoreVolumeStructure, restoredFileHandle); // // Rewrite the reparse point. // CHAR reparseBuffer[SIS_REPARSE_DATA_SIZE]; PSI_REPARSE_BUFFER sisReparseBuffer; #define reparseData ((PREPARSE_DATA_BUFFER)reparseBuffer) reparseData->ReparseTag = IO_REPARSE_TAG_SIS; reparseData->Reserved = 0xb010; // ?? reparseData->ReparseDataLength = sizeof(SI_REPARSE_BUFFER); sisReparseBuffer = (PSI_REPARSE_BUFFER)reparseData->GenericReparseBuffer.DataBuffer; sisReparseBuffer->ReparsePointFormatVersion = SIS_REPARSE_BUFFER_FORMAT_VERSION; sisReparseBuffer->Reserved = 0xb111b010; sisReparseBuffer->CSid = CSid; sisReparseBuffer->LinkIndex.QuadPart = 0; // This just gets reset by the filter driver sisReparseBuffer->LinkFileNtfsId.QuadPart = 0; // This just gets reset by the filter driver sisReparseBuffer->CSFileNtfsId.QuadPart = 0; // This just gets reset by the filter driver // // Use the CS file checksum that was read from the reparse point on the backup // tape. We need this for security reasons, because otherwise a bogus backed up // link could suddenly become valid. // sisReparseBuffer->CSChecksum = thisFile->CSFileChecksum; // // Compute the checksum. // sisReparseBuffer->Checksum.QuadPart = 0; SipComputeChecksum( sisReparseBuffer, sizeof(SI_REPARSE_BUFFER) - sizeof sisReparseBuffer->Checksum, &sisReparseBuffer->Checksum.QuadPart); // // Set the reparse point. // if (!DeviceIoControl( restoredFileHandle, FSCTL_SET_REPARSE_POINT, reparseBuffer, FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) + reparseData->ReparseDataLength, NULL, 0, &bytesReturned, NULL)) { #if DBG DbgPrint("sisbackup: SisRestoredCommonStoreFile: set reparse point failed %d\n",GetLastError()); #endif // DBG } CloseHandle(restoredFileHandle); #undef reparseData } else { #if DBG DbgPrint("sisbackup: unable to open link file for file %ws, %d\n",thisFile->fileName,GetLastError()); #endif // DBG } if (readOnlyAttributeCleared) { if (!SetFileAttributesW(thisFile->fileName,fileAttributes)) { #if DBG DbgPrint("sisbackup: unable to reset read only attribute on %ws\n",thisFile->fileName); #endif // DBG } } foundEntry->files = thisFile->next; free(thisFile->fileName); delete thisFile; } restoreVolumeStructure->linkTree->remove(foundEntry); delete foundEntry; } else { #if DBG DbgPrint("restored common store file: didn't find tree match\n"); #endif // DBG } LeaveCriticalSection(restoreVolumeStructure->criticalSection); return STATUS_SUCCESS; Error: if (INVALID_HANDLE_VALUE != fileHandle) { CloseHandle(fileHandle); } LeaveCriticalSection(restoreVolumeStructure->criticalSection); return status; } NTSTATUS SisFreeRestoreStructureI( IN PVOID sisRestoreStructure) { PSIB_RESTORE_VOLUME_STRUCTURE restoreVolumeStructure = (PSIB_RESTORE_VOLUME_STRUCTURE)sisRestoreStructure; RestoreFileEntry *entry; // // Cruise the link tree and clean up any remaining file entries. // while (NULL != (entry = restoreVolumeStructure->linkTree->findMin())) { while (NULL != entry->files) { PendingRestoredFile *thisFile = entry->files; entry->files = thisFile->next; free(thisFile->fileName); delete thisFile; } restoreVolumeStructure->linkTree->remove(entry); } DeleteCriticalSection(restoreVolumeStructure->criticalSection); free(restoreVolumeStructure->sector); free(restoreVolumeStructure->alignedSectorBuffer); free(restoreVolumeStructure->volumeRoot); delete restoreVolumeStructure->linkTree; free(restoreVolumeStructure); return STATUS_SUCCESS; } extern "C" { BOOL __stdcall SisCreateBackupStructure( IN PWCHAR volumeRoot, OUT PVOID *sisBackupStructure, OUT PWCHAR *commonStoreRootPathname, OUT PULONG countOfCommonStoreFilesToBackup, OUT PWCHAR **commonStoreFilesToBackup) { NTSTATUS status; status = SisCreateBackupStructureI( volumeRoot, sisBackupStructure, commonStoreRootPathname, countOfCommonStoreFilesToBackup, commonStoreFilesToBackup); if (STATUS_UNSUCCESSFUL != status) { SetLastError(RtlNtStatusToDosError(status)); } return NT_SUCCESS(status); } BOOL __stdcall SisCSFilesToBackupForLink( IN PVOID sisBackupStructure, IN PVOID reparseData, IN ULONG reparseDataSize, IN PVOID thisFileContext OPTIONAL, OUT PVOID *matchingFileContext OPTIONAL, OUT PULONG countOfCommonStoreFilesToBackup, OUT PWCHAR **commonStoreFilesToBackup) { NTSTATUS status; status = SisCSFilesToBackupForLinkI( sisBackupStructure, reparseData, reparseDataSize, thisFileContext, matchingFileContext, countOfCommonStoreFilesToBackup, commonStoreFilesToBackup); if (STATUS_UNSUCCESSFUL != status) { SetLastError(RtlNtStatusToDosError(status)); } return NT_SUCCESS(status); } BOOL __stdcall SisFreeBackupStructure( IN PVOID sisBackupStructure) { NTSTATUS status; status = SisFreeBackupStructureI( sisBackupStructure); if (STATUS_UNSUCCESSFUL != status) { SetLastError(RtlNtStatusToDosError(status)); } return NT_SUCCESS(status); } BOOL __stdcall SisCreateRestoreStructure( IN PWCHAR volumeRoot, OUT PVOID *sisRestoreStructure, OUT PWCHAR *commonStoreRootPathname, OUT PULONG countOfCommonStoreFilesToRestore, OUT PWCHAR **commonStoreFilesToRestore) { NTSTATUS status; status = SisCreateRestoreStructureI( volumeRoot, sisRestoreStructure, commonStoreRootPathname, countOfCommonStoreFilesToRestore, commonStoreFilesToRestore); if (STATUS_UNSUCCESSFUL != status) { SetLastError(RtlNtStatusToDosError(status)); } return NT_SUCCESS(status); } BOOL __stdcall SisRestoredLink( IN PVOID sisRestoreStructure, IN PWCHAR restoredFileName, IN PVOID reparseData, IN ULONG reparseDataSize, OUT PULONG countOfCommonStoreFilesToRestore, OUT PWCHAR **commonStoreFilesToRestore) { NTSTATUS status; status = SisRestoredLinkI( sisRestoreStructure, restoredFileName, reparseData, reparseDataSize, countOfCommonStoreFilesToRestore, commonStoreFilesToRestore); if (STATUS_UNSUCCESSFUL != status) { SetLastError(RtlNtStatusToDosError(status)); } return NT_SUCCESS(status); } BOOL __stdcall SisRestoredCommonStoreFile( IN PVOID sisRestoreStructure, IN PWCHAR commonStoreFileName) { NTSTATUS status; status = SisRestoredCommonStoreFileI( sisRestoreStructure, commonStoreFileName); if (STATUS_UNSUCCESSFUL != status) { SetLastError(RtlNtStatusToDosError(status)); } return NT_SUCCESS(status); } BOOL __stdcall SisFreeRestoreStructure( IN PVOID sisRestoreStructure) { NTSTATUS status; status = SisFreeRestoreStructureI( sisRestoreStructure); if (STATUS_UNSUCCESSFUL != status) { SetLastError(RtlNtStatusToDosError(status)); } return NT_SUCCESS(status); } VOID __stdcall SisFreeAllocatedMemory( IN PVOID allocatedSpace) { if (NULL != allocatedSpace) { free(allocatedSpace); } } BOOL WINAPI DLLEntryPoint(HANDLE hDLL, DWORD dwReason, LPVOID lpReserved) { return( TRUE ); } }