383 lines
12 KiB
C++
383 lines
12 KiB
C++
#include "stdafx.h"
|
|
|
|
#include <ntstatus.h>
|
|
#define WIN32_NO_STATUS
|
|
typedef long NTSTATUS;
|
|
|
|
#include <windows.h>
|
|
#include "..\..\..\Sandboxie\common\win32_ntddk.h"
|
|
|
|
#include "NtIO.h"
|
|
|
|
bool NtIo_WaitForFolder(POBJECT_ATTRIBUTES objattrs, int seconds, bool (*cb)(const WCHAR* info, void* param), void* param)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
|
|
for (int retries = 0; retries < seconds * 2; retries++)
|
|
{
|
|
if (cb && !cb(objattrs->ObjectName->Buffer, param))
|
|
return false;
|
|
|
|
HANDLE handle = NULL;
|
|
status = NtCreateFile(&handle, DELETE | SYNCHRONIZE, objattrs, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL,
|
|
0, FILE_OPEN, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
|
|
if (NT_SUCCESS(status)) {
|
|
NtClose(handle);
|
|
return true;
|
|
}
|
|
|
|
Sleep(500);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
NTSTATUS NtIo_RemoveProblematicAttributes(POBJECT_ATTRIBUTES objattrs)
|
|
{
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
NTSTATUS status;
|
|
|
|
HANDLE handle = NULL;
|
|
status = NtCreateFile(&handle, FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
|
|
objattrs, &IoStatusBlock, NULL, 0, 0, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
FILE_BASIC_INFORMATION info;
|
|
|
|
status = NtQueryInformationFile(handle, &IoStatusBlock, &info, sizeof(info), FileBasicInformation);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
info.FileAttributes &= ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
|
|
if (info.FileAttributes == 0 || info.FileAttributes == FILE_ATTRIBUTE_DIRECTORY)
|
|
info.FileAttributes |= FILE_ATTRIBUTE_NORMAL;
|
|
|
|
status = NtSetInformationFile(handle, &IoStatusBlock, &info, sizeof(info), FileBasicInformation);
|
|
}
|
|
|
|
NtClose(handle);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
typedef struct _REPARSE_DATA_MOUNT_POINT
|
|
{
|
|
DWORD ReparseTag;
|
|
WORD ReparseDataLength;
|
|
WORD Reserved;
|
|
WORD NameOffset;
|
|
WORD NameLength;
|
|
WORD DisplayNameOffset;
|
|
WORD DisplayNameLength;
|
|
BYTE Data[65536];
|
|
} REPARSE_DATA_MOUNT_POINT, *PREPARSE_DATA_MOUNT_POINT;
|
|
|
|
NTSTATUS NtIo_RemoveJunction(POBJECT_ATTRIBUTES objattrs)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
IO_STATUS_BLOCK Iosb;
|
|
|
|
HANDLE Handle;
|
|
status = NtCreateFile(&Handle, GENERIC_WRITE | DELETE, objattrs, &Iosb, 0, 0, FILE_SHARE_READ, FILE_OPEN, FILE_FLAG_OPEN_REPARSE_POINT, 0, 0); // 0x40100080, , , , , 0x00204020
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
REPARSE_DATA_MOUNT_POINT ReparseBuffer = { 0 };
|
|
status = NtFsControlFile(Handle, NULL, NULL, NULL, &Iosb, FSCTL_GET_REPARSE_POINT, NULL, 0, &ReparseBuffer, sizeof(ReparseBuffer));
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
REPARSE_GUID_DATA_BUFFER ReparseData = { 0 };
|
|
ReparseData.ReparseTag = ReparseBuffer.ReparseTag;
|
|
ReparseData.ReparseDataLength = 0;
|
|
status = NtFsControlFile(Handle, NULL, NULL, NULL, &Iosb, FSCTL_DELETE_REPARSE_POINT, &ReparseData, REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, NULL, 0);
|
|
}
|
|
|
|
NtClose(Handle);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS NtIo_DeleteFolderRecursivelyImpl(POBJECT_ATTRIBUTES objattrs, bool (*cb)(const WCHAR* info, void* param), void* param);
|
|
|
|
NTSTATUS NtIo_DeleteFile(ULONG FileAttributes, OBJECT_ATTRIBUTES* attr, bool (*cb)(const WCHAR* info, void* param), void* param)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
if (FileAttributes & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM))
|
|
NtIo_RemoveProblematicAttributes(attr);
|
|
|
|
if (FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
|
|
status = NtIo_RemoveJunction(attr);
|
|
else if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
status = NtIo_DeleteFolderRecursivelyImpl(attr, cb, param);
|
|
|
|
if (NT_SUCCESS(status))
|
|
status = NtDeleteFile(attr);
|
|
|
|
if (status == STATUS_OBJECT_NAME_NOT_FOUND || status == STATUS_OBJECT_PATH_NOT_FOUND)
|
|
status = STATUS_SUCCESS; // we wanted it gone and its not here, success
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS NtIo_DeleteFile(SNtObject& ntObject, bool (*cb)(const WCHAR* info, void* param), void* param)
|
|
{
|
|
FILE_BASIC_INFORMATION info = { 0 };
|
|
NtQueryAttributesFile(&ntObject.attr, &info);
|
|
|
|
return NtIo_DeleteFile(info.FileAttributes, &ntObject.attr, cb, param);
|
|
}
|
|
|
|
NTSTATUS NtIo_DeleteFolderRecursivelyImpl(POBJECT_ATTRIBUTES objattrs, bool (*cb)(const WCHAR* info, void* param), void* param)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
IO_STATUS_BLOCK Iosb;
|
|
|
|
if (cb && !cb(objattrs->ObjectName->Buffer, param))
|
|
return STATUS_CANCELLED;
|
|
|
|
HANDLE Handle;
|
|
status = NtCreateFile(&Handle, GENERIC_READ, objattrs, &Iosb,
|
|
0, FILE_ATTRIBUTE_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_DIRECTORY_FILE, 0, 0);
|
|
if (!NT_SUCCESS(status))
|
|
return status;
|
|
|
|
for ( ; status == STATUS_SUCCESS; )
|
|
{
|
|
std::wstring FileName;
|
|
ULONG FileAttributes;
|
|
|
|
PFILE_BOTH_DIRECTORY_INFORMATION Info = (PFILE_BOTH_DIRECTORY_INFORMATION)malloc(PAGE_SIZE);
|
|
|
|
HANDLE Event;
|
|
NtCreateEvent(&Event, GENERIC_ALL, 0, NotificationEvent, FALSE);
|
|
status = NtQueryDirectoryFile(Handle, Event, 0, 0, &Iosb, Info, PAGE_SIZE, FileBothDirectoryInformation, TRUE, NULL, FALSE);
|
|
if (status == STATUS_PENDING){
|
|
NtWaitForSingleObject(Event, TRUE, 0);
|
|
status = Iosb.Status;
|
|
}
|
|
NtClose(Event);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
FileName.assign(Info->FileName, Info->FileNameLength / sizeof(wchar_t));
|
|
FileAttributes = Info->FileAttributes;
|
|
}
|
|
|
|
free(Info);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
if(status == STATUS_NO_MORE_FILES)
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
if (wcscmp(FileName.c_str(), L".") == 0 || wcscmp(FileName.c_str(), L"..") == 0)
|
|
continue;
|
|
|
|
SNtObject ntFoundObject(FileName, Handle);
|
|
|
|
status = NtIo_DeleteFile(FileAttributes, &ntFoundObject.attr, cb, param);
|
|
}
|
|
|
|
NtClose(Handle);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS NtIo_DeleteFolderRecursively(POBJECT_ATTRIBUTES objattrs, bool (*cb)(const WCHAR* info, void* param), void* param)
|
|
{
|
|
NtIo_RemoveProblematicAttributes(objattrs);
|
|
|
|
NTSTATUS status = NtIo_DeleteFolderRecursivelyImpl(objattrs, cb, param);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
NtIo_RemoveJunction(objattrs);
|
|
status = NtDeleteFile(objattrs);
|
|
}
|
|
|
|
if (status == STATUS_OBJECT_NAME_NOT_FOUND || status == STATUS_OBJECT_PATH_NOT_FOUND)
|
|
status = STATUS_SUCCESS; // we wanted it gone and its not here, success
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS NtIo_RenameFileOrFolder(POBJECT_ATTRIBUTES src_objattrs, POBJECT_ATTRIBUTES dest_objattrs, const WCHAR* DestName, ULONG Share, ULONG Create)
|
|
{
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
|
|
HANDLE src_handle = NULL; // open source file/folder
|
|
status = NtCreateFile(&src_handle, DELETE | SYNCHRONIZE, src_objattrs, &IoStatusBlock, NULL,
|
|
0,
|
|
Share,
|
|
FILE_OPEN,
|
|
Create | FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL, NULL);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
return status;
|
|
|
|
HANDLE dst_handle = NULL; // open destination folder
|
|
status = NtCreateFile(&dst_handle, FILE_GENERIC_READ, dest_objattrs, &IoStatusBlock, NULL,
|
|
0, // for dir? FILE_ATTRIBUTE_NORMAL
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_OPEN,
|
|
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL, NULL);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
return status;
|
|
|
|
// do the rename and retry if needed
|
|
ULONG InfoSize = sizeof(FILE_RENAME_INFORMATION) + wcslen(DestName) * sizeof(WCHAR) + 16;
|
|
PFILE_RENAME_INFORMATION pInfo = (PFILE_RENAME_INFORMATION)malloc(InfoSize);
|
|
pInfo->ReplaceIfExists = FALSE;
|
|
pInfo->RootDirectory = dst_handle;
|
|
pInfo->FileNameLength = wcslen(DestName) * sizeof(WCHAR);
|
|
wcscpy(pInfo->FileName, DestName);
|
|
|
|
for (int retries = 0; retries < 20; retries++)
|
|
{
|
|
status = NtSetInformationFile(src_handle, &IoStatusBlock, pInfo, InfoSize, FileRenameInformation);
|
|
/*if (status == STATUS_ACCESS_DENIED || status == STATUS_SHARING_VIOLATION)
|
|
{
|
|
// Please terminate programs running in the sandbox before deleting its contents - 3221
|
|
}*/
|
|
if (status != STATUS_SHARING_VIOLATION)
|
|
break;
|
|
|
|
Sleep(300);
|
|
}
|
|
|
|
free(pInfo);
|
|
|
|
NtClose(dst_handle);
|
|
NtClose(src_handle);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS NtIo_RenameFile(POBJECT_ATTRIBUTES src_objattrs, POBJECT_ATTRIBUTES dest_objattrs, const WCHAR* DestName)
|
|
{
|
|
return NtIo_RenameFileOrFolder(src_objattrs, dest_objattrs, DestName, 0, 0);
|
|
}
|
|
|
|
NTSTATUS NtIo_RenameFolder(POBJECT_ATTRIBUTES src_objattrs, POBJECT_ATTRIBUTES dest_objattrs, const WCHAR* DestName)
|
|
{
|
|
return NtIo_RenameFileOrFolder(src_objattrs, dest_objattrs, DestName, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_DIRECTORY_FILE);
|
|
}
|
|
|
|
NTSTATUS NtIo_RenameJunction(POBJECT_ATTRIBUTES src_objattrs, POBJECT_ATTRIBUTES dest_objattrs, const WCHAR* DestName)
|
|
{
|
|
return NtIo_RenameFileOrFolder(src_objattrs, dest_objattrs, DestName, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_FLAG_OPEN_REPARSE_POINT);
|
|
}
|
|
|
|
BOOLEAN NtIo_FileExists(POBJECT_ATTRIBUTES objattrs)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
IO_STATUS_BLOCK Iosb;
|
|
|
|
HANDLE handle;
|
|
status = NtCreateFile(&handle, SYNCHRONIZE, objattrs, &Iosb, NULL, 0, 0, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT, NULL, 0);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
// STATUS_OBJECT_NAME_NOT_FOUND // STATUS_OBJECT_PATH_NOT_FOUND
|
|
NtClose(handle);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
NTSTATUS NtIo_MergeFolder(POBJECT_ATTRIBUTES src_objattrs, POBJECT_ATTRIBUTES dest_objattrs, bool (*cb)(const WCHAR* info, void* param), void* param)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
IO_STATUS_BLOCK Iosb;
|
|
|
|
if (cb && !cb(src_objattrs->ObjectName->Buffer, param))
|
|
return STATUS_CANCELLED;
|
|
|
|
HANDLE ScrHandle;
|
|
status = NtCreateFile(&ScrHandle, GENERIC_READ, src_objattrs, &Iosb,
|
|
0, FILE_ATTRIBUTE_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_DIRECTORY_FILE, 0, 0);
|
|
if (!NT_SUCCESS(status))
|
|
return status;
|
|
|
|
HANDLE DestHandle;
|
|
status = NtCreateFile(&DestHandle, GENERIC_READ, dest_objattrs, &Iosb,
|
|
0, FILE_ATTRIBUTE_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_DIRECTORY_FILE, 0, 0);
|
|
if (!NT_SUCCESS(status))
|
|
return status;
|
|
|
|
for (; status == STATUS_SUCCESS; )
|
|
{
|
|
std::wstring FileName;
|
|
ULONG FileAttributes;
|
|
|
|
PFILE_BOTH_DIRECTORY_INFORMATION Info = (PFILE_BOTH_DIRECTORY_INFORMATION)malloc(PAGE_SIZE);
|
|
|
|
HANDLE Event;
|
|
NtCreateEvent(&Event, GENERIC_ALL, 0, NotificationEvent, FALSE);
|
|
status = NtQueryDirectoryFile(ScrHandle, Event, 0, 0, &Iosb, Info, PAGE_SIZE, FileBothDirectoryInformation, TRUE, NULL, FALSE);
|
|
if (status == STATUS_PENDING){
|
|
NtWaitForSingleObject(Event, TRUE, 0);
|
|
status = Iosb.Status;
|
|
}
|
|
NtClose(Event);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
FileName.assign(Info->FileName, Info->FileNameLength / sizeof(wchar_t));
|
|
FileAttributes = Info->FileAttributes;
|
|
}
|
|
|
|
free(Info);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
if (status == STATUS_NO_MORE_FILES)
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
if (wcscmp(FileName.c_str(), L".") == 0 || wcscmp(FileName.c_str(), L"..") == 0)
|
|
continue;
|
|
|
|
SNtObject ntSrcObject(FileName, ScrHandle);
|
|
SNtObject ntDestObject(FileName, DestHandle);
|
|
|
|
BOOLEAN TargetExists = NtIo_FileExists(&ntDestObject.attr);
|
|
//if (FileAttributes & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM))
|
|
// NtIo_RemoveProblematicAttributes(&ntFoundObject.attr);
|
|
|
|
if (FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
|
|
if (TargetExists)
|
|
status = NtIo_DeleteFile(ntDestObject, cb, param);
|
|
if (NT_SUCCESS(status))
|
|
status = NtIo_RenameJunction(&ntSrcObject.attr, dest_objattrs, FileName.c_str());
|
|
}
|
|
else if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
if (TargetExists)
|
|
status = NtIo_MergeFolder(&ntSrcObject.attr, &ntDestObject.attr, cb, param);
|
|
else
|
|
status = NtIo_RenameFolder(&ntSrcObject.attr, dest_objattrs, FileName.c_str());
|
|
}
|
|
else
|
|
{
|
|
if (TargetExists)
|
|
status = NtIo_DeleteFile(ntDestObject, cb, param);
|
|
if (NT_SUCCESS(status))
|
|
status = NtIo_RenameFile(&ntSrcObject.attr, dest_objattrs, FileName.c_str());
|
|
}
|
|
}
|
|
|
|
NtClose(ScrHandle);
|
|
|
|
NtClose(DestHandle);
|
|
|
|
if (NT_SUCCESS(status))
|
|
status = NtDeleteFile(src_objattrs);
|
|
|
|
return status;
|
|
} |