/** * Loads plugins from the default plugins directory. */ VOID PhLoadPlugins( VOID ) { HANDLE pluginsDirectoryHandle; PPH_STRING pluginsDirectory; pluginsDirectory = PhGetStringSetting(L"PluginsDirectory"); if (RtlDetermineDosPathNameType_U(pluginsDirectory->Buffer) == RtlPathTypeRelative) { // Not absolute. Make sure it is. PluginsDirectory = PhConcatStrings(4, PhApplicationDirectory->Buffer, L"\\", pluginsDirectory->Buffer, L"\\"); PhDereferenceObject(pluginsDirectory); } else { PluginsDirectory = pluginsDirectory; } if (NT_SUCCESS(PhCreateFileWin32( &pluginsDirectoryHandle, PluginsDirectory->Buffer, FILE_GENERIC_READ, 0, FILE_SHARE_READ, FILE_OPEN, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ))) { UNICODE_STRING pattern = RTL_CONSTANT_STRING(L"*.dll"); PhEnumDirectoryFile(pluginsDirectoryHandle, &pattern, EnumPluginsDirectoryCallback, NULL); NtClose(pluginsDirectoryHandle); } // When we loaded settings before, we didn't know about plugin settings, so they // went into the ignored settings list. Now that they've had a chance to add // settings, we should scan the ignored settings list and move the settings to // the right places. if (PhSettingsFileName) PhConvertIgnoredSettings(); PhpExecuteCallbackForAllPlugins(PluginCallbackLoad); }
/* * @implemented */ BOOLEAN WINAPI CreateSymbolicLinkW(IN LPCWSTR lpSymlinkFileName, IN LPCWSTR lpTargetFileName, IN DWORD dwFlags) { IO_STATUS_BLOCK IoStatusBlock; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE hSymlink = NULL; UNICODE_STRING SymlinkFileName = { 0, 0, NULL }; UNICODE_STRING TargetFileName = { 0, 0, NULL }; BOOLEAN bAllocatedTarget = FALSE, bRelativePath = FALSE; LPWSTR lpTargetFullFileName = NULL; SIZE_T cbPrintName; SIZE_T cbReparseData; PREPARSE_DATA_BUFFER pReparseData = NULL; PBYTE pBufTail; NTSTATUS Status; ULONG dwCreateOptions; DWORD dwErr; if(!lpSymlinkFileName || !lpTargetFileName || (dwFlags | SYMBOLIC_LINK_FLAG_DIRECTORY) != SYMBOLIC_LINK_FLAG_DIRECTORY) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } if(dwFlags & SYMBOLIC_LINK_FLAG_DIRECTORY) dwCreateOptions = FILE_DIRECTORY_FILE; else dwCreateOptions = FILE_NON_DIRECTORY_FILE; switch(RtlDetermineDosPathNameType_U(lpTargetFileName)) { case RtlPathTypeUnknown: case RtlPathTypeRooted: case RtlPathTypeRelative: bRelativePath = TRUE; RtlInitUnicodeString(&TargetFileName, lpTargetFileName); break; case RtlPathTypeDriveRelative: { LPWSTR FilePart; SIZE_T cchTargetFullFileName; cchTargetFullFileName = GetFullPathNameW(lpTargetFileName, 0, NULL, &FilePart); if(cchTargetFullFileName == 0) { dwErr = GetLastError(); goto Cleanup; } lpTargetFullFileName = RtlAllocateHeap(RtlGetProcessHeap(), 0, cchTargetFullFileName * sizeof(WCHAR)); if(lpTargetFullFileName == NULL) { dwErr = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } if(GetFullPathNameW(lpTargetFileName, cchTargetFullFileName, lpTargetFullFileName, &FilePart) == 0) { dwErr = GetLastError(); goto Cleanup; } } lpTargetFileName = lpTargetFullFileName; // fallthrough case RtlPathTypeUncAbsolute: case RtlPathTypeDriveAbsolute: case RtlPathTypeLocalDevice: case RtlPathTypeRootLocalDevice: default: if(!RtlDosPathNameToNtPathName_U(lpTargetFileName, &TargetFileName, NULL, NULL)) { bAllocatedTarget = TRUE; dwErr = ERROR_INVALID_PARAMETER; goto Cleanup; } } cbPrintName = wcslen(lpTargetFileName) * sizeof(WCHAR); cbReparseData = FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + TargetFileName.Length + cbPrintName; pReparseData = RtlAllocateHeap(RtlGetProcessHeap(), 0, cbReparseData); if(pReparseData == NULL) { dwErr = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } pBufTail = (PBYTE)(pReparseData->SymbolicLinkReparseBuffer.PathBuffer); pReparseData->ReparseTag = (ULONG)IO_REPARSE_TAG_SYMLINK; pReparseData->ReparseDataLength = (USHORT)cbReparseData - REPARSE_DATA_BUFFER_HEADER_SIZE; pReparseData->Reserved = 0; pReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0; pReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength = TargetFileName.Length; pBufTail += pReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset; RtlCopyMemory(pBufTail, TargetFileName.Buffer, TargetFileName.Length); pReparseData->SymbolicLinkReparseBuffer.PrintNameOffset = pReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength; pReparseData->SymbolicLinkReparseBuffer.PrintNameLength = (USHORT)cbPrintName; pBufTail += pReparseData->SymbolicLinkReparseBuffer.PrintNameOffset; RtlCopyMemory(pBufTail, lpTargetFileName, cbPrintName); pReparseData->SymbolicLinkReparseBuffer.Flags = 0; if(bRelativePath) pReparseData->SymbolicLinkReparseBuffer.Flags |= 1; // TODO! give this lone flag a name if(!RtlDosPathNameToNtPathName_U(lpSymlinkFileName, &SymlinkFileName, NULL, NULL)) { dwErr = ERROR_PATH_NOT_FOUND; goto Cleanup; } InitializeObjectAttributes(&ObjectAttributes, &SymlinkFileName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtCreateFile ( &hSymlink, FILE_WRITE_ATTRIBUTES | DELETE | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_CREATE, FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT | dwCreateOptions, NULL, 0 ); if(!NT_SUCCESS(Status)) { dwErr = RtlNtStatusToDosError(Status); goto Cleanup; } Status = NtFsControlFile ( hSymlink, NULL, NULL, NULL, &IoStatusBlock, FSCTL_SET_REPARSE_POINT, pReparseData, cbReparseData, NULL, 0 ); if(!NT_SUCCESS(Status)) { FILE_DISPOSITION_INFORMATION DispInfo; DispInfo.DeleteFile = TRUE; NtSetInformationFile(hSymlink, &IoStatusBlock, &DispInfo, sizeof(DispInfo), FileDispositionInformation); dwErr = RtlNtStatusToDosError(Status); goto Cleanup; } dwErr = NO_ERROR; Cleanup: if(hSymlink) NtClose(hSymlink); RtlFreeUnicodeString(&SymlinkFileName); if (bAllocatedTarget) { RtlFreeHeap(RtlGetProcessHeap(), 0, TargetFileName.Buffer); } if(lpTargetFullFileName) RtlFreeHeap(RtlGetProcessHeap(), 0, lpTargetFullFileName); if(pReparseData) RtlFreeHeap(RtlGetProcessHeap(), 0, pReparseData); if(dwErr) { SetLastError(dwErr); return FALSE; } return TRUE; }
/* * @implemented */ BOOL WINAPI WaitNamedPipeW(LPCWSTR lpNamedPipeName, DWORD nTimeOut) { UNICODE_STRING NamedPipeName, NewName, DevicePath, PipePrefix; ULONG NameLength; ULONG i; PWCHAR p; ULONG Type; OBJECT_ATTRIBUTES ObjectAttributes; NTSTATUS Status; HANDLE FileHandle; IO_STATUS_BLOCK IoStatusBlock; ULONG WaitPipeInfoSize; PFILE_PIPE_WAIT_FOR_BUFFER WaitPipeInfo; /* Start by making a unicode string of the name */ TRACE("Sent path: %S\n", lpNamedPipeName); RtlCreateUnicodeString(&NamedPipeName, lpNamedPipeName); NameLength = NamedPipeName.Length / sizeof(WCHAR); /* All slashes must become backslashes */ for (i = 0; i < NameLength; i++) { /* Check and convert */ if (NamedPipeName.Buffer[i] == L'/') NamedPipeName.Buffer[i] = L'\\'; } /* Find the path type of the name we were given */ NewName = NamedPipeName; Type = RtlDetermineDosPathNameType_U(lpNamedPipeName); /* Check if this was a device path, ie : "\\.\pipe\name" */ if (Type == RtlPathTypeLocalDevice) { /* Make sure it's a valid prefix */ RtlInitUnicodeString(&PipePrefix, L"\\\\.\\pipe\\"); RtlPrefixString((PANSI_STRING)&PipePrefix, (PANSI_STRING)&NewName, TRUE); /* Move past it */ NewName.Buffer += 9; NewName.Length -= 9 * sizeof(WCHAR); /* Initialize the Dos Devices name */ TRACE("NewName: %wZ\n", &NewName); RtlInitUnicodeString(&DevicePath, L"\\DosDevices\\pipe\\"); } else if (Type == RtlPathTypeRootLocalDevice) { /* The path is \\server\\pipe\name; find the pipename itself */ p = &NewName.Buffer[2]; /* First loop to get past the server name */ do { /* Check if this is a backslash */ if (*p == L'\\') break; /* Check next */ p++; } while (*p); /* Now make sure the full name contains "pipe\" */ if ((*p) && !(_wcsnicmp(p + 1, L"pipe\\", sizeof("pipe\\")))) { /* Get to the pipe name itself now */ p += sizeof("pipe\\") - 1; } else { /* The name is invalid */ WARN("Invalid name!\n"); BaseSetLastNTError(STATUS_OBJECT_PATH_SYNTAX_BAD); return FALSE; } /* FIXME: Open \DosDevices\Unc\Server\Pipe\Name */ } else { WARN("Invalid path type\n"); BaseSetLastNTError(STATUS_OBJECT_PATH_SYNTAX_BAD); return FALSE; } /* Now calculate the total length of the structure and allocate it */ WaitPipeInfoSize = FIELD_OFFSET(FILE_PIPE_WAIT_FOR_BUFFER, Name[0]) + NewName.Length; WaitPipeInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, WaitPipeInfoSize); if (WaitPipeInfo == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } /* Initialize the object attributes */ TRACE("Opening: %wZ\n", &DevicePath); InitializeObjectAttributes(&ObjectAttributes, &DevicePath, OBJ_CASE_INSENSITIVE, NULL, NULL); /* Open the path */ Status = NtOpenFile(&FileHandle, FILE_READ_ATTRIBUTES | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT); if (!NT_SUCCESS(Status)) { /* Fail; couldn't open */ WARN("Status: %lx\n", Status); BaseSetLastNTError(Status); RtlFreeUnicodeString(&NamedPipeName); RtlFreeHeap(RtlGetProcessHeap(), 0, WaitPipeInfo); return FALSE; } /* Check what timeout we got */ if (nTimeOut == NMPWAIT_USE_DEFAULT_WAIT) { /* Don't use a timeout */ WaitPipeInfo->TimeoutSpecified = FALSE; } else { /* Check if we should wait forever */ if (nTimeOut == NMPWAIT_WAIT_FOREVER) { /* Set the max */ WaitPipeInfo->Timeout.LowPart = 0; WaitPipeInfo->Timeout.HighPart = 0x80000000; } else { /* Convert to NT format */ WaitPipeInfo->Timeout.QuadPart = UInt32x32To64(-10000, nTimeOut); } /* In both cases, we do have a timeout */ WaitPipeInfo->TimeoutSpecified = TRUE; } /* Set the length and copy the name */ WaitPipeInfo->NameLength = NewName.Length; RtlCopyMemory(WaitPipeInfo->Name, NewName.Buffer, NewName.Length); /* Get rid of the full name */ RtlFreeUnicodeString(&NamedPipeName); /* Let NPFS know of our request */ Status = NtFsControlFile(FileHandle, NULL, NULL, NULL, &IoStatusBlock, FSCTL_PIPE_WAIT, WaitPipeInfo, WaitPipeInfoSize, NULL, 0); /* Free our pipe info data and close the handle */ RtlFreeHeap(RtlGetProcessHeap(), 0, WaitPipeInfo); NtClose(FileHandle); /* Check the status */ if (!NT_SUCCESS(Status)) { /* Failure to wait on the pipe */ WARN("Status: %lx\n", Status); BaseSetLastNTError (Status); return FALSE; } /* Success */ return TRUE; }
BOOLEAN PhaGetProcessKnownCommandLine( __in PPH_STRING CommandLine, __in PH_KNOWN_PROCESS_TYPE KnownProcessType, __out PPH_KNOWN_PROCESS_COMMAND_LINE KnownCommandLine ) { switch (KnownProcessType & KnownProcessTypeMask) { case ServiceHostProcessType: { // svchost.exe -k <GroupName> static PH_COMMAND_LINE_OPTION options[] = { { 1, L"k", MandatoryArgumentType } }; KnownCommandLine->ServiceHost.GroupName = NULL; PhParseCommandLine( &CommandLine->sr, options, sizeof(options) / sizeof(PH_COMMAND_LINE_OPTION), PH_COMMAND_LINE_IGNORE_UNKNOWN_OPTIONS, PhpSvchostCommandLineCallback, KnownCommandLine ); if (KnownCommandLine->ServiceHost.GroupName) { PhaDereferenceObject(KnownCommandLine->ServiceHost.GroupName); return TRUE; } else { return FALSE; } } break; case RunDllAsAppProcessType: { // rundll32.exe <DllName>,<ProcedureName> ... SIZE_T i; ULONG_PTR lastIndexOfComma; PPH_STRING dllName; PPH_STRING procedureName; i = 0; // Get the rundll32.exe part. dllName = PhParseCommandLinePart(&CommandLine->sr, &i); if (!dllName) return FALSE; PhDereferenceObject(dllName); // Get the DLL name part. while (i < CommandLine->Length / 2 && CommandLine->Buffer[i] == ' ') i++; dllName = PhParseCommandLinePart(&CommandLine->sr, &i); if (!dllName) return FALSE; PhaDereferenceObject(dllName); // The procedure name begins after the last comma. lastIndexOfComma = PhFindLastCharInString(dllName, 0, ','); if (lastIndexOfComma == -1) return FALSE; procedureName = PhaSubstring( dllName, lastIndexOfComma + 1, dllName->Length / 2 - lastIndexOfComma - 1 ); dllName = PhaSubstring(dllName, 0, lastIndexOfComma); // If the DLL name isn't an absolute path, assume it's in system32. // TODO: Use a proper search function. if (RtlDetermineDosPathNameType_U(dllName->Buffer) == RtlPathTypeRelative) { dllName = PhaConcatStrings( 3, ((PPH_STRING)PHA_DEREFERENCE(PhGetSystemDirectory()))->Buffer, L"\\", dllName->Buffer ); } KnownCommandLine->RunDllAsApp.FileName = dllName; KnownCommandLine->RunDllAsApp.ProcedureName = procedureName; } break; case ComSurrogateProcessType: { // dllhost.exe /processid:<Guid> static PH_STRINGREF inprocServer32Name = PH_STRINGREF_INIT(L"InprocServer32"); SIZE_T i; ULONG_PTR indexOfProcessId; PPH_STRING argPart; PPH_STRING guidString; UNICODE_STRING guidStringUs; GUID guid; HANDLE clsidKeyHandle; HANDLE inprocServer32KeyHandle; PPH_STRING fileName; i = 0; // Get the dllhost.exe part. argPart = PhParseCommandLinePart(&CommandLine->sr, &i); if (!argPart) return FALSE; PhDereferenceObject(argPart); // Get the argument part. while (i < (ULONG)CommandLine->Length / 2 && CommandLine->Buffer[i] == ' ') i++; argPart = PhParseCommandLinePart(&CommandLine->sr, &i); if (!argPart) return FALSE; PhaDereferenceObject(argPart); // Find "/processid:"; the GUID is just after that. PhUpperString(argPart); indexOfProcessId = PhFindStringInString(argPart, 0, L"/PROCESSID:"); if (indexOfProcessId == -1) return FALSE; guidString = PhaSubstring( argPart, indexOfProcessId + 11, (ULONG)argPart->Length / 2 - indexOfProcessId - 11 ); PhStringRefToUnicodeString(&guidString->sr, &guidStringUs); if (!NT_SUCCESS(RtlGUIDFromString( &guidStringUs, &guid ))) return FALSE; KnownCommandLine->ComSurrogate.Guid = guid; KnownCommandLine->ComSurrogate.Name = NULL; KnownCommandLine->ComSurrogate.FileName = NULL; // Lookup the GUID in the registry to determine the name and file name. if (NT_SUCCESS(PhOpenKey( &clsidKeyHandle, KEY_READ, PH_KEY_CLASSES_ROOT, &PhaConcatStrings2(L"CLSID\\", guidString->Buffer)->sr, 0 ))) { KnownCommandLine->ComSurrogate.Name = PHA_DEREFERENCE(PhQueryRegistryString(clsidKeyHandle, NULL)); if (NT_SUCCESS(PhOpenKey( &inprocServer32KeyHandle, KEY_READ, clsidKeyHandle, &inprocServer32Name, 0 ))) { KnownCommandLine->ComSurrogate.FileName = PHA_DEREFERENCE(PhQueryRegistryString(inprocServer32KeyHandle, NULL)); if (fileName = PHA_DEREFERENCE(PhExpandEnvironmentStrings( &KnownCommandLine->ComSurrogate.FileName->sr ))) { KnownCommandLine->ComSurrogate.FileName = fileName; } NtClose(inprocServer32KeyHandle); } NtClose(clsidKeyHandle); } } break; default: return FALSE; } return TRUE; }
/** * Loads plugins from the default plugins directory. */ VOID PhLoadPlugins( VOID ) { HANDLE pluginsDirectoryHandle; PPH_STRING pluginsDirectory; pluginsDirectory = PhGetStringSetting(L"PluginsDirectory"); if (RtlDetermineDosPathNameType_U(pluginsDirectory->Buffer) == RtlPathTypeRelative) { // Not absolute. Make sure it is. PluginsDirectory = PhConcatStrings(4, PhApplicationDirectory->Buffer, L"\\", pluginsDirectory->Buffer, L"\\"); PhDereferenceObject(pluginsDirectory); } else { PluginsDirectory = pluginsDirectory; } if (NT_SUCCESS(PhCreateFileWin32( &pluginsDirectoryHandle, PluginsDirectory->Buffer, FILE_GENERIC_READ, 0, FILE_SHARE_READ, FILE_OPEN, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ))) { UNICODE_STRING pattern = RTL_CONSTANT_STRING(L"*.dll"); PhEnumDirectoryFile(pluginsDirectoryHandle, &pattern, EnumPluginsDirectoryCallback, NULL); NtClose(pluginsDirectoryHandle); } // Handle load errors. // In certain startup modes we want to ignore all plugin load errors. if (LoadErrors && LoadErrors->Count != 0 && !PhStartupParameters.PhSvc) { PH_STRING_BUILDER sb; ULONG i; PPHP_PLUGIN_LOAD_ERROR loadError; PPH_STRING baseName; PhInitializeStringBuilder(&sb, 100); PhAppendStringBuilder2(&sb, L"Unable to load the following plugin(s):\n\n"); for (i = 0; i < LoadErrors->Count; i++) { loadError = LoadErrors->Items[i]; baseName = PhGetBaseName(loadError->FileName); PhAppendFormatStringBuilder(&sb, L"%s: %s\n", baseName->Buffer, PhGetStringOrDefault(loadError->ErrorMessage, L"An unknown error occurred.")); PhDereferenceObject(baseName); } PhAppendStringBuilder2(&sb, L"\nDo you want to disable the above plugin(s)?"); if (PhShowMessage( NULL, MB_ICONERROR | MB_YESNO, sb.String->Buffer ) == IDYES) { ULONG i; for (i = 0; i < LoadErrors->Count; i++) { loadError = LoadErrors->Items[i]; baseName = PhGetBaseName(loadError->FileName); PhSetPluginDisabled(&baseName->sr, TRUE); PhDereferenceObject(baseName); } } PhDeleteStringBuilder(&sb); } // When we loaded settings before, we didn't know about plugin settings, so they // went into the ignored settings list. Now that they've had a chance to add // settings, we should scan the ignored settings list and move the settings to // the right places. if (PhSettingsFileName) PhConvertIgnoredSettings(); PhpExecuteCallbackForAllPlugins(PluginCallbackLoad, TRUE); }
/* * @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; _SEH2_TRY { /* Don't allow renaming to a disk */ if (lpNewFileName && RtlIsDosDeviceName_U(lpNewFileName)) { BaseSetLastNTError(STATUS_OBJECT_NAME_COLLISION); _SEH2_LEAVE; } ReplaceIfExists = !!(dwFlags & MOVEFILE_REPLACE_EXISTING); /* Get file path */ if (!RtlDosPathNameToNtPathName_U(lpExistingFileName, &ExistingPathU, NULL, NULL)) { BaseSetLastNTError(STATUS_OBJECT_PATH_NOT_FOUND); _SEH2_LEAVE; } /* Sanitize input */ DelayUntilReboot = !!(dwFlags & MOVEFILE_DELAY_UNTIL_REBOOT); if (DelayUntilReboot && (dwFlags & MOVEFILE_CREATE_HARDLINK)) { BaseSetLastNTError(STATUS_INVALID_PARAMETER); _SEH2_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); _SEH2_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); _SEH2_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); _SEH2_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); _SEH2_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); _SEH2_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); _SEH2_LEAVE; } Ret = TRUE; _SEH2_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); _SEH2_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; _SEH2_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); _SEH2_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); } } } _SEH2_FINALLY { if (SourceHandle != INVALID_HANDLE_VALUE) NtClose(SourceHandle); RtlFreeHeap(RtlGetProcessHeap(), 0, ExistingPathU.Buffer); RtlFreeHeap(RtlGetProcessHeap(), 0, NewPathU.Buffer); } _SEH2_END; return Ret; }