From b70df353271c08d1eca8dccc3c3b5fb723325e13 Mon Sep 17 00:00:00 2001 From: Pierre Schweitzer Date: Sun, 19 Oct 2014 21:38:32 +0000 Subject: [PATCH] [FASTFAT] - Implement vfatPrepareTargetForRename() that prepares for renaming. It tries to open target and deletes it if it exists and if allowed. And then, it opens the parent directory. - Implement VfatSetRenameInformation() that actually does the renaming (call as SetInformationFile). It handles cases we we have (or we don't have) TargetDirectory provided. It sends notifications as appropriated on demands. - Implement vfatRenameEntry() that renames an entry in place. So far, it only supports FATX entries renaming. FAT entries are a bit more complex. It falls back to vfatMoveEntry() in later cases. - Implement VfatMoveEntry() that will move an entry accross directories (or in place for FAT). Its principles are simple: it deletes the entry in old parent, and recreate it in new parent, keeping file metadata & data. - Modify VfatDelEntry() and VfatAddEntry() so that they can handle deleting an entry without touching its data and adding an entry with an already provided FCB and thus use the given metadata. - Implement vfatDelFCBFromTable() which is just old code moved to new routine to allow reuse. It deletes a FCB entry from hash table. Doesn't deal with references! - Implement vfatMakeFullName() which is mostly old code moved to new routine to allow reuse. It allocates buffer and copy data for FCB full name. - Implement vfatUpdateFCB() that will update a FCB with new names and parent. It will remove anything related to old name and will recreate using new data. It will adjust references count. - Modify vfatMakeFCBFromDirEntry() so that it calls vfatMakeFullName(). - Modify vfatReleaseFCB() so that it calls vfatDelFCBFromTable(). - Revert VfatOpenFile() to its previous features. - Modify VfatCreateFile() to reimplement support for SL_OPEN_TARGET_DIRECTORY. It is way less hackish than previously. It also properly opens parent now, by incrementing its handle count and by setting appropriate access rights. MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit [KERNEL32] - Rewritten MoveFileWithProgressW() to implement all the missing features that are used in Windows 2k3 including links and reparse points. - Implemented BasepMoveFileDelayed() to replace deprecated add_boot_rename_entry(). This functions is matching the features implemented in SMSS. - Implemented BasepMoveFileCopyProgress() which is used in MoveFileWithProgressW(). - Stubbed BasepNotifyTrackingService() which is not use at the moment (FastFAT, even in Windows doesn't provide such feature). - Reimplemented ReplaceFileA(), MoveFileWithProgressA() to quit Winisms and use our internal helpers. - Make MoveFileX() use MoveFileWithProgressX() directly. - Fixed a few prototypes. TL;DR: This (huge) commit implements file and directory renaming in FastFAT driver. This allows getting rid of old implementation in kernel32 where files were force copied. A feature is still missing, but Jérôme should implement it anytime soon (he prototyped it already): moving directories across volumes. This requires some work in BasepCopyFileExW(). Kudos to all the devs who helped me on this: Christoph, Hervé, Jérôme, Thomas. This finally allows killing CR-52... It was about time! svn path=/trunk/; revision=64836 --- reactos/dll/win32/kernel32/client/file/move.c | 1263 ++++++++--------- reactos/drivers/filesystems/fastfat/close.c | 4 +- reactos/drivers/filesystems/fastfat/create.c | 91 +- reactos/drivers/filesystems/fastfat/dirwr.c | 282 +++- reactos/drivers/filesystems/fastfat/fcb.c | 249 +++- reactos/drivers/filesystems/fastfat/finfo.c | 488 ++++++- reactos/drivers/filesystems/fastfat/vfat.h | 40 +- 7 files changed, 1567 insertions(+), 850 deletions(-) diff --git a/reactos/dll/win32/kernel32/client/file/move.c b/reactos/dll/win32/kernel32/client/file/move.c index e3c87c173b5..c0f0866a915 100644 --- a/reactos/dll/win32/kernel32/client/file/move.c +++ b/reactos/dll/win32/kernel32/client/file/move.c @@ -6,6 +6,7 @@ * PROGRAMMER: Ariadne ( ariadne@xs4all.nl) * Gerhard W. Gruber (sparhawk_at_gmx.at) * Dmitry Philippov (shedon@mail.ru) + * Pierre Schweitzer (pierre@reactos.org) * UPDATE HISTORY: * Created 01/11/98 * DP (29/07/2006) @@ -16,208 +17,227 @@ #include #include +#include #define NDEBUG #include DEBUG_CHANNEL(kernel32file); /* GLOBALS *****************************************************************/ -/* FUNCTIONS ****************************************************************/ -static BOOL -RemoveReadOnlyAttributeW(IN LPCWSTR lpFileName) +/* DEFINES *****************************************************************/ +typedef struct _COPY_PROGRESS_CONTEXT { - DWORD Attributes; - Attributes = GetFileAttributesW(lpFileName); - if (Attributes != INVALID_FILE_ATTRIBUTES) - { - return SetFileAttributesW(lpFileName,Attributes - - (Attributes & ~FILE_ATTRIBUTE_READONLY)); - } - - return FALSE; -} - + ULONG Flags; + LPPROGRESS_ROUTINE UserRoutine; + LPVOID UserData; +} COPY_PROGRESS_CONTEXT, *PCOPY_PROGRESS_CONTEXT; -/*********************************************************************** - * add_boot_rename_entry - * - * Adds an entry to the registry that is loaded when windows boots and - * checks if there are some files to be removed or renamed/moved. - * has to be valid and may be NULL. If both pointers are - * non-NULL then the file is moved, otherwise it is deleted. The - * entry of the registrykey is always appended with two zero - * terminated strings. If is NULL then the second entry is - * simply a single 0-byte. Otherwise the second filename goes - * there. The entries are prepended with \??\ before the path and the - * second filename gets also a '!' as the first character if - * MOVEFILE_REPLACE_EXISTING is set. After the final string another - * 0-byte follows to indicate the end of the strings. - * i.e.: - * \??\D:\test\file1[0] - * !\??\D:\test\file1_renamed[0] - * \??\D:\Test|delete[0] - * [0] <- file is to be deleted, second string empty - * \??\D:\test\file2[0] - * !\??\D:\test\file2_renamed[0] - * [0] <- indicates end of strings - * - * or: - * \??\D:\test\file1[0] - * !\??\D:\test\file1_renamed[0] - * \??\D:\Test|delete[0] - * [0] <- file is to be deleted, second string empty - * [0] <- indicates end of strings - * +/* FUNCTIONS ****************************************************************/ +/* + * @implemented */ -static BOOL add_boot_rename_entry( LPCWSTR source, LPCWSTR dest, DWORD flags ) +NTSTATUS +WINAPI +BasepMoveFileDelayed(IN PUNICODE_STRING ExistingPath, + IN PUNICODE_STRING NewPath, + IN INT KeyId, + IN BOOL CreateIfNotFound) { - static const WCHAR ValueName[] = {'P','e','n','d','i','n','g', - 'F','i','l','e','R','e','n','a','m','e', - 'O','p','e','r','a','t','i','o','n','s',0}; - - UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Session Manager"); - - static const int info_size = FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data ); - - OBJECT_ATTRIBUTES ObjectAttributes; - UNICODE_STRING nameW, source_name, dest_name; - KEY_VALUE_PARTIAL_INFORMATION *info; - BOOL rc = FALSE; - HANDLE Reboot = NULL; - DWORD len1, len2; - DWORD DestLen = 0; - DWORD DataSize = 0; - BYTE *Buffer = NULL; - WCHAR *p; +#define STRING_LENGTH 0x400 NTSTATUS Status; + HANDLE KeyHandle; + PVOID Buffer, BufferBegin; + OBJECT_ATTRIBUTES ObjectAttributes; + PWSTR PendingOperations, BufferWrite; + ULONG DataSize, BufferLength, StringLength = STRING_LENGTH; + UNICODE_STRING SessionManagerString, PendingOperationsString; + /* +6 because a INT shouldn't take more than 6 chars. Especially given the call path */ + WCHAR PendingOperationsBuffer[sizeof(L"PendingFileRenameOperations") / sizeof(WCHAR) + 6]; - TRACE("add_boot_rename_entry( %S, %S, %lu ) \n", source, dest, flags); - - if(dest) - DestLen = wcslen(dest); + RtlInitUnicodeString(&SessionManagerString, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager"); - if (!RtlDosPathNameToNtPathName_U( source, &source_name, NULL, NULL )) + /* Select appropriate key for adding our file */ + if (KeyId == 1) { - SetLastError( ERROR_PATH_NOT_FOUND ); - return FALSE; + PendingOperations = L"PendingFileRenameOperations"; } - dest_name.Buffer = NULL; - if (DestLen && !RtlDosPathNameToNtPathName_U( dest, &dest_name, NULL, NULL )) + else { - RtlFreeHeap( RtlGetProcessHeap(), 0, source_name.Buffer ); - SetLastError( ERROR_PATH_NOT_FOUND ); - return FALSE; + HRESULT hr = StringCbPrintfW(PendingOperationsBuffer, sizeof(PendingOperationsBuffer), L"PendingFileRenameOperations%d", KeyId); + ASSERT(SUCCEEDED(hr)); + PendingOperations = PendingOperationsBuffer; } + RtlInitUnicodeString(&PendingOperationsString, PendingOperations); InitializeObjectAttributes(&ObjectAttributes, - &KeyName, + &SessionManagerString, OBJ_OPENIF | OBJ_CASE_INSENSITIVE, - NULL, - NULL); - - Status = NtCreateKey(&Reboot, - KEY_QUERY_VALUE | KEY_SET_VALUE, - &ObjectAttributes, - 0, - NULL, - REG_OPTION_NON_VOLATILE, - NULL); - - if (Status == STATUS_ACCESS_DENIED) - { - Status = NtCreateKey( - &Reboot, - KEY_QUERY_VALUE | KEY_SET_VALUE, - &ObjectAttributes, - 0, - NULL, - REG_OPTION_BACKUP_RESTORE, - NULL); - } + NULL, NULL); + + /* Open parent key */ + Status = NtCreateKey(&KeyHandle, + GENERIC_READ | GENERIC_WRITE, + &ObjectAttributes, 0, NULL, + REG_OPTION_NON_VOLATILE, NULL); + if (Status == STATUS_ACCESS_DENIED) + { + Status = NtCreateKey(&KeyHandle, + GENERIC_READ | GENERIC_WRITE, + &ObjectAttributes, 0, NULL, + REG_OPTION_BACKUP_RESTORE, NULL); + } if (!NT_SUCCESS(Status)) { - WARN("NtCreateKey() failed (Status 0x%lx)\n", Status); - if (source_name.Buffer) - RtlFreeHeap(RtlGetProcessHeap(), 0, source_name.Buffer); - if (dest_name.Buffer) - RtlFreeHeap(RtlGetProcessHeap(), 0, dest_name.Buffer); - return FALSE; + return Status; } - len1 = source_name.Length + sizeof(WCHAR); - if (DestLen) + /* Reserve enough to read previous string + to append our with required null chars */ + BufferLength = NewPath->Length + ExistingPath->Length + STRING_LENGTH + 3 * sizeof(WCHAR); + /* Check we didn't overflow */ + if (BufferLength < STRING_LENGTH) { - len2 = dest_name.Length + sizeof(WCHAR); - if (flags & MOVEFILE_REPLACE_EXISTING) - len2 += sizeof(WCHAR); /* Plus 1 because of the leading '!' */ + NtClose(KeyHandle); + return STATUS_BUFFER_TOO_SMALL; } - else + + while (TRUE) { - len2 = sizeof(WCHAR); /* minimum is the 0 characters for the empty second string */ + /* Allocate output buffer */ + Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength); + if (Buffer == NULL) + { + NtClose(KeyHandle); + return STATUS_NO_MEMORY; + } + + Status = NtQueryValueKey(KeyHandle, + &PendingOperationsString, + KeyValuePartialInformation, + Buffer, StringLength, &DataSize); + if (Status != STATUS_BUFFER_OVERFLOW) + { + break; + } + + /* If buffer was too small, then, reallocate one which is big enough */ + StringLength = DataSize; + RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); + BufferLength = ExistingPath->Length + StringLength + NewPath->Length + 3 * sizeof(WCHAR); + if (BufferLength < StringLength) + { + NtClose(KeyHandle); + return STATUS_BUFFER_TOO_SMALL; + } } - RtlInitUnicodeString( &nameW, ValueName ); - - /* First we check if the key exists and if so how many bytes it already contains. */ - Status = NtQueryValueKey( - Reboot, - &nameW, - KeyValuePartialInformation, - NULL, - 0, - &DataSize ); - if ((Status == STATUS_BUFFER_OVERFLOW) || - (Status == STATUS_BUFFER_TOO_SMALL)) + /* Check if it existed - if not, create only IF asked to */ + if (!NT_SUCCESS(Status) && (Status != STATUS_OBJECT_NAME_NOT_FOUND || !CreateIfNotFound)) { - if (!(Buffer = HeapAlloc(GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR)))) - goto Quit; - Status = NtQueryValueKey(Reboot, &nameW, KeyValuePartialInformation, - Buffer, DataSize, &DataSize); - if(!NT_SUCCESS(Status)) - goto Quit; - info = (KEY_VALUE_PARTIAL_INFORMATION *)Buffer; - if (info->Type != REG_MULTI_SZ) goto Quit; - if (DataSize > sizeof(info)) DataSize -= sizeof(WCHAR); /* remove terminating null (will be added back later) */ + NtClose(KeyHandle); + RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); + return Status; + } + + if (!NT_SUCCESS(Status)) + { + /* We didn't find any - ie, we create, so use complete buffer */ + BufferBegin = Buffer; + BufferWrite = Buffer; } else { - DataSize = info_size; - if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) ))) - goto Quit; + PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION)Buffer; + + /* Get data, our buffer begin and then where we should append data (+ null char) */ + BufferBegin = PartialInfo->Data; + BufferWrite = (PWSTR)((ULONG_PTR)PartialInfo->Data + PartialInfo->DataLength + sizeof(WCHAR)); } - memcpy( Buffer + DataSize, source_name.Buffer, len1 ); - DataSize += len1; - p = (WCHAR *)(Buffer + DataSize); - if (DestLen) + /* First copy existing */ + RtlCopyMemory(BufferWrite, ExistingPath->Buffer, ExistingPath->Length); + BufferWrite += ExistingPath->Length / sizeof(WCHAR); + /* And append null char */ + *BufferWrite = UNICODE_NULL; + ++BufferWrite; + /* Append destination */ + RtlCopyMemory(BufferWrite, NewPath->Buffer, NewPath->Length); + BufferWrite += NewPath->Length / sizeof(WCHAR); + /* And append two null char (end of string) */ + *BufferWrite = UNICODE_NULL; + ++BufferWrite; + *BufferWrite = UNICODE_NULL; + + /* Set new value */ + Status = NtSetValueKey(KeyHandle, + &PendingOperationsString, + 0, REG_MULTI_SZ, BufferBegin, + (ULONG_PTR)BufferWrite - (ULONG_PTR)BufferBegin + sizeof(WCHAR)); + + NtClose(KeyHandle); + RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); + + return Status; +} + + +/* + * @unimplemented + */ +NTSTATUS +WINAPI +BasepNotifyTrackingService(IN PHANDLE ExistingHandle, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN HANDLE NewHandle, + IN PUNICODE_STRING NewPath) +{ + return STATUS_NOT_IMPLEMENTED; +} + + +/* + * @implemented + */ +DWORD +WINAPI +BasepMoveFileCopyProgress(IN LARGE_INTEGER TotalFileSize, + IN LARGE_INTEGER TotalBytesTransferred, + IN LARGE_INTEGER StreamSize, + IN LARGE_INTEGER StreamBytesTransferred, + IN DWORD dwStreamNumber, + IN DWORD dwCallbackReason, + IN HANDLE hSourceFile, + IN HANDLE hDestinationFile, + IN LPVOID lpData OPTIONAL) +{ + DWORD Ret = 0; + PCOPY_PROGRESS_CONTEXT Context = (PCOPY_PROGRESS_CONTEXT)lpData; + + if (Context->Flags & MOVEFILE_WRITE_THROUGH) { - if (flags & MOVEFILE_REPLACE_EXISTING) - *p++ = '!'; - memcpy( p, dest_name.Buffer, len2 ); - DataSize += len2; + if (!dwCallbackReason) + { + if (StreamBytesTransferred.QuadPart == StreamSize.QuadPart) + { + FlushFileBuffers(hDestinationFile); + } + } } - else + + if (Context->UserRoutine) { - *p = 0; - DataSize += sizeof(WCHAR); + Ret = Context->UserRoutine(TotalFileSize, + TotalBytesTransferred, + StreamSize, + StreamBytesTransferred, + dwStreamNumber, + dwCallbackReason, + hSourceFile, + hDestinationFile, + Context->UserData); } - /* add final null */ - p = (WCHAR *)(Buffer + DataSize); - *p = 0; - DataSize += sizeof(WCHAR); - - rc = NT_SUCCESS(NtSetValueKey(Reboot, &nameW, 0, REG_MULTI_SZ, Buffer + info_size, DataSize - info_size)); - - Quit: - RtlFreeHeap(RtlGetProcessHeap(), 0, source_name.Buffer); - if (dest_name.Buffer) - RtlFreeHeap(RtlGetProcessHeap(), 0, dest_name.Buffer); - NtClose(Reboot); - if(Buffer) - HeapFree(GetProcessHeap(), 0, Buffer); - return(rc); + return Ret; } @@ -226,465 +246,329 @@ static BOOL add_boot_rename_entry( LPCWSTR source, LPCWSTR dest, DWORD flags ) */ BOOL WINAPI -MoveFileWithProgressW ( - LPCWSTR lpExistingFileName, - LPCWSTR lpNewFileName, - LPPROGRESS_ROUTINE lpProgressRoutine, - LPVOID lpData, - DWORD dwFlags - ) +MoveFileWithProgressW(IN LPCWSTR lpExistingFileName, + IN LPCWSTR lpNewFileName, + IN LPPROGRESS_ROUTINE lpProgressRoutine, + IN LPVOID lpData, + IN DWORD dwFlags) { - HANDLE hFile = NULL, hNewFile = NULL; - IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS Status; + PWSTR NewBuffer; + IO_STATUS_BLOCK IoStatusBlock; + COPY_PROGRESS_CONTEXT CopyContext; OBJECT_ATTRIBUTES ObjectAttributes; - PFILE_RENAME_INFORMATION FileRename; - NTSTATUS errCode; - BOOL Result; - UNICODE_STRING DstPathU; - BOOL folder = FALSE; + PFILE_RENAME_INFORMATION RenameInfo; + UNICODE_STRING NewPathU, ExistingPathU; + FILE_ATTRIBUTE_TAG_INFORMATION FileAttrTagInfo; + HANDLE SourceHandle = INVALID_HANDLE_VALUE, NewHandle, ExistingHandle; + BOOL Ret = FALSE, ReplaceIfExists, DelayUntilReboot, AttemptReopenWithoutReparse; - TRACE("MoveFileWithProgressW()\n"); + DPRINT("MoveFileWithProgressW(%S, %S, %p, %p, %x)\n", lpExistingFileName, lpNewFileName, lpProgressRoutine, lpData, dwFlags); - if (dwFlags & MOVEFILE_DELAY_UNTIL_REBOOT) - return add_boot_rename_entry( lpExistingFileName, lpNewFileName, dwFlags ); + NewPathU.Buffer = NULL; + ExistingPathU.Buffer = NULL; -// if (dwFlags & MOVEFILE_WRITE_THROUGH) -// FIXME("MOVEFILE_WRITE_THROUGH unimplemented\n"); + _SEH3_TRY + { + /* Don't allow renaming to a disk */ + if (lpNewFileName && RtlIsDosDeviceName_U(lpNewFileName)) + { + BaseSetLastNTError(STATUS_OBJECT_NAME_COLLISION); + _SEH3_LEAVE; + } - if (!lpNewFileName) - return DeleteFileW(lpExistingFileName); + ReplaceIfExists = !!(dwFlags & MOVEFILE_REPLACE_EXISTING); - /* validate & translate the filename */ - if (!RtlDosPathNameToNtPathName_U (lpNewFileName, - &DstPathU, - NULL, - NULL)) - { - WARN("Invalid destination path\n"); - SetLastError(ERROR_PATH_NOT_FOUND); - return FALSE; - } + /* Get file path */ + if (!RtlDosPathNameToNtPathName_U(lpExistingFileName, &ExistingPathU, NULL, NULL)) + { + BaseSetLastNTError(STATUS_OBJECT_PATH_NOT_FOUND); + _SEH3_LEAVE; + } - InitializeObjectAttributes(&ObjectAttributes, - &DstPathU, - OBJ_CASE_INSENSITIVE, + /* Sanitize input */ + DelayUntilReboot = !!(dwFlags & MOVEFILE_DELAY_UNTIL_REBOOT); + if (DelayUntilReboot && (dwFlags & MOVEFILE_CREATE_HARDLINK)) + { + BaseSetLastNTError(STATUS_INVALID_PARAMETER); + _SEH3_LEAVE; + } + + /* Unless we manage a proper opening, we'll attempt to reopen without reparse support */ + AttemptReopenWithoutReparse = TRUE; + InitializeObjectAttributes(&ObjectAttributes, + &ExistingPathU, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + /* Attempt to open source file */ + Status = NtOpenFile(&SourceHandle, + FILE_READ_ATTRIBUTES | DELETE | SYNCHRONIZE, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN_FOR_BACKUP_INTENT | ((dwFlags & MOVEFILE_WRITE_THROUGH) ? FILE_WRITE_THROUGH : 0)); + if (!NT_SUCCESS(Status)) + { + /* If we failed and the file doesn't exist, don't attempt to reopen without reparse */ + if (DelayUntilReboot && + (Status == STATUS_SHARING_VIOLATION || Status == STATUS_OBJECT_NAME_NOT_FOUND || Status == STATUS_OBJECT_PATH_NOT_FOUND)) + { + /* Here we don't fail completely, as we postpone the operation to reboot + * File might exist afterwards, and we don't need a handle here + */ + SourceHandle = INVALID_HANDLE_VALUE; + AttemptReopenWithoutReparse = FALSE; + } + /* If we failed for any reason than unsupported reparse, fail completely */ + else if (Status != STATUS_INVALID_PARAMETER) + { + BaseSetLastNTError(Status); + _SEH3_LEAVE; + } + } + else + { + /* We managed to open, so query information */ + Status = NtQueryInformationFile(SourceHandle, + &IoStatusBlock, + &FileAttrTagInfo, + sizeof(FILE_ATTRIBUTE_TAG_INFORMATION), + FileAttributeTagInformation); + if (!NT_SUCCESS(Status)) + { + /* Do not tolerate any other error than something related to not supported operation */ + if (Status != STATUS_NOT_IMPLEMENTED && Status != STATUS_INVALID_PARAMETER) + { + BaseSetLastNTError(Status); + _SEH3_LEAVE; + } + + /* Not a reparse point, no need to reopen, it's fine */ + AttemptReopenWithoutReparse = FALSE; + } + /* Validate the reparse point (do we support it?) */ + else if (FileAttrTagInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && + FileAttrTagInfo.ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) + { + NtClose(SourceHandle); + SourceHandle = INVALID_HANDLE_VALUE; + } + } + + /* Simply reopen if required */ + if (AttemptReopenWithoutReparse) + { + Status = NtOpenFile(&SourceHandle, + DELETE | SYNCHRONIZE, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_WRITE, + ((dwFlags & MOVEFILE_WRITE_THROUGH) ? FILE_WRITE_THROUGH : 0)); + if (!NT_SUCCESS(Status)) + { + BaseSetLastNTError(Status); + _SEH3_LEAVE; + } + } + + /* Nullify string if we're to use it */ + if (DelayUntilReboot && !lpNewFileName) + { + RtlInitUnicodeString(&NewPathU, 0); + } + /* Check whether path exists */ + else if (!RtlDosPathNameToNtPathName_U(lpNewFileName, &NewPathU, 0, 0)) + { + BaseSetLastNTError(STATUS_OBJECT_PATH_NOT_FOUND); + _SEH3_LEAVE; + } + + /* Handle postponed renaming */ + if (DelayUntilReboot) + { + /* If new file exists and we're allowed to replace, then mark the path with ! */ + if (ReplaceIfExists && NewPathU.Length) + { + NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, NewPathU.Length + sizeof(WCHAR)); + if (NewBuffer == NULL) + { + BaseSetLastNTError(STATUS_NO_MEMORY); + _SEH3_LEAVE; + } + + NewBuffer[0] = L'!'; + RtlCopyMemory(&NewBuffer[1], NewPathU.Buffer, NewPathU.Length); + NewPathU.Length += sizeof(WCHAR); + NewPathU.MaximumLength += sizeof(WCHAR); + RtlFreeHeap(RtlGetProcessHeap(), 0, NewPathU.Buffer); + NewPathU.Buffer = NewBuffer; + } + + /* Check whether 'copy' renaming is allowed if required */ + if (RtlDetermineDosPathNameType_U(lpExistingFileName) == RtlPathTypeUncAbsolute || dwFlags & MOVEFILE_COPY_ALLOWED) + { + Status = STATUS_INVALID_PARAMETER; + } + else + { + /* First, probe 2nd key to see whether it exists - if so, it will be appended there */ + Status = BasepMoveFileDelayed(&ExistingPathU, &NewPathU, 2, FALSE); + if (Status == STATUS_OBJECT_NAME_NOT_FOUND) + { + /* If doesn't exist, append to first key first, creating it if it doesn't exist */ + Status = BasepMoveFileDelayed(&ExistingPathU, &NewPathU, 1, TRUE); + + if (Status == STATUS_INSUFFICIENT_RESOURCES) + { + /* If it failed because it's too big, then create 2nd key and put it there */ + Status = BasepMoveFileDelayed(&ExistingPathU, &NewPathU, 2, TRUE); + } + } + } + + /* If we failed at some point, return the error */ + if (!NT_SUCCESS(Status)) + { + BaseSetLastNTError(Status); + _SEH3_LEAVE; + } + + Ret = TRUE; + _SEH3_LEAVE; + } + + /* At that point, we MUST have a source handle */ + ASSERT(SourceHandle != INVALID_HANDLE_VALUE); + + /* Allocate renaming buffer and fill it */ + RenameInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, NewPathU.Length + sizeof(FILE_RENAME_INFORMATION)); + if (RenameInfo == NULL) + { + BaseSetLastNTError(STATUS_NO_MEMORY); + _SEH3_LEAVE; + } + + RtlCopyMemory(&RenameInfo->FileName, NewPathU.Buffer, NewPathU.Length); + RenameInfo->ReplaceIfExists = ReplaceIfExists; + RenameInfo->RootDirectory = 0; + RenameInfo->FileNameLength = NewPathU.Length; + + /* Attempt to rename the file */ + Status = NtSetInformationFile(SourceHandle, + &IoStatusBlock, + RenameInfo, + NewPathU.Length + sizeof(FILE_RENAME_INFORMATION), + ((dwFlags & MOVEFILE_CREATE_HARDLINK) ? FileLinkInformation : FileRenameInformation)); + RtlFreeHeap(RtlGetProcessHeap(), 0, RenameInfo); + if (NT_SUCCESS(Status)) + { + /* If it succeed, all fine, quit */ + Ret = TRUE; + _SEH3_LEAVE; + } + /* If we failed for any other reason than not the same device, fail + * If we failed because of different devices, only allow renaming if user allowed copy + */ + if (Status != STATUS_NOT_SAME_DEVICE || !(dwFlags & MOVEFILE_COPY_ALLOWED)) + { + /* ReactOS hack! To be removed once all FSD have proper renaming support + * Just leave status to error and leave + */ + if (Status == STATUS_NOT_IMPLEMENTED) + { + DPRINT1("Forcing copy, renaming not supported by FSD\n"); + } + else + { + BaseSetLastNTError(Status); + _SEH3_LEAVE; + } + } + + /* Close source file */ + NtClose(SourceHandle); + SourceHandle = INVALID_HANDLE_VALUE; + + /* Issue the copy of the file */ + CopyContext.Flags = dwFlags; + CopyContext.UserRoutine = lpProgressRoutine; + CopyContext.UserData = lpData; + NewHandle = INVALID_HANDLE_VALUE; + ExistingHandle = INVALID_HANDLE_VALUE; + + Ret = BasepCopyFileExW(lpExistingFileName, + lpNewFileName, + BasepMoveFileCopyProgress, + &CopyContext, NULL, - NULL); + (ReplaceIfExists == 0) | COPY_FILE_OPEN_SOURCE_FOR_WRITE, + 0, + &ExistingHandle, + &NewHandle); + if (!Ret) + { + /* If it failed, don't leak any handle */ + if (ExistingHandle != INVALID_HANDLE_VALUE) + { + CloseHandle(ExistingHandle); + ExistingHandle = INVALID_HANDLE_VALUE; + } + } + else if (ExistingHandle != INVALID_HANDLE_VALUE) + { + if (NewHandle != INVALID_HANDLE_VALUE) + { + /* If copying succeed, notify */ + Status = BasepNotifyTrackingService(&ExistingHandle, &ObjectAttributes, NewHandle, &NewPathU); + if (!NT_SUCCESS(Status)) + { + /* Fail in case it had to succeed */ + if (dwFlags & MOVEFILE_FAIL_IF_NOT_TRACKABLE) + { + if (NewHandle != INVALID_HANDLE_VALUE) + CloseHandle(NewHandle); + NewHandle = INVALID_HANDLE_VALUE; + DeleteFileW(lpNewFileName); + Ret = FALSE; + BaseSetLastNTError(Status); + } + } + } - errCode = NtOpenFile( &hNewFile, - GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, - &ObjectAttributes, - &IoStatusBlock, - 0, - FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | - ((dwFlags & MOVEFILE_WRITE_THROUGH) ? FILE_WRITE_THROUGH : 0) ); + CloseHandle(ExistingHandle); + ExistingHandle = INVALID_HANDLE_VALUE; + } - if (NT_SUCCESS(errCode)) /* Destination exists */ - { - NtClose(hNewFile); + /* In case copy worked, close file */ + if (NewHandle != INVALID_HANDLE_VALUE) + { + CloseHandle(NewHandle); + NewHandle = INVALID_HANDLE_VALUE; + } - if (!(dwFlags & MOVEFILE_REPLACE_EXISTING)) + /* If it succeed, delete source file */ + if (Ret) { - SetLastError(ERROR_ALREADY_EXISTS); - return FALSE; - } - else if (GetFileAttributesW(lpNewFileName) & FILE_ATTRIBUTE_DIRECTORY) - { - SetLastError(ERROR_ACCESS_DENIED); - return FALSE; - } + if (!DeleteFileW(lpExistingFileName)) + { + /* Reset file attributes if required */ + SetFileAttributesW(lpExistingFileName, FILE_ATTRIBUTE_NORMAL); + DeleteFileW(lpExistingFileName); + } + } } + _SEH3_FINALLY + { + if (SourceHandle != INVALID_HANDLE_VALUE) + NtClose(SourceHandle); + + RtlFreeHeap(RtlGetProcessHeap(), 0, ExistingPathU.Buffer); + RtlFreeHeap(RtlGetProcessHeap(), 0, NewPathU.Buffer); + } + _SEH3_END; - hFile = CreateFileW (lpExistingFileName, - GENERIC_ALL, - FILE_SHARE_WRITE|FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS | - ((dwFlags & MOVEFILE_WRITE_THROUGH) ? FILE_FLAG_WRITE_THROUGH : 0), - NULL); - - if (hFile == INVALID_HANDLE_VALUE) - { - return FALSE; - } - - FileRename = RtlAllocateHeap( - RtlGetProcessHeap(), - HEAP_ZERO_MEMORY, - sizeof(FILE_RENAME_INFORMATION) + DstPathU.Length); - if( !FileRename ) { - CloseHandle(hFile); - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return FALSE; - } - if( dwFlags & MOVEFILE_REPLACE_EXISTING ) { - FileRename->ReplaceIfExists = TRUE; - } - else { - FileRename->ReplaceIfExists = FALSE; - } - - - memcpy(FileRename->FileName, DstPathU.Buffer, DstPathU.Length); - RtlFreeHeap (RtlGetProcessHeap (), - 0, - DstPathU.Buffer); - - FileRename->FileNameLength = DstPathU.Length; - errCode = NtSetInformationFile (hFile, - &IoStatusBlock, - FileRename, - sizeof(FILE_RENAME_INFORMATION) + DstPathU.Length, - FileRenameInformation); - CloseHandle(hFile); - RtlFreeHeap(RtlGetProcessHeap(), 0, FileRename); - - if (GetFileAttributesW(lpExistingFileName) & FILE_ATTRIBUTE_DIRECTORY) - { - folder = TRUE; - } - - - /* - * FIXME: - * Fail now move the folder - * Before we fail at CreateFileW - */ - - - if (NT_SUCCESS(errCode)) - { - Result = TRUE; - } - else - { - if (folder==FALSE) - { - Result = CopyFileExW (lpExistingFileName, - lpNewFileName, - lpProgressRoutine, - lpData, - NULL, - (dwFlags & MOVEFILE_REPLACE_EXISTING) ? 0 : COPY_FILE_FAIL_IF_EXISTS); - if (Result) - { - /* Cleanup the source file */ - Result = DeleteFileW (lpExistingFileName); - } - } - else - { - /* move folder code start */ - WIN32_FIND_DATAW findBuffer; - LPWSTR lpExistingFileName2 = NULL; - LPWSTR lpNewFileName2 = NULL; - LPWSTR lpDeleteFile = NULL; - INT size; - INT size2; - BOOL loop = TRUE; - BOOL Result = FALSE; - INT max_size = MAX_PATH; - - - /* Build the string */ - size = wcslen(lpExistingFileName); - if (size+6> max_size) - max_size = size + 6; - - lpDeleteFile = (LPWSTR) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,max_size * sizeof(WCHAR)); - if (lpDeleteFile == NULL) - return FALSE; - - lpNewFileName2 = (LPWSTR) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,max_size * sizeof(WCHAR)); - if (lpNewFileName2 == NULL) - { - HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile); - return FALSE; - } - - lpExistingFileName2 = (LPWSTR) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,max_size * sizeof(WCHAR)); - if (lpExistingFileName2 == NULL) - { - HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2); - HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile); - return FALSE; - } - - wcscpy( (WCHAR *)lpExistingFileName2,lpExistingFileName); - wcscpy( (WCHAR *)&lpExistingFileName2[size],L"\\*.*\0"); - - /* Get the file name */ - memset(&findBuffer,0,sizeof(WIN32_FIND_DATAW)); - hFile = FindFirstFileW(lpExistingFileName2, &findBuffer); - if (hFile == INVALID_HANDLE_VALUE) - loop=FALSE; - - if (findBuffer.cFileName[0] == L'\0') - loop=FALSE; - - - /* FIXME - * remove readonly flag from source folder and do not set the readonly flag to dest folder - */ - RemoveReadOnlyAttributeW(lpExistingFileName); - RemoveReadOnlyAttributeW(lpNewFileName); - //CreateDirectoryExW(lpExistingFileName,lpNewFileName,NULL); - CreateDirectoryW(lpNewFileName, NULL); - - /* search the files/folders and move them */ - while (loop==TRUE) - { - Result = TRUE; - - if ((!wcscmp(findBuffer.cFileName,L"..")) || (!wcscmp(findBuffer.cFileName,L"."))) - { - loop = FindNextFileW(hFile, &findBuffer); - - if (!loop) - { - size = wcslen(lpExistingFileName2)-4; - FindClose(hFile); - hFile = INVALID_HANDLE_VALUE; - - wcscpy( &lpExistingFileName2[size],L"\0"); - - if (wcsncmp(lpExistingFileName,lpExistingFileName2,size)) - { - DWORD Attributes; - - /* delete folder */ - TRACE("MoveFileWithProgressW : Delete folder : %S\n",lpDeleteFile); - - /* remove system folder flag other wise we can not delete the folder */ - Attributes = GetFileAttributesW(lpExistingFileName2); - if (Attributes != INVALID_FILE_ATTRIBUTES) - { - SetFileAttributesW(lpExistingFileName2,(Attributes & ~FILE_ATTRIBUTE_SYSTEM)); - } - - RemoveReadOnlyAttributeW(lpExistingFileName2); - - Result = RemoveDirectoryW(lpExistingFileName2); - if (Result == FALSE) - break; - - loop=TRUE; - size = wcslen(lpExistingFileName); - - if (size+6>max_size) - { - if (lpNewFileName2 != NULL) - HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2); - - if (lpExistingFileName2 != NULL) - HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2); - - if (lpDeleteFile != NULL) - HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile); - - return FALSE; - } - - wcscpy( lpExistingFileName2,lpExistingFileName); - wcscpy( &lpExistingFileName2[size],L"\\*.*\0"); - - /* Get the file name */ - memset(&findBuffer,0,sizeof(WIN32_FIND_DATAW)); - hFile = FindFirstFileW(lpExistingFileName2, &findBuffer); - } - } - continue; - } - - if (findBuffer.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - - /* Build the new src string */ - size = wcslen(findBuffer.cFileName); - size2= wcslen(lpExistingFileName2); - - if (size2+size+6>max_size) - { - FindClose(hFile); - - if (lpNewFileName2 != NULL) - HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2); - - if (lpExistingFileName2 != NULL) - HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2); - - if (lpDeleteFile != NULL) - HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile); - - return FALSE; - } - - wcscpy( &lpExistingFileName2[size2-3],findBuffer.cFileName); - wcscpy( &lpExistingFileName2[size2+size-3],L"\0"); - - - /* Continue */ - wcscpy( lpDeleteFile,lpExistingFileName2); - wcscpy( &lpExistingFileName2[size2+size-3],L"\\*.*\0"); - - - /* Build the new dst string */ - size = wcslen(lpExistingFileName2) + wcslen(lpNewFileName); - size2 = wcslen(lpExistingFileName); - - if (size>max_size) - { - FindClose(hFile); - - if (lpNewFileName2 != NULL) - HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2); - - if (lpExistingFileName2 != NULL) - HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2); - - if (lpDeleteFile != NULL) - HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile); - - return FALSE; - } - - wcscpy( lpNewFileName2,lpNewFileName); - size = wcslen(lpNewFileName); - wcscpy( &lpNewFileName2[size], &lpExistingFileName2[size2]); - size = wcslen(lpNewFileName2); - wcscpy( &lpNewFileName2[size-4],L"\0"); - - /* Create Folder */ - - /* FIXME - * remove readonly flag from source folder and do not set the readonly flag to dest folder - */ - RemoveReadOnlyAttributeW(lpDeleteFile); - RemoveReadOnlyAttributeW(lpNewFileName2); - - CreateDirectoryW(lpNewFileName2,NULL); - //CreateDirectoryExW(lpDeleteFile, lpNewFileName2,NULL); - - - /* set new search path from src string */ - FindClose(hFile); - memset(&findBuffer,0,sizeof(WIN32_FIND_DATAW)); - hFile = FindFirstFileW(lpExistingFileName2, &findBuffer); - } - else - { - - /* Build the new string */ - size = wcslen(findBuffer.cFileName); - size2= wcslen(lpExistingFileName2); - wcscpy( lpDeleteFile,lpExistingFileName2); - wcscpy( &lpDeleteFile[size2-3],findBuffer.cFileName); - - /* Build dest string */ - size = wcslen(lpDeleteFile) + wcslen(lpNewFileName); - size2 = wcslen(lpExistingFileName); - - if (size>max_size) - { - FindClose(hFile); - - if (lpNewFileName2 != NULL) - HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2); - - if (lpExistingFileName2 != NULL) - HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2); - - if (lpDeleteFile != NULL) - HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile); - - return FALSE; - } - - wcscpy( lpNewFileName2,lpNewFileName); - size = wcslen(lpNewFileName); - wcscpy(&lpNewFileName2[size],&lpDeleteFile[size2]); - - - /* overrite existsen file, if the file got the flag have readonly - * we need reomve that flag - */ - - /* copy file */ - - TRACE("MoveFileWithProgressW : Copy file : %S to %S\n",lpDeleteFile, lpNewFileName2); - RemoveReadOnlyAttributeW(lpDeleteFile); - RemoveReadOnlyAttributeW(lpNewFileName2); - - Result = CopyFileExW (lpDeleteFile, - lpNewFileName2, - lpProgressRoutine, - lpData, - NULL, - 0); - - if (Result == FALSE) - break; - - /* delete file */ - TRACE("MoveFileWithProgressW : remove readonly flag from file : %S\n",lpNewFileName2); - Result = RemoveReadOnlyAttributeW(lpDeleteFile); - if (Result == FALSE) - break; - - TRACE("MoveFileWithProgressW : Delete file : %S\n",lpDeleteFile); - Result = DeleteFileW(lpDeleteFile); - if (Result == FALSE) - break; - - } - loop = FindNextFileW(hFile, &findBuffer); - } - - - /* Remove last folder */ - if ((loop == FALSE) && (Result != FALSE)) - { - DWORD Attributes; - - Attributes = GetFileAttributesW(lpDeleteFile); - if (Attributes != INVALID_FILE_ATTRIBUTES) - { - SetFileAttributesW(lpDeleteFile,(Attributes & ~FILE_ATTRIBUTE_SYSTEM)); - } - - Result = RemoveDirectoryW(lpExistingFileName); - } - - /* Cleanup */ - FindClose(hFile); - - if (lpNewFileName2 != NULL) - { - HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2); - lpNewFileName2 = NULL; - } - - if (lpExistingFileName2 != NULL) - { - HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2); - lpExistingFileName2 = NULL; - } - - if (lpDeleteFile != NULL) - { - HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile); - lpDeleteFile = NULL; - } - - return Result; - - // end move folder code - } - } - - - return Result; + return Ret; } @@ -693,33 +577,39 @@ MoveFileWithProgressW ( */ BOOL WINAPI -MoveFileWithProgressA ( - LPCSTR lpExistingFileName, - LPCSTR lpNewFileName, - LPPROGRESS_ROUTINE lpProgressRoutine, - LPVOID lpData, - DWORD dwFlags - ) +MoveFileWithProgressA(IN LPCSTR lpExistingFileName, + IN LPCSTR lpNewFileName OPTIONAL, + IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL, + IN LPVOID lpData OPTIONAL, + IN DWORD dwFlags) { - PWCHAR ExistingFileNameW; - PWCHAR NewFileNameW; - BOOL ret; + BOOL Ret; + UNICODE_STRING ExistingFileNameW, NewFileNameW; - if (!(ExistingFileNameW = FilenameA2W(lpExistingFileName, FALSE))) - return FALSE; + if (!Basep8BitStringToDynamicUnicodeString(&ExistingFileNameW, lpExistingFileName)) + { + return FALSE; + } - if (!(NewFileNameW= FilenameA2W(lpNewFileName, TRUE))) - return FALSE; + if (lpNewFileName) + { + if (!Basep8BitStringToDynamicUnicodeString(&NewFileNameW, lpNewFileName)) + { + RtlFreeUnicodeString(&ExistingFileNameW); + return FALSE; + } + } + else + { + NewFileNameW.Buffer = NULL; + } - ret = MoveFileWithProgressW (ExistingFileNameW , - NewFileNameW, - lpProgressRoutine, - lpData, - dwFlags); + Ret = MoveFileWithProgressW(ExistingFileNameW.Buffer, NewFileNameW.Buffer, lpProgressRoutine, lpData, dwFlags); - RtlFreeHeap (RtlGetProcessHeap (), 0, NewFileNameW); + RtlFreeUnicodeString(&ExistingFileNameW); + RtlFreeUnicodeString(&NewFileNameW); - return ret; + return Ret; } @@ -728,14 +618,14 @@ MoveFileWithProgressA ( */ BOOL WINAPI -MoveFileW ( - LPCWSTR lpExistingFileName, - LPCWSTR lpNewFileName - ) +MoveFileW(IN LPCWSTR lpExistingFileName, + IN LPCWSTR lpNewFileName) { - return MoveFileExW (lpExistingFileName, - lpNewFileName, - MOVEFILE_COPY_ALLOWED); + return MoveFileWithProgressW(lpExistingFileName, + lpNewFileName, + NULL, + NULL, + MOVEFILE_COPY_ALLOWED); } @@ -744,17 +634,15 @@ MoveFileW ( */ BOOL WINAPI -MoveFileExW ( - LPCWSTR lpExistingFileName, - LPCWSTR lpNewFileName, - DWORD dwFlags - ) +MoveFileExW(IN LPCWSTR lpExistingFileName, + IN LPCWSTR lpNewFileName OPTIONAL, + IN DWORD dwFlags) { - return MoveFileWithProgressW (lpExistingFileName, - lpNewFileName, - NULL, - NULL, - dwFlags); + return MoveFileWithProgressW(lpExistingFileName, + lpNewFileName, + NULL, + NULL, + dwFlags); } @@ -763,14 +651,14 @@ MoveFileExW ( */ BOOL WINAPI -MoveFileA ( - LPCSTR lpExistingFileName, - LPCSTR lpNewFileName - ) +MoveFileA(IN LPCSTR lpExistingFileName, + IN LPCSTR lpNewFileName) { - return MoveFileExA (lpExistingFileName, - lpNewFileName, - MOVEFILE_COPY_ALLOWED); + return MoveFileWithProgressA(lpExistingFileName, + lpNewFileName, + NULL, + NULL, + MOVEFILE_COPY_ALLOWED); } @@ -779,17 +667,15 @@ MoveFileA ( */ BOOL WINAPI -MoveFileExA ( - LPCSTR lpExistingFileName, - LPCSTR lpNewFileName, - DWORD dwFlags - ) +MoveFileExA(IN LPCSTR lpExistingFileName, + IN LPCSTR lpNewFileName OPTIONAL, + IN DWORD dwFlags) { - return MoveFileWithProgressA (lpExistingFileName, - lpNewFileName, - NULL, - NULL, - dwFlags); + return MoveFileWithProgressA(lpExistingFileName, + lpNewFileName, + NULL, + NULL, + dwFlags); } /* @@ -797,50 +683,57 @@ MoveFileExA ( */ BOOL WINAPI -ReplaceFileA( - LPCSTR lpReplacedFileName, - LPCSTR lpReplacementFileName, - LPCSTR lpBackupFileName, - DWORD dwReplaceFlags, - LPVOID lpExclude, - LPVOID lpReserved - ) +ReplaceFileA(IN LPCSTR lpReplacedFileName, + IN LPCSTR lpReplacementFileName, + IN LPCSTR lpBackupFileName OPTIONAL, + IN DWORD dwReplaceFlags, + IN LPVOID lpExclude, + IN LPVOID lpReserved) { - WCHAR *replacedW, *replacementW, *backupW = NULL; - BOOL ret; + BOOL Ret; + UNICODE_STRING ReplacedFileNameW, ReplacementFileNameW, BackupFileNameW; - /* This function only makes sense when the first two parameters are defined */ - if (!lpReplacedFileName || !(replacedW = FilenameA2W(lpReplacedFileName, TRUE))) + if (!lpReplacedFileName || !lpReplacementFileName || lpExclude || lpReserved || dwReplaceFlags & ~(REPLACEFILE_WRITE_THROUGH | REPLACEFILE_IGNORE_MERGE_ERRORS)) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } - if (!lpReplacementFileName || !(replacementW = FilenameA2W(lpReplacementFileName, TRUE))) + if (!Basep8BitStringToDynamicUnicodeString(&ReplacedFileNameW, lpReplacedFileName)) { - HeapFree(GetProcessHeap(), 0, replacedW); - SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } - /* The backup parameter, however, is optional */ + if (!Basep8BitStringToDynamicUnicodeString(&ReplacementFileNameW, lpReplacementFileName)) + { + RtlFreeUnicodeString(&ReplacedFileNameW); + return FALSE; + } + if (lpBackupFileName) { - if (!(backupW = FilenameA2W(lpBackupFileName, TRUE))) + if (!Basep8BitStringToDynamicUnicodeString(&BackupFileNameW, lpBackupFileName)) { - HeapFree(GetProcessHeap(), 0, replacedW); - HeapFree(GetProcessHeap(), 0, replacementW); - SetLastError(ERROR_INVALID_PARAMETER); + RtlFreeUnicodeString(&ReplacementFileNameW); + RtlFreeUnicodeString(&ReplacedFileNameW); return FALSE; } } + else + { + BackupFileNameW.Buffer = NULL; + } - ret = ReplaceFileW(replacedW, replacementW, backupW, dwReplaceFlags, lpExclude, lpReserved); - HeapFree(GetProcessHeap(), 0, replacedW); - HeapFree(GetProcessHeap(), 0, replacementW); - HeapFree(GetProcessHeap(), 0, backupW); + Ret = ReplaceFileW(ReplacedFileNameW.Buffer, ReplacementFileNameW.Buffer, BackupFileNameW.Buffer, dwReplaceFlags, 0, 0); - return ret; + if (lpBackupFileName) + { + RtlFreeUnicodeString(&BackupFileNameW); + } + RtlFreeUnicodeString(&ReplacementFileNameW); + RtlFreeUnicodeString(&ReplacedFileNameW); + + return Ret; } /* diff --git a/reactos/drivers/filesystems/fastfat/close.c b/reactos/drivers/filesystems/fastfat/close.c index 007c3c5f444..3ad1a9f6091 100644 --- a/reactos/drivers/filesystems/fastfat/close.c +++ b/reactos/drivers/filesystems/fastfat/close.c @@ -1,7 +1,7 @@ /* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel - * FILE: drivers/fs/vfat/close.c + * FILE: drivers/filesystems/fastfat/close.c * PURPOSE: VFAT Filesystem * PROGRAMMER: Jason Filby (jasonfilby@yahoo.com) */ @@ -51,7 +51,7 @@ VfatCloseFile( { if (pFcb->Flags & FCB_DELETE_PENDING) { - VfatDelEntry(DeviceExt, pFcb); + VfatDelEntry(DeviceExt, pFcb, NULL); FsRtlNotifyFullReportChange(DeviceExt->NotifySync, &(DeviceExt->NotifyList), diff --git a/reactos/drivers/filesystems/fastfat/create.c b/reactos/drivers/filesystems/fastfat/create.c index 468b902977c..9352d1c0820 100644 --- a/reactos/drivers/filesystems/fastfat/create.c +++ b/reactos/drivers/filesystems/fastfat/create.c @@ -18,9 +18,10 @@ */ /* * PROJECT: ReactOS kernel - * FILE: drivers/fs/vfat/create.c + * FILE: drivers/filesystems/fastfat/create.c * PURPOSE: VFAT Filesystem * PROGRAMMER: Jason Filby (jasonfilby@yahoo.com) + * Pierre Schweitzer (pierre@reactos.org) */ /* INCLUDES *****************************************************************/ @@ -352,7 +353,6 @@ VfatOpenFile( PUNICODE_STRING PathNameU, PFILE_OBJECT FileObject, ULONG RequestedDisposition, - BOOLEAN OpenTargetDir, PVFATFCB *ParentFcb) { PVFATFCB Fcb; @@ -404,13 +404,6 @@ VfatOpenFile( return Status; } - /* In case we're to open target, just check whether file exist, but don't open it */ - if (OpenTargetDir) - { - vfatReleaseFCB(DeviceExt, Fcb); - return STATUS_OBJECT_NAME_COLLISION; - } - if (Fcb->Flags & FCB_DELETE_PENDING) { vfatReleaseFCB(DeviceExt, Fcb); @@ -462,11 +455,8 @@ VfatCreateFile( RequestedDisposition = ((Stack->Parameters.Create.Options >> 24) & 0xff); RequestedOptions = Stack->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS; PagingFileCreate = (Stack->Flags & SL_OPEN_PAGING_FILE) ? TRUE : FALSE; -#if 0 OpenTargetDir = (Stack->Flags & SL_OPEN_TARGET_DIRECTORY) ? TRUE : FALSE; -#else - OpenTargetDir = FALSE; -#endif + FileObject = Stack->FileObject; DeviceExt = DeviceObject->DeviceExtension; @@ -558,14 +548,22 @@ VfatCreateFile( } /* Try opening the file. */ - Status = VfatOpenFile(DeviceExt, &PathNameU, FileObject, RequestedDisposition, OpenTargetDir, &ParentFcb); - - if (OpenTargetDir) + if (!OpenTargetDir) { + Status = VfatOpenFile(DeviceExt, &PathNameU, FileObject, RequestedDisposition, &ParentFcb); + } + else + { + PVFATFCB TargetFcb; LONG idx, FileNameLen; - if (Status == STATUS_OBJECT_NAME_COLLISION) + ParentFcb = (FileObject->RelatedFileObject != NULL) ? FileObject->RelatedFileObject->FsContext : NULL; + Status = vfatGetFCBForFile(DeviceExt, &ParentFcb, &TargetFcb, &PathNameU); + + if (Status == STATUS_SUCCESS) { + ParentFcb->RefCount++; + vfatReleaseFCB(DeviceExt, TargetFcb); Irp->IoStatus.Information = FILE_EXISTS; } else @@ -593,10 +591,6 @@ VfatCreateFile( /* We don't want to include / in the name */ FileNameLen = PathNameU.Length - ((idx + 1) * sizeof(WCHAR)); - /* Try to open parent */ - PathNameU.Length -= (PathNameU.Length - idx * sizeof(WCHAR)); - Status = VfatOpenFile(DeviceExt, &PathNameU, FileObject, RequestedDisposition, FALSE, &ParentFcb); - /* Update FO just to keep file name */ /* Skip first slash */ ++idx; @@ -608,32 +602,47 @@ VfatCreateFile( /* This is a relative open and we have only the filename, so open the parent directory * It is in RelatedFileObject */ - BOOLEAN Chomp = FALSE; - PFILE_OBJECT RelatedFileObject = FileObject->RelatedFileObject; + ASSERT(FileObject->RelatedFileObject != NULL); - DPRINT("%wZ\n", &PathNameU); + /* No need to modify the FO, it already has the name */ + } - ASSERT(RelatedFileObject != NULL); + /* We're done with opening! */ + if (ParentFcb != NULL) + { + Status = vfatAttachFCBToFileObject(DeviceExt, ParentFcb, FileObject); + } - DPRINT("Relative opening\n"); - DPRINT("FileObject->RelatedFileObject->FileName: %wZ\n", &RelatedFileObject->FileName); + if (NT_SUCCESS(Status)) + { + pFcb = FileObject->FsContext; - /* VfatOpenFile() doesn't like our name ends with \, so chomp it if there's one */ - if (RelatedFileObject->FileName.Buffer[RelatedFileObject->FileName.Length / sizeof(WCHAR) - 1] == L'\\') + if (pFcb->OpenHandleCount == 0) { - Chomp = TRUE; - RelatedFileObject->FileName.Length -= sizeof(WCHAR); + IoSetShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess, + Stack->Parameters.Create.ShareAccess, + FileObject, + &pFcb->FCBShareAccess); + } + else + { + Status = IoCheckShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess, + Stack->Parameters.Create.ShareAccess, + FileObject, + &pFcb->FCBShareAccess, + FALSE); + if (!NT_SUCCESS(Status)) + { + VfatCloseFile(DeviceExt, FileObject); + return Status; + } } - /* Tricky part - fake our FO. It's NOT relative, we want to open the complete file path */ - FileObject->RelatedFileObject = NULL; - Status = VfatOpenFile(DeviceExt, &RelatedFileObject->FileName, FileObject, RequestedDisposition, FALSE, &ParentFcb); - - /* We're done opening, restore what we broke */ - FileObject->RelatedFileObject = RelatedFileObject; - if (Chomp) RelatedFileObject->FileName.Length += sizeof(WCHAR); - - /* No need to modify the FO, it already has the name */ + pFcb->OpenHandleCount++; + } + else if (ParentFcb != NULL) + { + vfatReleaseFCB(DeviceExt, ParentFcb); } return Status; @@ -673,7 +682,7 @@ VfatCreateFile( Attributes |= FILE_ATTRIBUTE_ARCHIVE; vfatSplitPathName(&PathNameU, NULL, &FileNameU); Status = VfatAddEntry(DeviceExt, &FileNameU, &pFcb, ParentFcb, RequestedOptions, - (UCHAR)(Attributes & FILE_ATTRIBUTE_VALID_FLAGS)); + (UCHAR)(Attributes & FILE_ATTRIBUTE_VALID_FLAGS), NULL); vfatReleaseFCB(DeviceExt, ParentFcb); if (NT_SUCCESS(Status)) { diff --git a/reactos/drivers/filesystems/fastfat/dirwr.c b/reactos/drivers/filesystems/fastfat/dirwr.c index 1444665fcbb..21b0e6945c0 100644 --- a/reactos/drivers/filesystems/fastfat/dirwr.c +++ b/reactos/drivers/filesystems/fastfat/dirwr.c @@ -1,8 +1,11 @@ /* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel - * FILE: drivers/fs/vfat/dirwr.c + * FILE: drivers/filesystems/fastfat/dirwr.c * PURPOSE: VFAT Filesystem : write in directory + * PROGRAMMER: Rex Jolliff (rex@lvcablemodem.com) + * Herve Poussineau (reactos@poussine.freesurf.fr) + * Pierre Schweitzer (pierre@reactos.org) * */ @@ -66,6 +69,70 @@ VfatUpdateEntry( } } +/* + * rename an existing FAT entry + */ +NTSTATUS +vfatRenameEntry( + IN PDEVICE_EXTENSION DeviceExt, + IN PVFATFCB pFcb, + IN PUNICODE_STRING FileName, + IN BOOLEAN CaseChangeOnly) +{ + OEM_STRING NameA; + ULONG StartIndex; + PVOID Context = NULL; + LARGE_INTEGER Offset; + PFATX_DIR_ENTRY pDirEntry; + UNICODE_STRING ShortName; + NTSTATUS Status; + + DPRINT("vfatRenameEntry(%p, %p, %wZ, %d)\n", DeviceExt, pFcb, FileName, CaseChangeOnly); + + if (pFcb->Flags & FCB_IS_FATX_ENTRY) + { + /* Open associated dir entry */ + StartIndex = pFcb->startIndex; + Offset.u.HighPart = 0; + Offset.u.LowPart = (StartIndex * sizeof(FATX_DIR_ENTRY) / PAGE_SIZE) * PAGE_SIZE; + if (!CcPinRead(pFcb->parentFcb->FileObject, &Offset, sizeof(FATX_DIR_ENTRY), TRUE, + &Context, (PVOID*)&pDirEntry)) + { + DPRINT1("CcPinRead(Offset %x:%x, Length %d) failed\n", Offset.u.HighPart, Offset.u.LowPart, PAGE_SIZE); + return STATUS_UNSUCCESSFUL; + } + + pDirEntry = &pDirEntry[StartIndex % (PAGE_SIZE / sizeof(FATX_DIR_ENTRY))]; + + /* Set file name */ + NameA.Buffer = (PCHAR)pDirEntry->Filename; + NameA.Length = 0; + NameA.MaximumLength = 42; + RtlUnicodeStringToOemString(&NameA, FileName, FALSE); + pDirEntry->FilenameLength = (unsigned char)NameA.Length; + + CcSetDirtyPinnedData(Context, NULL); + CcUnpinData(Context); + + /* Update FCB */ + ShortName.Length = 0; + ShortName.MaximumLength = 0; + ShortName.Buffer = NULL; + Status = vfatUpdateFCB(DeviceExt, pFcb, FileName, &ShortName, pFcb->parentFcb); + if (NT_SUCCESS(Status)) + { + CcPurgeCacheSection(&pFcb->parentFcb->SectionObjectPointers, NULL, 0, FALSE); + } + + return Status; + } + else + { + /* This we cannot handle properly, move file - would likely need love */ + return VfatMoveEntry(DeviceExt, pFcb, FileName, pFcb->parentFcb); + } +} + /* * try to find contiguous entries frees in directory, * extend a directory if is neccesary @@ -200,7 +267,8 @@ FATAddEntry( IN PVFATFCB* Fcb, IN PVFATFCB ParentFcb, IN ULONG RequestedOptions, - IN UCHAR ReqAttr) + IN UCHAR ReqAttr, + IN PVFAT_MOVE_CONTEXT MoveContext) { PVOID Context = NULL; PFAT_DIR_ENTRY pFatEntry; @@ -385,6 +453,13 @@ FATAddEntry( DirContext.DirEntry.Fat.UpdateDate = DirContext.DirEntry.Fat.CreationDate; DirContext.DirEntry.Fat.UpdateTime = DirContext.DirEntry.Fat.CreationTime; DirContext.DirEntry.Fat.AccessDate = DirContext.DirEntry.Fat.CreationDate; + /* If it's moving, preserve creation time and file size */ + if (MoveContext != NULL) + { + DirContext.DirEntry.Fat.CreationDate = MoveContext->CreationDate; + DirContext.DirEntry.Fat.CreationTime = MoveContext->CreationTime; + DirContext.DirEntry.Fat.FileSize = MoveContext->FileSize; + } if (needLong) { @@ -423,17 +498,36 @@ FATAddEntry( DirContext.DirIndex = DirContext.StartIndex + nbSlots - 1; if (RequestedOptions & FILE_DIRECTORY_FILE) { - CurrentCluster = 0; - Status = NextCluster(DeviceExt, 0, &CurrentCluster, TRUE); - if (CurrentCluster == 0xffffffff || !NT_SUCCESS(Status)) + /* If we aren't moving, use next */ + if (MoveContext == NULL) { - ExFreePoolWithTag(Buffer, TAG_VFAT); - if (!NT_SUCCESS(Status)) + CurrentCluster = 0; + Status = NextCluster(DeviceExt, 0, &CurrentCluster, TRUE); + if (CurrentCluster == 0xffffffff || !NT_SUCCESS(Status)) { - return Status; + ExFreePoolWithTag(Buffer, TAG_VFAT); + if (!NT_SUCCESS(Status)) + { + return Status; + } + return STATUS_DISK_FULL; } - return STATUS_DISK_FULL; } + else + { + CurrentCluster = MoveContext->FirstCluster; + } + + if (DeviceExt->FatInfo.FatType == FAT32) + { + DirContext.DirEntry.Fat.FirstClusterHigh = (unsigned short)(CurrentCluster >> 16); + } + DirContext.DirEntry.Fat.FirstCluster = (unsigned short)CurrentCluster; + } + else if (MoveContext != NULL) + { + CurrentCluster = MoveContext->FirstCluster; + if (DeviceExt->FatInfo.FatType == FAT32) { DirContext.DirEntry.Fat.FirstClusterHigh = (unsigned short)(CurrentCluster >> 16); @@ -491,7 +585,17 @@ FATAddEntry( CcSetDirtyPinnedData(Context, NULL); CcUnpinData(Context); - Status = vfatMakeFCBFromDirEntry(DeviceExt, ParentFcb, &DirContext, Fcb); + if (MoveContext != NULL) + { + /* We're modifying an existing FCB - likely rename/move */ + Status = vfatUpdateFCB(DeviceExt, *Fcb, &DirContext.LongNameU, &DirContext.ShortNameU, ParentFcb); + (*Fcb)->dirIndex = DirContext.DirIndex; + (*Fcb)->startIndex = DirContext.StartIndex; + } + else + { + Status = vfatMakeFCBFromDirEntry(DeviceExt, ParentFcb, &DirContext, Fcb); + } if (!NT_SUCCESS(Status)) { ExFreePoolWithTag(Buffer, TAG_VFAT); @@ -510,13 +614,17 @@ FATAddEntry( ExFreePoolWithTag(Buffer, TAG_VFAT); return STATUS_UNSUCCESSFUL; } - /* clear the new directory cluster */ - RtlZeroMemory(pFatEntry, DeviceExt->FatInfo.BytesPerCluster); - /* create '.' and '..' */ - RtlCopyMemory(&pFatEntry[0].Attrib, &DirContext.DirEntry.Fat.Attrib, sizeof(FAT_DIR_ENTRY) - 11); - RtlCopyMemory(pFatEntry[0].ShortName, ". ", 11); - RtlCopyMemory(&pFatEntry[1].Attrib, &DirContext.DirEntry.Fat.Attrib, sizeof(FAT_DIR_ENTRY) - 11); - RtlCopyMemory(pFatEntry[1].ShortName, ".. ", 11); + /* clear the new directory cluster if not moving */ + if (MoveContext == NULL) + { + RtlZeroMemory(pFatEntry, DeviceExt->FatInfo.BytesPerCluster); + /* create '.' and '..' */ + RtlCopyMemory(&pFatEntry[0].Attrib, &DirContext.DirEntry.Fat.Attrib, sizeof(FAT_DIR_ENTRY) - 11); + RtlCopyMemory(pFatEntry[0].ShortName, ". ", 11); + RtlCopyMemory(&pFatEntry[1].Attrib, &DirContext.DirEntry.Fat.Attrib, sizeof(FAT_DIR_ENTRY) - 11); + RtlCopyMemory(pFatEntry[1].ShortName, ".. ", 11); + } + pFatEntry[1].FirstCluster = ParentFcb->entry.Fat.FirstCluster; pFatEntry[1].FirstClusterHigh = ParentFcb->entry.Fat.FirstClusterHigh; if (vfatFCBIsRoot(ParentFcb)) @@ -542,7 +650,8 @@ FATXAddEntry( IN PVFATFCB* Fcb, IN PVFATFCB ParentFcb, IN ULONG RequestedOptions, - IN UCHAR ReqAttr) + IN UCHAR ReqAttr, + IN PVFAT_MOVE_CONTEXT MoveContext) { PVOID Context = NULL; LARGE_INTEGER SystemTime, FileOffset; @@ -578,7 +687,15 @@ FATXAddEntry( DirContext.ShortNameU.MaximumLength = 0; RtlZeroMemory(&DirContext.DirEntry.FatX, sizeof(FATX_DIR_ENTRY)); memset(DirContext.DirEntry.FatX.Filename, 0xff, 42); - DirContext.DirEntry.FatX.FirstCluster = 0; + /* Use cluster, if moving */ + if (MoveContext != NULL) + { + DirContext.DirEntry.FatX.FirstCluster = MoveContext->FirstCluster; + } + else + { + DirContext.DirEntry.FatX.FirstCluster = 0; + } DirContext.DirEntry.FatX.FileSize = 0; /* set file name */ @@ -603,6 +720,13 @@ FATXAddEntry( DirContext.DirEntry.FatX.UpdateTime = DirContext.DirEntry.FatX.CreationTime; DirContext.DirEntry.FatX.AccessDate = DirContext.DirEntry.FatX.CreationDate; DirContext.DirEntry.FatX.AccessTime = DirContext.DirEntry.FatX.CreationTime; + /* If it's moving, preserve creation time and file size */ + if (MoveContext != NULL) + { + DirContext.DirEntry.FatX.CreationDate = MoveContext->CreationDate; + DirContext.DirEntry.FatX.CreationTime = MoveContext->CreationTime; + DirContext.DirEntry.FatX.FileSize = MoveContext->FileSize; + } /* add entry into parent directory */ FileOffset.u.HighPart = 0; @@ -616,8 +740,19 @@ FATXAddEntry( CcSetDirtyPinnedData(Context, NULL); CcUnpinData(Context); - /* FIXME: check status */ - vfatMakeFCBFromDirEntry(DeviceExt, ParentFcb, &DirContext, Fcb); + if (MoveContext != NULL) + { + /* We're modifying an existing FCB - likely rename/move */ + /* FIXME: check status */ + vfatUpdateFCB(DeviceExt, *Fcb, &DirContext.LongNameU, &DirContext.ShortNameU, ParentFcb); + (*Fcb)->dirIndex = DirContext.DirIndex; + (*Fcb)->startIndex = DirContext.StartIndex; + } + else + { + /* FIXME: check status */ + vfatMakeFCBFromDirEntry(DeviceExt, ParentFcb, &DirContext, Fcb); + } DPRINT("addentry ok\n"); return STATUS_SUCCESS; @@ -630,12 +765,13 @@ VfatAddEntry( IN PVFATFCB *Fcb, IN PVFATFCB ParentFcb, IN ULONG RequestedOptions, - IN UCHAR ReqAttr) + IN UCHAR ReqAttr, + IN PVFAT_MOVE_CONTEXT MoveContext) { if (DeviceExt->Flags & VCB_IS_FATX) - return FATXAddEntry(DeviceExt, NameU, Fcb, ParentFcb, RequestedOptions, ReqAttr); + return FATXAddEntry(DeviceExt, NameU, Fcb, ParentFcb, RequestedOptions, ReqAttr, MoveContext); else - return FATAddEntry(DeviceExt, NameU, Fcb, ParentFcb, RequestedOptions, ReqAttr); + return FATAddEntry(DeviceExt, NameU, Fcb, ParentFcb, RequestedOptions, ReqAttr, MoveContext); } /* @@ -644,7 +780,8 @@ VfatAddEntry( static NTSTATUS FATDelEntry( IN PDEVICE_EXTENSION DeviceExt, - IN PVFATFCB pFcb) + IN PVFATFCB pFcb, + OUT PVFAT_MOVE_CONTEXT MoveContext) { ULONG CurrentCluster = 0, NextCluster, i; PVOID Context = NULL; @@ -687,13 +824,26 @@ FATDelEntry( CcUnpinData(Context); } - while (CurrentCluster && CurrentCluster != 0xffffffff) + /* In case of moving, don't delete data */ + if (MoveContext != NULL) { - GetNextCluster(DeviceExt, CurrentCluster, &NextCluster); - /* FIXME: check status */ - WriteCluster(DeviceExt, CurrentCluster, 0); - CurrentCluster = NextCluster; + pDirEntry = &pDirEntry[pFcb->dirIndex % (PAGE_SIZE / sizeof(FAT_DIR_ENTRY))]; + MoveContext->FirstCluster = CurrentCluster; + MoveContext->FileSize = pDirEntry->FileSize; + MoveContext->CreationTime = pDirEntry->CreationTime; + MoveContext->CreationDate = pDirEntry->CreationDate; } + else + { + while (CurrentCluster && CurrentCluster != 0xffffffff) + { + GetNextCluster(DeviceExt, CurrentCluster, &NextCluster); + /* FIXME: check status */ + WriteCluster(DeviceExt, CurrentCluster, 0); + CurrentCluster = NextCluster; + } + } + return STATUS_SUCCESS; } @@ -703,7 +853,8 @@ FATDelEntry( static NTSTATUS FATXDelEntry( IN PDEVICE_EXTENSION DeviceExt, - IN PVFATFCB pFcb) + IN PVFATFCB pFcb, + OUT PVFAT_MOVE_CONTEXT MoveContext) { ULONG CurrentCluster = 0, NextCluster; PVOID Context = NULL; @@ -734,25 +885,78 @@ FATXDelEntry( CcSetDirtyPinnedData(Context, NULL); CcUnpinData(Context); - while (CurrentCluster && CurrentCluster != 0xffffffff) + /* In case of moving, don't delete data */ + if (MoveContext != NULL) { - GetNextCluster(DeviceExt, CurrentCluster, &NextCluster); - /* FIXME: check status */ - WriteCluster(DeviceExt, CurrentCluster, 0); - CurrentCluster = NextCluster; + MoveContext->FirstCluster = CurrentCluster; + MoveContext->FileSize = pDirEntry->FileSize; + MoveContext->CreationTime = pDirEntry->CreationTime; + MoveContext->CreationDate = pDirEntry->CreationDate; } + else + { + while (CurrentCluster && CurrentCluster != 0xffffffff) + { + GetNextCluster(DeviceExt, CurrentCluster, &NextCluster); + /* FIXME: check status */ + WriteCluster(DeviceExt, CurrentCluster, 0); + CurrentCluster = NextCluster; + } + } + return STATUS_SUCCESS; } NTSTATUS VfatDelEntry( IN PDEVICE_EXTENSION DeviceExt, - IN PVFATFCB pFcb) + IN PVFATFCB pFcb, + OUT PVFAT_MOVE_CONTEXT MoveContext) { if (DeviceExt->Flags & VCB_IS_FATX) - return FATXDelEntry(DeviceExt, pFcb); + return FATXDelEntry(DeviceExt, pFcb, MoveContext); else - return FATDelEntry(DeviceExt, pFcb); + return FATDelEntry(DeviceExt, pFcb, MoveContext); +} + +/* + * move an existing FAT entry + */ +NTSTATUS +VfatMoveEntry( + IN PDEVICE_EXTENSION DeviceExt, + IN PVFATFCB pFcb, + IN PUNICODE_STRING FileName, + IN PVFATFCB ParentFcb) +{ + NTSTATUS Status; + PVFATFCB OldParent; + VFAT_MOVE_CONTEXT MoveContext; + + DPRINT("VfatMoveEntry(%p, %p, %wZ, %p)\n", DeviceExt, pFcb, FileName, ParentFcb); + + /* Delete old entry while keeping data */ + Status = VfatDelEntry(DeviceExt, pFcb, &MoveContext); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + OldParent = pFcb->parentFcb; + CcPurgeCacheSection(&OldParent->SectionObjectPointers, NULL, 0, FALSE); + + /* Add our new entry with our cluster */ + Status = VfatAddEntry(DeviceExt, + FileName, + &pFcb, + ParentFcb, + (vfatFCBIsDirectory(pFcb) ? FILE_DIRECTORY_FILE : 0), + *pFcb->Attributes, + &MoveContext); + + CcPurgeCacheSection(&pFcb->parentFcb->SectionObjectPointers, NULL, 0, FALSE); + + return Status; } /* EOF */ diff --git a/reactos/drivers/filesystems/fastfat/fcb.c b/reactos/drivers/filesystems/fastfat/fcb.c index fad85a26c10..9f104a28296 100644 --- a/reactos/drivers/filesystems/fastfat/fcb.c +++ b/reactos/drivers/filesystems/fastfat/fcb.c @@ -1,11 +1,12 @@ /* -* FILE: drivers/fs/vfat/fcb.c +* FILE: drivers/filesystems/fastfat/fcb.c * PURPOSE: Routines to manipulate FCBs. * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * PROGRAMMER: Jason Filby (jasonfilby@yahoo.com) * Rex Jolliff (rex@lvcablemodem.com) * Herve Poussineau (reactos@poussine.freesurf.fr) +* Pierre Schweitzer (pierre@reactos.org) */ /* ------------------------------------------------------- INCLUDES */ @@ -156,6 +157,98 @@ vfatNewFCB( return rcFCB; } +static +VOID +vfatDelFCBFromTable( + PDEVICE_EXTENSION pVCB, + PVFATFCB pFCB) +{ + ULONG Index; + ULONG ShortIndex; + HASHENTRY* entry; + + Index = pFCB->Hash.Hash % pVCB->HashTableSize; + ShortIndex = pFCB->ShortHash.Hash % pVCB->HashTableSize; + + if (pFCB->Hash.Hash != pFCB->ShortHash.Hash) + { + entry = pVCB->FcbHashTable[ShortIndex]; + if (entry->self == pFCB) + { + pVCB->FcbHashTable[ShortIndex] = entry->next; + } + else + { + while (entry->next->self != pFCB) + { + entry = entry->next; + } + entry->next = pFCB->ShortHash.next; + } + } + entry = pVCB->FcbHashTable[Index]; + if (entry->self == pFCB) + { + pVCB->FcbHashTable[Index] = entry->next; + } + else + { + while (entry->next->self != pFCB) + { + entry = entry->next; + } + entry->next = pFCB->Hash.next; + } +} + +static +NTSTATUS +vfatMakeFullName( + PVFATFCB directoryFCB, + PUNICODE_STRING LongNameU, + PUNICODE_STRING ShortNameU, + PUNICODE_STRING NameU) +{ + PWCHAR PathNameBuffer; + USHORT PathNameLength; + + PathNameLength = directoryFCB->PathNameU.Length + max(LongNameU->Length, ShortNameU->Length); + if (!vfatFCBIsRoot(directoryFCB)) + { + PathNameLength += sizeof(WCHAR); + } + + if (PathNameLength > LONGNAME_MAX_LENGTH * sizeof(WCHAR)) + { + return STATUS_OBJECT_NAME_INVALID; + } + PathNameBuffer = ExAllocatePoolWithTag(NonPagedPool, PathNameLength + sizeof(WCHAR), TAG_FCB); + if (!PathNameBuffer) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + NameU->Buffer = PathNameBuffer; + NameU->Length = 0; + NameU->MaximumLength = PathNameLength; + + RtlCopyUnicodeString(NameU, &directoryFCB->PathNameU); + if (!vfatFCBIsRoot(directoryFCB)) + { + RtlAppendUnicodeToString(NameU, L"\\"); + } + if (LongNameU->Length > 0) + { + RtlAppendUnicodeStringToString(NameU, LongNameU); + } + else + { + RtlAppendUnicodeStringToString(NameU, ShortNameU); + } + NameU->Buffer[NameU->Length / sizeof(WCHAR)] = 0; + + return STATUS_SUCCESS; +} + VOID vfatDestroyCCB( PVFATCCB pCcb) @@ -182,7 +275,7 @@ BOOLEAN vfatFCBIsDirectory( PVFATFCB FCB) { - return *FCB->Attributes & FILE_ATTRIBUTE_DIRECTORY; + return ((*FCB->Attributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY); } BOOLEAN @@ -197,9 +290,6 @@ vfatReleaseFCB( PDEVICE_EXTENSION pVCB, PVFATFCB pFCB) { - HASHENTRY* entry; - ULONG Index; - ULONG ShortIndex; PVFATFCB tmpFcb; DPRINT("releasing FCB at %p: %wZ, refCount:%d\n", @@ -207,42 +297,13 @@ vfatReleaseFCB( while (pFCB) { - Index = pFCB->Hash.Hash % pVCB->HashTableSize; - ShortIndex = pFCB->ShortHash.Hash % pVCB->HashTableSize; pFCB->RefCount--; if (pFCB->RefCount == 0) { + ASSERT(pFCB->OpenHandleCount == 0); tmpFcb = pFCB->parentFcb; RemoveEntryList (&pFCB->FcbListEntry); - if (pFCB->Hash.Hash != pFCB->ShortHash.Hash) - { - entry = pVCB->FcbHashTable[ShortIndex]; - if (entry->self == pFCB) - { - pVCB->FcbHashTable[ShortIndex] = entry->next; - } - else - { - while (entry->next->self != pFCB) - { - entry = entry->next; - } - entry->next = pFCB->ShortHash.next; - } - } - entry = pVCB->FcbHashTable[Index]; - if (entry->self == pFCB) - { - pVCB->FcbHashTable[Index] = entry->next; - } - else - { - while (entry->next->self != pFCB) - { - entry = entry->next; - } - entry->next = pFCB->Hash.next; - } + vfatDelFCBFromTable(pVCB, pFCB); vfatDestroyFCB(pFCB); } else @@ -253,6 +314,7 @@ vfatReleaseFCB( } } +static VOID vfatAddFCBToTable( PDEVICE_EXTENSION pVCB, @@ -279,6 +341,76 @@ vfatAddFCBToTable( } } +NTSTATUS +vfatUpdateFCB( + PDEVICE_EXTENSION pVCB, + PVFATFCB Fcb, + PUNICODE_STRING LongName, + PUNICODE_STRING ShortName, + PVFATFCB ParentFcb) +{ + NTSTATUS Status; + PVFATFCB OldParent; + + DPRINT("vfatUpdateFCB(%p, %p, %wZ, %wZ, %p)\n", pVCB, Fcb, LongName, ShortName, ParentFcb); + + /* Delete old name */ + if (Fcb->PathNameBuffer) + { + ExFreePoolWithTag(Fcb->PathNameBuffer, TAG_FCB); + } + + /* Delete from table */ + vfatDelFCBFromTable(pVCB, Fcb); + + /* Get full path name */ + Status = vfatMakeFullName(ParentFcb, LongName, ShortName, &Fcb->PathNameU); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* Split it properly */ + Fcb->PathNameBuffer = Fcb->PathNameU.Buffer; + Fcb->DirNameU.Buffer = Fcb->PathNameU.Buffer; + vfatSplitPathName(&Fcb->PathNameU, &Fcb->DirNameU, &Fcb->LongNameU); + + /* Copy short name */ + RtlCopyUnicodeString(&Fcb->ShortNameU, ShortName); + + /* Recompute hashes */ + Fcb->Hash.Hash = vfatNameHash(0, &Fcb->PathNameU); + if (pVCB->Flags & VCB_IS_FATX) + { + Fcb->ShortHash.Hash = Fcb->Hash.Hash; + } + else + { + Fcb->ShortHash.Hash = vfatNameHash(0, &Fcb->DirNameU); + Fcb->ShortHash.Hash = vfatNameHash(Fcb->ShortHash.Hash, &Fcb->ShortNameU); + } + + /* Set parent */ + OldParent = Fcb->parentFcb; + Fcb->parentFcb = ParentFcb; + + /* Add to the table */ + vfatAddFCBToTable(pVCB, Fcb); + + /* If we moved accross directories, dereferenced our old parent + * We also derefence in case we're just renaming since AddFCBToTable references it + */ + vfatReleaseFCB(pVCB, OldParent); + + /* In case we were moving accross directories, reset caching on old parent */ + //if (OldParent != ParentFcb) + //{ + // CcUninitializeCacheMap(OldParent->FileObject, NULL, NULL); + //} + + return STATUS_SUCCESS; +} + PVFATFCB vfatGrabFCBFromTable( PDEVICE_EXTENSION pVCB, @@ -460,47 +592,15 @@ vfatMakeFCBFromDirEntry( PVFATFCB *fileFCB) { PVFATFCB rcFCB; - PWCHAR PathNameBuffer; - USHORT PathNameLength; ULONG Size; - ULONG hash; - UNICODE_STRING NameU; + NTSTATUS Status; - PathNameLength = directoryFCB->PathNameU.Length + max(DirContext->LongNameU.Length, DirContext->ShortNameU.Length); - if (!vfatFCBIsRoot (directoryFCB)) - { - PathNameLength += sizeof(WCHAR); - } - - if (PathNameLength > LONGNAME_MAX_LENGTH * sizeof(WCHAR)) - { - return STATUS_OBJECT_NAME_INVALID; - } - PathNameBuffer = ExAllocatePoolWithTag(NonPagedPool, PathNameLength + sizeof(WCHAR), TAG_FCB); - if (!PathNameBuffer) - { - return STATUS_INSUFFICIENT_RESOURCES; - } - NameU.Buffer = PathNameBuffer; - NameU.Length = 0; - NameU.MaximumLength = PathNameLength; - - RtlCopyUnicodeString(&NameU, &directoryFCB->PathNameU); - if (!vfatFCBIsRoot(directoryFCB)) - { - RtlAppendUnicodeToString(&NameU, L"\\"); - } - hash = vfatNameHash(0, &NameU); - if (DirContext->LongNameU.Length > 0) - { - RtlAppendUnicodeStringToString(&NameU, &DirContext->LongNameU); - } - else + Status = vfatMakeFullName(directoryFCB, &DirContext->LongNameU, &DirContext->ShortNameU, &NameU); + if (!NT_SUCCESS(Status)) { - RtlAppendUnicodeStringToString(&NameU, &DirContext->ShortNameU); + return Status; } - NameU.Buffer[NameU.Length / sizeof(WCHAR)] = 0; rcFCB = vfatNewFCB(vcb, &NameU); RtlCopyMemory(&rcFCB->entry, &DirContext->DirEntry, sizeof (DIR_ENTRY)); @@ -511,7 +611,8 @@ vfatMakeFCBFromDirEntry( } else { - rcFCB->ShortHash.Hash = vfatNameHash(hash, &rcFCB->ShortNameU); + rcFCB->ShortHash.Hash = vfatNameHash(0, &rcFCB->DirNameU); + rcFCB->ShortHash.Hash = vfatNameHash(rcFCB->ShortHash.Hash, &rcFCB->ShortNameU); } if (vfatFCBIsDirectory(rcFCB)) @@ -562,7 +663,7 @@ vfatMakeFCBFromDirEntry( vfatAddFCBToTable(vcb, rcFCB); *fileFCB = rcFCB; - ExFreePool(PathNameBuffer); + ExFreePool(NameU.Buffer); return STATUS_SUCCESS; } diff --git a/reactos/drivers/filesystems/fastfat/finfo.c b/reactos/drivers/filesystems/fastfat/finfo.c index d0db17852b5..87a30843b4b 100644 --- a/reactos/drivers/filesystems/fastfat/finfo.c +++ b/reactos/drivers/filesystems/fastfat/finfo.c @@ -1,10 +1,11 @@ /* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel - * FILE: drivers/fs/vfat/finfo.c + * FILE: drivers/filesystems/fastfat/finfo.c * PURPOSE: VFAT Filesystem * PROGRAMMER: Jason Filby (jasonfilby@yahoo.com) * Herve Poussineau (reactos@poussine.freesurf.fr) + * Pierre Schweitzer (pierre@reactos.org) * */ @@ -373,6 +374,467 @@ VfatSetDispositionInformation( return STATUS_SUCCESS; } +static NTSTATUS +vfatPrepareTargetForRename( + IN PDEVICE_EXTENSION DeviceExt, + IN PVFATFCB * ParentFCB, + IN PUNICODE_STRING NewName, + IN BOOLEAN ReplaceIfExists, + IN PUNICODE_STRING ParentName, + OUT PBOOLEAN Deleted) +{ + NTSTATUS Status; + PVFATFCB TargetFcb; + + DPRINT("vfatPrepareTargetForRename(%p, %p, %wZ, %d, %wZ, %p)\n", DeviceExt, ParentFCB, NewName, ReplaceIfExists, ParentName); + + *Deleted = FALSE; + /* Try to open target */ + Status = vfatGetFCBForFile(DeviceExt, ParentFCB, &TargetFcb, NewName); + /* If it exists */ + if (NT_SUCCESS(Status)) + { + /* Check whether we are allowed to replace */ + if (ReplaceIfExists) + { + /* If that's a directory or a read-only file, we're not allowed */ + if (vfatFCBIsDirectory(TargetFcb) || ((*TargetFcb->Attributes & FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY)); + { + *ParentFCB = NULL; + vfatReleaseFCB(DeviceExt, TargetFcb); + return STATUS_OBJECT_NAME_COLLISION; + } + + /* Attempt to flush (might close the file) */ + if (!MmFlushImageSection(TargetFcb->FileObject->SectionObjectPointer, MmFlushForDelete)) + { + *ParentFCB = NULL; + vfatReleaseFCB(DeviceExt, TargetFcb); + return STATUS_ACCESS_DENIED; + } + + /* If we are, ensure the file isn't open by anyone! */ + if (TargetFcb->OpenHandleCount != 0) + { + *ParentFCB = NULL; + vfatReleaseFCB(DeviceExt, TargetFcb); + return STATUS_ACCESS_DENIED; + } + + /* Effectively delete old file to allow renaming */ + VfatDelEntry(DeviceExt, TargetFcb, NULL); + (*ParentFCB)->RefCount++; + vfatReleaseFCB(DeviceExt, TargetFcb); + *Deleted = TRUE; + } + else + { + *ParentFCB = NULL; + vfatReleaseFCB(DeviceExt, TargetFcb); + return STATUS_OBJECT_NAME_COLLISION; + } + } + else if (*ParentFCB != NULL) + { + return STATUS_SUCCESS; + } + + /* Failure */ + return Status; +} + +/* + * FUNCTION: Set the file name information + */ +static +NTSTATUS +VfatSetRenameInformation( + PFILE_OBJECT FileObject, + PVFATFCB FCB, + PDEVICE_EXTENSION DeviceObject, + PFILE_RENAME_INFORMATION RenameInfo, + PFILE_OBJECT TargetFileObject) +{ + NTSTATUS Status; + UNICODE_STRING NewName; + UNICODE_STRING SourcePath; + UNICODE_STRING SourceFile; + UNICODE_STRING NewPath; + UNICODE_STRING NewFile; + PFILE_OBJECT RootFileObject; + PVFATFCB RootFCB; + UNICODE_STRING RenameInfoString; + PVFATFCB ParentFCB; + IO_STATUS_BLOCK IoStatusBlock; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE TargetHandle; + BOOLEAN DeletedTarget; + + DPRINT("VfatSetRenameInfo(%p, %p, %p, %p, %p)\n", FileObject, FCB, DeviceObject, RenameInfo, TargetFileObject); + + /* Disallow renaming root */ + if (vfatFCBIsRoot(FCB)) + { + return STATUS_INVALID_PARAMETER; + } + + /* If we are performing relative opening for rename, get FO for getting FCB and path name */ + if (RenameInfo->RootDirectory != NULL) + { + /* We cannot tolerate relative opening with a full path */ + if (RenameInfo->FileName[0] == L'\\') + { + return STATUS_OBJECT_NAME_INVALID; + } + + Status = ObReferenceObjectByHandle(RenameInfo->RootDirectory, + FILE_READ_DATA, + *IoFileObjectType, + ExGetPreviousMode(), + (PVOID *)&RootFileObject, + NULL); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + RootFCB = RootFileObject->FsContext; + } + + ParentFCB = NULL; + + if (TargetFileObject == NULL) + { + /* If we don't have target file object, construct paths thanks to relative FCB, if any, and with + * information supplied by the user + */ + + /* First, setup a string we'll work on */ + RenameInfoString.Length = RenameInfo->FileNameLength; + RenameInfoString.MaximumLength = RenameInfo->FileNameLength; + RenameInfoString.Buffer = RenameInfo->FileName; + + /* Check whether we have FQN */ + if (RenameInfoString.Length > 6 * sizeof(WCHAR)) + { + if (RenameInfoString.Buffer[0] == L'\\' && RenameInfoString.Buffer[1] == L'?' && + RenameInfoString.Buffer[2] == L'?' && RenameInfoString.Buffer[3] == L'\\' && + RenameInfoString.Buffer[5] == L':' && (RenameInfoString.Buffer[4] >= L'A' && + RenameInfoString.Buffer[4] <= L'Z')) + { + /* If so, open its target directory */ + InitializeObjectAttributes(&ObjectAttributes, + &RenameInfoString, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, NULL); + + Status = IoCreateFile(&TargetHandle, + FILE_WRITE_DATA | SYNCHRONIZE, + &ObjectAttributes, + &IoStatusBlock, + NULL, 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_OPEN_FOR_BACKUP_INTENT, + NULL, 0, + CreateFileTypeNone, + NULL, + IO_FORCE_ACCESS_CHECK | IO_OPEN_TARGET_DIRECTORY); + if (!NT_SUCCESS(Status)) + { + goto Cleanup; + } + + /* Get its FO to get the FCB */ + Status = ObReferenceObjectByHandle(TargetHandle, + FILE_WRITE_DATA, + *IoFileObjectType, + KernelMode, + (PVOID *)&TargetFileObject, + NULL); + if (!NT_SUCCESS(Status)) + { + ZwClose(TargetHandle); + goto Cleanup; + } + + /* Are we working on the same volume? */ + if (IoGetRelatedDeviceObject(TargetFileObject) != IoGetRelatedDeviceObject(FileObject)) + { + ObDereferenceObject(TargetFileObject); + ZwClose(TargetHandle); + TargetFileObject = NULL; + Status = STATUS_NOT_SAME_DEVICE; + goto Cleanup; + } + } + } + + NewName.Length = 0; + NewName.MaximumLength = RenameInfo->FileNameLength; + if (RenameInfo->RootDirectory != NULL) + { + NewName.MaximumLength += sizeof(WCHAR) + RootFCB->PathNameU.Length; + } + else if (RenameInfo->FileName[0] != L'\\') + { + /* We don't have full path, and we don't have root directory: + * => we move inside the same directory + */ + NewName.MaximumLength += sizeof(WCHAR) + FCB->DirNameU.Length; + } + else if (TargetFileObject != NULL) + { + /* We had a FQN: + * => we need to use its correct path + */ + NewName.MaximumLength += sizeof(WCHAR) + ((PVFATFCB)TargetFileObject->FsContext)->PathNameU.Length; + } + + NewName.Buffer = ExAllocatePoolWithTag(NonPagedPool, NewName.MaximumLength, TAG_VFAT); + if (NewName.Buffer == NULL) + { + if (TargetFileObject != NULL) + { + ObDereferenceObject(TargetFileObject); + ZwClose(TargetHandle); + TargetFileObject = NULL; + } + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Cleanup; + } + + if (RenameInfo->RootDirectory != NULL) + { + /* Here, copy first absolute and then append relative */ + RtlCopyUnicodeString(&NewName, &RootFCB->PathNameU); + NewName.Buffer[NewName.Length / sizeof(WCHAR)] = L'\\'; + NewName.Length += sizeof(WCHAR); + RtlAppendUnicodeStringToString(&NewName, &RenameInfoString); + } + else if (RenameInfo->FileName[0] != L'\\') + { + /* Here, copy first work directory and then append filename */ + RtlCopyUnicodeString(&NewName, &FCB->DirNameU); + NewName.Buffer[NewName.Length / sizeof(WCHAR)] = L'\\'; + NewName.Length += sizeof(WCHAR); + RtlAppendUnicodeStringToString(&NewName, &RenameInfoString); + } + else if (TargetFileObject != NULL) + { + /* Here, copy first path name and then append filename */ + RtlCopyUnicodeString(&NewName, &((PVFATFCB)TargetFileObject->FsContext)->PathNameU); + NewName.Buffer[NewName.Length / sizeof(WCHAR)] = L'\\'; + NewName.Length += sizeof(WCHAR); + RtlAppendUnicodeStringToString(&NewName, &RenameInfoString); + } + else + { + /* Here we should have full path, so simply copy it */ + RtlCopyUnicodeString(&NewName, &RenameInfoString); + } + + /* Do we have to cleanup some stuff? */ + if (TargetFileObject != NULL) + { + ObDereferenceObject(TargetFileObject); + ZwClose(TargetHandle); + TargetFileObject = NULL; + } + } + else + { + /* At that point, we shouldn't care about whether we are relative opening + * Target FO FCB should already have full path + */ + + /* Before constructing string, just make a sanity check (just to be sure!) */ + if (IoGetRelatedDeviceObject(TargetFileObject) != IoGetRelatedDeviceObject(FileObject)) + { + Status = STATUS_NOT_SAME_DEVICE; + goto Cleanup; + } + + NewName.Length = 0; + NewName.MaximumLength = TargetFileObject->FileName.Length + ((PVFATFCB)TargetFileObject->FsContext)->PathNameU.Length + sizeof(WCHAR); + NewName.Buffer = ExAllocatePoolWithTag(NonPagedPool, NewName.MaximumLength, TAG_VFAT); + if (NewName.Buffer == NULL) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Cleanup; + } + + RtlCopyUnicodeString(&NewName, &((PVFATFCB)TargetFileObject->FsContext)->PathNameU); + NewName.Buffer[NewName.Length / sizeof(WCHAR)] = L'\\'; + NewName.Length += sizeof(WCHAR); + RtlAppendUnicodeStringToString(&NewName, &TargetFileObject->FileName); + } + + /* Explode our paths to get path & filename */ + vfatSplitPathName(&FCB->PathNameU, &SourcePath, &SourceFile); + DPRINT("Old dir: %wZ, Old file: %wZ\n", &SourcePath, &SourceFile); + vfatSplitPathName(&NewName, &NewPath, &NewFile); + DPRINT("New dir: %wZ, New file: %wZ\n", &NewPath, &NewFile); + + /* Are we working in place? */ + if (FsRtlAreNamesEqual(&SourcePath, &NewPath, TRUE, NULL)) + { + if (FsRtlAreNamesEqual(&SourceFile, &NewFile, FALSE, NULL)) + { + Status = STATUS_SUCCESS; + goto Cleanup; + } + + if (FsRtlAreNamesEqual(&SourceFile, &NewFile, TRUE, NULL)) + { + FsRtlNotifyFullReportChange(DeviceObject->NotifySync, + &(DeviceObject->NotifyList), + (PSTRING)&FCB->PathNameU, + FCB->PathNameU.Length - FCB->LongNameU.Length, + NULL, + NULL, + ((*FCB->Attributes & FILE_ATTRIBUTE_DIRECTORY) ? + FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME), + FILE_ACTION_RENAMED_OLD_NAME, + NULL); + Status = vfatRenameEntry(DeviceObject, FCB, &NewFile, TRUE); + if (NT_SUCCESS(Status)) + { + FsRtlNotifyFullReportChange(DeviceObject->NotifySync, + &(DeviceObject->NotifyList), + (PSTRING)&FCB->PathNameU, + FCB->PathNameU.Length - FCB->LongNameU.Length, + NULL, + NULL, + ((*FCB->Attributes & FILE_ATTRIBUTE_DIRECTORY) ? + FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME), + FILE_ACTION_RENAMED_NEW_NAME, + NULL); + } + } + else + { + /* Try to find target */ + ParentFCB = FCB->parentFcb; + ParentFCB->RefCount++; + Status = vfatPrepareTargetForRename(DeviceObject, + &ParentFCB, + &NewFile, + RenameInfo->ReplaceIfExists, + &NewPath, + &DeletedTarget); + if (!NT_SUCCESS(Status)) + { + goto Cleanup; + } + + FsRtlNotifyFullReportChange(DeviceObject->NotifySync, + &(DeviceObject->NotifyList), + (PSTRING)&FCB->PathNameU, + FCB->PathNameU.Length - FCB->LongNameU.Length, + NULL, + NULL, + ((*FCB->Attributes & FILE_ATTRIBUTE_DIRECTORY) ? + FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME), + (DeletedTarget ? FILE_ACTION_REMOVED : FILE_ACTION_RENAMED_OLD_NAME), + NULL); + Status = vfatRenameEntry(DeviceObject, FCB, &NewFile, FALSE); + if (NT_SUCCESS(Status)) + { + if (DeletedTarget) + { + FsRtlNotifyFullReportChange(DeviceObject->NotifySync, + &(DeviceObject->NotifyList), + (PSTRING)&FCB->PathNameU, + FCB->PathNameU.Length - FCB->LongNameU.Length, + NULL, + NULL, + FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE + | FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_EA, + FILE_ACTION_MODIFIED, + NULL); + } + else + { + FsRtlNotifyFullReportChange(DeviceObject->NotifySync, + &(DeviceObject->NotifyList), + (PSTRING)&FCB->PathNameU, + FCB->PathNameU.Length - FCB->LongNameU.Length, + NULL, + NULL, + ((*FCB->Attributes & FILE_ATTRIBUTE_DIRECTORY) ? + FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME), + FILE_ACTION_RENAMED_NEW_NAME, + NULL); + } + } + } + } + else + { + /* Try to find target */ + ParentFCB = NULL; + Status = vfatPrepareTargetForRename(DeviceObject, + &ParentFCB, + &NewName, + RenameInfo->ReplaceIfExists, + &NewPath, + &DeletedTarget); + if (!NT_SUCCESS(Status)) + { + goto Cleanup; + } + + FsRtlNotifyFullReportChange(DeviceObject->NotifySync, + &(DeviceObject->NotifyList), + (PSTRING)&FCB->PathNameU, + FCB->PathNameU.Length - FCB->LongNameU.Length, + NULL, + NULL, + ((*FCB->Attributes & FILE_ATTRIBUTE_DIRECTORY) ? + FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME), + FILE_ACTION_REMOVED, + NULL); + Status = VfatMoveEntry(DeviceObject, FCB, &NewFile, ParentFCB); + if (NT_SUCCESS(Status)) + { + if (DeletedTarget) + { + FsRtlNotifyFullReportChange(DeviceObject->NotifySync, + &(DeviceObject->NotifyList), + (PSTRING)&FCB->PathNameU, + FCB->PathNameU.Length - FCB->LongNameU.Length, + NULL, + NULL, + FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE + | FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_EA, + FILE_ACTION_MODIFIED, + NULL); + } + else + { + FsRtlNotifyFullReportChange(DeviceObject->NotifySync, + &(DeviceObject->NotifyList), + (PSTRING)&FCB->PathNameU, + FCB->PathNameU.Length - FCB->LongNameU.Length, + NULL, + NULL, + ((*FCB->Attributes & FILE_ATTRIBUTE_DIRECTORY) ? + FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME), + FILE_ACTION_ADDED, + NULL); + } + } + } + +Cleanup: + if (ParentFCB != NULL) vfatReleaseFCB(DeviceObject, ParentFCB); + if (NewName.Buffer != NULL) ExFreePoolWithTag(NewName.Buffer, TAG_VFAT); + if (RenameInfo->RootDirectory != NULL) ObDereferenceObject(RootFileObject); + + return Status; +} + /* * FUNCTION: Retrieve the file name information */ @@ -1005,11 +1467,24 @@ VfatSetInformation( DPRINT("Can set file size\n"); } + if (FileInformationClass == FileRenameInformation) + { + if (!ExAcquireResourceExclusiveLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource, + (BOOLEAN)(IrpContext->Flags & IRPCONTEXT_CANWAIT))) + { + return VfatQueueRequest(IrpContext); + } + } + if (!(FCB->Flags & FCB_IS_PAGE_FILE)) { if (!ExAcquireResourceExclusiveLite(&FCB->MainResource, (BOOLEAN)(IrpContext->Flags & IRPCONTEXT_CANWAIT))) { + if (FileInformationClass == FileRenameInformation) + { + ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource); + } return VfatQueueRequest(IrpContext); } } @@ -1044,7 +1519,11 @@ VfatSetInformation( break; case FileRenameInformation: - Status = STATUS_NOT_IMPLEMENTED; + Status = VfatSetRenameInformation(IrpContext->FileObject, + FCB, + IrpContext->DeviceExt, + SystemBuffer, + IrpContext->Stack->Parameters.SetFile.FileObject); break; default: @@ -1056,6 +1535,11 @@ VfatSetInformation( ExReleaseResourceLite(&FCB->MainResource); } + if (FileInformationClass == FileRenameInformation) + { + ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource); + } + IrpContext->Irp->IoStatus.Status = Status; IrpContext->Irp->IoStatus.Information = 0; IoCompleteRequest(IrpContext->Irp, IO_NO_INCREMENT); diff --git a/reactos/drivers/filesystems/fastfat/vfat.h b/reactos/drivers/filesystems/fastfat/vfat.h index 4faab7e0340..416fa74c9fa 100644 --- a/reactos/drivers/filesystems/fastfat/vfat.h +++ b/reactos/drivers/filesystems/fastfat/vfat.h @@ -460,6 +460,13 @@ typedef struct _VFAT_DIRENTRY_CONTEXT UNICODE_STRING ShortNameU; } VFAT_DIRENTRY_CONTEXT, *PVFAT_DIRENTRY_CONTEXT; +typedef struct _VFAT_MOVE_CONTEXT +{ + ULONG FirstCluster; + ULONG FileSize; + USHORT CreationDate; + USHORT CreationTime; +} VFAT_MOVE_CONTEXT, *PVFAT_MOVE_CONTEXT; /* blockdev.c */ @@ -594,7 +601,8 @@ VfatAddEntry( PVFATFCB* Fcb, PVFATFCB ParentFcb, ULONG RequestedOptions, - UCHAR ReqAttr); + UCHAR ReqAttr, + PVFAT_MOVE_CONTEXT MoveContext); NTSTATUS VfatUpdateEntry( @@ -603,7 +611,8 @@ VfatUpdateEntry( NTSTATUS VfatDelEntry( PDEVICE_EXTENSION, - PVFATFCB); + PVFATFCB, + PVFAT_MOVE_CONTEXT); BOOLEAN vfatFindDirSpace( @@ -612,6 +621,20 @@ vfatFindDirSpace( ULONG nbSlots, PULONG start); +NTSTATUS +vfatRenameEntry( + IN PDEVICE_EXTENSION DeviceExt, + IN PVFATFCB pFcb, + IN PUNICODE_STRING FileName, + IN BOOLEAN CaseChangeOnly); + +NTSTATUS +VfatMoveEntry( + IN PDEVICE_EXTENSION DeviceExt, + IN PVFATFCB pFcb, + IN PUNICODE_STRING FileName, + IN PVFATFCB ParentFcb); + /* ea.h */ NTSTATUS @@ -747,6 +770,14 @@ vfatNewFCB( PDEVICE_EXTENSION pVCB, PUNICODE_STRING pFileNameU); +NTSTATUS +vfatUpdateFCB( + PDEVICE_EXTENSION pVCB, + PVFATFCB Fcb, + PUNICODE_STRING LongName, + PUNICODE_STRING ShortName, + PVFATFCB ParentFcb); + VOID vfatDestroyFCB( PVFATFCB pFCB); @@ -765,11 +796,6 @@ vfatReleaseFCB( PDEVICE_EXTENSION pVCB, PVFATFCB pFCB); -VOID -vfatAddFCBToTable( - PDEVICE_EXTENSION pVCB, - PVFATFCB pFCB); - PVFATFCB vfatGrabFCBFromTable( PDEVICE_EXTENSION pDeviceExt, -- 2.17.1