#define UNICODE 1 #define _UNICODE 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define UNICODE_PATH_SEP L'\\' DWORD DeleteLinkReparsePoint( PUNICODE_STRING pDirectoryName, HANDLE ParentHandle ); DWORD DisplayFileName( PTSTR pszVolume, HANDLE hVolume, LONGLONG llFileId, BOOL fRemoveDirectory); void CountReparsePoints(PCTSTR pcszInput); DWORD xGetRootDirectory(PCTSTR pcszInput, PTSTR* pszRootDir); HANDLE xOpenReparseIndex(PCTSTR pcszVolume); HANDLE xOpenVolume(PCTSTR pcszVolume); BOOL xGetNextReparseRecord( HANDLE hIndex, PFILE_REPARSE_POINT_INFORMATION ReparseInfo); DWORD CountReparsePoints(LPWSTR pcszInput, BOOL fRemoveDirectory) { PTSTR pszVolume = NULL; DWORD dwCount = 0; DWORD Error = 0; BOOL fDone = TRUE; HANDLE hIndex = INVALID_HANDLE_VALUE; HANDLE hVolume = INVALID_HANDLE_VALUE; FILE_REPARSE_POINT_INFORMATION ReparseInfo; Error = xGetRootDirectory( pcszInput, &pszVolume); if(Error != 0) { return Error; } hIndex = xOpenReparseIndex( pszVolume); if(hIndex == INVALID_HANDLE_VALUE) { Error = GetLastError(); goto Exit; } hVolume = xOpenVolume( pszVolume); if(hVolume == INVALID_HANDLE_VALUE) { Error = GetLastError(); goto Exit; } fDone = xGetNextReparseRecord( hIndex, &ReparseInfo); while (!fDone) { if (IO_REPARSE_TAG_DFS == ReparseInfo.Tag) { dwCount++; DisplayFileName( pszVolume, hVolume, ReparseInfo.FileReference, fRemoveDirectory); } fDone = xGetNextReparseRecord( hIndex, &ReparseInfo); } Error = GetLastError(); Exit: if(hIndex != INVALID_HANDLE_VALUE ) { CloseHandle(hIndex); hIndex = INVALID_HANDLE_VALUE; } _tprintf( _T("This volume (%ws) contains %u DFS Directories.\n"), pszVolume, dwCount); if (hVolume!=INVALID_HANDLE_VALUE) { CloseHandle(hVolume); } if(pszVolume) { delete []pszVolume; } return Error; } DWORD xGetRootDirectory(PCTSTR pcszInput, PTSTR* pszRootDir) { DWORD dwBufferSize = MAX_PATH; PTSTR pszTemp = NULL; BOOL bResult = FALSE; DWORD dwGleCode = 0; *pszRootDir = NULL; do { pszTemp = new TCHAR[dwBufferSize]; if(pszTemp == NULL) { dwGleCode = ERROR_NOT_ENOUGH_MEMORY; return dwGleCode; } bResult = GetVolumePathName( pcszInput, pszTemp, dwBufferSize); if (!bResult) { delete []pszTemp; dwGleCode = GetLastError(); if (ERROR_BUFFER_OVERFLOW==dwGleCode) { dwBufferSize *= 2; } else { break; } } } while (!bResult); *pszRootDir = pszTemp; return dwGleCode; } HANDLE xOpenReparseIndex(PCTSTR pcszVolume) { HANDLE hReparseIndex = INVALID_HANDLE_VALUE; PTSTR pszReparseIndex = NULL; DWORD Error = 0; pszReparseIndex = new TCHAR[_tcslen(pcszVolume)+64]; if(pszReparseIndex == NULL) { SetLastError (ERROR_NOT_ENOUGH_MEMORY); return hReparseIndex; } _tcscpy( pszReparseIndex, pcszVolume); PathAddBackslash(pszReparseIndex); _tcscat( pszReparseIndex, _T("$Extend\\$Reparse:$R:$INDEX_ALLOCATION")); hReparseIndex = CreateFile( pszReparseIndex, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | SECURITY_IMPERSONATION, NULL); Error = GetLastError (); delete []pszReparseIndex; SetLastError (Error); return hReparseIndex; } HANDLE xOpenVolume(PCTSTR pcszVolume) { HANDLE hVolume = INVALID_HANDLE_VALUE; PTSTR pszVolumeName = NULL; DWORD Error = 0; pszVolumeName = new TCHAR[MAX_PATH]; BOOL bResult = GetVolumeNameForVolumeMountPoint( pcszVolume, pszVolumeName, MAX_PATH); if (bResult) { hVolume = CreateFile( pszVolumeName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | SECURITY_IMPERSONATION, NULL); Error = GetLastError (); } else { Error = GetLastError (); } delete []pszVolumeName; SetLastError (Error); return hVolume; } BOOL xGetNextReparseRecord( HANDLE hIndex, PFILE_REPARSE_POINT_INFORMATION ReparseInfo) { BOOL bResult = FALSE; DWORD Error = 0; IO_STATUS_BLOCK ioStatus; NTSTATUS status = NtQueryDirectoryFile(hIndex, NULL, NULL, NULL, &ioStatus, ReparseInfo, sizeof(FILE_REPARSE_POINT_INFORMATION), FileReparsePointInformation, TRUE, NULL, FALSE); if (!NT_SUCCESS(status)) { Error = RtlNtStatusToDosError(status); if(Error == ERROR_NO_MORE_FILES) { Error = 0; } bResult = TRUE; } SetLastError(Error); return bResult; } DWORD DeleteDirectory(LPWSTR VolumeName, LPWSTR pDfsDirectory) { DWORD Status = 0; DWORD BuffLen = 0; LPWSTR lpExistingFileName = NULL; UNICODE_STRING UnicodeFileName; BuffLen = (wcslen(L"\\??\\") + 1) * sizeof(WCHAR); BuffLen += ((wcslen((const wchar_t *)pDfsDirectory) + 1)* sizeof(WCHAR)); BuffLen += ((wcslen((const wchar_t *)VolumeName) + 1)* sizeof(WCHAR)); lpExistingFileName = (LPWSTR)HeapAlloc (GetProcessHeap(), 0, BuffLen); if(lpExistingFileName == NULL) { wprintf(L"Out of memory\n"); Status = ERROR_NOT_ENOUGH_MEMORY; return Status; } wcscpy(lpExistingFileName, L"\\??\\"); wcscat(lpExistingFileName, (const wchar_t *) VolumeName); wcscat(lpExistingFileName, (const wchar_t *) pDfsDirectory); UnicodeFileName.Buffer = lpExistingFileName ; UnicodeFileName.Length = wcslen(lpExistingFileName) * sizeof(WCHAR); UnicodeFileName.MaximumLength = UnicodeFileName.Length; Status = DeleteLinkReparsePoint( &UnicodeFileName, NULL); HeapFree(GetProcessHeap(), 0, lpExistingFileName); return Status; } DWORD DisplayFileName( PTSTR pszVolume, HANDLE hVolume, LONGLONG llFileId, BOOL fRemoveDirectory) { UNICODE_STRING usIdString; NTSTATUS status = 0; DWORD ErrorCode = 0; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE hFile = INVALID_HANDLE_VALUE; IO_STATUS_BLOCK IoStatusBlock; struct { FILE_NAME_INFORMATION FileInformation; WCHAR FileName[MAX_PATH]; } NameFile; fRemoveDirectory; ZeroMemory( &NameFile, sizeof(NameFile)); usIdString.Length = sizeof(LONGLONG); usIdString.MaximumLength = sizeof(LONGLONG); usIdString.Buffer = (PWCHAR)&llFileId; InitializeObjectAttributes( &ObjectAttributes, &usIdString, OBJ_CASE_INSENSITIVE, hVolume, NULL); // security descriptor status = NtCreateFile( &hFile, FILE_READ_ATTRIBUTES | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, NULL, // allocation size FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT | FILE_OPEN_BY_FILE_ID, NULL, // EA buffer 0); // EA length if (NT_SUCCESS(status)) { status = NtQueryInformationFile( hFile, &IoStatusBlock, &(NameFile.FileInformation), sizeof(NameFile), FileNameInformation); if (NT_SUCCESS(status)) { wprintf(L"%ws%ws\n",pszVolume, NameFile.FileInformation.FileName+1); } else { ErrorCode = RtlNtStatusToDosError(status); SetLastError(ErrorCode); _tprintf(_T("Unable to query file name.\n")); } } else { ErrorCode = RtlNtStatusToDosError(status); SetLastError(ErrorCode); _tprintf(_T("Unable to open file by ID.\n")); } if (hFile!=INVALID_HANDLE_VALUE) { CloseHandle(hFile); } if((ErrorCode == 0) && fRemoveDirectory) { DeleteDirectory(pszVolume, NameFile.FileInformation.FileName+1); } return ErrorCode; } VOID CloseDirectory( HANDLE DirHandle ) { NtClose( DirHandle ); } NTSTATUS ClearDfsReparsePoint( IN HANDLE DirHandle ) { NTSTATUS NtStatus = STATUS_SUCCESS; REPARSE_DATA_BUFFER ReparseDataBuffer; IO_STATUS_BLOCK IoStatusBlock; // // Attempt to set a reparse point on the directory // RtlZeroMemory( &ReparseDataBuffer, sizeof(ReparseDataBuffer) ); ReparseDataBuffer.ReparseTag = IO_REPARSE_TAG_DFS; ReparseDataBuffer.ReparseDataLength = 0; NtStatus = NtFsControlFile( DirHandle, NULL, NULL, NULL, &IoStatusBlock, FSCTL_DELETE_REPARSE_POINT, &ReparseDataBuffer, REPARSE_DATA_BUFFER_HEADER_SIZE + ReparseDataBuffer.ReparseDataLength, NULL, 0 ); return NtStatus; } NTSTATUS OpenDirectory( PUNICODE_STRING pDirectoryName, ULONG ShareMode, HANDLE RelativeHandle, PHANDLE pOpenedHandle, PBOOLEAN pIsNewlyCreated ) { NTSTATUS NtStatus; OBJECT_ATTRIBUTES ObjectAttributes; ACCESS_MASK DesiredAccess; PLARGE_INTEGER AllocationSize; ULONG FileAttributes; ULONG CreateDisposition; ULONG CreateOptions; IO_STATUS_BLOCK IoStatusBlock; AllocationSize = NULL; FileAttributes = FILE_ATTRIBUTE_NORMAL; CreateDisposition = FILE_OPEN_IF; CreateOptions = FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT; DesiredAccess = FILE_READ_DATA | FILE_WRITE_DATA | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE; InitializeObjectAttributes ( &ObjectAttributes, pDirectoryName, //Object Name 0, //Attributes RelativeHandle, //Root handle NULL); //Security descriptor. NtStatus = NtCreateFile(pOpenedHandle, DesiredAccess, &ObjectAttributes, &IoStatusBlock, AllocationSize, FileAttributes, ShareMode, CreateDisposition, CreateOptions, NULL, // EaBuffer 0 ); // EaLength if ( (NtStatus == STATUS_SUCCESS) && (pIsNewlyCreated != NULL) ) { *pIsNewlyCreated = (IoStatusBlock.Information == FILE_CREATED)? TRUE : FALSE; } return NtStatus; } NTSTATUS IsDirectoryReparsePoint( IN HANDLE DirHandle, OUT PBOOLEAN pReparsePoint, OUT PBOOLEAN pDfsReparsePoint ) { NTSTATUS NtStatus; FILE_BASIC_INFORMATION BasicInfo; IO_STATUS_BLOCK IoStatusBlock; // //we assume these are not reparse points. // *pReparsePoint = FALSE; *pDfsReparsePoint = FALSE; // // Query for the basic information, which has the attributes. // NtStatus = NtQueryInformationFile( DirHandle, &IoStatusBlock, (PVOID)&BasicInfo, sizeof(BasicInfo), FileBasicInformation ); if (NtStatus == STATUS_SUCCESS) { // // If the attributes indicate reparse point, we have a reparse // point directory on our hands. // if ( BasicInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ) { FILE_ATTRIBUTE_TAG_INFORMATION FileTagInformation; *pReparsePoint = TRUE; NtStatus = NtQueryInformationFile( DirHandle, &IoStatusBlock, (PVOID)&FileTagInformation, sizeof(FileTagInformation), FileAttributeTagInformation ); if (NtStatus == STATUS_SUCCESS) { // // Checkif the tag indicates its a DFS reparse point, // and setup the return accordingly. // if (FileTagInformation.ReparseTag == IO_REPARSE_TAG_DFS) { *pDfsReparsePoint = TRUE; } } } } return NtStatus; } BOOLEAN IsEmptyDirectory( HANDLE DirectoryHandle, PVOID pDirectoryBuffer, ULONG DirectoryBufferSize ) { NTSTATUS NtStatus; FILE_NAMES_INFORMATION *pFileInfo; ULONG NumberOfFiles = 1; BOOLEAN ReturnValue = FALSE; IO_STATUS_BLOCK IoStatus; NtStatus = NtQueryDirectoryFile ( DirectoryHandle, NULL, // no event NULL, // no apc routine NULL, // no apc context &IoStatus, pDirectoryBuffer, DirectoryBufferSize, FileNamesInformation, FALSE, // return single entry = false NULL, // filename FALSE ); // restart scan = false if (NtStatus == ERROR_SUCCESS) { pFileInfo = (FILE_NAMES_INFORMATION *)pDirectoryBuffer; while (pFileInfo->NextEntryOffset) { NumberOfFiles++; if (NumberOfFiles > 3) { break; } pFileInfo = (FILE_NAMES_INFORMATION *)((ULONG_PTR)(pFileInfo) + pFileInfo->NextEntryOffset); } if (NumberOfFiles <= 2) { ReturnValue = TRUE; } } return ReturnValue; } NTSTATUS DeleteLinkDirectories( PUNICODE_STRING pLinkName, HANDLE RelativeHandle ) { UNICODE_STRING DirectoryToDelete = *pLinkName; NTSTATUS NtStatus = STATUS_SUCCESS; HANDLE CurrentDirectory = NULL; ULONG ShareMode = 0; OBJECT_ATTRIBUTES ObjectAttributes; ShareMode = FILE_SHARE_READ; // // dfsdev: fix this fixed size limit. it will hurt us in the future. // ULONG DirectoryBufferSize = 4096; PBYTE pDirectoryBuffer = new BYTE [DirectoryBufferSize]; if (pDirectoryBuffer == NULL) { NtStatus = STATUS_INSUFFICIENT_RESOURCES; } // BUG 701594: Don't delete all directories under a reparse dir. // Just remove the reparse dir. if ( (NtStatus == STATUS_SUCCESS) && (DirectoryToDelete.Length != 0) ) { NtStatus = OpenDirectory( &DirectoryToDelete, ShareMode, RelativeHandle, &CurrentDirectory, NULL ); if (NtStatus == ERROR_SUCCESS) { if (IsEmptyDirectory(CurrentDirectory, pDirectoryBuffer, DirectoryBufferSize) == FALSE) { CloseDirectory( CurrentDirectory ); } else { CloseDirectory( CurrentDirectory ); InitializeObjectAttributes ( &ObjectAttributes, &DirectoryToDelete, 0, RelativeHandle, NULL); NtStatus = NtDeleteFile( &ObjectAttributes ); DebugInformation((L"Removed Directory %wZ, Status 0x%x\n", &DirectoryToDelete, NtStatus)); } } } if (pDirectoryBuffer != NULL) { delete [] pDirectoryBuffer; } return NtStatus; } DWORD DeleteLinkReparsePoint( PUNICODE_STRING pDirectoryName, HANDLE ParentHandle ) { NTSTATUS NtStatus = STATUS_SUCCESS; DWORD DosStatus = ERROR_SUCCESS; HANDLE LinkDirectoryHandle = INVALID_HANDLE_VALUE; BOOLEAN IsReparsePoint = FALSE; BOOLEAN IsDfsReparsePoint = FALSE; NtStatus = OpenDirectory( pDirectoryName, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, ParentHandle, &LinkDirectoryHandle, NULL ); if (NtStatus == STATUS_SUCCESS) { NtStatus = IsDirectoryReparsePoint( LinkDirectoryHandle, &IsReparsePoint, &IsDfsReparsePoint ); if ((NtStatus == STATUS_SUCCESS) && (IsDfsReparsePoint == TRUE) ) { NtStatus = ClearDfsReparsePoint( LinkDirectoryHandle ); DosStatus = RtlNtStatusToDosError(NtStatus); wprintf(L"ClearDfsReparsePoint for %ws returned %x\n", pDirectoryName->Buffer, DosStatus); } else if(NtStatus != STATUS_SUCCESS) { DosStatus = RtlNtStatusToDosError(NtStatus); wprintf(L"Clearing DFS reparse point for %ws failed %x\n", pDirectoryName->Buffer, DosStatus); } else if(IsDfsReparsePoint == FALSE) { DosStatus = RtlNtStatusToDosError(NtStatus); wprintf(L"%ws does not have a DFS reparse point %x\n", pDirectoryName->Buffer, DosStatus); } NtClose( LinkDirectoryHandle ); } else { NtStatus = RtlNtStatusToDosError(NtStatus); wprintf(L"Open for %ws returned %x\n", pDirectoryName->Buffer, NtStatus); } if (NtStatus == STATUS_SUCCESS) { NtStatus = DeleteLinkDirectories( pDirectoryName, NULL); } DosStatus = RtlNtStatusToDosError(NtStatus); return DosStatus; } DWORD DeleteReparsePoint(LPWSTR pDfsDirectory) { DWORD Status = 0; DWORD BuffLen = 0; LPWSTR lpExistingFileName = NULL; UNICODE_STRING UnicodeFileName; BuffLen = (wcslen(L"\\??\\") + 1) * sizeof(WCHAR); BuffLen += ((wcslen((const wchar_t *)pDfsDirectory) + 1)* sizeof(WCHAR)); lpExistingFileName = (LPWSTR)HeapAlloc (GetProcessHeap(), 0, BuffLen); if(lpExistingFileName == NULL) { wprintf(L"Out of memory\n"); Status = ERROR_NOT_ENOUGH_MEMORY; return Status; } wcscpy(lpExistingFileName, L"\\??\\"); wcscat(lpExistingFileName, (const wchar_t *) pDfsDirectory); UnicodeFileName.Buffer = lpExistingFileName ; UnicodeFileName.Length = wcslen(lpExistingFileName) * sizeof(WCHAR); UnicodeFileName.MaximumLength = UnicodeFileName.Length; Status = DeleteLinkReparsePoint( &UnicodeFileName, NULL); HeapFree(GetProcessHeap(), 0, lpExistingFileName); return Status; }