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; }