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,831 +17,723 @@ #include #include +#include #define NDEBUG #include DEBUG_CHANNEL(kernel32file); /* GLOBALS *****************************************************************/ +/* DEFINES *****************************************************************/ +typedef struct _COPY_PROGRESS_CONTEXT +{ + ULONG Flags; + LPPROGRESS_ROUTINE UserRoutine; + LPVOID UserData; +} COPY_PROGRESS_CONTEXT, *PCOPY_PROGRESS_CONTEXT; + /* FUNCTIONS ****************************************************************/ -static BOOL -RemoveReadOnlyAttributeW(IN LPCWSTR lpFileName) -{ - DWORD Attributes; - Attributes = GetFileAttributesW(lpFileName); - if (Attributes != INVALID_FILE_ATTRIBUTES) - { - return SetFileAttributesW(lpFileName,Attributes - - (Attributes & ~FILE_ATTRIBUTE_READONLY)); - } - - return FALSE; -} - - -/*********************************************************************** - * 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 - * +/* + * @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); + RtlInitUnicodeString(&SessionManagerString, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager"); - if(dest) - DestLen = wcslen(dest); - - 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); + 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); - } + /* 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 '!' */ - } - else - { - len2 = sizeof(WCHAR); /* minimum is the 0 characters for the empty second string */ + 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)) + while (TRUE) { - 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) */ - } - else - { - DataSize = info_size; - if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) ))) - goto Quit; - } - - memcpy( Buffer + DataSize, source_name.Buffer, len1 ); - DataSize += len1; - p = (WCHAR *)(Buffer + DataSize); - if (DestLen) - { - if (flags & MOVEFILE_REPLACE_EXISTING) - *p++ = '!'; - memcpy( p, dest_name.Buffer, len2 ); - DataSize += len2; - } - else - { - *p = 0; - DataSize += sizeof(WCHAR); - } - - /* 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); -} - - -/* - * @implemented - */ -BOOL -WINAPI -MoveFileWithProgressW ( - LPCWSTR lpExistingFileName, - LPCWSTR lpNewFileName, - LPPROGRESS_ROUTINE lpProgressRoutine, - LPVOID lpData, - DWORD dwFlags - ) -{ - HANDLE hFile = NULL, hNewFile = NULL; - IO_STATUS_BLOCK IoStatusBlock; - OBJECT_ATTRIBUTES ObjectAttributes; - PFILE_RENAME_INFORMATION FileRename; - NTSTATUS errCode; - BOOL Result; - UNICODE_STRING DstPathU; - BOOL folder = FALSE; - - TRACE("MoveFileWithProgressW()\n"); - - if (dwFlags & MOVEFILE_DELAY_UNTIL_REBOOT) - return add_boot_rename_entry( lpExistingFileName, lpNewFileName, dwFlags ); - -// if (dwFlags & MOVEFILE_WRITE_THROUGH) -// FIXME("MOVEFILE_WRITE_THROUGH unimplemented\n"); - - if (!lpNewFileName) - return DeleteFileW(lpExistingFileName); - - /* validate & translate the filename */ - if (!RtlDosPathNameToNtPathName_U (lpNewFileName, - &DstPathU, - NULL, - NULL)) - { - WARN("Invalid destination path\n"); - SetLastError(ERROR_PATH_NOT_FOUND); - return FALSE; - } - - InitializeObjectAttributes(&ObjectAttributes, - &DstPathU, - OBJ_CASE_INSENSITIVE, - NULL, - NULL); - - 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) ); - - if (NT_SUCCESS(errCode)) /* Destination exists */ - { - NtClose(hNewFile); - - if (!(dwFlags & MOVEFILE_REPLACE_EXISTING)) + /* Allocate output buffer */ + Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength); + if (Buffer == NULL) { - SetLastError(ERROR_ALREADY_EXISTS); - return FALSE; - } - else if (GetFileAttributesW(lpNewFileName) & FILE_ATTRIBUTE_DIRECTORY) - { - SetLastError(ERROR_ACCESS_DENIED); - return FALSE; - } - } + NtClose(KeyHandle); + return STATUS_NO_MEMORY; + } - 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; -} - - -/* - * @implemented - */ -BOOL -WINAPI -MoveFileWithProgressA ( - LPCSTR lpExistingFileName, - LPCSTR lpNewFileName, - LPPROGRESS_ROUTINE lpProgressRoutine, - LPVOID lpData, - DWORD dwFlags - ) -{ - PWCHAR ExistingFileNameW; - PWCHAR NewFileNameW; - BOOL ret; - - if (!(ExistingFileNameW = FilenameA2W(lpExistingFileName, FALSE))) - return FALSE; - - if (!(NewFileNameW= FilenameA2W(lpNewFileName, TRUE))) - return FALSE; - - ret = MoveFileWithProgressW (ExistingFileNameW , - NewFileNameW, - lpProgressRoutine, - lpData, - dwFlags); - - RtlFreeHeap (RtlGetProcessHeap (), 0, NewFileNameW); - - return ret; -} - - -/* - * @implemented - */ -BOOL -WINAPI -MoveFileW ( - LPCWSTR lpExistingFileName, - LPCWSTR lpNewFileName - ) -{ - return MoveFileExW (lpExistingFileName, - lpNewFileName, - MOVEFILE_COPY_ALLOWED); -} - - -/* - * @implemented - */ -BOOL -WINAPI -MoveFileExW ( - LPCWSTR lpExistingFileName, - LPCWSTR lpNewFileName, - DWORD dwFlags - ) -{ - return MoveFileWithProgressW (lpExistingFileName, - lpNewFileName, - NULL, - NULL, - dwFlags); -} - - -/* - * @implemented - */ -BOOL -WINAPI -MoveFileA ( - LPCSTR lpExistingFileName, - LPCSTR lpNewFileName - ) -{ - return MoveFileExA (lpExistingFileName, - lpNewFileName, - MOVEFILE_COPY_ALLOWED); -} - - -/* - * @implemented - */ -BOOL -WINAPI -MoveFileExA ( - LPCSTR lpExistingFileName, - LPCSTR lpNewFileName, - DWORD dwFlags - ) -{ - return MoveFileWithProgressA (lpExistingFileName, - lpNewFileName, - NULL, - NULL, - dwFlags); -} - -/* - * @implemented - */ -BOOL -WINAPI -ReplaceFileA( - LPCSTR lpReplacedFileName, - LPCSTR lpReplacementFileName, - LPCSTR lpBackupFileName, - DWORD dwReplaceFlags, - LPVOID lpExclude, - LPVOID lpReserved - ) -{ - WCHAR *replacedW, *replacementW, *backupW = NULL; - BOOL ret; - - /* This function only makes sense when the first two parameters are defined */ - if (!lpReplacedFileName || !(replacedW = FilenameA2W(lpReplacedFileName, TRUE))) - { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - - if (!lpReplacementFileName || !(replacementW = FilenameA2W(lpReplacementFileName, TRUE))) - { - HeapFree(GetProcessHeap(), 0, replacedW); - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - - /* The backup parameter, however, is optional */ - if (lpBackupFileName) - { - if (!(backupW = FilenameA2W(lpBackupFileName, TRUE))) + Status = NtQueryValueKey(KeyHandle, + &PendingOperationsString, + KeyValuePartialInformation, + Buffer, StringLength, &DataSize); + if (Status != STATUS_BUFFER_OVERFLOW) { - HeapFree(GetProcessHeap(), 0, replacedW); - HeapFree(GetProcessHeap(), 0, replacementW); - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; + 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; } } - ret = ReplaceFileW(replacedW, replacementW, backupW, dwReplaceFlags, lpExclude, lpReserved); - HeapFree(GetProcessHeap(), 0, replacedW); - HeapFree(GetProcessHeap(), 0, replacementW); - HeapFree(GetProcessHeap(), 0, backupW); + /* Check if it existed - if not, create only IF asked to */ + if (!NT_SUCCESS(Status) && (Status != STATUS_OBJECT_NAME_NOT_FOUND || !CreateIfNotFound)) + { + NtClose(KeyHandle); + RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); + return Status; + } - return ret; + if (!NT_SUCCESS(Status)) + { + /* We didn't find any - ie, we create, so use complete buffer */ + BufferBegin = Buffer; + BufferWrite = Buffer; + } + else + { + 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)); + } + + /* 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 (!dwCallbackReason) + { + if (StreamBytesTransferred.QuadPart == StreamSize.QuadPart) + { + FlushFileBuffers(hDestinationFile); + } + } + } + + if (Context->UserRoutine) + { + Ret = Context->UserRoutine(TotalFileSize, + TotalBytesTransferred, + StreamSize, + StreamBytesTransferred, + dwStreamNumber, + dwCallbackReason, + hSourceFile, + hDestinationFile, + Context->UserData); + } + + return Ret; +} + + +/* + * @implemented + */ +BOOL +WINAPI +MoveFileWithProgressW(IN LPCWSTR lpExistingFileName, + IN LPCWSTR lpNewFileName, + IN LPPROGRESS_ROUTINE lpProgressRoutine, + IN LPVOID lpData, + IN DWORD dwFlags) +{ + NTSTATUS Status; + PWSTR NewBuffer; + IO_STATUS_BLOCK IoStatusBlock; + COPY_PROGRESS_CONTEXT CopyContext; + OBJECT_ATTRIBUTES ObjectAttributes; + 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; + + DPRINT("MoveFileWithProgressW(%S, %S, %p, %p, %x)\n", lpExistingFileName, lpNewFileName, lpProgressRoutine, lpData, dwFlags); + + NewPathU.Buffer = NULL; + ExistingPathU.Buffer = NULL; + + _SEH3_TRY + { + /* Don't allow renaming to a disk */ + if (lpNewFileName && RtlIsDosDeviceName_U(lpNewFileName)) + { + BaseSetLastNTError(STATUS_OBJECT_NAME_COLLISION); + _SEH3_LEAVE; + } + + ReplaceIfExists = !!(dwFlags & MOVEFILE_REPLACE_EXISTING); + + /* Get file path */ + if (!RtlDosPathNameToNtPathName_U(lpExistingFileName, &ExistingPathU, NULL, NULL)) + { + BaseSetLastNTError(STATUS_OBJECT_PATH_NOT_FOUND); + _SEH3_LEAVE; + } + + /* 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, + (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); + } + } + } + + CloseHandle(ExistingHandle); + ExistingHandle = INVALID_HANDLE_VALUE; + } + + /* In case copy worked, close file */ + if (NewHandle != INVALID_HANDLE_VALUE) + { + CloseHandle(NewHandle); + NewHandle = INVALID_HANDLE_VALUE; + } + + /* If it succeed, delete source file */ + if (Ret) + { + 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; + + return Ret; +} + + +/* + * @implemented + */ +BOOL +WINAPI +MoveFileWithProgressA(IN LPCSTR lpExistingFileName, + IN LPCSTR lpNewFileName OPTIONAL, + IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL, + IN LPVOID lpData OPTIONAL, + IN DWORD dwFlags) +{ + BOOL Ret; + UNICODE_STRING ExistingFileNameW, NewFileNameW; + + if (!Basep8BitStringToDynamicUnicodeString(&ExistingFileNameW, lpExistingFileName)) + { + return FALSE; + } + + if (lpNewFileName) + { + if (!Basep8BitStringToDynamicUnicodeString(&NewFileNameW, lpNewFileName)) + { + RtlFreeUnicodeString(&ExistingFileNameW); + return FALSE; + } + } + else + { + NewFileNameW.Buffer = NULL; + } + + Ret = MoveFileWithProgressW(ExistingFileNameW.Buffer, NewFileNameW.Buffer, lpProgressRoutine, lpData, dwFlags); + + RtlFreeUnicodeString(&ExistingFileNameW); + RtlFreeUnicodeString(&NewFileNameW); + + return Ret; +} + + +/* + * @implemented + */ +BOOL +WINAPI +MoveFileW(IN LPCWSTR lpExistingFileName, + IN LPCWSTR lpNewFileName) +{ + return MoveFileWithProgressW(lpExistingFileName, + lpNewFileName, + NULL, + NULL, + MOVEFILE_COPY_ALLOWED); +} + + +/* + * @implemented + */ +BOOL +WINAPI +MoveFileExW(IN LPCWSTR lpExistingFileName, + IN LPCWSTR lpNewFileName OPTIONAL, + IN DWORD dwFlags) +{ + return MoveFileWithProgressW(lpExistingFileName, + lpNewFileName, + NULL, + NULL, + dwFlags); +} + + +/* + * @implemented + */ +BOOL +WINAPI +MoveFileA(IN LPCSTR lpExistingFileName, + IN LPCSTR lpNewFileName) +{ + return MoveFileWithProgressA(lpExistingFileName, + lpNewFileName, + NULL, + NULL, + MOVEFILE_COPY_ALLOWED); +} + + +/* + * @implemented + */ +BOOL +WINAPI +MoveFileExA(IN LPCSTR lpExistingFileName, + IN LPCSTR lpNewFileName OPTIONAL, + IN DWORD dwFlags) +{ + return MoveFileWithProgressA(lpExistingFileName, + lpNewFileName, + NULL, + NULL, + dwFlags); +} + +/* + * @implemented + */ +BOOL +WINAPI +ReplaceFileA(IN LPCSTR lpReplacedFileName, + IN LPCSTR lpReplacementFileName, + IN LPCSTR lpBackupFileName OPTIONAL, + IN DWORD dwReplaceFlags, + IN LPVOID lpExclude, + IN LPVOID lpReserved) +{ + BOOL Ret; + UNICODE_STRING ReplacedFileNameW, ReplacementFileNameW, BackupFileNameW; + + if (!lpReplacedFileName || !lpReplacementFileName || lpExclude || lpReserved || dwReplaceFlags & ~(REPLACEFILE_WRITE_THROUGH | REPLACEFILE_IGNORE_MERGE_ERRORS)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + if (!Basep8BitStringToDynamicUnicodeString(&ReplacedFileNameW, lpReplacedFileName)) + { + return FALSE; + } + + if (!Basep8BitStringToDynamicUnicodeString(&ReplacementFileNameW, lpReplacementFileName)) + { + RtlFreeUnicodeString(&ReplacedFileNameW); + return FALSE; + } + + if (lpBackupFileName) + { + if (!Basep8BitStringToDynamicUnicodeString(&BackupFileNameW, lpBackupFileName)) + { + RtlFreeUnicodeString(&ReplacementFileNameW); + RtlFreeUnicodeString(&ReplacedFileNameW); + return FALSE; + } + } + else + { + BackupFileNameW.Buffer = NULL; + } + + Ret = ReplaceFileW(ReplacedFileNameW.Buffer, ReplacementFileNameW.Buffer, BackupFileNameW.Buffer, dwReplaceFlags, 0, 0); + + 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,34 +602,49 @@ 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; - - DPRINT("%wZ\n", &PathNameU); - - ASSERT(RelatedFileObject != NULL); - - DPRINT("Relative opening\n"); - DPRINT("FileObject->RelatedFileObject->FileName: %wZ\n", &RelatedFileObject->FileName); - - /* 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'\\') - { - Chomp = TRUE; - RelatedFileObject->FileName.Length -= sizeof(WCHAR); - } - - /* 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); + ASSERT(FileObject->RelatedFileObject != NULL); /* No need to modify the FO, it already has the name */ } + /* We're done with opening! */ + if (ParentFcb != NULL) + { + Status = vfatAttachFCBToFileObject(DeviceExt, ParentFcb, FileObject); + } + + if (NT_SUCCESS(Status)) + { + pFcb = FileObject->FsContext; + + if (pFcb->OpenHandleCount == 0) + { + 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; + } + } + + 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,48 +592,16 @@ 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)) + Status = vfatMakeFullName(directoryFCB, &DirContext->LongNameU, &DirContext->ShortNameU, &NameU); + if (!NT_SUCCESS(Status)) { - PathNameLength += sizeof(WCHAR); + return Status; } - 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 - { - RtlAppendUnicodeStringToString(&NameU, &DirContext->ShortNameU); - } - NameU.Buffer[NameU.Length / sizeof(WCHAR)] = 0; - rcFCB = vfatNewFCB(vcb, &NameU); RtlCopyMemory(&rcFCB->entry, &DirContext->DirEntry, sizeof (DIR_ENTRY)); RtlCopyUnicodeString(&rcFCB->ShortNameU, &DirContext->ShortNameU); @@ -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,