reactos/dll/win32/kernel32/client/file/dir.c

967 lines
33 KiB
C

/*
* 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 <k32.h>
#define NDEBUG
#include <debug.h>
/* 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 */