//+------------------------------------------------------------------------- // // Function: main // // Arguments: // argc, argv: the passed in argument list. // // //-------------------------------------------------------------------------- #include #include #include #include #include #include #include #include #include #include "rpc.h" #include "rpcdce.h" #include #include #include #define IO_REPARSE_TAG_DFS 0x8000000A #define UNICODE_PATH_SEP L'\\' 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 StripLastPathComponent( PUNICODE_STRING pPath ) { USHORT i = 0, j; NTSTATUS Status = STATUS_SUCCESS; if (pPath->Length == 0) { return Status; } for( i = (pPath->Length - 1)/ sizeof(WCHAR); i != 0; i--) { if (pPath->Buffer[i] != UNICODE_PATH_SEP) { break; } } for (j = i; j != 0; j--){ if (pPath->Buffer[j] == UNICODE_PATH_SEP) { break; } } pPath->Length = (j) * sizeof(WCHAR); return Status; } NTSTATUS DeleteLinkDirectories( PUNICODE_STRING pLinkName, HANDLE RelativeHandle ) { UNICODE_STRING DirectoryToDelete = *pLinkName; NTSTATUS NtStatus = STATUS_SUCCESS; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE CurrentDirectory = NULL; ULONG ShareMode = 0; 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; } while ( (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 ); break; } CloseDirectory( CurrentDirectory ); InitializeObjectAttributes ( &ObjectAttributes, &DirectoryToDelete, 0, RelativeHandle, NULL); NtStatus = NtDeleteFile( &ObjectAttributes ); StripLastPathComponent( &DirectoryToDelete ); } } if (pDirectoryBuffer != NULL) { delete [] pDirectoryBuffer; } return NtStatus; } NTSTATUS DeleteLinkReparsePoint( PUNICODE_STRING pDirectoryName, HANDLE ParentHandle ) { NTSTATUS NtStatus; HANDLE LinkDirectoryHandle; BOOLEAN IsReparsePoint, IsDfsReparsePoint; 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 ); wprintf(L"ClearDfsReparsePoint for %s returned %x\n", pDirectoryName->Buffer, NtStatus); } else if(NtStatus != STATUS_SUCCESS) { NtStatus = RtlNtStatusToDosError(NtStatus); wprintf(L"Clearing DFS reparse point for %s failed %x\n", pDirectoryName->Buffer); } else if(IsDfsReparsePoint == FALSE) { wprintf(L"%s does not have a DFS reparse point %x\n", pDirectoryName->Buffer); } NtClose( LinkDirectoryHandle ); } else { NtStatus = RtlNtStatusToDosError(NtStatus); wprintf(L"Open for %s returned %x\n", pDirectoryName->Buffer, NtStatus); } if (NtStatus == STATUS_SUCCESS) { NtStatus = DeleteLinkDirectories( pDirectoryName, NULL); } return NtStatus; } void _cdecl _tmain(int argc, LPWSTR *argv[]) { DWORD Status = 0; DWORD BuffLen = 0; LPWSTR lpExistingFileName = NULL; UNICODE_STRING UnicodeFileName; if(argc == 1) { wprintf(L"Usage: DfsRemoverp [Dir]\n"); ExitProcess (0); } BuffLen = (wcslen(L"\\??\\") + 1) * sizeof(WCHAR); BuffLen += ((wcslen((const wchar_t *)argv[1]) + 1)* sizeof(WCHAR)); lpExistingFileName = (LPWSTR)HeapAlloc (GetProcessHeap(), 0, BuffLen); if(lpExistingFileName == NULL) { wprintf(L"Out of memory\n"); ExitProcess (0); } wcscpy(lpExistingFileName, L"\\??\\"); wcscat(lpExistingFileName, (const wchar_t *) argv[1]); UnicodeFileName.Buffer = lpExistingFileName ; UnicodeFileName.Length = wcslen(lpExistingFileName) * sizeof(WCHAR); UnicodeFileName.MaximumLength = UnicodeFileName.Length; wprintf(L"getting rid of reparse point for %s\n", UnicodeFileName.Buffer); Status = DeleteLinkReparsePoint( &UnicodeFileName, NULL); HeapFree(GetProcessHeap(), 0, lpExistingFileName); }