Author: pschweitzer
Date: Sun Jan 1 17:22:35 2012
New Revision: 54804
URL:
http://svn.reactos.org/svn/reactos?rev=54804&view=rev
Log:
[KERNEL32]
Reimplement CreateDirectoryA, CreateDirectoryExA, CreateDirectoryW, CreateDirectoryExW,
RemoveDirectoryA, RemoveDirectoryW.
This reimplementation now matches the w2k3 one, properly handles reparse points
(especially mount points) and also brings several fixes.
Modified:
trunk/reactos/dll/win32/kernel32/client/file/dir.c
Modified: trunk/reactos/dll/win32/kernel32/client/file/dir.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/kernel32/client/…
==============================================================================
--- trunk/reactos/dll/win32/kernel32/client/file/dir.c [iso-8859-1] (original)
+++ trunk/reactos/dll/win32/kernel32/client/file/dir.c [iso-8859-1] Sun Jan 1 17:22:35
2012
@@ -2,15 +2,9 @@
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS system libraries
- * FILE: lib/kernel32/file/dir.c
+ * FILE: lib/kernel32/client/file/dir.c
* PURPOSE: Directory functions
- * PROGRAMMER: Ariadne ( ariadne(a)xs4all.nl)
- * UPDATE HISTORY:
- * Created 01/11/98
- */
-
-/*
- * NOTES: Changed to using ZwCreateFile
+ * PROGRAMMER: Pierre Schweitzer (pierre(a)reactos.org)
*/
/* INCLUDES ******************************************************************/
@@ -18,7 +12,53 @@
#include <k32.h>
#define NDEBUG
#include <debug.h>
-DEBUG_CHANNEL(kernel32file);
+
+/* 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] == '}')
+
+/* FIXME - Get it out of here */
+typedef struct _REPARSE_DATA_BUFFER {
+ ULONG ReparseTag;
+ USHORT ReparseDataLength;
+ USHORT Reserved;
+ union {
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ ULONG Flags;
+ WCHAR PathBuffer[1];
+ } SymbolicLinkReparseBuffer;
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ WCHAR PathBuffer[1];
+ } MountPointReparseBuffer;
+ struct {
+ UCHAR DataBuffer[1];
+ } GenericReparseBuffer;
+ };
+} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
+
+typedef struct _FILE_ATTRIBUTE_TAG_INFORMATION {
+ ULONG FileAttributes;
+ ULONG ReparseTag;
+} FILE_ATTRIBUTE_TAG_INFORMATION, *PFILE_ATTRIBUTE_TAG_INFORMATION;
/* FUNCTIONS *****************************************************************/
@@ -27,559 +67,933 @@
*/
BOOL
WINAPI
-CreateDirectoryA (
- LPCSTR lpPathName,
- LPSECURITY_ATTRIBUTES lpSecurityAttributes
- )
+CreateDirectoryA(IN LPCSTR lpPathName,
+ IN LPSECURITY_ATTRIBUTES lpSecurityAttributes)
{
- PWCHAR PathNameW;
-
- if (!(PathNameW = FilenameA2W(lpPathName, FALSE)))
- return FALSE;
-
- return CreateDirectoryW (PathNameW,
+ PUNICODE_STRING PathNameW;
+
+ PathNameW = Basep8BitStringToStaticUnicodeString(lpPathName);
+ if (!PathNameW)
+ {
+ return FALSE;
+ }
+
+ return CreateDirectoryW(PathNameW->Buffer,
lpSecurityAttributes);
}
-
/*
* @implemented
*/
BOOL
WINAPI
-CreateDirectoryExA (
- LPCSTR lpTemplateDirectory,
- LPCSTR lpNewDirectory,
- LPSECURITY_ATTRIBUTES lpSecurityAttributes)
+CreateDirectoryExA(IN LPCSTR lpTemplateDirectory,
+ IN LPCSTR lpNewDirectory,
+ IN LPSECURITY_ATTRIBUTES lpSecurityAttributes)
{
- PWCHAR TemplateDirectoryW;
- PWCHAR NewDirectoryW;
- BOOL ret;
-
- if (!(TemplateDirectoryW = FilenameA2W(lpTemplateDirectory, TRUE)))
- return FALSE;
-
- if (!(NewDirectoryW = FilenameA2W(lpNewDirectory, FALSE)))
- {
- RtlFreeHeap (RtlGetProcessHeap (),
- 0,
- TemplateDirectoryW);
- return FALSE;
- }
-
- ret = CreateDirectoryExW (TemplateDirectoryW,
- NewDirectoryW,
+ 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);
- RtlFreeHeap (RtlGetProcessHeap (),
- 0,
- TemplateDirectoryW);
-
- return ret;
+ RtlFreeUnicodeString(&NewDirectoryW);
+
+ return ret;
}
-
/*
* @implemented
*/
BOOL
WINAPI
-CreateDirectoryW (
- LPCWSTR lpPathName,
- LPSECURITY_ATTRIBUTES lpSecurityAttributes
- )
+CreateDirectoryW(IN LPCWSTR lpPathName,
+ IN LPSECURITY_ATTRIBUTES lpSecurityAttributes)
{
- OBJECT_ATTRIBUTES ObjectAttributes;
- IO_STATUS_BLOCK IoStatusBlock;
- UNICODE_STRING NtPathU;
- HANDLE DirectoryHandle = NULL;
- NTSTATUS Status;
-
- TRACE ("lpPathName %S lpSecurityAttributes %p\n",
- lpPathName, lpSecurityAttributes);
-
- if (!RtlDosPathNameToNtPathName_U (lpPathName,
- &NtPathU,
- NULL,
- NULL))
- {
- SetLastError(ERROR_PATH_NOT_FOUND);
- return FALSE;
- }
-
- InitializeObjectAttributes(&ObjectAttributes,
- &NtPathU,
- OBJ_CASE_INSENSITIVE,
- NULL,
- (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);
-
- RtlFreeHeap (RtlGetProcessHeap (),
- 0,
- NtPathU.Buffer);
-
- if (!NT_SUCCESS(Status))
- {
- WARN("NtCreateFile failed with Status %lx\n", Status);
- BaseSetLastNTError(Status);
- return FALSE;
- }
-
- NtClose (DirectoryHandle);
-
+ 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(GetProcessHeap(), 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(GetProcessHeap(), 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 (
- LPCWSTR lpTemplateDirectory,
- LPCWSTR lpNewDirectory,
- LPSECURITY_ATTRIBUTES lpSecurityAttributes
- )
+CreateDirectoryExW(IN LPCWSTR lpTemplateDirectory,
+ IN LPCWSTR lpNewDirectory,
+ IN LPSECURITY_ATTRIBUTES lpSecurityAttributes)
{
- OBJECT_ATTRIBUTES ObjectAttributes;
- IO_STATUS_BLOCK IoStatusBlock;
- UNICODE_STRING NtPathU, NtTemplatePathU;
- HANDLE DirectoryHandle = NULL;
- HANDLE TemplateHandle = NULL;
- FILE_EA_INFORMATION EaInformation;
- FILE_BASIC_INFORMATION FileBasicInfo;
- NTSTATUS Status;
- ULONG OpenOptions, CreateOptions;
- ACCESS_MASK DesiredAccess;
- BOOLEAN ReparsePoint = FALSE;
- PVOID EaBuffer = NULL;
- ULONG EaLength = 0;
-
- OpenOptions = FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT |
- FILE_OPEN_FOR_BACKUP_INTENT;
- CreateOptions = FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT;
- DesiredAccess = FILE_LIST_DIRECTORY | SYNCHRONIZE | FILE_WRITE_ATTRIBUTES |
- FILE_READ_ATTRIBUTES;
-
- TRACE ("lpTemplateDirectory %ws lpNewDirectory %ws lpSecurityAttributes
%p\n",
- lpTemplateDirectory, lpNewDirectory, lpSecurityAttributes);
-
- /*
- * Translate the template directory path
- */
-
- if (!RtlDosPathNameToNtPathName_U (lpTemplateDirectory,
- &NtTemplatePathU,
- NULL,
- NULL))
- {
- SetLastError(ERROR_PATH_NOT_FOUND);
- return FALSE;
- }
-
- InitializeObjectAttributes(&ObjectAttributes,
- &NtTemplatePathU,
- OBJ_CASE_INSENSITIVE,
- NULL,
- NULL);
-
- /*
- * Open the template directory
- */
-
-OpenTemplateDir:
- Status = NtOpenFile (&TemplateHandle,
- FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | FILE_READ_EA,
- &ObjectAttributes,
- &IoStatusBlock,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- OpenOptions);
+ DWORD Length;
+ NTSTATUS Status;
+ ULONG EaLength = 0;
+ PVOID EaBuffer = NULL;
+ BOOL ReparsePoint = FALSE;
+ IO_STATUS_BLOCK IoStatusBlock;
+ FILE_EA_INFORMATION FileEaInfo;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ FILE_BASIC_INFORMATION FileBasicInfo;
+ PREPARSE_DATA_BUFFER ReparseDataBuffer;
+ HANDLE TemplateHandle, DirectoryHandle;
+ 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(GetProcessHeap(), 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))
{
- if (Status == STATUS_INVALID_PARAMETER &&
- (OpenOptions & FILE_OPEN_REPARSE_POINT))
- {
- /* Some FSs (FAT) don't support reparse points, try opening
- the directory without FILE_OPEN_REPARSE_POINT */
- OpenOptions &= ~FILE_OPEN_REPARSE_POINT;
-
- TRACE("Reparse points not supported, try with less
options\n");
-
- /* try again */
- goto OpenTemplateDir;
- }
- else
- {
- WARN("Failed to open the template directory: 0x%x\n", Status);
- goto CleanupNoNtPath;
- }
- }
-
- /*
- * Translate the new directory path and check if they're the same
- */
-
- if (!RtlDosPathNameToNtPathName_U (lpNewDirectory,
- &NtPathU,
- NULL,
- NULL))
- {
- Status = STATUS_OBJECT_PATH_NOT_FOUND;
- goto CleanupNoNtPath;
- }
-
- if (RtlEqualUnicodeString(&NtPathU,
- &NtTemplatePathU,
- TRUE))
- {
- WARN("Both directory paths are the same!\n");
- Status = STATUS_OBJECT_NAME_INVALID;
- goto Cleanup;
- }
-
- InitializeObjectAttributes(&ObjectAttributes,
- &NtPathU,
- OBJ_CASE_INSENSITIVE,
- NULL,
- (lpSecurityAttributes ?
lpSecurityAttributes->lpSecurityDescriptor : NULL));
-
- /*
- * Query the basic file attributes from the template directory
- */
-
- /* Make sure FILE_ATTRIBUTE_NORMAL is used in case the information
- isn't set by the FS */
+ RtlReleaseRelativeName(&TemplateRelativeName);
+ RtlFreeHeap(GetProcessHeap(), 0, TemplateBuffer);
+ BaseSetLastNTError(Status);
+ return FALSE;
+ }
+
+ /* Request file attributes */
FileBasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
Status = NtQueryInformationFile(TemplateHandle,
&IoStatusBlock,
&FileBasicInfo,
- sizeof(FILE_BASIC_INFORMATION),
+ sizeof(FileBasicInfo),
FileBasicInformation);
if (!NT_SUCCESS(Status))
{
- WARN("Failed to query the basic directory attributes\n");
- goto Cleanup;
- }
-
- /* clear the reparse point attribute if present. We're going to set the
- reparse point later which will cause the attribute to be set */
+ RtlReleaseRelativeName(&TemplateRelativeName);
+ RtlFreeHeap(GetProcessHeap(), 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(GetProcessHeap(), 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)
{
- FileBasicInfo.FileAttributes &= ~FILE_ATTRIBUTE_REPARSE_POINT;
-
- /* writing the extended attributes requires the FILE_WRITE_DATA
- access right */
- DesiredAccess |= FILE_WRITE_DATA;
-
- CreateOptions |= FILE_OPEN_REPARSE_POINT;
+ Status = NtQueryInformationFile(TemplateHandle,
+ &IoStatusBlock,
+ &FileTagInfo,
+ sizeof(FileTagInfo),
+ FileAttributeTagInformation);
+ if (!NT_SUCCESS(Status))
+ {
+ RtlReleaseRelativeName(&TemplateRelativeName);
+ RtlFreeHeap(GetProcessHeap(), 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;
}
-
- /*
- * Read the Extended Attributes if present
- */
-
- for (;;)
- {
- Status = NtQueryInformationFile(TemplateHandle,
- &IoStatusBlock,
- &EaInformation,
- sizeof(FILE_EA_INFORMATION),
- FileEaInformation);
- if (NT_SUCCESS(Status) && (EaInformation.EaSize != 0))
- {
- EaBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
- 0,
- EaInformation.EaSize);
- if (EaBuffer == NULL)
- {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- break;
- }
-
+ }
+
+ /* Get relative name of the directory */
+ if (!RtlDosPathNameToRelativeNtPathName_U(lpNewDirectory, &NtPathU, NULL,
&RelativeName))
+ {
+ RtlReleaseRelativeName(&TemplateRelativeName);
+ RtlFreeHeap(GetProcessHeap(), 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(GetProcessHeap(), 0, TemplateBuffer);
+ RtlFreeHeap(GetProcessHeap(), 0, PathUBuffer);
+ NtClose(TemplateHandle);
+ SetLastError(ERROR_INVALID_NAME);
+ return FALSE;
+ }
+
+ RtlReleaseRelativeName(&TemplateRelativeName);
+ RtlFreeHeap(GetProcessHeap(), 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(GetProcessHeap(), 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(GetProcessHeap(), 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(GetProcessHeap(), 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(GetProcessHeap(), 0, PathUBuffer);
+ CloseHandle(TemplateHandle);
+ BaseSetLastNTError(STATUS_NO_MEMORY);
+ return FALSE;
+ }
+
+ /* Query EAs */
Status = NtQueryEaFile(TemplateHandle,
&IoStatusBlock,
EaBuffer,
- EaInformation.EaSize,
+ EaLength,
FALSE,
NULL,
0,
NULL,
TRUE);
-
- if (NT_SUCCESS(Status))
- {
- /* we successfully read the extended attributes */
- EaLength = EaInformation.EaSize;
- break;
+ if (!NT_SUCCESS(Status))
+ {
+ RtlFreeHeap(GetProcessHeap(), 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
{
- RtlFreeHeap(RtlGetProcessHeap(),
- 0,
- EaBuffer);
- EaBuffer = NULL;
-
- if (Status != STATUS_BUFFER_TOO_SMALL)
- {
- /* unless we just allocated not enough memory, break the loop
- and just continue without copying extended attributes */
- break;
- }
- }
- }
- else
- {
- if (Status == STATUS_EAS_NOT_SUPPORTED)
- {
- /* Extended attributes are not supported, so, this is OK */
- /* FIXME: Would deserve a deeper look, comparing with Windows */
- Status = STATUS_SUCCESS;
- }
- /* failure or no extended attributes present, break the loop */
- break;
- }
- }
-
- if (!NT_SUCCESS(Status))
- {
- WARN("Querying the EA data failed: 0x%x\n", Status);
- goto Cleanup;
- }
-
- /*
- * Create the new directory
- */
-
- Status = NtCreateFile (&DirectoryHandle,
- DesiredAccess,
- &ObjectAttributes,
- &IoStatusBlock,
- NULL,
- FileBasicInfo.FileAttributes,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- FILE_CREATE,
- CreateOptions,
- EaBuffer,
- EaLength);
- if (!NT_SUCCESS(Status))
- {
- if (ReparsePoint &&
- (Status == STATUS_INVALID_PARAMETER || Status == STATUS_ACCESS_DENIED))
- {
- /* The FS doesn't seem to support reparse points... */
- WARN("Cannot copy the hardlink, destination doesn\'t support
reparse points!\n");
- }
-
- goto Cleanup;
- }
-
- if (ReparsePoint)
- {
- /*
- * Copy the reparse point
- */
-
- PREPARSE_GUID_DATA_BUFFER ReparseDataBuffer =
- (PREPARSE_GUID_DATA_BUFFER)RtlAllocateHeap(RtlGetProcessHeap(),
- 0,
-
MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
-
- if (ReparseDataBuffer == NULL)
- {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto Cleanup;
- }
-
- /* query the size of the reparse data buffer structure */
- Status = NtFsControlFile(TemplateHandle,
- NULL,
- NULL,
- NULL,
- &IoStatusBlock,
- FSCTL_GET_REPARSE_POINT,
- NULL,
- 0,
- ReparseDataBuffer,
- MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
- if (NT_SUCCESS(Status))
- {
- /* write the reparse point */
- Status = NtFsControlFile(DirectoryHandle,
- NULL,
- NULL,
- NULL,
- &IoStatusBlock,
- FSCTL_SET_REPARSE_POINT,
- ReparseDataBuffer,
- MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
- NULL,
- 0);
- }
-
- RtlFreeHeap(RtlGetProcessHeap(),
- 0,
- ReparseDataBuffer);
-
- if (!NT_SUCCESS(Status))
- {
- /* fail, we were unable to read the reparse point data! */
- WARN("Querying or setting the reparse point failed: 0x%x\n",
Status);
- goto Cleanup;
- }
- }
- else
- {
- /*
- * Copy alternate file streams, if existing
- */
-
- /* FIXME - enumerate and copy the file streams */
- }
-
- /*
- * We successfully created the directory and copied all information
- * from the template directory
- */
- Status = STATUS_SUCCESS;
-
-Cleanup:
- RtlFreeHeap (RtlGetProcessHeap (),
- 0,
- NtPathU.Buffer);
-
-CleanupNoNtPath:
- if (TemplateHandle != NULL)
- {
- NtClose(TemplateHandle);
- }
-
- RtlFreeHeap (RtlGetProcessHeap (),
- 0,
- NtTemplatePathU.Buffer);
-
- /* free the he extended attributes buffer */
- if (EaBuffer != NULL)
- {
- RtlFreeHeap (RtlGetProcessHeap (),
- 0,
- EaBuffer);
- }
-
- if (DirectoryHandle != NULL)
- {
- NtClose(DirectoryHandle);
- }
-
- if (!NT_SUCCESS(Status))
- {
+ RtlReleaseRelativeName(&RelativeName);
+ RtlFreeHeap(GetProcessHeap(), 0, PathUBuffer);
+ if (EaBuffer)
+ {
+ RtlFreeHeap(GetProcessHeap(), 0, EaBuffer);
+ }
+ CloseHandle(TemplateHandle);
BaseSetLastNTError(Status);
return FALSE;
- }
-
+ }
+ }
+ }
+
+ RtlReleaseRelativeName(&RelativeName);
+ RtlFreeHeap(GetProcessHeap(), 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
+ {
+ PVOID StreamInfo;
+ ULONG StreamSize = 0x1000;
+ for (;;)
+ {
+ StreamInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, StreamSize);
+ if (!StreamInfo)
+ {
+ BaseMarkFileForDelete(DirectoryHandle, FileBasicInfo.FileAttributes);
+ SetLastError(STATUS_NO_MEMORY);
+ break;
+ }
+
+ /* Query stream information */
+ Status = NtQueryInformationFile(TemplateHandle,
+ &IoStatusBlock,
+ StreamInfo,
+ StreamSize,
+ FileStreamInformation);
+ if (!NT_SUCCESS(Status))
+ {
+ RtlFreeHeap(RtlGetProcessHeap(), 0, StreamInfo);
+ StreamInfo = NULL;
+ StreamSize << 1;
+ }
+
+ /* 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 (StreamInfo)
+ {
+ RtlFreeHeap(RtlGetProcessHeap(), 0, StreamInfo);
+ }
+
+ NtClose(TemplateHandle);
+ NtClose(DirectoryHandle);
+ return TRUE;
+ }
+
+#if 1
+ /* FIXME: TODO */
+ DPRINT1("Warning: streams copying is unimplemented!\n");
+ RtlFreeHeap(RtlGetProcessHeap(), 0, StreamInfo);
+ NtClose(TemplateHandle);
+ NtClose(DirectoryHandle);
+#endif
return TRUE;
+ }
}
-
/*
* @implemented
*/
BOOL
WINAPI
-RemoveDirectoryA (
- LPCSTR lpPathName
- )
+RemoveDirectoryA(IN LPCSTR lpPathName)
{
- PWCHAR PathNameW;
-
- TRACE("RemoveDirectoryA(%s)\n",lpPathName);
-
- if (!(PathNameW = FilenameA2W(lpPathName, FALSE)))
- return FALSE;
-
- return RemoveDirectoryW (PathNameW);
+ PUNICODE_STRING PathNameW;
+
+ PathNameW = Basep8BitStringToStaticUnicodeString(lpPathName);
+ if (!PathNameW)
+ {
+ return FALSE;
+ }
+
+ return RemoveDirectoryW(PathNameW->Buffer);
}
-
/*
* @implemented
*/
BOOL
WINAPI
-RemoveDirectoryW (
- LPCWSTR lpPathName
- )
+RemoveDirectoryW(IN LPCWSTR lpPathName)
{
- FILE_DISPOSITION_INFORMATION FileDispInfo;
- OBJECT_ATTRIBUTES ObjectAttributes;
- IO_STATUS_BLOCK IoStatusBlock;
- UNICODE_STRING NtPathU;
- HANDLE DirectoryHandle = NULL;
- NTSTATUS Status;
-
- TRACE("lpPathName %S\n", lpPathName);
-
- if (!RtlDosPathNameToNtPathName_U (lpPathName,
- &NtPathU,
- NULL,
- NULL))
- {
- SetLastError(ERROR_PATH_NOT_FOUND);
- return FALSE;
- }
-
- InitializeObjectAttributes(&ObjectAttributes,
- &NtPathU,
- OBJ_CASE_INSENSITIVE,
- NULL,
- NULL);
-
- TRACE("NtPathU '%S'\n", NtPathU.Buffer);
-
+ 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);
-
- RtlFreeUnicodeString(&NtPathU);
-
+ FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT |
+ FILE_OPEN_FOR_BACKUP_INTENT);
if (!NT_SUCCESS(Status))
{
- WARN("Status 0x%08x\n", Status);
- BaseSetLastNTError (Status);
- return FALSE;
- }
-
- FileDispInfo.DeleteFile = TRUE;
-
- Status = NtSetInformationFile (DirectoryHandle,
- &IoStatusBlock,
- &FileDispInfo,
- sizeof(FILE_DISPOSITION_INFORMATION),
- FileDispositionInformation);
+ 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);
- if (!NT_SUCCESS(Status))
- {
- BaseSetLastNTError (Status);
- return FALSE;
- }
-
- return TRUE;
+ /* 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 */