1.10.1
This commit is contained in:
parent
0d7034ce7e
commit
3372b74a22
|
@ -17,6 +17,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||
### Changed
|
||||
- reworked Nt Object Handle handling
|
||||
- "OpenClipboard=n" now is also implemented in user mode, making it work for green boxes
|
||||
- changed Delete V2 scheme to use drive letters in FilePaths.dat (remains backwards compatible with using NT Paths) [#3053](https://github.com/sandboxie-plus/Sandboxie/issues/3053)
|
||||
|
||||
### Fixed
|
||||
- fixed "Disable Security Isolation" causes a game to stop playing audio [#2893](https://github.com/sandboxie-plus/Sandboxie/issues/2893)
|
||||
|
@ -31,6 +32,12 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||
- fixed crash issue with not peroeprly termianted script engine [#3120](https://github.com/sandboxie-plus/Sandboxie/issues/3120)
|
||||
- fixed ImDisk under Sandboxie supervision causes SBIE2337 and sometimes BSoD [#1092)(https://github.com/sandboxie-plus/Sandboxie/issues/1092)
|
||||
- fixed Snapshots don't merge duplicate directory junctions [#3016](https://github.com/sandboxie-plus/Sandboxie/issues/3016)
|
||||
- fixed Snapshot related issue when using Delete V2 rename functionality
|
||||
- fixed issue with Delete V2 when using network shares
|
||||
- fixed issue when using "UseVolumeSerialNumbers=y" with accessing drive roots
|
||||
- fixed Remove-Snapshot resurrects deleted files when using Delete V2 [#3015](https://github.com/sandboxie-plus/Sandboxie/issues/3015)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -48,9 +48,10 @@ extern __declspec(dllexport) int __CRTDECL Sbie_snprintf(char *_Buffer, size_t C
|
|||
#define TRUE_NAME_BUFFER 0
|
||||
#define COPY_NAME_BUFFER 1
|
||||
#define TMPL_NAME_BUFFER 2
|
||||
#define MISC_NAME_BUFFER 3 // 4, 5, 6, 7
|
||||
#define NAME_BUFFER_COUNT 8
|
||||
#define NAME_BUFFER_DEPTH 16 // 12
|
||||
#define NORM_NAME_BUFFER 3
|
||||
#define MISC_NAME_BUFFER 4 // 5 - 11
|
||||
#define NAME_BUFFER_COUNT 12
|
||||
#define NAME_BUFFER_DEPTH 16
|
||||
|
||||
|
||||
#ifdef _WIN64
|
||||
|
|
|
@ -338,7 +338,7 @@ ALIGNED WCHAR *Dll_GetTlsNameBuffer(THREAD_DATA *data, ULONG which, ULONG size)
|
|||
DbgTrace("Dll_GetTlsNameBuffer, %s, %d\r\n", func, which);
|
||||
#endif
|
||||
|
||||
if (which >= NAME_BUFFER_COUNT - 4)
|
||||
if (which >= NAME_BUFFER_COUNT - 2)
|
||||
SbieApi_Log(2310, L"%d", which);
|
||||
if (which >= NAME_BUFFER_COUNT) {
|
||||
ExitProcess(-1);
|
||||
|
|
|
@ -873,8 +873,8 @@ check_sandbox_prefix:
|
|||
// skip any suffix after the drive letter
|
||||
if (File_DriveAddSN) {
|
||||
WCHAR* ptr = wcschr(*OutTruePath + _DriveLen + 1, L'\\');
|
||||
if (ptr)
|
||||
len = (ULONG)(ptr - *OutTruePath);
|
||||
if (!ptr) ptr = wcschr(*OutTruePath + _DriveLen + 1, L'\0');
|
||||
len = (ULONG)(ptr - *OutTruePath);
|
||||
}
|
||||
|
||||
File_GetName_FixTruePrefix(TlsData,
|
||||
|
@ -1259,8 +1259,8 @@ check_sandbox_prefix:
|
|||
*name = drive_letter;
|
||||
++name;
|
||||
|
||||
if (File_DriveAddSN && *drive->sn)
|
||||
{
|
||||
if (File_DriveAddSN && *drive->sn) {
|
||||
|
||||
*name = L'~';
|
||||
++name;
|
||||
wcscpy(name, drive->sn);
|
||||
|
@ -2538,7 +2538,8 @@ _FX NTSTATUS File_NtCreateFileImpl(
|
|||
// SbieDrv has removed privileges
|
||||
//
|
||||
|
||||
CreateOptions &= ~FILE_OPEN_FOR_BACKUP_INTENT;
|
||||
if (!Dll_CompartmentMode)
|
||||
CreateOptions &= ~FILE_OPEN_FOR_BACKUP_INTENT;
|
||||
|
||||
//
|
||||
// get the full paths for the true and copy files.
|
||||
|
@ -2916,8 +2917,7 @@ ReparseLoop:
|
|||
}
|
||||
|
||||
}
|
||||
else if (status == STATUS_OBJECT_NAME_NOT_FOUND ||
|
||||
status == STATUS_OBJECT_PATH_NOT_FOUND) {
|
||||
else if (status == STATUS_OBJECT_NAME_NOT_FOUND || status == STATUS_OBJECT_PATH_NOT_FOUND) {
|
||||
|
||||
//
|
||||
// the CopyPath file does not exist, but its parent path may exist
|
||||
|
@ -2969,7 +2969,7 @@ ReparseLoop:
|
|||
// When using Rule specificity we need to create some dummy directories
|
||||
//
|
||||
|
||||
File_CreateBoxedPath(TruePath);
|
||||
File_CreateBoxedPath(OriginalPath ? OriginalPath : TruePath);
|
||||
}
|
||||
else if (OriginalPath) {
|
||||
|
||||
|
|
|
@ -314,6 +314,71 @@ _FX ULONG File_GetPathFlags_internal(LIST* Root, const WCHAR* Path, WCHAR** pRel
|
|||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// File_NormalizePath
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
_FX const WCHAR* File_NormalizePath(const WCHAR* path, int slot)
|
||||
{
|
||||
//
|
||||
// if we have a path that looks like any of these
|
||||
// \Device\LanmanRedirector\server\shr\f1.txt
|
||||
// \Device\LanmanRedirector\;Q:000000000000b09f\server\shr\f1.txt
|
||||
// \Device\Mup\;LanmanRedirector\server\share\f1.txt
|
||||
// \Device\Mup\;LanmanRedirector\;Q:000000000000b09f\server\share\f1.txt
|
||||
// then translate to
|
||||
// \Device\Mup\server\shr\f1.txt
|
||||
// and test again. We do this because open/closed paths are
|
||||
// recorded in the \Device\Mup format. See File_TranslateShares.
|
||||
//
|
||||
|
||||
ULONG PrefixLen;
|
||||
if (_wcsnicmp(path, File_Redirector, File_RedirectorLen - 1) == 0)
|
||||
PrefixLen = File_RedirectorLen - 1;
|
||||
else if (_wcsnicmp(path, File_MupRedir, File_MupRedirLen - 1) == 0)
|
||||
PrefixLen = File_MupRedirLen - 1;
|
||||
else if (_wcsnicmp(path, File_DfsClientRedir, File_DfsClientRedirLen - 1) == 0)
|
||||
PrefixLen = File_DfsClientRedirLen - 1;
|
||||
else if (_wcsnicmp(path, File_HgfsRedir, File_HgfsRedirLen - 1) == 0)
|
||||
PrefixLen = File_HgfsRedirLen - 1;
|
||||
else if (_wcsnicmp(path, File_Mup, File_MupLen - 1) == 0)
|
||||
PrefixLen = File_MupLen - 1;
|
||||
else
|
||||
PrefixLen = 0;
|
||||
|
||||
if (PrefixLen && path[PrefixLen] == L'\\' &&
|
||||
path[PrefixLen + 1] != L'\0') {
|
||||
|
||||
const WCHAR* ptr = path + PrefixLen;
|
||||
if (ptr[1] == L';')
|
||||
ptr = wcschr(ptr + 2, L'\\');
|
||||
|
||||
if (ptr && ptr[0] && ptr[1]) {
|
||||
|
||||
//
|
||||
// the path represents a network share
|
||||
//
|
||||
|
||||
THREAD_DATA *TlsData = Dll_GetTlsData(NULL);
|
||||
|
||||
ULONG len1 = wcslen(ptr + 1);
|
||||
ULONG len2 = (File_MupLen + len1 + 8) * sizeof(WCHAR);
|
||||
WCHAR* path2 = Dll_GetTlsNameBuffer(TlsData, slot, len2);
|
||||
|
||||
wmemcpy(path2, File_Mup, File_MupLen);
|
||||
path2[File_MupLen] = L'\\';
|
||||
wmemcpy(path2 + File_MupLen + 1, ptr + 1, len1 + 1);
|
||||
len1 += File_MupLen + 1;
|
||||
|
||||
return path2;
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// File_GetPathFlags
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -327,7 +392,7 @@ _FX ULONG File_GetPathFlags(const WCHAR* Path, WCHAR** pRelocation)
|
|||
|
||||
EnterCriticalSection(File_PathRoot_CritSec);
|
||||
|
||||
Flags = File_GetPathFlags_internal(&File_PathRoot, Path, pRelocation, TRUE);
|
||||
Flags = File_GetPathFlags_internal(&File_PathRoot, File_NormalizePath(Path, NORM_NAME_BUFFER), pRelocation, TRUE);
|
||||
|
||||
LeaveCriticalSection(File_PathRoot_CritSec);
|
||||
|
||||
|
@ -340,7 +405,7 @@ _FX ULONG File_GetPathFlags(const WCHAR* Path, WCHAR** pRelocation)
|
|||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
_FX VOID File_SavePathNode_internal(HANDLE hPathsFile, LIST* parent, WCHAR* Path, ULONG Length, ULONG SetFlags)
|
||||
_FX VOID File_SavePathNode_internal(HANDLE hPathsFile, LIST* parent, WCHAR* Path, ULONG Length, ULONG SetFlags, WCHAR* (*TranslatePath)(const WCHAR *))
|
||||
{
|
||||
IO_STATUS_BLOCK IoStatusBlock;
|
||||
|
||||
|
@ -369,7 +434,9 @@ _FX VOID File_SavePathNode_internal(HANDLE hPathsFile, LIST* parent, WCHAR* Path
|
|||
if ((child->flags & ~SetFlags) != 0 || child->relocation != NULL) {
|
||||
|
||||
// write the path
|
||||
NtWriteFile(hPathsFile, NULL, NULL, NULL, &IoStatusBlock, Path, Path_Len * sizeof(WCHAR), NULL, NULL);
|
||||
WCHAR* PathEx = TranslatePath ? TranslatePath(Path) : NULL;
|
||||
NtWriteFile(hPathsFile, NULL, NULL, NULL, &IoStatusBlock, PathEx ? PathEx : Path, wcslen(PathEx ? PathEx : Path) * sizeof(WCHAR), NULL, NULL);
|
||||
if (PathEx) Dll_Free(PathEx);
|
||||
|
||||
// write the flags
|
||||
_ultow(child->flags, FlagStr + 1, 16);
|
||||
|
@ -377,15 +444,19 @@ _FX VOID File_SavePathNode_internal(HANDLE hPathsFile, LIST* parent, WCHAR* Path
|
|||
|
||||
// write the relocation
|
||||
if (child->relocation != NULL) {
|
||||
|
||||
NtWriteFile(hPathsFile, NULL, NULL, NULL, &IoStatusBlock, FlagStr, sizeof(WCHAR), NULL, NULL); // write |
|
||||
NtWriteFile(hPathsFile, NULL, NULL, NULL, &IoStatusBlock, child->relocation, wcslen(child->relocation) * sizeof(WCHAR), NULL, NULL);
|
||||
|
||||
WCHAR* RelocationEx = TranslatePath ? TranslatePath(child->relocation) : NULL;
|
||||
NtWriteFile(hPathsFile, NULL, NULL, NULL, &IoStatusBlock, RelocationEx ? RelocationEx : child->relocation, wcslen(RelocationEx ? RelocationEx : child->relocation) * sizeof(WCHAR), NULL, NULL);
|
||||
if (RelocationEx) Dll_Free(RelocationEx);
|
||||
}
|
||||
|
||||
// write line ending
|
||||
NtWriteFile(hPathsFile, NULL, NULL, NULL, &IoStatusBlock, (void*)CrLf, sizeof(CrLf) - sizeof(WCHAR), NULL, NULL);
|
||||
}
|
||||
|
||||
File_SavePathNode_internal(hPathsFile, &child->items, Path, Path_Len, SetFlags | child->flags);
|
||||
File_SavePathNode_internal(hPathsFile, &child->items, Path, Path_Len, SetFlags | child->flags, TranslatePath);
|
||||
|
||||
child = List_Next(child);
|
||||
}
|
||||
|
@ -397,7 +468,7 @@ _FX VOID File_SavePathNode_internal(HANDLE hPathsFile, LIST* parent, WCHAR* Path
|
|||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
_FX VOID File_SavePathTree_internal(LIST* Root, const WCHAR* name)
|
||||
_FX VOID File_SavePathTree_internal(LIST* Root, const WCHAR* name, WCHAR* (*TranslatePath)(const WCHAR *))
|
||||
{
|
||||
WCHAR PathsFile[MAX_PATH] = { 0 };
|
||||
wcscpy(PathsFile, Dll_BoxFilePath);
|
||||
|
@ -417,7 +488,7 @@ _FX VOID File_SavePathTree_internal(LIST* Root, const WCHAR* name)
|
|||
|
||||
WCHAR* Path = (WCHAR *)Dll_Alloc((0x7FFF + 1)*sizeof(WCHAR)); // max nt path
|
||||
|
||||
File_SavePathNode_internal(hPathsFile, Root, Path, 0, 0);
|
||||
File_SavePathNode_internal(hPathsFile, Root, Path, 0, 0, TranslatePath);
|
||||
|
||||
Dll_Free(Path);
|
||||
|
||||
|
@ -425,6 +496,84 @@ _FX VOID File_SavePathTree_internal(LIST* Root, const WCHAR* name)
|
|||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// File_TranslateNtToDosPath2
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
_FX WCHAR* File_TranslateNtToDosPath2(const WCHAR *NtPath)
|
||||
{
|
||||
WCHAR *DosPath = NULL;
|
||||
ULONG len_nt;
|
||||
|
||||
len_nt = wcslen(NtPath) + 11;
|
||||
DosPath = Dll_Alloc(len_nt * sizeof(WCHAR));
|
||||
wcscpy(DosPath, NtPath);
|
||||
|
||||
//
|
||||
// Hack Hack: when we load a drive which does not exist we create an entry like
|
||||
// L"\\C:\\path" in out tree to not forget it even though the NtPath is unknown
|
||||
// here we must handle that special case and strip the L'\\'
|
||||
//
|
||||
|
||||
const WCHAR* backslash = wcschr(DosPath+1, L'\\');
|
||||
if (!backslash) backslash = wcschr(DosPath, L'\0');
|
||||
if (*(backslash - 1) == L':') {
|
||||
wmemmove(DosPath, DosPath + 1, wcslen(DosPath)); // -1 (for '\\') + 1 (for '\0')
|
||||
return DosPath;
|
||||
}
|
||||
|
||||
|
||||
if (_wcsnicmp(DosPath, File_Mup, File_MupLen) == 0) {
|
||||
|
||||
WCHAR *ptr = DosPath + File_MupLen - 1;
|
||||
wmemmove(DosPath + 1, ptr, wcslen(ptr) + 1);
|
||||
|
||||
} else {
|
||||
|
||||
const FILE_DRIVE *drive;
|
||||
ULONG path_len, prefix_len;
|
||||
|
||||
path_len = wcslen(DosPath);
|
||||
|
||||
drive = File_GetDriveForPath(DosPath, path_len);
|
||||
if (drive)
|
||||
prefix_len = drive->len;
|
||||
else
|
||||
drive = File_GetDriveForUncPath(DosPath, path_len, &prefix_len);
|
||||
|
||||
if (drive) {
|
||||
|
||||
WCHAR drive_letter = drive->letter;
|
||||
WCHAR *ptr = DosPath + prefix_len;
|
||||
|
||||
LeaveCriticalSection(File_DrivesAndLinks_CritSec);
|
||||
|
||||
if (*ptr == L'\\' || *ptr == L'\0') {
|
||||
path_len = wcslen(ptr);
|
||||
wmemmove(DosPath + 2, ptr, path_len + 1);
|
||||
DosPath[0] = drive_letter;
|
||||
DosPath[1] = L':';
|
||||
|
||||
if (File_DriveAddSN && *drive->sn) {
|
||||
|
||||
wmemmove(DosPath + 11, DosPath + 1, path_len + 2);
|
||||
DosPath[1] = L'~';
|
||||
wmemcpy(DosPath + 2, drive->sn, 9);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
Dll_Free(DosPath);
|
||||
DosPath = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return DosPath;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// File_SavePathTree
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -434,7 +583,7 @@ _FX BOOLEAN File_SavePathTree()
|
|||
{
|
||||
EnterCriticalSection(File_PathRoot_CritSec);
|
||||
|
||||
File_SavePathTree_internal(&File_PathRoot, FILE_PATH_FILE_NAME);
|
||||
File_SavePathTree_internal(&File_PathRoot, FILE_PATH_FILE_NAME, File_TranslateNtToDosPath2);
|
||||
|
||||
File_GetAttributes_internal(FILE_PATH_FILE_NAME, &File_PathsFileSize, &File_PathsFileDate, NULL);
|
||||
|
||||
|
@ -482,7 +631,7 @@ _FX void File_ReleaseMutex(HANDLE hMutex)
|
|||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
_FX BOOLEAN File_LoadPathTree_internal(LIST* Root, const WCHAR* name)
|
||||
_FX BOOLEAN File_LoadPathTree_internal(LIST* Root, const WCHAR* name, WCHAR* (*TranslatePath)(const WCHAR *))
|
||||
{
|
||||
WCHAR PathsFile[MAX_PATH] = { 0 };
|
||||
wcscpy(PathsFile, Dll_BoxFilePath);
|
||||
|
@ -529,16 +678,26 @@ _FX BOOLEAN File_LoadPathTree_internal(LIST* Root, const WCHAR* name)
|
|||
WCHAR savechar = Line[LineLen];
|
||||
Line[LineLen] = L'\0';
|
||||
|
||||
WCHAR* Path = Line;
|
||||
|
||||
WCHAR* Sep = wcschr(Line, L'|');
|
||||
if (!Sep || Sep > Next) continue; // invalid line, flags field missing
|
||||
*Sep = L'\0';
|
||||
|
||||
WCHAR* Relocation = NULL;
|
||||
|
||||
WCHAR* endptr;
|
||||
ULONG Flags = wcstoul(Sep + 1, &endptr, 16);
|
||||
if (endptr && *endptr == L'|') endptr++;
|
||||
else endptr = NULL;
|
||||
if (endptr && *endptr == L'|')
|
||||
Relocation = endptr + 1;
|
||||
|
||||
File_SetPathFlags_internal(Root, Line, Flags, 0, endptr);
|
||||
WCHAR* PathEx = TranslatePath ? TranslatePath(Path) : NULL;
|
||||
WCHAR* RelocationEx = TranslatePath ? TranslatePath(Relocation) : NULL;
|
||||
|
||||
File_SetPathFlags_internal(Root, PathEx ? PathEx : Path, Flags, 0, RelocationEx ? RelocationEx : Relocation);
|
||||
|
||||
if (PathEx) Dll_Free(PathEx);
|
||||
if (RelocationEx) Dll_Free(RelocationEx);
|
||||
|
||||
*Sep = L'|';
|
||||
Line[LineLen] = savechar;
|
||||
|
@ -552,6 +711,73 @@ _FX BOOLEAN File_LoadPathTree_internal(LIST* Root, const WCHAR* name)
|
|||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// File_TranslateDosToNtPath2
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
_FX WCHAR *File_TranslateDosToNtPath2(const WCHAR *DosPath)
|
||||
{
|
||||
WCHAR *NtPath = NULL;
|
||||
ULONG len_dos;
|
||||
|
||||
if (DosPath && DosPath[0] && DosPath[1]) {
|
||||
|
||||
if (DosPath[0] == L'\\' && DosPath[1] == L'\\') {
|
||||
|
||||
//
|
||||
// network path
|
||||
//
|
||||
|
||||
DosPath += 2;
|
||||
len_dos = wcslen(DosPath) + 1;
|
||||
NtPath = Dll_Alloc((File_MupLen + len_dos) * sizeof(WCHAR));
|
||||
wmemcpy(NtPath, File_Mup, File_MupLen);
|
||||
wmemcpy(NtPath + File_MupLen, DosPath, len_dos);
|
||||
|
||||
} else if (DosPath[0] != L'\\') {
|
||||
|
||||
const WCHAR* backslash = wcschr(DosPath, L'\\');
|
||||
if(!backslash) backslash = wcschr(DosPath, L'\0');
|
||||
if (*(backslash - 1) == L':') {
|
||||
|
||||
ULONG path_pos = (ULONG)(backslash - DosPath);
|
||||
|
||||
//
|
||||
// drive-letter path
|
||||
//
|
||||
|
||||
FILE_DRIVE* drive = File_GetDriveForLetter(DosPath[0]);
|
||||
if (drive) {
|
||||
|
||||
if (File_DriveAddSN && *drive->sn) {
|
||||
|
||||
//
|
||||
// if the volume serial numbers dont match return NULL
|
||||
//
|
||||
|
||||
if (_wcsnicmp(DosPath + 2, drive->sn, 9) != 0) {
|
||||
LeaveCriticalSection(File_DrivesAndLinks_CritSec);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
DosPath += path_pos;
|
||||
len_dos = wcslen(DosPath) + 1;
|
||||
NtPath = Dll_Alloc((drive->len + len_dos) * sizeof(WCHAR));
|
||||
wmemcpy(NtPath, drive->path, drive->len);
|
||||
wmemcpy(NtPath + drive->len, DosPath, len_dos);
|
||||
|
||||
LeaveCriticalSection(File_DrivesAndLinks_CritSec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NtPath;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// File_LoadPathTree
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -563,7 +789,7 @@ _FX BOOLEAN File_LoadPathTree()
|
|||
|
||||
EnterCriticalSection(File_PathRoot_CritSec);
|
||||
|
||||
File_LoadPathTree_internal(&File_PathRoot, FILE_PATH_FILE_NAME);
|
||||
File_LoadPathTree_internal(&File_PathRoot, FILE_PATH_FILE_NAME, File_TranslateDosToNtPath2);
|
||||
|
||||
LeaveCriticalSection(File_PathRoot_CritSec);
|
||||
|
||||
|
@ -738,7 +964,7 @@ _FX NTSTATUS File_MarkDeleted_v2(const WCHAR* TruePath)
|
|||
|
||||
EnterCriticalSection(File_PathRoot_CritSec);
|
||||
|
||||
BOOLEAN bSet = File_MarkDeleted_internal(&File_PathRoot, TruePath);
|
||||
BOOLEAN bSet = File_MarkDeleted_internal(&File_PathRoot, File_NormalizePath(TruePath, NORM_NAME_BUFFER));
|
||||
|
||||
LeaveCriticalSection(File_PathRoot_CritSec);
|
||||
|
||||
|
@ -876,7 +1102,7 @@ _FX NTSTATUS File_SetRelocation(const WCHAR* OldTruePath, const WCHAR* NewTruePa
|
|||
|
||||
EnterCriticalSection(File_PathRoot_CritSec);
|
||||
|
||||
File_SetRelocation_internal(&File_PathRoot, OldTruePath, NewTruePath);
|
||||
File_SetRelocation_internal(&File_PathRoot, File_NormalizePath(OldTruePath, NORM_NAME_BUFFER), File_NormalizePath(NewTruePath, MISC_NAME_BUFFER));
|
||||
|
||||
LeaveCriticalSection(File_PathRoot_CritSec);
|
||||
|
||||
|
|
|
@ -617,8 +617,21 @@ _FX NTSTATUS File_OpenForMerge(
|
|||
if (FILE_PATH_DELETED(TruePathFlags))
|
||||
TruePathDeleted = TRUE;
|
||||
else if (OldTruePath) {
|
||||
|
||||
OriginalPath = TruePath;
|
||||
TruePath = OldTruePath;
|
||||
|
||||
if (File_Snapshot != NULL) {
|
||||
|
||||
//
|
||||
// note: File_ResolveTruePath returns a buffer from the TMPL_NAME_BUFFER slot,
|
||||
// which is reused byFile_MakeSnapshotPath, so we need to make non reusable copy
|
||||
//
|
||||
|
||||
TruePath = Dll_GetTlsNameBuffer(TlsData, MISC_NAME_BUFFER, (wcslen(OldTruePath) + 1) * sizeof(WCHAR));
|
||||
wcscpy(TruePath, OldTruePath);
|
||||
}
|
||||
else
|
||||
TruePath = OldTruePath;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -1535,7 +1548,7 @@ _FX NTSTATUS File_MergeDummy(
|
|||
}
|
||||
|
||||
if (cmp != 0) { // skip duplicates
|
||||
|
||||
|
||||
if (ins_point)
|
||||
List_Insert_Before(cache_list, ins_point, cache_file);
|
||||
else
|
||||
|
|
|
@ -278,7 +278,7 @@ _FX ULONG File_GetPathFlagsEx(const WCHAR *TruePath, const WCHAR *CopyPath, WCHA
|
|||
// check true path relocation and deletion for the active state
|
||||
//
|
||||
|
||||
Flags = File_GetPathFlags_internal(&File_PathRoot, TruePath, &Relocation, TRUE); // this requires a name buffer
|
||||
Flags = File_GetPathFlags_internal(&File_PathRoot, File_NormalizePath(TruePath, NORM_NAME_BUFFER), &Relocation, TRUE); // this requires a name buffer
|
||||
if (FILE_PATH_DELETED(Flags))
|
||||
goto finish;
|
||||
}
|
||||
|
@ -373,7 +373,7 @@ _FX ULONG File_GetPathFlagsEx(const WCHAR *TruePath, const WCHAR *CopyPath, WCHA
|
|||
//
|
||||
|
||||
TmplRelocation = NULL;
|
||||
Flags = File_GetPathFlags_internal(&Cur_Snapshot->PathRoot, TruePath, &TmplRelocation, TRUE);
|
||||
Flags = File_GetPathFlags_internal(&Cur_Snapshot->PathRoot, File_NormalizePath(TruePath, NORM_NAME_BUFFER), &TmplRelocation, TRUE);
|
||||
if(TmplRelocation)
|
||||
Relocation = TmplRelocation;
|
||||
if (FILE_PATH_DELETED(Flags))
|
||||
|
@ -477,7 +477,7 @@ _FX void File_InitSnapshots(void)
|
|||
wcscat(PathFile, L"\\");
|
||||
wcscat(PathFile, FILE_PATH_FILE_NAME);
|
||||
|
||||
File_LoadPathTree_internal(&Cur_Snapshot->PathRoot, PathFile);
|
||||
File_LoadPathTree_internal(&Cur_Snapshot->PathRoot, PathFile, File_TranslateDosToNtPath);
|
||||
}
|
||||
|
||||
//WCHAR SnapshotName[BOXNAME_COUNT] = { 0 };
|
||||
|
|
|
@ -80,11 +80,10 @@ static ULONG Key_IsDeletedEx_v2(const WCHAR* TruePath, const WCHAR* ValueName, B
|
|||
//
|
||||
|
||||
VOID File_ClearPathBranche_internal(LIST* parent);
|
||||
VOID File_SavePathTree_internal(LIST* Root, const WCHAR* name);
|
||||
BOOLEAN File_LoadPathTree_internal(LIST* Root, const WCHAR* name);
|
||||
VOID File_SavePathTree_internal(LIST* Root, const WCHAR* name, WCHAR* (*TranslatePath)(const WCHAR *));
|
||||
BOOLEAN File_LoadPathTree_internal(LIST* Root, const WCHAR* name, WCHAR* (*TranslatePath)(const WCHAR *));
|
||||
VOID File_SetPathFlags_internal(LIST* Root, const WCHAR* Path, ULONG setFlags, ULONG clrFlags, const WCHAR* Relocation);
|
||||
ULONG File_GetPathFlags_internal(LIST* Root, const WCHAR* Path, WCHAR** pRelocation, BOOLEAN CheckChildren);
|
||||
VOID File_SavePathNode_internal(HANDLE hPathsFile, LIST* parent, WCHAR* Path, ULONG Length, ULONG SetFlags);
|
||||
BOOLEAN File_MarkDeleted_internal(LIST* Root, const WCHAR* Path);
|
||||
VOID File_SetRelocation_internal(LIST* Root, const WCHAR* OldTruePath, const WCHAR* NewTruePath);
|
||||
|
||||
|
@ -128,7 +127,7 @@ _FX BOOLEAN Key_SavePathTree()
|
|||
{
|
||||
EnterCriticalSection(Key_PathRoot_CritSec);
|
||||
|
||||
File_SavePathTree_internal(&Key_PathRoot, KEY_PATH_FILE_NAME);
|
||||
File_SavePathTree_internal(&Key_PathRoot, KEY_PATH_FILE_NAME, NULL);
|
||||
|
||||
File_GetAttributes_internal(KEY_PATH_FILE_NAME, &Key_PathsFileSize, &Key_PathsFileDate, NULL);
|
||||
|
||||
|
@ -151,7 +150,7 @@ _FX BOOLEAN Key_LoadPathTree()
|
|||
|
||||
EnterCriticalSection(Key_PathRoot_CritSec);
|
||||
|
||||
Key_RegPaths_Loaded = File_LoadPathTree_internal(&Key_PathRoot, KEY_PATH_FILE_NAME);
|
||||
Key_RegPaths_Loaded = File_LoadPathTree_internal(&Key_PathRoot, KEY_PATH_FILE_NAME, NULL);
|
||||
|
||||
LeaveCriticalSection(Key_PathRoot_CritSec);
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include <QAbstractEventDispatcher>
|
||||
#include <QSettings>
|
||||
|
||||
#include <ntstatus.h>
|
||||
#define WIN32_NO_STATUS
|
||||
|
@ -348,6 +349,28 @@ CSymbolProvider* CSymbolProvider::Instance()
|
|||
|
||||
if (MyBeginInitOnce(InitOnce))
|
||||
{
|
||||
/*#ifdef _WIN64
|
||||
QSettings settings("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows Kits\\Installed Roots", QSettings::NativeFormat);
|
||||
#else
|
||||
QSettings settings("HKEY_LOCAL_MACHINE\\Software\\Wow6432Node\\Microsoft\\Windows Kits\\Installed Roots", QSettings::NativeFormat);
|
||||
#endif
|
||||
QString KitsRoot = settings.value("KitsRoot10").toString(); // Windows 10 SDK
|
||||
if(KitsRoot.isEmpty())
|
||||
KitsRoot = settings.value("KitsRoot81").toString(); // Windows 8.1 SDK
|
||||
if(KitsRoot.isEmpty())
|
||||
KitsRoot = settings.value("KitsRoot").toString(); // Windows 8 SDK
|
||||
#if defined(_M_AMD64)
|
||||
KitsRoot.append("\\Debuggers\\x64\\");
|
||||
#elif defined(_M_ARM64)
|
||||
KitsRoot.append("\\Debuggers\\arm64\\");
|
||||
#else
|
||||
KitsRoot.append("\\Debuggers\\x86\\");
|
||||
#endif
|
||||
|
||||
HMODULE DbgCoreMod = LoadLibrary((KitsRoot + "dbgcore.dll").toStdWString().c_str());
|
||||
HMODULE DbgHelpMod = LoadLibrary((KitsRoot + "dbghelp.dll").toStdWString().c_str());
|
||||
HMODULE SymSrvMod = LoadLibrary((KitsRoot + "symsrv.dll").toStdWString().c_str());*/
|
||||
|
||||
HMODULE DbgHelpMod = LoadLibrary(L"dbghelp.dll");
|
||||
|
||||
__sys_SymFromAddr = (P_SymFromAddr)GetProcAddress(DbgHelpMod, "SymFromAddr");
|
||||
|
|
|
@ -100,7 +100,7 @@ NTSTATUS NtIo_RemoveJunction(POBJECT_ATTRIBUTES objattrs)
|
|||
|
||||
NTSTATUS NtIo_DeleteFolderRecursivelyImpl(POBJECT_ATTRIBUTES objattrs, bool (*cb)(const WCHAR* info, void* param), void* param);
|
||||
|
||||
NTSTATUS NtIo_DeleteFileImpl(ULONG FileAttributes, OBJECT_ATTRIBUTES* attr, 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;
|
||||
|
||||
|
@ -115,15 +115,18 @@ NTSTATUS NtIo_DeleteFileImpl(ULONG FileAttributes, OBJECT_ATTRIBUTES* attr, bool
|
|||
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_DeleteFileImpl(SNtObject& ntObject, bool (*cb)(const WCHAR* info, void* param), void* param)
|
||||
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_DeleteFileImpl(info.FileAttributes, &ntObject.attr, cb, param);
|
||||
return NtIo_DeleteFile(info.FileAttributes, &ntObject.attr, cb, param);
|
||||
}
|
||||
|
||||
NTSTATUS NtIo_DeleteFolderRecursivelyImpl(POBJECT_ATTRIBUTES objattrs, bool (*cb)(const WCHAR* info, void* param), void* param)
|
||||
|
@ -175,7 +178,7 @@ NTSTATUS NtIo_DeleteFolderRecursivelyImpl(POBJECT_ATTRIBUTES objattrs, bool (*cb
|
|||
|
||||
SNtObject ntFoundObject(FileName, Handle);
|
||||
|
||||
status = NtIo_DeleteFileImpl(FileAttributes, &ntFoundObject.attr, cb, param);
|
||||
status = NtIo_DeleteFile(FileAttributes, &ntFoundObject.attr, cb, param);
|
||||
}
|
||||
|
||||
NtClose(Handle);
|
||||
|
@ -194,6 +197,9 @@ NTSTATUS NtIo_DeleteFolderRecursively(POBJECT_ATTRIBUTES objattrs, bool (*cb)(co
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -346,7 +352,7 @@ NTSTATUS NtIo_MergeFolder(POBJECT_ATTRIBUTES src_objattrs, POBJECT_ATTRIBUTES de
|
|||
|
||||
if (FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
|
||||
if (TargetExists)
|
||||
status = NtIo_DeleteFileImpl(ntDestObject, cb, param);
|
||||
status = NtIo_DeleteFile(ntDestObject, cb, param);
|
||||
if (NT_SUCCESS(status))
|
||||
status = NtIo_RenameJunction(&ntSrcObject.attr, dest_objattrs, FileName.c_str());
|
||||
}
|
||||
|
@ -360,7 +366,7 @@ NTSTATUS NtIo_MergeFolder(POBJECT_ATTRIBUTES src_objattrs, POBJECT_ATTRIBUTES de
|
|||
else
|
||||
{
|
||||
if (TargetExists)
|
||||
status = NtIo_DeleteFileImpl(ntDestObject, cb, param);
|
||||
status = NtIo_DeleteFile(ntDestObject, cb, param);
|
||||
if (NT_SUCCESS(status))
|
||||
status = NtIo_RenameFile(&ntSrcObject.attr, dest_objattrs, FileName.c_str());
|
||||
}
|
||||
|
|
|
@ -23,10 +23,13 @@ private:
|
|||
|
||||
bool NtIo_WaitForFolder(POBJECT_ATTRIBUTES objattrs, int seconds = 10, bool (*cb)(const WCHAR* info, void* param) = NULL, void* param = NULL);
|
||||
|
||||
BOOLEAN NtIo_FileExists(POBJECT_ATTRIBUTES objattrs);
|
||||
|
||||
NTSTATUS NtIo_RemoveProblematicAttributes(POBJECT_ATTRIBUTES objattrs);
|
||||
|
||||
NTSTATUS NtIo_RemoveJunction(POBJECT_ATTRIBUTES objattrs);
|
||||
|
||||
NTSTATUS NtIo_DeleteFile(SNtObject& ntObject, bool (*cb)(const WCHAR* info, void* param) = NULL, void* param = NULL);
|
||||
NTSTATUS NtIo_DeleteFolderRecursively(POBJECT_ATTRIBUTES objattrs, bool (*cb)(const WCHAR* info, void* param) = NULL, void* param = NULL);
|
||||
|
||||
NTSTATUS NtIo_RenameFile(POBJECT_ATTRIBUTES src_objattrs, POBJECT_ATTRIBUTES dest_objattrs, const WCHAR* DestName);
|
||||
|
|
|
@ -471,7 +471,7 @@ SB_PROGRESS CSandBox::RemoveSnapshot(const QString& ID)
|
|||
|
||||
CSbieProgressPtr pProgress = CSbieProgressPtr(new CSbieProgress());
|
||||
if (ChildIDs.count() == 1 || IsCurrent)
|
||||
QtConcurrent::run(CSandBox::MergeSnapshotAsync, pProgress, m_FilePath, ID, IsCurrent ? QString() : ChildIDs.first());
|
||||
QtConcurrent::run(CSandBox::MergeSnapshotAsync, pProgress, m_FilePath, ID, IsCurrent ? QString() : ChildIDs.first(), m_Name, m_pAPI);
|
||||
else
|
||||
QtConcurrent::run(CSandBox::DeleteSnapshotAsync, pProgress, m_FilePath, ID);
|
||||
return SB_PROGRESS(OP_ASYNC, pProgress);
|
||||
|
@ -549,7 +549,11 @@ SB_STATUS CSandBox__CleanupSnapshot(const QString& Folder)
|
|||
return SB_OK;
|
||||
}
|
||||
|
||||
void CSandBox::MergeSnapshotAsync(const CSbieProgressPtr& pProgress, const QString& BoxPath, const QString& TargetID, const QString& SourceID)
|
||||
// path flags, saved to file
|
||||
#define FILE_DELETED_FLAG 0x0001
|
||||
#define FILE_RELOCATION_FLAG 0x0002
|
||||
|
||||
void CSandBox::MergeSnapshotAsync(const CSbieProgressPtr& pProgress, const QString& BoxPath, const QString& TargetID, const QString& SourceID, const QString& BoxName, class CSbieAPI* pAPI)
|
||||
{
|
||||
//
|
||||
// Targe is to be removed;
|
||||
|
@ -563,8 +567,82 @@ void CSandBox::MergeSnapshotAsync(const CSbieProgressPtr& pProgress, const QStri
|
|||
QString SourceFolder = IsCurrent ? BoxPath : (BoxPath + "\\snapshot-" + SourceID);
|
||||
QString TargetFolder = BoxPath + "\\snapshot-" + TargetID;
|
||||
|
||||
auto GetBoxedPath = [BoxPath, BoxName, pAPI](const QString& Path, const QString& TargetFolder) {
|
||||
QString SubPath = pAPI->GetBoxedPath(BoxName, Path).mid(BoxPath.length());
|
||||
return TargetFolder + SubPath;
|
||||
};
|
||||
|
||||
SB_STATUS Status = SB_OK;
|
||||
|
||||
|
||||
// apply source FilePaths.dat on the targetfolder
|
||||
if (QFile::exists(SourceFolder + "\\FilePaths.dat"))
|
||||
{
|
||||
QFile datSource(SourceFolder + "\\FilePaths.dat");
|
||||
if (datSource.open(QFile::ReadOnly))
|
||||
{
|
||||
QStringList datData = QString::fromWCharArray((wchar_t*)datSource.readAll().data(), datSource.size() / sizeof(wchar_t)).split("\n");
|
||||
|
||||
// process relocations
|
||||
foreach (const QString& Line, datData) {
|
||||
QStringList Data = Line.trimmed().split("|");
|
||||
|
||||
QString Path = Data[0];
|
||||
if (Path.isEmpty()) continue;
|
||||
Path = GetBoxedPath(Path, TargetFolder);
|
||||
int Flags = Data.size() >= 2 ? Data[1].toInt() : 0;
|
||||
|
||||
if (Flags & FILE_RELOCATION_FLAG)
|
||||
{
|
||||
QString Relocation = Data.size() >= 3 ? GetBoxedPath(Data[2], TargetFolder) : QString();
|
||||
|
||||
SNtObject ntSrc(L"\\??\\" + Relocation.toStdWString());
|
||||
|
||||
if (NtIo_FileExists(&ntSrc.attr)) {
|
||||
|
||||
SNtObject ntOld(L"\\??\\" + Path.toStdWString());
|
||||
|
||||
NTSTATUS status = NtIo_DeleteFolderRecursively(&ntOld.attr, [](const WCHAR* info, void* param) {
|
||||
CSbieProgress* pProgress = (CSbieProgress*)param;
|
||||
pProgress->ShowMessage(CSandBox::tr("Deleting folder: %1").arg(QString::fromWCharArray(info)));
|
||||
return !pProgress->IsCanceled();
|
||||
}, pProgress.data());
|
||||
|
||||
if (NT_SUCCESS(status))
|
||||
{
|
||||
QStringList PathX = Path.split("\\");
|
||||
QString Name = PathX.takeLast();
|
||||
SNtObject ntDest(L"\\??\\" + PathX.join("\\").toStdWString());
|
||||
|
||||
status = NtIo_RenameFolder(&ntSrc.attr, &ntDest.attr, Name.toStdWString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// process deletions
|
||||
foreach (const QString& Line, datData) {
|
||||
QStringList Data = Line.trimmed().split("|");
|
||||
|
||||
QString Path = Data[0];
|
||||
if (Path.isEmpty()) continue;
|
||||
Path = GetBoxedPath(Path, TargetFolder);
|
||||
int Flags = Data.size() >= 2 ? Data[1].toInt() : 0;
|
||||
|
||||
if (Flags & FILE_DELETED_FLAG)
|
||||
{
|
||||
SNtObject ntPath(L"\\??\\" + Path.toStdWString());
|
||||
|
||||
NTSTATUS status = NtIo_DeleteFile(ntPath, [](const WCHAR* info, void* param) {
|
||||
CSbieProgress* pProgress = (CSbieProgress*)param;
|
||||
pProgress->ShowMessage(CSandBox::tr("Deleting: %1").arg(QString::fromWCharArray(info)));
|
||||
return !pProgress->IsCanceled();
|
||||
}, pProgress.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// merge source folders to the target snapshot
|
||||
foreach(const QString& BoxSubFolder, CSandBox__BoxSubFolders)
|
||||
{
|
||||
Status = CSandBox__MergeFolders(pProgress, TargetFolder + "\\" + BoxSubFolder, SourceFolder + "\\" + BoxSubFolder);
|
||||
|
@ -576,6 +654,32 @@ void CSandBox::MergeSnapshotAsync(const CSbieProgressPtr& pProgress, const QStri
|
|||
|
||||
if(!Status.IsError())
|
||||
{
|
||||
// merge DeleteV2 file entries
|
||||
if (QFile::exists(SourceFolder + "\\FilePaths.dat")) {
|
||||
QFile datSource(SourceFolder + "\\FilePaths.dat");
|
||||
if (datSource.open(QFile::ReadOnly)) {
|
||||
QFile datTarget(TargetFolder + "\\FilePaths.dat");
|
||||
if (datTarget.open(QFile::ReadWrite)) {
|
||||
// merge with target
|
||||
datTarget.seek(datTarget.size());
|
||||
datTarget.write(datSource.readAll());
|
||||
// remove source
|
||||
datSource.close();
|
||||
datSource.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copy other data files from source to target
|
||||
foreach(const SBoxDataFile& BoxDataFile, CSandBox__BoxDataFiles)
|
||||
{
|
||||
if (!QFile::exists(SourceFolder + "\\" + BoxDataFile.Name))
|
||||
continue;
|
||||
|
||||
QFile::remove(TargetFolder + "\\" + BoxDataFile.Name);
|
||||
QFile::rename(SourceFolder + "\\" + BoxDataFile.Name, TargetFolder + "\\" + BoxDataFile.Name);
|
||||
}
|
||||
|
||||
if (IsCurrent)
|
||||
{
|
||||
// move all folders out of the snapshot to root
|
||||
|
@ -586,6 +690,12 @@ void CSandBox::MergeSnapshotAsync(const CSbieProgressPtr& pProgress, const QStri
|
|||
break;
|
||||
}
|
||||
|
||||
// move all data files out of the snapshot to root
|
||||
foreach(const SBoxDataFile& BoxDataFile, CSandBox__BoxDataFiles)
|
||||
{
|
||||
QFile::rename(TargetFolder + "\\" + BoxDataFile.Name, SourceFolder + "\\" + BoxDataFile.Name);
|
||||
}
|
||||
|
||||
// delete snapshot rest
|
||||
if (!Status.IsError())
|
||||
Status = CSandBox__CleanupSnapshot(TargetFolder);
|
||||
|
@ -595,7 +705,7 @@ void CSandBox::MergeSnapshotAsync(const CSbieProgressPtr& pProgress, const QStri
|
|||
// delete rest of source snpshot
|
||||
Status = CSandBox__CleanupSnapshot(SourceFolder);
|
||||
|
||||
// rename target snapshot o source snapshot
|
||||
// rename target snapshot to source snapshot
|
||||
if (!Status.IsError())
|
||||
Status = CSandBox__MoveFolder(TargetFolder, BoxPath, "snapshot-" + SourceID);
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ protected:
|
|||
static void CleanBoxAsync(const CSbieProgressPtr& pProgress, const QStringList& BoxFolders);
|
||||
|
||||
static void DeleteSnapshotAsync(const CSbieProgressPtr& pProgress, const QString& BoxPath, const QString& ID);
|
||||
static void MergeSnapshotAsync(const CSbieProgressPtr& pProgress, const QString& BoxPath, const QString& TargetID, const QString& SourceID);
|
||||
static void MergeSnapshotAsync(const CSbieProgressPtr& pProgress, const QString& BoxPath, const QString& TargetID, const QString& SourceID, const QString& BoxName, class CSbieAPI* pAPI);
|
||||
|
||||
QString m_FilePath;
|
||||
QString m_RegPath;
|
||||
|
|
Loading…
Reference in New Issue