Sandboxie/Sandboxie/apps/start/delete.cpp

1207 lines
33 KiB
C++

/*
* Copyright 2004-2020 Sandboxie Holdings, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
// Delete Contents of Sandbox
//---------------------------------------------------------------------------
#include "stdafx.h"
#include <shellapi.h>
#include "common/win32_ntddk.h"
#include "common/defines.h"
#include "core/dll/sbiedll.h"
#include "msgs/msgs.h"
#include "common/my_version.h"
#include "core/svc/SbieIniWire.h"
#include <string>
#include <set>
#include "resource.h"
#include "apps/common/CommonUtils.h"
#define INITGUID
#include <guiddef.h>
#include <commdlg.h>
#include "apps/common/MyGdi.h"
//---------------------------------------------------------------------------
// Functions
//---------------------------------------------------------------------------
extern void Show_Error(WCHAR *Descr);
extern void DeleteSandbox(
const WCHAR *BoxName, BOOL bLogoff, BOOL bSilent, int phase);
static void RenameSandbox(void);
static void LaunchPhase2(void);
static void DeleteFiles(void);
static void DeleteFilesInBox(const WCHAR *boxname);
static void TranslateCommand(
WCHAR *cmd1, WCHAR *cmd2, const WCHAR *BoxFolder);
static void WaitForFolder(const WCHAR *folder, ULONG seconds);
static void ProcessFiles(const WCHAR *BoxPath);
static WCHAR *GetBoxFilePath(const WCHAR *boxname, ULONG extra);
//---------------------------------------------------------------------------
// Variables
//---------------------------------------------------------------------------
static const WCHAR *g_BoxName;
static bool g_Logoff;
static bool g_Silent;
static HANDLE g_event_handle = NULL;
static HICON hProgramIcon;
static HBITMAP hLogoBitmap;
static BOOL initialized = FALSE;
extern BOOLEAN layout_rtl;
static HWND status_wnd = NULL;
static BOOL processing = TRUE;
//---------------------------------------------------------------------------
// StatusDialogProc
//---------------------------------------------------------------------------
INT_PTR StatusDialogProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
switch (uMsg) {
//
// initialize dialog
//
case WM_INITDIALOG:
{
RECT rc;
status_wnd = hwnd;
//
// position window in the middle of the screen
//
GetClientRect(GetDesktopWindow(), &rc);
int x = (rc.left + rc.right) / 3;
int y = (rc.top + rc.bottom) / 3;
GetClientRect(hwnd, &rc);
x -= rc.right / 2;
y -= rc.bottom / 2;
SetWindowPos(hwnd, HWND_TOP, x, y, 0, 0, SWP_NOSIZE);
//
// set window title
//
SetWindowText(hwnd, SbieDll_FormatMessage0(MSG_3315));
//
// set info text
//
//WCHAR* info = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, 2048);
//SetDlgItemText(hwnd, ID_STATUS_INFO, info);
//HeapFree(GetProcessHeap(), 0, info);
//
// end dialog initialization
//
SetForegroundWindow(hwnd); // explicitly go to foreground
return FALSE; // don't set focus, we already did
}
//
// handle buttons
//
case WM_COMMAND:
if (LOWORD(wParam) == IDCANCEL) {
int rc = MessageBox(hwnd,
SbieDll_FormatMessage0(MSG_3316),
SbieDll_FormatMessage0(MSG_3315),
MB_ICONQUESTION | MB_YESNO |
(layout_rtl ? MB_RTLREADING | MB_RIGHT : 0));
if (rc == IDYES)
processing = 0;
}
break;
}
return FALSE;
}
//---------------------------------------------------------------------------
// DoStatusDialog
//---------------------------------------------------------------------------
_FX ULONG DoStatusDialog(void *arg)
{
HINSTANCE hInstance = GetModuleHandle(NULL);
INITCOMMONCONTROLSEX icc;
INT_PTR r;
if (! initialized) {
icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
icc.dwICC = ICC_USEREX_CLASSES | ICC_TAB_CLASSES;
InitCommonControlsEx(&icc);
hProgramIcon = (HICON)LoadImage(
hInstance, MAKEINTRESOURCE(IDICON), IMAGE_ICON,
0, 0, LR_DEFAULTSIZE);
MyGdi_Init();
hLogoBitmap = MyGdi_CreateFromResource(L"MASTHEADLOGO");
initialized = TRUE;
}
if (layout_rtl) {
LPCDLGTEMPLATE tmpl = (LPCDLGTEMPLATE)Common_DlgTmplRtl(
hInstance, MAKEINTRESOURCE(STATUS_DIALOG));
r = DialogBoxIndirectParam(hInstance, tmpl,
NULL, StatusDialogProc, (LPARAM)hInstance);
} else {
r = DialogBoxParam(hInstance, MAKEINTRESOURCE(STATUS_DIALOG),
NULL, StatusDialogProc, (LPARAM)hInstance);
}
status_wnd = NULL;
return (r == IDOK);
}
//---------------------------------------------------------------------------
// StartStatusDialog
//---------------------------------------------------------------------------
void StartStatusDialog()
{
CreateThread(NULL, 0, DoStatusDialog, 0, 0, NULL);
for(int i=0; i < 100 && !status_wnd; i++)
Sleep(100);
}
//---------------------------------------------------------------------------
// StopStatusDialog
//---------------------------------------------------------------------------
void StopStatusDialog()
{
if (status_wnd)
EndDialog(status_wnd, IDOK);
}
//---------------------------------------------------------------------------
// SetStatusInfo
//---------------------------------------------------------------------------
void SetStatusInfo(const WCHAR* info)
{
if(status_wnd)
SetDlgItemText(status_wnd, ID_STATUS_INFO, info);
}
//---------------------------------------------------------------------------
// SetStatusMsg
//---------------------------------------------------------------------------
void SetStatusMsg(ULONG msg, const WCHAR* info)
{
if(status_wnd) {
WCHAR* str = SbieDll_FormatMessage1(msg, info);
SetDlgItemText(status_wnd, ID_STATUS_INFO, str);
LocalFree(str);
}
}
//---------------------------------------------------------------------------
// Error
//---------------------------------------------------------------------------
void Error(const WCHAR *Descr, NTSTATUS Status)
{
if (g_event_handle) {
CloseHandle(g_event_handle);
g_event_handle = NULL;
}
if (! g_Silent) {
WCHAR text[512];
wcscpy(text, SbieDll_FormatMessage1(MSG_3214, g_BoxName));
wcscat(text, Descr);
if (Status) {
if (Status == STATUS_ACCESS_DENIED ||
Status == STATUS_SHARING_VIOLATION)
{
wcscat(text, L"\n\n");
wcscat(text,
SbieDll_FormatMessage0(MSG_3215));
}
SetLastError(RtlNtStatusToDosError(Status));
}
Show_Error(text);
}
if (g_Logoff)
ExitWindowsEx(EWX_LOGOFF, 0);
ExitProcess(1);
}
//---------------------------------------------------------------------------
// DeleteSandbox
//---------------------------------------------------------------------------
void DeleteSandbox(
const WCHAR *BoxName, BOOL bLogoff, BOOL bSilent, int phase)
{
g_BoxName = BoxName;
g_Logoff = bLogoff ? true : false;
g_Silent = bSilent ? true : false;
if (SbieApi_QueryProcess(NULL, NULL, NULL, NULL, NULL) == 0) {
SetLastError(0);
Error(SbieDll_FormatMessage0(MSG_3221), 0);
}
if ((phase <= 1) &&
SbieApi_QueryConfBool(g_BoxName, L"NeverDelete", FALSE)) {
SetLastError(0);
Error(SbieDll_FormatMessage0(MSG_3051), 0);
} else {
WCHAR evname[128];
ULONG session_id = 0;
if (! ProcessIdToSessionId(GetCurrentProcessId(), &session_id))
session_id = 0;
wsprintf(evname,
SANDBOXIE L"_Delete_Sandbox_Session_%d", session_id);
g_event_handle = CreateEvent(NULL, FALSE, FALSE, evname);
if (phase == 2) {
DeleteFiles();
if (g_Logoff)
ExitWindowsEx(EWX_LOGOFF, 0);
} else if (phase <= 1) {
RenameSandbox();
if (phase == 0)
LaunchPhase2();
}
}
ExitProcess(0);
}
//---------------------------------------------------------------------------
//
// DELETE FILES
//
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// RenameSandbox
//---------------------------------------------------------------------------
void RenameSandbox(void)
{
NTSTATUS status;
union {
FILE_RENAME_INFORMATION info;
WCHAR space[128];
} u;
UNICODE_STRING uni;
OBJECT_ATTRIBUTES objattrs;
IO_STATUS_BLOCK MyIoStatusBlock;
HANDLE hSandbox, hTopLevel;
//
// open sandbox folder: root\Sandbox\user\BoxName
//
WCHAR *boxpath = GetBoxFilePath(g_BoxName, 128);
if (! boxpath)
Error(SbieDll_FormatMessage0(MSG_3216), 0);
RtlMoveMemory(
boxpath + 4, boxpath, (wcslen(boxpath) + 1) * sizeof(WCHAR));
RtlCopyMemory(boxpath, L"\\??\\", 4 * sizeof(WCHAR));
RtlInitUnicodeString(&uni, boxpath);
InitializeObjectAttributes(
&objattrs, &uni, OBJ_CASE_INSENSITIVE, NULL, NULL);
status = NtCreateFile(
&hSandbox, DELETE | SYNCHRONIZE, &objattrs,
&MyIoStatusBlock, NULL, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
NULL, NULL);
if (status != STATUS_SUCCESS) {
if (status == STATUS_OBJECT_NAME_NOT_FOUND ||
status == STATUS_OBJECT_PATH_NOT_FOUND)
{
// no sandbox folder -- nothing to do
return;
}
Error(SbieDll_FormatMessage0(MSG_3217), status);
}
//
// open top level folder: root\Sandbox\user
//
WCHAR *backslash = wcsrchr(boxpath, L'\\');
if (backslash - boxpath == 6)
++backslash; // "\??\X:" --> "\??\X:\" .
*backslash = L'\0';
RtlInitUnicodeString(&uni, boxpath);
status = NtCreateFile(
&hTopLevel, FILE_GENERIC_READ, &objattrs,
&MyIoStatusBlock, NULL, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
NULL, NULL);
if (status != STATUS_SUCCESS)
Error(SbieDll_FormatMessage0(MSG_3218), status);
// rename sandbox using Nt file operations
u.info.ReplaceIfExists = FALSE;
u.info.RootDirectory = hTopLevel;
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
wsprintf(u.info.FileName, L"__Delete_%s_%08X%08X",
g_BoxName, ft.dwHighDateTime, ft.dwLowDateTime);
u.info.FileNameLength = wcslen(u.info.FileName) * sizeof(WCHAR);
ULONG retries = 0;
while (1) {
status = NtSetInformationFile(
hSandbox, &MyIoStatusBlock,
&u.info, sizeof(u), FileRenameInformation);
if (NT_SUCCESS(status))
break;
if (status == STATUS_ACCESS_DENIED ||
status == STATUS_SHARING_VIOLATION) {
ULONG pid_count = 0;
SbieApi_EnumProcessEx(g_BoxName, FALSE, -1, NULL, &pid_count);
if (pid_count) {
SetLastError(0);
Error(SbieDll_FormatMessage0(MSG_3221), 0);
}
}
if (retries == 30)
Error(SbieDll_FormatMessage0(MSG_3219), status);
++retries;
Sleep(200);
}
}
//---------------------------------------------------------------------------
// LaunchProgram
//---------------------------------------------------------------------------
void LaunchProgram(WCHAR *cmdSrc, bool bWait)
{
WCHAR cmd[768];
ExpandEnvironmentStrings(cmdSrc, cmd, 760);
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_FORCEOFFFEEDBACK | STARTF_USESHOWWINDOW;
si.wShowWindow = bWait ? SW_HIDE : SW_SHOWNORMAL;
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
BOOL ok = CreateProcess(
NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
if (ok && bWait) {
WaitForSingleObject(pi.hProcess, INFINITE);
ULONG ExitCode = 0;
if (GetExitCodeProcess(pi.hProcess, &ExitCode) && ExitCode != 0) {
CloseHandle(pi.hProcess);
SetLastError(ExitCode);
ok = FALSE;
} else
CloseHandle(pi.hProcess);
}
if (! ok) {
WCHAR txt[1024];
wcscpy(txt, SbieDll_FormatMessage0(MSG_3222));
wcscat(txt, cmd);
Error(txt, 0);
}
}
//---------------------------------------------------------------------------
// LaunchPhase2
//---------------------------------------------------------------------------
NOINLINE void LaunchPhase2(void)
{
WCHAR cmd[512];
cmd[0] = L'\"';
GetModuleFileName(NULL, &cmd[1], 500);
wcscat(cmd, L"\" delete_sandbox");
if (g_Logoff)
wcscat(cmd, L"_logoff");
if (g_Silent)
wcscat(cmd, L"_silent");
wcscat(cmd, L"_phase2");
LaunchProgram(cmd, FALSE);
}
//---------------------------------------------------------------------------
// DeleteFiles
//---------------------------------------------------------------------------
NOINLINE void DeleteFiles(void)
{
//if(!g_Silent)
// StartStatusDialog();
WCHAR boxname[BOXNAME_COUNT];
int index = -1;
while (1) {
index = SbieApi_EnumBoxes(index, boxname);
if (index == -1)
break;
g_BoxName = boxname;
DeleteFilesInBox(boxname);
}
//if(!g_Silent)
// StopStatusDialog();
}
//---------------------------------------------------------------------------
// DeleteFilesInBox
//---------------------------------------------------------------------------
void DeleteFilesInBox(const WCHAR *boxname)
{
static const WCHAR *_DeleteCommand = L"DeleteCommand";
static const WCHAR *_DefaultDeleteCommand =
L"%SystemRoot%\\System32\\cmd.exe /c rmdir /s /q \"%SANDBOX%\"";
//
// open parent folder of sandbox
//
WCHAR *boxpath = GetBoxFilePath(boxname, 128);
if (! boxpath) {
// Error(SbieDll_FormatMessage0(MSG_3216), 0);
return;
}
WCHAR *backslash = wcsrchr(boxpath, L'\\');
if (backslash)
*backslash = L'\0';
wcscat(boxpath, L"\\*");
//
// get delete command
//
ULONG len = 2048;
WCHAR *cmd = (WCHAR *)HeapAlloc(
GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, len);
memzero(cmd, len);
len -= 8;
SbieApi_QueryConfAsIs(boxname, _DeleteCommand, 0, cmd, len);
if (! cmd[0])
wcscpy(cmd, _DefaultDeleteCommand);
//
// find all folders named __Delete_something
//
WCHAR *tmpl = (WCHAR *)HeapAlloc(
GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, 256);
wsprintf(tmpl, L"__Delete_%s_", boxname);
WCHAR *BoxFolder = (WCHAR *)
HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, 2048);
WIN32_FIND_DATA data;
HANDLE hFind = FindFirstFile(boxpath, &data);
if (hFind == INVALID_HANDLE_VALUE) {
HeapFree(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, boxpath);
return;
}
while (1) {
if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
wcslen(data.cFileName) == wcslen(tmpl) + 16 &&
_wcsnicmp(data.cFileName, tmpl, wcslen(tmpl)) == 0)
{
wcscpy(BoxFolder, boxpath);
wcscpy(BoxFolder + wcslen(BoxFolder) - 1, data.cFileName);
//
// prepare sandbox for deletion by taking care of read-only
// files, junction points, and paths that are too long
//
SetStatusMsg(MSG_3317, BoxFolder);
WaitForFolder(BoxFolder, 10);
ProcessFiles(BoxFolder);
SetStatusMsg(MSG_3318, BoxFolder);
WaitForFolder(BoxFolder, 10);
//
// translate %SANDBOX% placeholders in DeleteCommand,
// then invoke DeleteCommand
//
WCHAR cmd2[1536];
TranslateCommand(cmd, cmd2, BoxFolder);
LaunchProgram(cmd2, TRUE);
}
if (! FindNextFile(hFind, &data))
break;
}
HeapFree(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, boxpath);
}
//---------------------------------------------------------------------------
// TranslateCommand
//---------------------------------------------------------------------------
_FX void TranslateCommand(WCHAR *cmd1, WCHAR *cmd2, const WCHAR *BoxFolder)
{
static const WCHAR *_sandbox = L"%SANDBOX%";
static const WCHAR *_sandbox_bs = L"%SANDBOX\\\\%";
WCHAR *ptr1 = cmd1, *ptr2 = cmd2;
while (*ptr1) {
if (*ptr1 == L'%') {
if (_wcsnicmp(ptr1, _sandbox, wcslen(_sandbox)) == 0) {
wcscpy(ptr2, BoxFolder);
ptr2 += wcslen(ptr2);
ptr1 += wcslen(_sandbox);
continue;
}
if (_wcsnicmp(ptr1, _sandbox_bs, wcslen(_sandbox_bs)) == 0) {
const WCHAR *boxptr = BoxFolder;
while (*boxptr) {
if (*boxptr == L'\\') {
*ptr2 = *boxptr;
++ptr2;
}
*ptr2 = *boxptr;
++ptr2;
++boxptr;
}
ptr1 += wcslen(_sandbox_bs);
continue;
}
}
*ptr2 = *ptr1;
++ptr2;
++ptr1;
}
*ptr2 = L'\0';
}
//---------------------------------------------------------------------------
// WaitForFolder
//---------------------------------------------------------------------------
NOINLINE void WaitForFolder(const WCHAR *folder, ULONG seconds)
{
NTSTATUS status;
UNICODE_STRING uni;
OBJECT_ATTRIBUTES objattrs;
IO_STATUS_BLOCK MyIoStatusBlock;
HANDLE handle;
ULONG retries;
ULONG len = (wcslen(folder) + 8) * sizeof(WCHAR);
WCHAR *path = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, len);
if (! path)
return;
wsprintf(path, L"\\??\\%s", folder);
RtlInitUnicodeString(&uni, path);
InitializeObjectAttributes(
&objattrs, &uni, OBJ_CASE_INSENSITIVE, NULL, NULL);
for (retries = 0; retries < seconds * 2; ++retries) {
status = NtCreateFile(
&handle, DELETE | SYNCHRONIZE, &objattrs,
&MyIoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL,
0, FILE_OPEN, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
NULL, 0);
if (NT_SUCCESS(status)) {
NtClose(handle);
break;
}
Sleep(500);
}
HeapFree(GetProcessHeap(), 0, path);
}
//---------------------------------------------------------------------------
// RemoveFileAttributes
//---------------------------------------------------------------------------
bool RemoveFileAttributes(
HANDLE heap, const WCHAR *parent, const WCHAR *child)
{
static ULONG counter = 0;
UNICODE_STRING uni;
OBJECT_ATTRIBUTES objattrs;
IO_STATUS_BLOCK MyIoStatusBlock;
NTSTATUS status = STATUS_SUCCESS;
HANDLE handle = NULL;
// prepare path name
ULONG len = (wcslen(parent) + wcslen(child) + 8) * sizeof(WCHAR);
WCHAR *path = (WCHAR *)HeapAlloc(heap, 0, len);
wsprintf(path, L"\\??\\%s\\%s", parent, child);
// open file
if (NT_SUCCESS(status)) {
RtlInitUnicodeString(&uni, path);
InitializeObjectAttributes(
&objattrs, &uni, OBJ_CASE_INSENSITIVE, NULL, NULL);
status = NtCreateFile(
&handle,
FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
&objattrs, &MyIoStatusBlock, NULL, 0, 0,
FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
}
if (NT_SUCCESS(status)) {
union {
FILE_BASIC_INFORMATION info;
WCHAR space[128];
} u;
status = NtQueryInformationFile(
handle, &MyIoStatusBlock,
&u.info, sizeof(u), FileBasicInformation);
if (NT_SUCCESS(status)) {
u.info.FileAttributes &= ~(FILE_ATTRIBUTE_READONLY |
FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
if (u.info.FileAttributes == 0 ||
u.info.FileAttributes == FILE_ATTRIBUTE_DIRECTORY)
u.info.FileAttributes |= FILE_ATTRIBUTE_NORMAL;
status = NtSetInformationFile(
handle, &MyIoStatusBlock,
&u.info, sizeof(u), FileBasicInformation);
}
}
// close and free everything
if (handle)
NtClose(handle);
HeapFree(heap, 0, path);
SetLastError(RtlNtStatusToDosError(status));
return (status == STATUS_SUCCESS);
}
//---------------------------------------------------------------------------
// RenameSingleFile
//---------------------------------------------------------------------------
bool RenameSingleFile(
HANDLE heap,
const WCHAR *parent, const WCHAR *child, const WCHAR *BoxPath)
{
static ULONG counter = 0;
UNICODE_STRING uni;
OBJECT_ATTRIBUTES objattrs;
IO_STATUS_BLOCK MyIoStatusBlock;
NTSTATUS status = STATUS_SUCCESS;
HANDLE src_handle = NULL, dst_handle = NULL;
ULONG retries;
// prepare source and destination path names
ULONG len = (wcslen(parent) + wcslen(child) + 8) * sizeof(WCHAR);
WCHAR *src_path = (WCHAR *)HeapAlloc(heap, 0, len);
wsprintf(src_path, L"\\??\\%s\\%s", parent, child);
len = (wcslen(BoxPath) + 64) * sizeof(WCHAR);
FILETIME now;
GetSystemTimeAsFileTime(&now);
++counter;
WCHAR *dst_path = (WCHAR *)HeapAlloc(heap, 0, len);
wsprintf(dst_path, L"\\??\\%s\\%08X-%08X-%08X",
BoxPath, now.dwHighDateTime, now.dwLowDateTime, counter);
WCHAR *dst_name = wcsrchr(dst_path, L'\\');
*dst_name = L'\0';
++dst_name;
// open files and directories
if (NT_SUCCESS(status)) {
RtlInitUnicodeString(&uni, src_path);
InitializeObjectAttributes(
&objattrs, &uni, OBJ_CASE_INSENSITIVE, NULL, NULL);
status = NtCreateFile(
&src_handle, DELETE | SYNCHRONIZE,
&objattrs, &MyIoStatusBlock, NULL, 0, 0,
FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
}
if (NT_SUCCESS(status)) {
RtlInitUnicodeString(&uni, dst_path);
InitializeObjectAttributes(
&objattrs, &uni, OBJ_CASE_INSENSITIVE, NULL, NULL);
status = NtCreateFile(
&dst_handle, FILE_GENERIC_READ, &objattrs,
&MyIoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
NULL, 0);
}
if (NT_SUCCESS(status)) {
union {
FILE_RENAME_INFORMATION info;
WCHAR space[128];
} u;
u.info.ReplaceIfExists = FALSE;
u.info.RootDirectory = dst_handle;
u.info.FileNameLength = wcslen(dst_name) * sizeof(WCHAR);
wcscpy(u.info.FileName, dst_name);
for (retries = 0; retries < 20; ++retries) {
status = NtSetInformationFile(
src_handle, &MyIoStatusBlock,
&u.info, sizeof(u), FileRenameInformation);
if (status != STATUS_SHARING_VIOLATION)
break;
Sleep(300);
}
}
// close and free everything
if (dst_handle)
NtClose(dst_handle);
if (src_handle)
NtClose(src_handle);
HeapFree(heap, 0, dst_path);
HeapFree(heap, 0, src_path);
if (status == STATUS_OBJECT_NAME_NOT_FOUND ||
status == STATUS_OBJECT_PATH_NOT_FOUND) {
status = STATUS_SUCCESS;
}
SetLastError(RtlNtStatusToDosError(status));
return (status == STATUS_SUCCESS);
}
//---------------------------------------------------------------------------
// ProcessFiles
//---------------------------------------------------------------------------
typedef struct _PATHELEM {
struct _PATHELEM *next;
WCHAR path[1];
} PATHELEM;
PATHELEM *AllocPathElem(HANDLE heap, const WCHAR *parent, const WCHAR *child)
{
ULONG len = sizeof(PATHELEM)
+ (wcslen(parent) + wcslen(child) + 4) * sizeof(WCHAR);
PATHELEM *elem = (PATHELEM *)HeapAlloc(heap, 0, len);
elem->next = NULL;
len = wcslen(parent);
CopyMemory(elem->path, parent, len * sizeof(WCHAR));
if (*child) {
elem->path[len] = L'\\';
wcscpy(elem->path + len + 1, child);
} else
elem->path[len] = L'\0';
return elem;
}
void ProcessFiles(const WCHAR *BoxPath)
{
static const UCHAR valid_chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
"0123456789 ^&@{}[],$=!-#()%.+~_";
bool anyRenames;
HANDLE heap = HeapCreate(HEAP_GENERATE_EXCEPTIONS, 0, 0);
mainloop:
anyRenames = false;
//
// process every file in the sandbox folder. remove file attributes
// and junction points where we find them. also rename any files
// or directories in the sandbox that have a path which is too long
// for normal Win32 processing
//
PATHELEM *elem_next = AllocPathElem(heap, BoxPath, L"");
while (processing) {
PATHELEM *elem = elem_next;
if (! elem)
break;
elem_next = elem->next;
SetStatusInfo(elem->path);
WCHAR *search_path = AllocPathElem(heap, elem->path, L"*")->path;
WIN32_FIND_DATA data;
HANDLE hFind = FindFirstFile(search_path, &data);
if (hFind == INVALID_HANDLE_VALUE)
continue;
bool firstTime = true;
while (processing) {
if (firstTime)
firstTime = false;
else {
if (! FindNextFile(hFind, &data))
break;
}
const WCHAR *name = data.cFileName;
if (wcscmp(name, L".") == 0 || wcscmp(name, L"..") == 0)
continue;
ULONG name_len = wcslen(name);
if (data.dwFileAttributes & (FILE_ATTRIBUTE_READONLY |
FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) {
RemoveFileAttributes(heap, elem->path, name);
}
//
// check if the path is too long,
// or if the file name matches a DOS device name,
// or if the file name contains non-ASCII characters
// or if the file name contains invalid ASCII characters
// or if the file name ends with a dot or a space
//
bool needRename = ((wcslen(elem->path) + name_len) > 220);
if ((! needRename) && (name_len <= 8)) {
if(SbieDll_IsReservedFileName(name))
needRename = true;
}
if (! needRename) {
for (const WCHAR *nameptr = name; *nameptr; ++nameptr) {
const UCHAR *charptr;
if (*nameptr >= 0x80) { // non-ASCII
needRename = TRUE;
break;
}
for (charptr = valid_chars; *charptr; ++charptr) {
if ((UCHAR)*nameptr == *charptr)
break;
}
if (! *charptr) { // invalid ASCII character
needRename = TRUE;
break;
}
}
}
if (! needRename) {
if (name_len > 1 && (name[name_len - 1] == L'.' ||
name[name_len - 1] == L' ')) {
needRename = TRUE; // file ends with a dot/space
}
}
//
// if necessary, move the file to a simple name at sandbox root
//
if (needRename) {
bool ok = RenameSingleFile(heap, elem->path, name, BoxPath);
if (! ok) {
Error(SbieDll_FormatMessage0(MSG_3220), 0);
return;
}
anyRenames = true;
//
// otherwise delete the directory if it is a reparse point
//
} else if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
bool removed = false;
if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
ULONG len = wcslen(elem->path) + wcslen(name) + 4;
WCHAR *fullpath =
(WCHAR *)HeapAlloc(heap, 0, len * sizeof(WCHAR));
if (fullpath) {
wsprintf(fullpath, L"%s\\%s", elem->path, name);
if (RemoveDirectory(fullpath))
removed = true;
HeapFree(heap, 0, fullpath);
}
}
if (! removed) {
PATHELEM *elem_new =
AllocPathElem(heap, elem->path, name);
elem_new->next = elem_next;
elem_next = elem_new;
}
}
}
FindClose(hFind);
}
//
// in case we detected a file path that was too long for normal Win32
// processing, and renamed it to a shorter name, we must now repeat
// the process, because that file may have been a directory which
// could itself go deep enough to cause a long path again
//
if (anyRenames)
goto mainloop;
RemoveFileAttributes(heap, BoxPath, L"");
HeapDestroy(heap);
}
//---------------------------------------------------------------------------
// GetBoxPath
//---------------------------------------------------------------------------
ALIGNED WCHAR *GetBoxFilePath(const WCHAR *boxname, ULONG extra)
{
LONG status;
ULONG len;
WCHAR *path;
len = 0;
status = SbieApi_QueryBoxPath(
boxname, NULL, NULL, NULL, &len, NULL, NULL);
if (status != 0)
return NULL;
path = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, len + extra);
if (path) {
status = SbieApi_QueryBoxPath(
boxname, path, NULL, NULL, &len, NULL, NULL);
if (status == 0) {
BOOLEAN ok = SbieDll_TranslateNtToDosPath(path);
if (! ok)
status = 1;
}
if (status != 0) {
HeapFree(GetProcessHeap(), 0, path);
path = NULL;
}
}
return path;
}
//---------------------------------------------------------------------------
// Delete_All_Sandboxes
//---------------------------------------------------------------------------
int Delete_All_Sandboxes()
{
std::set<std::wstring> dirs_to_remove;
int index = -1;
WCHAR BoxName[BOXNAME_COUNT];
while (1) {
index = SbieApi_EnumBoxes(index, BoxName);
if (index == -1)
break;
WCHAR* buf = GetBoxFilePath(BoxName, 0);
while(*buf)
{
std::wstring test_path = buf;
size_t pos = test_path.find_last_of(L'\\');
test_path.erase(pos+1);
test_path.append(L"DONT-USE.TXT");
if (PathFileExists(test_path.c_str()))
buf[pos] = L'\0';
else
break;
}
dirs_to_remove.insert(buf);
HeapFree(GetProcessHeap(), 0, buf);
}
StartStatusDialog();
for (auto I = dirs_to_remove.begin(); I != dirs_to_remove.end() && processing; ++I) {
const WCHAR* BoxFolder = I->c_str();
SetStatusMsg(MSG_3317, BoxFolder);
WaitForFolder(BoxFolder, 10);
ProcessFiles(BoxFolder);
SetStatusMsg(MSG_3318, BoxFolder);
WaitForFolder(BoxFolder, 10);
WCHAR cmd[1536];
wcscpy(cmd, L"%SystemRoot%\\System32\\cmd.exe /c rmdir /s /q \"");
wcscat(cmd, BoxFolder);
wcscat(cmd, L"\"");
LaunchProgram(cmd, TRUE);
}
StopStatusDialog();
return processing == 1 ? EXIT_SUCCESS : EXIT_FAILURE;
}