Some fixes to CreateDirectoryEx:
- properly copy the directory attributes
- add basic support to handle reparse points and add fallback code if
the file system doesn't support them
Modified: trunk/reactos/lib/kernel32/file/dir.c
_____
Modified: trunk/reactos/lib/kernel32/file/dir.c
--- trunk/reactos/lib/kernel32/file/dir.c 2005-09-29 16:55:59 UTC
(rev 18157)
+++ trunk/reactos/lib/kernel32/file/dir.c 2005-09-29 19:34:53 UTC
(rev 18158)
@@ -116,7 +116,8 @@
(lpSecurityAttributes ?
lpSecurityAttributes->lpSecurityDescriptor : NULL));
Status = NtCreateFile (&DirectoryHandle,
- GENERIC_READ,
+ FILE_LIST_DIRECTORY | SYNCHRONIZE |
+ FILE_OPEN_FOR_BACKUP_INTENT,
&ObjectAttributes,
&IoStatusBlock,
NULL,
@@ -157,17 +158,28 @@
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
UNICODE_STRING NtPathU, NtTemplatePathU;
- HANDLE DirectoryHandle, TemplateHandle;
+ 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;
- DPRINT ("lpTemplateDirectory %S lpNewDirectory %S
lpSecurityAttributes %p\n",
+ DPRINT ("lpTemplateDirectory %ws lpNewDirectory %ws
lpSecurityAttributes %p\n",
lpTemplateDirectory, lpNewDirectory,
lpSecurityAttributes);
/*
- * Read the extended attributes from the template directory
+ * Translate the template directory path
*/
if (!RtlDosPathNameToNtPathName_U ((LPWSTR)lpTemplateDirectory,
@@ -180,31 +192,107 @@
}
InitializeObjectAttributes(&ObjectAttributes,
+ &NtPathU,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ (lpSecurityAttributes ?
lpSecurityAttributes->lpSecurityDescriptor : NULL));
+
+ InitializeObjectAttributes(&ObjectAttributes,
&NtTemplatePathU,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
- Status = NtCreateFile (&TemplateHandle,
- GENERIC_READ,
- &ObjectAttributes,
- &IoStatusBlock,
- NULL,
- 0,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- FILE_OPEN,
- FILE_DIRECTORY_FILE,
- NULL,
- 0);
+ /*
+ * 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);
if (!NT_SUCCESS(Status))
{
- RtlFreeHeap (RtlGetProcessHeap (),
- 0,
- NtTemplatePathU.Buffer);
- SetLastErrorByStatus (Status);
- return FALSE;
+ 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;
+
+ DPRINT("Reparse points not supported, try with less
options\n");
+
+ /* try again */
+ goto OpenTemplateDir;
+ }
+ else
+ {
+ DPRINT1("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 ((LPWSTR)lpNewDirectory,
+ &NtPathU,
+ NULL,
+ NULL))
+ {
+ Status = STATUS_OBJECT_PATH_NOT_FOUND;
+ goto CleanupNoNtPath;
+ }
+
+ if (RtlEqualUnicodeString(&NtPathU,
+ &NtTemplatePathU,
+ TRUE))
+ {
+ DPRINT1("Both directory paths are the same!\n");
+ Status = STATUS_OBJECT_NAME_INVALID;
+ goto Cleanup;
+ }
+
+ /*
+ * 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 */
+ FileBasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
+ Status = NtQueryInformationFile(TemplateHandle,
+ &IoStatusBlock,
+ &FileBasicInfo,
+ sizeof(FILE_BASIC_INFORMATION),
+ FileBasicInformation);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("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
*/
+ 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;
+ ReparsePoint = TRUE;
+ }
+
+ /*
+ * Read the Extended Attributes if present
+ */
+
for (;;)
{
Status = NtQueryInformationFile(TemplateHandle,
@@ -261,66 +349,122 @@
}
}
- NtClose(TemplateHandle);
-
- RtlFreeHeap (RtlGetProcessHeap (),
- 0,
- NtTemplatePathU.Buffer);
-
if (!NT_SUCCESS(Status))
{
- /* free the he extended attributes buffer */
- if (EaBuffer != NULL)
- {
- RtlFreeHeap (RtlGetProcessHeap (),
- 0,
- EaBuffer);
- }
-
- SetLastErrorByStatus (Status);
- return FALSE;
+ DPRINT1("Querying the EA data failed: 0x%x\n", Status);
+ goto Cleanup;
}
/*
- * Create the new directory and copy over the extended
attributes if
- * needed
+ * Create the new directory
*/
-
- if (!RtlDosPathNameToNtPathName_U ((LPWSTR)lpNewDirectory,
- &NtPathU,
- NULL,
- NULL))
- {
- /* free the he extended attributes buffer */
- if (EaBuffer != NULL)
- {
- RtlFreeHeap (RtlGetProcessHeap (),
- 0,
- EaBuffer);
- }
-
- SetLastError(ERROR_PATH_NOT_FOUND);
- return FALSE;
- }
-
- InitializeObjectAttributes(&ObjectAttributes,
- &NtPathU,
- OBJ_CASE_INSENSITIVE,
- NULL,
- (lpSecurityAttributes ?
lpSecurityAttributes->lpSecurityDescriptor : NULL));
-
Status = NtCreateFile (&DirectoryHandle,
- GENERIC_READ,
+ DesiredAccess,
&ObjectAttributes,
&IoStatusBlock,
NULL,
- FILE_ATTRIBUTE_NORMAL,
+ FileBasicInfo.FileAttributes,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_CREATE,
- FILE_DIRECTORY_FILE |
FILE_SYNCHRONOUS_IO_NONALERT,
+ 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... */
+ DPRINT1("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!
*/
+ DPRINT1("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)
{
@@ -329,9 +473,10 @@
EaBuffer);
}
- RtlFreeHeap (RtlGetProcessHeap (),
- 0,
- NtPathU.Buffer);
+ if (DirectoryHandle != NULL)
+ {
+ NtClose(DirectoryHandle);
+ }
if (!NT_SUCCESS(Status))
{
@@ -339,8 +484,6 @@
return FALSE;
}
- NtClose (DirectoryHandle);
-
return TRUE;
}