/*++ Copyright (c) 2000 Microsoft Corporation Module Name: reparse.c Abstract: This file contains code for commands that affect reparse points. Author: Wesley Witt [wesw] 1-March-2000 Revision History: --*/ #include INT ReparseHelp( IN INT argc, IN PWSTR argv[] ) { DisplayMsg( MSG_USAGE_REPARSEPOINT ); return EXIT_CODE_SUCCESS; } // // This is the definition of the DATA portion of the SIS reparse buffer. // #define SIS_REPARSE_BUFFER_FORMAT_VERSION 5 typedef struct _SIS_REPARSE_BUFFER { ULONG ReparsePointFormatVersion; ULONG Reserved; // // The id of the common store file. // GUID CSid; // // The index of this link file. // LARGE_INTEGER LinkIndex; // // The file ID of the link file. // LARGE_INTEGER LinkFileNtfsId; // // The file ID of the common store file. // LARGE_INTEGER CSFileNtfsId; // // A "131 hash" checksum of the contents of the // common store file. // LARGE_INTEGER CSChecksum; // // A "131 hash" checksum of this structure. // N.B. Must be last. // LARGE_INTEGER Checksum; } SIS_REPARSE_BUFFER, *PSIS_REPARSE_BUFFER; INT DisplaySISReparsePointData( PREPARSE_DATA_BUFFER ReparseData ) /*++ Routine Description: This routine displays the SIS reparse data Arguments: Return Value: None --*/ { PSIS_REPARSE_BUFFER sisRp; WCHAR csID[40]; // // Point to the SIS unique portion of the buffer // sisRp = (PSIS_REPARSE_BUFFER)&ReparseData->GenericReparseBuffer.DataBuffer; DisplayMsg( MSG_SIS_REPARSE_INFO, sisRp->ReparsePointFormatVersion, Guid2Str( &sisRp->CSid, csID, sizeof(csID) ), sisRp->LinkIndex.HighPart, sisRp->LinkIndex.LowPart, sisRp->LinkFileNtfsId.HighPart, sisRp->LinkFileNtfsId.LowPart, sisRp->CSFileNtfsId.HighPart, sisRp->CSFileNtfsId.LowPart, sisRp->CSChecksum.HighPart, sisRp->CSChecksum.LowPart, sisRp->Checksum.HighPart, sisRp->Checksum.LowPart); return EXIT_CODE_SUCCESS; } // // Placeholder data - all versions unioned together // #define RP_RESV_SIZE 52 typedef struct _RP_PRIVATE_DATA { CHAR reserved[RP_RESV_SIZE]; // Must be 0 ULONG bitFlags; // bitflags indicating status of the segment LARGE_INTEGER migrationTime; // When migration occurred GUID hsmId; GUID bagId; LARGE_INTEGER fileStart; LARGE_INTEGER fileSize; LARGE_INTEGER dataStart; LARGE_INTEGER dataSize; LARGE_INTEGER fileVersionId; LARGE_INTEGER verificationData; ULONG verificationType; ULONG recallCount; LARGE_INTEGER recallTime; LARGE_INTEGER dataStreamStart; LARGE_INTEGER dataStreamSize; ULONG dataStream; ULONG dataStreamCRCType; LARGE_INTEGER dataStreamCRC; } RP_PRIVATE_DATA, *PRP_PRIVATE_DATA; typedef struct _RP_DATA { GUID vendorId; // Unique HSM vendor ID -- This is first to match REPARSE_GUID_DATA_BUFFER ULONG qualifier; // Used to checksum the data ULONG version; // Version of the structure ULONG globalBitFlags; // bitflags indicating status of the file ULONG numPrivateData; // number of private data entries GUID fileIdentifier; // Unique file ID RP_PRIVATE_DATA data; // Vendor specific data } RP_DATA, *PRP_DATA; INT DisplayRISReparsePointData( PREPARSE_DATA_BUFFER ReparseData ) /*++ Routine Description: This routine displays the SIS reparse data Arguments: Return Value: None --*/ { PRP_DATA risRp; WCHAR vendorID[40]; WCHAR fileID[40]; WCHAR hsmID[40]; WCHAR bagID[40]; WCHAR migrationTime[32]; WCHAR recallTime[32]; // // Point to the SIS unique portion of the buffer // risRp = (PRP_DATA)&ReparseData->GenericReparseBuffer.DataBuffer; DisplayMsg( MSG_RIS_REPARSE_INFO, Guid2Str( &risRp->vendorId, vendorID, sizeof(vendorID) ), risRp->qualifier, risRp->version, risRp->globalBitFlags, risRp->numPrivateData, Guid2Str( &risRp->fileIdentifier, fileID, sizeof(fileID) ), risRp->data.bitFlags, FileTime2String( &risRp->data.migrationTime, migrationTime, sizeof(migrationTime) ), Guid2Str( &risRp->data.hsmId, hsmID, sizeof(hsmID) ), Guid2Str( &risRp->data.bagId, bagID, sizeof(bagID) ), risRp->data.fileStart.HighPart, risRp->data.fileStart.LowPart, risRp->data.fileSize.HighPart, risRp->data.fileSize.LowPart, risRp->data.dataStart.HighPart, risRp->data.dataStart.LowPart, risRp->data.dataSize.HighPart, risRp->data.dataSize.LowPart, risRp->data.fileVersionId.HighPart, risRp->data.fileVersionId.LowPart, risRp->data.verificationData.HighPart, risRp->data.verificationData.LowPart, risRp->data.verificationType, risRp->data.recallCount, FileTime2String( &risRp->data.recallTime, recallTime, sizeof(recallTime) ), risRp->data.dataStreamStart.HighPart, risRp->data.dataStreamStart.LowPart, risRp->data.dataStreamSize.HighPart, risRp->data.dataStreamSize.LowPart, risRp->data.dataStream, risRp->data.dataStreamCRCType, risRp->data.dataStreamCRC.HighPart, risRp->data.dataStreamCRC.LowPart ); return EXIT_CODE_SUCCESS; } INT DisplayMountPointData( PREPARSE_DATA_BUFFER ReparseData ) /*++ Routine Description: This routine displays the SIS reparse data Arguments: Return Value: None --*/ { // // Display offset and length values // DisplayMsg( MSG_MOUNT_POINT_INFO, ReparseData->MountPointReparseBuffer.SubstituteNameOffset, ReparseData->MountPointReparseBuffer.SubstituteNameLength, ReparseData->MountPointReparseBuffer.PrintNameOffset, ReparseData->MountPointReparseBuffer.PrintNameLength); // // Display Name Substitue name if there is one // if (ReparseData->MountPointReparseBuffer.SubstituteNameLength > 0) { DisplayMsg( MSG_MOUNT_POINT_SUBSTITUE_NAME, (ReparseData->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR)), &ReparseData->MountPointReparseBuffer.PathBuffer[ReparseData->MountPointReparseBuffer.SubstituteNameOffset/sizeof(WCHAR)]); } // // Display PRINT NAME if there is one // if (ReparseData->MountPointReparseBuffer.PrintNameLength > 0) { DisplayMsg( MSG_MOUNT_POINT_PRINT_NAME, (ReparseData->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR)), &ReparseData->MountPointReparseBuffer.PathBuffer[ReparseData->MountPointReparseBuffer.PrintNameOffset/sizeof(WCHAR)]); } return EXIT_CODE_SUCCESS; } VOID DisplayGenericReparseData( UCHAR *ReparseData, DWORD DataSize ) /*++ Routine Description: This routine displays the SIS reparse data Arguments: Return Value: None --*/ { ULONG i, j; WCHAR Buf[17]; WCHAR CharBuf[3 + 1]; OutputMessage( L"\r\n" ); DisplayMsg( MSG_REPARSE_DATA_LENGTH, DataSize ); if (DataSize > 0) { DisplayMsg( MSG_GETREPARSE_DATA ); for (i = 0; i < DataSize; i += 16 ) { swprintf( Buf, L"%04x: ", i ); OutputMessage( Buf ); for (j = 0; j < 16 && j + i < DataSize; j++) { UCHAR c = ReparseData[ i + j ]; if (c >= 0x20 && c <= 0x7F) { Buf[j] = c; } else { Buf[j] = L'.'; } swprintf( CharBuf, L" %02x", c ); OutputMessage( CharBuf ); if (j == 7) OutputMessage( L" " ); } Buf[j] = L'\0'; for ( ; j < 16; j++ ) { OutputMessage( L" " ); if (j == 7) OutputMessage( L" " ); } OutputMessage( L" " ); OutputMessage( Buf ); OutputMessage( L"\r\n" ); } } } INT GetReparsePoint( IN INT argc, IN PWSTR argv[] ) /*++ Routine Description: This routine gets the reparse point for the file specified. Arguments: argc - The argument count. argv - Array of Strings of the form : ' fscutl getrp '. Return Value: None --*/ { PWSTR Filename = NULL; HANDLE FileHandle = INVALID_HANDLE_VALUE; PREPARSE_DATA_BUFFER lpOutBuffer = NULL; BOOL Status; HRESULT Result; DWORD BytesReturned; ULONG ulMask; WCHAR Buffer[256]; LPWSTR GuidStr; INT ExitCode = EXIT_CODE_SUCCESS; #define MAX_REPARSE_DATA 0x4000 try { if (argc != 1) { DisplayMsg( MSG_USAGE_GETREPARSE ); if (argc != 0) { ExitCode = EXIT_CODE_FAILURE; } leave; } Filename = GetFullPath( argv[0] ); if (!Filename) { DisplayError(); ExitCode = EXIT_CODE_FAILURE; leave; } if (!IsVolumeLocalNTFS( Filename[0] )) { DisplayMsg( MSG_NTFS_REQUIRED ); ExitCode = EXIT_CODE_FAILURE; leave; } lpOutBuffer = (PREPARSE_DATA_BUFFER) malloc ( MAX_REPARSE_DATA ); if (lpOutBuffer == NULL) { DisplayErrorMsg( ERROR_NOT_ENOUGH_MEMORY ); ExitCode = EXIT_CODE_FAILURE; leave; } FileHandle = CreateFile( Filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL ); if (FileHandle == INVALID_HANDLE_VALUE) { DisplayError(); ExitCode = EXIT_CODE_FAILURE; leave; } Status = DeviceIoControl( FileHandle, FSCTL_GET_REPARSE_POINT, NULL, 0, (LPVOID) lpOutBuffer, MAX_REPARSE_DATA, &BytesReturned, (LPOVERLAPPED)NULL ); if (!Status) { DisplayError(); ExitCode = EXIT_CODE_FAILURE; leave; } DisplayMsg( MSG_GETREPARSE_TAGVAL, lpOutBuffer->ReparseTag ); if (IsReparseTagMicrosoft( lpOutBuffer->ReparseTag )) { DisplayMsg( MSG_TAG_MICROSOFT ); } if (IsReparseTagNameSurrogate( lpOutBuffer->ReparseTag )) { DisplayMsg( MSG_TAG_NAME_SURROGATE ); } if (lpOutBuffer->ReparseTag == IO_REPARSE_TAG_SYMBOLIC_LINK) { DisplayMsg( MSG_TAG_SYMBOLIC_LINK ); } if (lpOutBuffer->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) { DisplayMsg( MSG_TAG_MOUNT_POINT ); ExitCode = DisplayMountPointData( lpOutBuffer ); } if (lpOutBuffer->ReparseTag == IO_REPARSE_TAG_HSM) { DisplayMsg( MSG_TAG_HSM ); ExitCode = DisplayRISReparsePointData( lpOutBuffer ); } if (lpOutBuffer->ReparseTag == IO_REPARSE_TAG_SIS) { DisplayMsg( MSG_TAG_SIS ); ExitCode = DisplaySISReparsePointData( lpOutBuffer ); } if (lpOutBuffer->ReparseTag == IO_REPARSE_TAG_FILTER_MANAGER) { DisplayMsg( MSG_TAG_FILTER_MANAGER ); } if (lpOutBuffer->ReparseTag == IO_REPARSE_TAG_DFS) { DisplayMsg( MSG_TAG_DFS ); } // // This is an unknown tag, display the data // if (IsReparseTagMicrosoft( lpOutBuffer->ReparseTag )) { // // Display Microsoft tag data, note that these do NOT use // the GUID form of the buffer // DisplayGenericReparseData( lpOutBuffer->GenericReparseBuffer.DataBuffer, lpOutBuffer->ReparseDataLength ); } else { // // Display NON-Microsoft tag data, note that these DO use // the GUID form of the buffer // PREPARSE_GUID_DATA_BUFFER nmReparseData = (PREPARSE_GUID_DATA_BUFFER)lpOutBuffer; Result = StringFromIID( &nmReparseData->ReparseGuid, &GuidStr ); if (Result != S_OK) { DisplayErrorMsg( Result ); ExitCode = EXIT_CODE_FAILURE; leave; } DisplayMsg( MSG_GETREPARSE_GUID, GuidStr ); DisplayGenericReparseData( nmReparseData->GenericReparseBuffer.DataBuffer, nmReparseData->ReparseDataLength ); CoTaskMemFree(GuidStr); } } finally { if (FileHandle != INVALID_HANDLE_VALUE) { CloseHandle( FileHandle ); } free( lpOutBuffer ); free( Filename ); } return ExitCode; } INT DeleteReparsePoint( IN INT argc, IN PWSTR argv[] ) /*++ Routine Description: This routine deletes the reparse point associated with the file specified. Arguments: argc - The argument count. argv - Array of Strings of the form : ' fscutl delrp '. Return Value: None --*/ { BOOL Status; PWSTR Filename = NULL; HANDLE FileHandle = INVALID_HANDLE_VALUE; PREPARSE_GUID_DATA_BUFFER lpInOutBuffer = NULL; DWORD nInOutBufferSize; DWORD BytesReturned; INT ExitCode = EXIT_CODE_SUCCESS; try { if (argc != 1) { DisplayMsg( MSG_DELETE_REPARSE ); if (argc != 0) { ExitCode = EXIT_CODE_FAILURE; } leave; } Filename = GetFullPath( argv[0] ); if (!Filename) { DisplayError(); ExitCode = EXIT_CODE_FAILURE; leave; } if (!IsVolumeLocalNTFS( Filename[0] )) { DisplayMsg( MSG_NTFS_REQUIRED ); ExitCode = EXIT_CODE_FAILURE; leave; } nInOutBufferSize = REPARSE_GUID_DATA_BUFFER_HEADER_SIZE + MAX_REPARSE_DATA; lpInOutBuffer = (PREPARSE_GUID_DATA_BUFFER) malloc ( nInOutBufferSize ); if (lpInOutBuffer == NULL) { DisplayErrorMsg( ERROR_NOT_ENOUGH_MEMORY ); ExitCode = EXIT_CODE_FAILURE; leave; } FileHandle = CreateFile( Filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL ); if (FileHandle == INVALID_HANDLE_VALUE) { DisplayError(); ExitCode = EXIT_CODE_FAILURE; leave; } Status = DeviceIoControl( FileHandle, FSCTL_GET_REPARSE_POINT, NULL, 0, (LPVOID) lpInOutBuffer, nInOutBufferSize, &BytesReturned, (LPOVERLAPPED)NULL ); if (!Status) { DisplayError(); ExitCode = EXIT_CODE_FAILURE; leave; } lpInOutBuffer->ReparseDataLength = 0; Status = DeviceIoControl( FileHandle, FSCTL_DELETE_REPARSE_POINT, (LPVOID) lpInOutBuffer, REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, NULL, 0, &BytesReturned, (LPOVERLAPPED)NULL ); if (!Status) { DisplayError(); ExitCode = EXIT_CODE_FAILURE; } } finally { if (FileHandle != INVALID_HANDLE_VALUE) { CloseHandle( FileHandle ); } free( lpInOutBuffer ); free( Filename ); } return ExitCode; }