/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS system libraries * FILE: dll/win32/kernel32/client/file/dir.c * PURPOSE: Directory functions * PROGRAMMER: Pierre Schweitzer (pierre@reactos.org) */ /* INCLUDES ******************************************************************/ #include #define NDEBUG #include /* Short File Name length in chars (8.3) */ #define SFN_LENGTH 12 /* Match a volume name like: * \\?\Volume{GUID} */ #define IS_VOLUME_NAME(s, l) \ ((l == 96 || (l == 98 && s[48] == '\\')) && \ s[0] == '\\'&& (s[1] == '?' || s[1] == '\\') && \ s[2] == '?' && s[3] == '\\' && s[4] == 'V' && \ s[5] == 'o' && s[6] == 'l' && s[7] == 'u' && \ s[8] == 'm' && s[9] == 'e' && s[10] == '{' && \ s[19] == '-' && s[24] == '-' && s[29] == '-' && \ s[34] == '-' && s[47] == '}') /* FUNCTIONS *****************************************************************/ /* * @implemented */ BOOL WINAPI CreateDirectoryA(IN LPCSTR lpPathName, IN LPSECURITY_ATTRIBUTES lpSecurityAttributes) { PUNICODE_STRING PathNameW; PathNameW = Basep8BitStringToStaticUnicodeString(lpPathName); if (!PathNameW) { return FALSE; } return CreateDirectoryW(PathNameW->Buffer, lpSecurityAttributes); } /* * @implemented */ BOOL WINAPI CreateDirectoryExA(IN LPCSTR lpTemplateDirectory, IN LPCSTR lpNewDirectory, IN LPSECURITY_ATTRIBUTES lpSecurityAttributes) { PUNICODE_STRING TemplateDirectoryW; UNICODE_STRING NewDirectoryW; BOOL ret; TemplateDirectoryW = Basep8BitStringToStaticUnicodeString(lpTemplateDirectory); if (!TemplateDirectoryW) { return FALSE; } if (!Basep8BitStringToDynamicUnicodeString(&NewDirectoryW, lpNewDirectory)) { return FALSE; } ret = CreateDirectoryExW(TemplateDirectoryW->Buffer, NewDirectoryW.Buffer, lpSecurityAttributes); RtlFreeUnicodeString(&NewDirectoryW); return ret; } /* * @implemented */ BOOL WINAPI CreateDirectoryW(IN LPCWSTR lpPathName, IN LPSECURITY_ATTRIBUTES lpSecurityAttributes) { DWORD Length; NTSTATUS Status; HANDLE DirectoryHandle; UNICODE_STRING NtPathU; PWSTR PathUBuffer, FilePart; IO_STATUS_BLOCK IoStatusBlock; RTL_RELATIVE_NAME_U RelativeName; OBJECT_ATTRIBUTES ObjectAttributes; /* Get relative name */ if (!RtlDosPathNameToRelativeNtPathName_U(lpPathName, &NtPathU, NULL, &RelativeName)) { SetLastError(ERROR_PATH_NOT_FOUND); return FALSE; } /* Check if path length is < MAX_PATH (with space for file name). * If not, prefix is required. */ if (NtPathU.Length > (MAX_PATH - SFN_LENGTH) * sizeof(WCHAR) && lpPathName[0] != L'\\' && lpPathName[1] != L'\\' && lpPathName[2] != L'?' && lpPathName[3] != L'\\') { /* Get file name position and full path length */ Length = GetFullPathNameW(lpPathName, 0, NULL, &FilePart); if (Length == 0) { RtlReleaseRelativeName(&RelativeName); RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathU.Buffer); SetLastError(ERROR_FILENAME_EXCED_RANGE); return FALSE; } /* Keep place for 8.3 file name */ Length += SFN_LENGTH; /* No prefix, so, must be smaller than MAX_PATH */ if (Length > MAX_PATH) { RtlReleaseRelativeName(&RelativeName); RtlFreeHeap(GetProcessHeap(), 0, NtPathU.Buffer); SetLastError(ERROR_FILENAME_EXCED_RANGE); return FALSE; } } /* Save buffer to allow later freeing */ PathUBuffer = NtPathU.Buffer; /* If we have relative name (and root dir), use them instead */ if (RelativeName.RelativeName.Length != 0) { NtPathU.Length = RelativeName.RelativeName.Length; NtPathU.MaximumLength = RelativeName.RelativeName.MaximumLength; NtPathU.Buffer = RelativeName.RelativeName.Buffer; } else { RelativeName.ContainingDirectory = NULL; } InitializeObjectAttributes(&ObjectAttributes, &NtPathU, OBJ_CASE_INSENSITIVE, RelativeName.ContainingDirectory, (lpSecurityAttributes ? lpSecurityAttributes->lpSecurityDescriptor : NULL)); Status = NtCreateFile(&DirectoryHandle, FILE_LIST_DIRECTORY | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_CREATE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT, NULL, 0); RtlReleaseRelativeName(&RelativeName); RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer); if (NT_SUCCESS(Status)) { NtClose(DirectoryHandle); return TRUE; } if (RtlIsDosDeviceName_U(lpPathName)) { Status = STATUS_NOT_A_DIRECTORY; } BaseSetLastNTError(Status); return FALSE; } /* * @implemented */ BOOL WINAPI CreateDirectoryExW(IN LPCWSTR lpTemplateDirectory, IN LPCWSTR lpNewDirectory, IN LPSECURITY_ATTRIBUTES lpSecurityAttributes) { DWORD Length; NTSTATUS Status; PVOID EaBuffer = NULL; BOOL ReparsePoint = FALSE; IO_STATUS_BLOCK IoStatusBlock; FILE_EA_INFORMATION FileEaInfo; ULONG EaLength = 0, StreamSize; OBJECT_ATTRIBUTES ObjectAttributes; FILE_BASIC_INFORMATION FileBasicInfo; PREPARSE_DATA_BUFFER ReparseDataBuffer; HANDLE TemplateHandle, DirectoryHandle; PFILE_STREAM_INFORMATION FileStreamInfo; FILE_ATTRIBUTE_TAG_INFORMATION FileTagInfo; UNICODE_STRING NtPathU, NtTemplatePathU, NewDirectory; RTL_RELATIVE_NAME_U RelativeName, TemplateRelativeName; PWSTR TemplateBuffer, PathUBuffer, FilePart, SubstituteName; /* Get relative name of the template */ if (!RtlDosPathNameToRelativeNtPathName_U(lpTemplateDirectory, &NtTemplatePathU, NULL, &TemplateRelativeName)) { SetLastError(ERROR_PATH_NOT_FOUND); return FALSE; } /* Save buffer for further freeing */ TemplateBuffer = NtTemplatePathU.Buffer; /* If we have relative name (and root dir), use them instead */ if (TemplateRelativeName.RelativeName.Length != 0) { NtTemplatePathU.Length = TemplateRelativeName.RelativeName.Length; NtTemplatePathU.MaximumLength = TemplateRelativeName.RelativeName.MaximumLength; NtTemplatePathU.Buffer = TemplateRelativeName.RelativeName.Buffer; } else { TemplateRelativeName.ContainingDirectory = NULL; } InitializeObjectAttributes(&ObjectAttributes, &NtTemplatePathU, OBJ_CASE_INSENSITIVE, NULL, NULL); /* Open template directory */ Status = NtOpenFile(&TemplateHandle, FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | FILE_READ_EA, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT | FILE_OPEN_FOR_BACKUP_INTENT); if (!NT_SUCCESS(Status)) { if (Status != STATUS_INVALID_PARAMETER) { RtlReleaseRelativeName(&TemplateRelativeName); RtlFreeHeap(RtlGetProcessHeap(), 0, TemplateBuffer); BaseSetLastNTError(Status); return FALSE; } OpenWithoutReparseSupport: /* Opening failed due to lacking reparse points support in the FSD, try without */ Status = NtOpenFile(&TemplateHandle, FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | FILE_READ_EA, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT); if (!NT_SUCCESS(Status)) { RtlReleaseRelativeName(&TemplateRelativeName); RtlFreeHeap(RtlGetProcessHeap(), 0, TemplateBuffer); BaseSetLastNTError(Status); return FALSE; } /* Request file attributes */ FileBasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL; Status = NtQueryInformationFile(TemplateHandle, &IoStatusBlock, &FileBasicInfo, sizeof(FileBasicInfo), FileBasicInformation); if (!NT_SUCCESS(Status)) { RtlReleaseRelativeName(&TemplateRelativeName); RtlFreeHeap(RtlGetProcessHeap(), 0, TemplateBuffer); CloseHandle(TemplateHandle); BaseSetLastNTError(Status); return FALSE; } } else { /* Request file attributes */ FileBasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL; Status = NtQueryInformationFile(TemplateHandle, &IoStatusBlock, &FileBasicInfo, sizeof(FileBasicInfo), FileBasicInformation); if (!NT_SUCCESS(Status)) { RtlReleaseRelativeName(&TemplateRelativeName); RtlFreeHeap(RtlGetProcessHeap(), 0, TemplateBuffer); CloseHandle(TemplateHandle); BaseSetLastNTError(Status); return FALSE; } /* If it is a reparse point, then get information about it */ if (FileBasicInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { Status = NtQueryInformationFile(TemplateHandle, &IoStatusBlock, &FileTagInfo, sizeof(FileTagInfo), FileAttributeTagInformation); if (!NT_SUCCESS(Status)) { RtlReleaseRelativeName(&TemplateRelativeName); RtlFreeHeap(RtlGetProcessHeap(), 0, TemplateBuffer); CloseHandle(TemplateHandle); BaseSetLastNTError(Status); return FALSE; } /* Only mount points are supported, retry without if anything different */ if (FileTagInfo.ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) { CloseHandle(TemplateHandle); goto OpenWithoutReparseSupport; } /* Mark we are playing with a reparse point */ ReparsePoint = TRUE; } } /* Get relative name of the directory */ if (!RtlDosPathNameToRelativeNtPathName_U(lpNewDirectory, &NtPathU, NULL, &RelativeName)) { RtlReleaseRelativeName(&TemplateRelativeName); RtlFreeHeap(RtlGetProcessHeap(), 0, TemplateBuffer); NtClose(TemplateHandle); SetLastError(ERROR_PATH_NOT_FOUND); return FALSE; } /* Save its buffer for further freeing */ PathUBuffer = NtPathU.Buffer; /* Template & directory can't be the same */ if (RtlEqualUnicodeString(&NtPathU, &NtTemplatePathU, TRUE)) { RtlReleaseRelativeName(&RelativeName); RtlReleaseRelativeName(&TemplateRelativeName); RtlFreeHeap(RtlGetProcessHeap(), 0, TemplateBuffer); RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer); NtClose(TemplateHandle); SetLastError(ERROR_INVALID_NAME); return FALSE; } RtlReleaseRelativeName(&TemplateRelativeName); RtlFreeHeap(RtlGetProcessHeap(), 0, TemplateBuffer); /* Check if path length is < MAX_PATH (with space for file name). * If not, prefix is required. */ if (NtPathU.Length > (MAX_PATH - SFN_LENGTH) * sizeof(WCHAR) && lpNewDirectory[0] != L'\\' && lpNewDirectory[1] != L'\\' && lpNewDirectory[2] != L'?' && lpNewDirectory[3] != L'\\') { /* Get file name position and full path length */ Length = GetFullPathNameW(lpNewDirectory, 0, NULL, &FilePart); if (Length == 0) { RtlReleaseRelativeName(&RelativeName); RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer); CloseHandle(TemplateHandle); SetLastError(ERROR_FILENAME_EXCED_RANGE); return FALSE; } /* Keep place for 8.3 file name */ Length += SFN_LENGTH; /* No prefix, so, must be smaller than MAX_PATH */ if (Length > MAX_PATH) { RtlReleaseRelativeName(&RelativeName); RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer); CloseHandle(TemplateHandle); SetLastError(ERROR_FILENAME_EXCED_RANGE); return FALSE; } } /* If we have relative name (and root dir), use them instead */ if (RelativeName.RelativeName.Length != 0) { NtPathU.Length = RelativeName.RelativeName.Length; NtPathU.MaximumLength = RelativeName.RelativeName.MaximumLength; NtPathU.Buffer = RelativeName.RelativeName.Buffer; } else { RelativeName.ContainingDirectory = NULL; } /* Get extended attributes */ Status = NtQueryInformationFile(TemplateHandle, &IoStatusBlock, &FileEaInfo, sizeof(FileEaInfo), FileEaInformation); if (!NT_SUCCESS(Status)) { RtlReleaseRelativeName(&RelativeName); RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer); CloseHandle(TemplateHandle); BaseSetLastNTError(Status); return FALSE; } /* Start reading extended attributes */ if (FileEaInfo.EaSize != 0) { for (EaLength = FileEaInfo.EaSize * 2; ; EaLength = EaLength * 2) { /* Allocate buffer for reading */ EaBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, EaLength); if (!EaBuffer) { RtlReleaseRelativeName(&RelativeName); RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer); CloseHandle(TemplateHandle); BaseSetLastNTError(STATUS_NO_MEMORY); return FALSE; } /* Query EAs */ Status = NtQueryEaFile(TemplateHandle, &IoStatusBlock, EaBuffer, EaLength, FALSE, NULL, 0, NULL, TRUE); if (!NT_SUCCESS(Status)) { RtlFreeHeap(RtlGetProcessHeap(), 0, EaBuffer); IoStatusBlock.Information = 0; } /* If we don't fail because of too small buffer, stop here */ if (Status != STATUS_BUFFER_OVERFLOW && Status != STATUS_BUFFER_TOO_SMALL) { EaLength = IoStatusBlock.Information; break; } } } InitializeObjectAttributes(&ObjectAttributes, &NtPathU, OBJ_CASE_INSENSITIVE, RelativeName.ContainingDirectory, (lpSecurityAttributes ? lpSecurityAttributes->lpSecurityDescriptor : NULL)); /* Ensure attributes are valid */ FileBasicInfo.FileAttributes &= FILE_ATTRIBUTE_VALID_FLAGS; /* Create the new directory */ Status = NtCreateFile(&DirectoryHandle, FILE_LIST_DIRECTORY | SYNCHRONIZE | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | (FileBasicInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ? FILE_ADD_FILE : 0), &ObjectAttributes, &IoStatusBlock, NULL, FileBasicInfo.FileAttributes, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_CREATE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT, EaBuffer, EaLength); if (!NT_SUCCESS(Status)) { if (Status == STATUS_INVALID_PARAMETER || Status == STATUS_ACCESS_DENIED) { /* If creation failed, it might be because FSD doesn't support reparse points * Retry without asking for such support in case template is not a reparse point */ if (!ReparsePoint) { Status = NtCreateFile(&DirectoryHandle, FILE_LIST_DIRECTORY | SYNCHRONIZE | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES, &ObjectAttributes, &IoStatusBlock, NULL, FileBasicInfo.FileAttributes, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_CREATE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT, EaBuffer, EaLength); } else { RtlReleaseRelativeName(&RelativeName); RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer); if (EaBuffer) { RtlFreeHeap(RtlGetProcessHeap(), 0, EaBuffer); } CloseHandle(TemplateHandle); BaseSetLastNTError(Status); return FALSE; } } } RtlReleaseRelativeName(&RelativeName); RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer); if (EaBuffer) { RtlFreeHeap(RtlGetProcessHeap(), 0, EaBuffer); } if (!NT_SUCCESS(Status)) { NtClose(TemplateHandle); if (RtlIsDosDeviceName_U(lpNewDirectory)) { Status = STATUS_NOT_A_DIRECTORY; } BaseSetLastNTError(Status); return FALSE; } /* If template is a reparse point, copy reparse data */ if (ReparsePoint) { ReparseDataBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, MAXIMUM_REPARSE_DATA_BUFFER_SIZE); if (!ReparseDataBuffer) { NtClose(TemplateHandle); NtClose(DirectoryHandle); SetLastError(STATUS_NO_MEMORY); return FALSE; } /* First query data */ Status = NtFsControlFile(TemplateHandle, NULL, NULL, NULL, &IoStatusBlock, FSCTL_GET_REPARSE_POINT, NULL, 0, ReparseDataBuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE); if (!NT_SUCCESS(Status)) { RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer); NtClose(TemplateHandle); NtClose(DirectoryHandle); SetLastError(Status); return FALSE; } /* Once again, ensure it is a mount point */ if (ReparseDataBuffer->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) { RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer); NtClose(TemplateHandle); NtClose(DirectoryHandle); SetLastError(STATUS_OBJECT_NAME_INVALID); return FALSE; } /* Get volume name */ SubstituteName = (PWSTR)((ULONG_PTR)ReparseDataBuffer->MountPointReparseBuffer.PathBuffer + ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameOffset); if (IS_VOLUME_NAME(SubstituteName, ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength)) { /* Prepare to define a new mount point for that volume */ RtlInitUnicodeString(&NewDirectory, lpNewDirectory); NewDirectory.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, NewDirectory.Length + 2 * sizeof(WCHAR)); if (!NewDirectory.Buffer) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer); NtClose(TemplateHandle); NtClose(DirectoryHandle); return FALSE; } RtlCopyMemory(&NewDirectory.Buffer, lpNewDirectory, NewDirectory.Length); if (NewDirectory.Buffer[NewDirectory.Length / sizeof(WCHAR)] != L'\\') { NewDirectory.Buffer[NewDirectory.Length / sizeof(WCHAR)] = L'\\'; NewDirectory.Buffer[(NewDirectory.Length / sizeof(WCHAR)) + 1] = UNICODE_NULL; } /* Define a new mount point for that volume */ SetVolumeMountPointW(NewDirectory.Buffer, SubstituteName); RtlFreeHeap(RtlGetProcessHeap(), 0, NewDirectory.Buffer); RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer); NtClose(TemplateHandle); NtClose(DirectoryHandle); return TRUE; } /* Otherwise copy data raw */ Status = NtFsControlFile(DirectoryHandle, NULL, NULL, NULL, &IoStatusBlock, FSCTL_SET_REPARSE_POINT, ReparseDataBuffer, ReparseDataBuffer->ReparseDataLength + FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer), NULL, 0); RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer); NtClose(TemplateHandle); NtClose(DirectoryHandle); if (NT_SUCCESS(Status)) { return TRUE; } BaseSetLastNTError(Status); return FALSE; } /* In case it's not a reparse point, handle streams on the file */ else { for (StreamSize = 0x1000; ; StreamSize = StreamSize * 2) { FileStreamInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, StreamSize); if (!FileStreamInfo) { BaseMarkFileForDelete(DirectoryHandle, FileBasicInfo.FileAttributes); SetLastError(STATUS_NO_MEMORY); break; } /* Query stream information */ Status = NtQueryInformationFile(TemplateHandle, &IoStatusBlock, FileStreamInfo, StreamSize, FileStreamInformation); if (NT_SUCCESS(Status)) { break; } RtlFreeHeap(RtlGetProcessHeap(), 0, FileStreamInfo); FileStreamInfo = NULL; /* If it failed, ensure that's not because of too small buffer */ if (Status != STATUS_BUFFER_OVERFLOW && Status != STATUS_BUFFER_TOO_SMALL) { break; } } if (!NT_SUCCESS(Status) || IoStatusBlock.Information == 0) { if (FileStreamInfo) { RtlFreeHeap(RtlGetProcessHeap(), 0, FileStreamInfo); } NtClose(TemplateHandle); NtClose(DirectoryHandle); return TRUE; } #if 1 /* FIXME: TODO */ DPRINT1("Warning: streams copying is unimplemented!\n"); RtlFreeHeap(RtlGetProcessHeap(), 0, FileStreamInfo); NtClose(TemplateHandle); NtClose(DirectoryHandle); #endif return TRUE; } } /* * @implemented */ BOOL WINAPI RemoveDirectoryA(IN LPCSTR lpPathName) { PUNICODE_STRING PathNameW; PathNameW = Basep8BitStringToStaticUnicodeString(lpPathName); if (!PathNameW) { return FALSE; } return RemoveDirectoryW(PathNameW->Buffer); } /* * @implemented */ BOOL WINAPI RemoveDirectoryW(IN LPCWSTR lpPathName) { NTSTATUS Status; DWORD BytesReturned; HANDLE DirectoryHandle; IO_STATUS_BLOCK IoStatusBlock; UNICODE_STRING NtPathU, PathName; RTL_RELATIVE_NAME_U RelativeName; PWSTR PathUBuffer, SubstituteName; OBJECT_ATTRIBUTES ObjectAttributes; PREPARSE_DATA_BUFFER ReparseDataBuffer; FILE_DISPOSITION_INFORMATION FileDispInfo; FILE_ATTRIBUTE_TAG_INFORMATION FileTagInfo; /* Get relative name */ if (!RtlDosPathNameToRelativeNtPathName_U(lpPathName, &NtPathU, NULL, &RelativeName)) { SetLastError(ERROR_PATH_NOT_FOUND); return FALSE; } /* Save buffer to allow later freeing */ PathUBuffer = NtPathU.Buffer; /* If we have relative name (and root dir), use them instead */ if (RelativeName.RelativeName.Length != 0) { NtPathU.Length = RelativeName.RelativeName.Length; NtPathU.MaximumLength = RelativeName.RelativeName.MaximumLength; NtPathU.Buffer = RelativeName.RelativeName.Buffer; } else { RelativeName.ContainingDirectory = NULL; } InitializeObjectAttributes(&ObjectAttributes, &NtPathU, OBJ_CASE_INSENSITIVE, RelativeName.ContainingDirectory, NULL); /* Try to open directory */ Status = NtOpenFile(&DirectoryHandle, DELETE | SYNCHRONIZE | FAILED_ACCESS_ACE_FLAG, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT); if (!NT_SUCCESS(Status)) { /* We only accept failure for reparse points not being supported */ if (Status != STATUS_INVALID_PARAMETER) { goto Cleanup; } /* Try to open, with reparse points support */ Status = NtOpenFile(&DirectoryHandle, DELETE | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT); if (!NT_SUCCESS(Status)) { goto Cleanup; } /* Success, mark directory */ goto MarkFileForDelete; } /* Get information about file (and reparse point) */ Status = NtQueryInformationFile(DirectoryHandle, &IoStatusBlock, &FileTagInfo, sizeof(FileTagInfo), FileAttributeTagInformation); if (!NT_SUCCESS(Status)) { /* FSD might not support querying reparse points information */ if (Status != STATUS_NOT_IMPLEMENTED && Status != STATUS_INVALID_PARAMETER) { goto CleanupHandle; } /* If that's the case, then just delete directory */ goto MarkFileForDelete; } /* If that's not a reparse point, nothing more to do than just delete */ if (!(FileTagInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { goto MarkFileForDelete; } /* Check if that's a mount point */ if (FileTagInfo.ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) { /* It's not */ NtClose(DirectoryHandle); /* So, try to reopen directory, ignoring mount point */ Status = NtOpenFile(&DirectoryHandle, DELETE | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT); if (NT_SUCCESS(Status)) { /* It succeed, we can safely delete directory (and ignore reparse point) */ goto MarkFileForDelete; } /* If it failed, only allow case where IO mount point was ignored */ if (Status != STATUS_IO_REPARSE_TAG_NOT_HANDLED) { goto Cleanup; } /* Reopen with reparse point support */ Status = NtOpenFile(&DirectoryHandle, DELETE | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT); if (NT_SUCCESS(Status)) { /* And mark for delete */ goto MarkFileForDelete; } goto Cleanup; } /* Here, we have a mount point, prepare to query information about it */ ReparseDataBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, MAXIMUM_REPARSE_DATA_BUFFER_SIZE); if (!ReparseDataBuffer) { RtlReleaseRelativeName(&RelativeName); RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer); NtClose(DirectoryHandle); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } /* Query */ if (!DeviceIoControl(DirectoryHandle, FSCTL_GET_REPARSE_POINT, NULL, 0, ReparseDataBuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &BytesReturned, NULL)) { RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer); goto MarkFileForDelete; } /* Get volume name */ SubstituteName = (PWSTR)((ULONG_PTR)ReparseDataBuffer->MountPointReparseBuffer.PathBuffer + ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameOffset); if (!IS_VOLUME_NAME(SubstituteName, ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength)) { /* This is not a volume, we can safely delete */ RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer); goto MarkFileForDelete; } /* Prepare to delete mount point */ RtlInitUnicodeString(&PathName, lpPathName); PathName.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathName.Length + 2 * sizeof(WCHAR)); if (!PathName.Buffer) { RtlReleaseRelativeName(&RelativeName); RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer); NtClose(DirectoryHandle); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } RtlCopyMemory(&PathName.Buffer, lpPathName, PathName.Length); if (PathName.Buffer[PathName.Length / sizeof(WCHAR)] != L'\\') { PathName.Buffer[PathName.Length / sizeof(WCHAR)] = L'\\'; PathName.Buffer[(PathName.Length / sizeof(WCHAR)) + 1] = UNICODE_NULL; } /* Delete mount point for that volume */ DeleteVolumeMountPointW(PathName.Buffer); RtlFreeHeap(RtlGetProcessHeap(), 0, PathName.Buffer); RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer); /* And mark directory for delete */ MarkFileForDelete: RtlReleaseRelativeName(&RelativeName); RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer); /* Mark & set */ FileDispInfo.DeleteFile = TRUE; Status = NtSetInformationFile(DirectoryHandle, &IoStatusBlock, &FileDispInfo, sizeof(FILE_DISPOSITION_INFORMATION), FileDispositionInformation); NtClose(DirectoryHandle); if (!NT_SUCCESS(Status)) { BaseSetLastNTError(Status); return FALSE; } return TRUE; CleanupHandle: NtClose(DirectoryHandle); Cleanup: RtlReleaseRelativeName(&RelativeName); RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer); BaseSetLastNTError(Status); return FALSE; } /* EOF */