mirror of
https://github.com/reactos/reactos.git
synced 2024-08-19 18:02:55 +00:00
546 lines
17 KiB
C
546 lines
17 KiB
C
/*
|
|
* PROJECT: ReactOS Application compatibility module
|
|
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
|
|
* PURPOSE: Registry layer manipulation functions
|
|
* COPYRIGHT: Copyright 2015-2019 Mark Jansen (mark.jansen@reactos.org)
|
|
*/
|
|
|
|
#define WIN32_NO_STATUS
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "strsafe.h"
|
|
#include <ntndk.h>
|
|
#include "apphelp.h"
|
|
|
|
#define GPLK_USER 1
|
|
#define GPLK_MACHINE 2
|
|
#define MAX_LAYER_LENGTH 256
|
|
#define LAYER_APPLY_TO_SYSTEM_EXES 1
|
|
#define LAYER_UNK_FLAG2 2
|
|
|
|
#ifndef REG_SZ
|
|
#define REG_SZ 1
|
|
#endif
|
|
|
|
#if defined(__GNUC__)
|
|
#define APPCOMPAT_LAYER_KEY (const WCHAR[]){'\\','S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s',' ','N','T','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','A','p','p','C','o','m','p','a','t','F','l','a','g','s','\\','L','a','y','e','r','s',0}
|
|
#define REGISTRY_MACHINE (const WCHAR[]){'\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e',0}
|
|
#define SPACE_ONLY (const WCHAR[]){' ',0}
|
|
#define DISALLOWED_LAYER_CHARS (const WCHAR[]){' ','#','!',0}
|
|
#define LAYER_SEPARATORS (const WCHAR[]){' ','\t',0}
|
|
#define SIGN_MEDIA_FMT (const WCHAR[]){'S','I','G','N','.','M','E','D','I','A','=','%','X',' ','%','s',0}
|
|
|
|
#else
|
|
#define APPCOMPAT_LAYER_KEY L"\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers"
|
|
#define REGISTRY_MACHINE L"\\Registry\\Machine"
|
|
#define SPACE_ONLY L" "
|
|
#define DISALLOWED_LAYER_CHARS L" #!"
|
|
#define LAYER_SEPARATORS L" \t"
|
|
#define SIGN_MEDIA_FMT L"SIGN.MEDIA=%X %s"
|
|
#endif
|
|
|
|
/* Fixme: use RTL_UNICODE_STRING_BUFFER */
|
|
typedef struct SDB_TMP_STR
|
|
{
|
|
UNICODE_STRING Str;
|
|
WCHAR FixedBuffer[MAX_PATH];
|
|
} SDB_TMP_STR, *PSDB_TMP_STR;
|
|
|
|
void SdbpInitTempStr(PSDB_TMP_STR String)
|
|
{
|
|
String->Str.Buffer = String->FixedBuffer;
|
|
String->Str.Length = 0;
|
|
String->Str.MaximumLength = sizeof(String->FixedBuffer);
|
|
}
|
|
|
|
void SdbpFreeTempStr(PSDB_TMP_STR String)
|
|
{
|
|
if (String->Str.Buffer != String->FixedBuffer)
|
|
{
|
|
SdbFree(String->Str.Buffer);
|
|
}
|
|
}
|
|
|
|
void SdbpResizeTempStr(PSDB_TMP_STR String, WORD newLength)
|
|
{
|
|
if (newLength > String->Str.MaximumLength)
|
|
{
|
|
SdbpFreeTempStr(String);
|
|
String->Str.MaximumLength = newLength * sizeof(WCHAR);
|
|
String->Str.Buffer = SdbAlloc(String->Str.MaximumLength);
|
|
String->Str.Length = 0;
|
|
}
|
|
}
|
|
|
|
BOOL SdbpGetLongPathName(PCWSTR wszPath, PSDB_TMP_STR Result)
|
|
{
|
|
DWORD max = Result->Str.MaximumLength / 2;
|
|
DWORD ret = GetLongPathNameW(wszPath, Result->Str.Buffer, max);
|
|
if (ret)
|
|
{
|
|
if (ret >= max)
|
|
{
|
|
SdbpResizeTempStr(Result, ret);
|
|
max = Result->Str.MaximumLength / 2;
|
|
ret = GetLongPathNameW(wszPath, Result->Str.Buffer, max);
|
|
}
|
|
if (ret && ret < max)
|
|
{
|
|
Result->Str.Length = ret * 2;
|
|
return TRUE;
|
|
}
|
|
}
|
|
SHIM_ERR("Failed to convert short path to long path error 0x%lx\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL SdbpIsPathOnRemovableMedia(PCWSTR Path)
|
|
{
|
|
WCHAR tmp[] = { 'A',':','\\',0 };
|
|
ULONG type;
|
|
if (!Path || Path[0] == UNICODE_NULL)
|
|
{
|
|
SHIM_ERR("Invalid argument\n");
|
|
return FALSE;
|
|
}
|
|
switch (Path[1])
|
|
{
|
|
case L':':
|
|
break;
|
|
case L'\\':
|
|
SHIM_INFO("\"%S\" is a network path.\n", Path);
|
|
return FALSE;
|
|
default:
|
|
SHIM_INFO("\"%S\" not a full path we can operate on.\n", Path);
|
|
return FALSE;
|
|
}
|
|
tmp[0] = Path[0];
|
|
type = GetDriveTypeW(tmp);
|
|
|
|
return type == DRIVE_REMOVABLE || type == DRIVE_CDROM;
|
|
}
|
|
|
|
/* Convert a path on removable media to 'SIGN.MEDIA=%X filename' */
|
|
BOOL SdbpBuildSignMediaId(PSDB_TMP_STR LongPath)
|
|
{
|
|
SDB_TMP_STR Scratch;
|
|
PWCHAR Ptr;
|
|
|
|
SdbpInitTempStr(&Scratch);
|
|
SdbpResizeTempStr(&Scratch, LongPath->Str.Length / sizeof(WCHAR) + 30);
|
|
StringCbCopyNW(Scratch.Str.Buffer, Scratch.Str.MaximumLength, LongPath->Str.Buffer, LongPath->Str.Length);
|
|
Ptr = wcsrchr(LongPath->Str.Buffer, '\\');
|
|
if (Ptr)
|
|
{
|
|
HANDLE FindHandle;
|
|
WIN32_FIND_DATAW FindData;
|
|
Ptr[1] = '*';
|
|
Ptr[2] = '\0';
|
|
FindHandle = FindFirstFileW(LongPath->Str.Buffer, &FindData);
|
|
if (FindHandle != INVALID_HANDLE_VALUE)
|
|
{
|
|
DWORD SignMedia = 0;
|
|
do
|
|
{
|
|
if (!(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && FindData.nFileSizeLow)
|
|
SignMedia = SignMedia << 1 ^ FindData.nFileSizeLow;
|
|
} while (FindNextFileW(FindHandle, &FindData));
|
|
|
|
FindClose(FindHandle);
|
|
SdbpResizeTempStr(LongPath, (LongPath->Str.Length >> 1) + 20);
|
|
StringCbPrintfW(LongPath->Str.Buffer, LongPath->Str.MaximumLength, SIGN_MEDIA_FMT, SignMedia, Scratch.Str.Buffer + 3);
|
|
LongPath->Str.Length = (USHORT)SdbpStrlen(LongPath->Str.Buffer) * sizeof(WCHAR);
|
|
SdbpFreeTempStr(&Scratch);
|
|
return TRUE;
|
|
}
|
|
}
|
|
SdbpFreeTempStr(&Scratch);
|
|
SdbpFreeTempStr(LongPath);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Convert a given path to a long or media path */
|
|
BOOL SdbpResolvePath(PSDB_TMP_STR LongPath, PCWSTR wszPath)
|
|
{
|
|
SdbpInitTempStr(LongPath);
|
|
if (!SdbpGetLongPathName(wszPath, LongPath))
|
|
{
|
|
SdbpFreeTempStr(LongPath);
|
|
return FALSE;
|
|
}
|
|
if (SdbpIsPathOnRemovableMedia(LongPath->Str.Buffer))
|
|
{
|
|
return SdbpBuildSignMediaId(LongPath);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static ACCESS_MASK g_QueryFlag = 0xffffffff;
|
|
ACCESS_MASK Wow64QueryFlag(void)
|
|
{
|
|
if (g_QueryFlag == 0xffffffff)
|
|
{
|
|
ULONG_PTR wow64_ptr = 0;
|
|
NTSTATUS Status = NtQueryInformationProcess(NtCurrentProcess(), ProcessWow64Information, &wow64_ptr, sizeof(wow64_ptr), NULL);
|
|
g_QueryFlag = (NT_SUCCESS(Status) && wow64_ptr != 0) ? KEY_WOW64_64KEY : 0;
|
|
}
|
|
return g_QueryFlag;
|
|
}
|
|
|
|
NTSTATUS SdbpOpenKey(PUNICODE_STRING FullPath, BOOL bMachine, ACCESS_MASK Access, PHANDLE KeyHandle)
|
|
{
|
|
UNICODE_STRING BasePath;
|
|
const WCHAR* LayersKey = APPCOMPAT_LAYER_KEY;
|
|
OBJECT_ATTRIBUTES ObjectLayer = RTL_INIT_OBJECT_ATTRIBUTES(FullPath, OBJ_CASE_INSENSITIVE);
|
|
NTSTATUS Status;
|
|
FullPath->Buffer = NULL;
|
|
FullPath->Length = FullPath->MaximumLength = 0;
|
|
if (bMachine)
|
|
{
|
|
RtlInitUnicodeString(&BasePath, REGISTRY_MACHINE);
|
|
}
|
|
else
|
|
{
|
|
Status = RtlFormatCurrentUserKeyPath(&BasePath);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SHIM_ERR("Unable to acquire user registry key, Error: 0x%lx\n", Status);
|
|
return Status;
|
|
}
|
|
}
|
|
FullPath->MaximumLength = (USHORT)(BasePath.Length + SdbpStrsize(LayersKey));
|
|
FullPath->Buffer = SdbAlloc(FullPath->MaximumLength);
|
|
FullPath->Length = 0;
|
|
RtlAppendUnicodeStringToString(FullPath, &BasePath);
|
|
if (!bMachine)
|
|
RtlFreeUnicodeString(&BasePath);
|
|
RtlAppendUnicodeToString(FullPath, LayersKey);
|
|
|
|
Status = NtOpenKey(KeyHandle, Access | Wow64QueryFlag(), &ObjectLayer);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SHIM_ERR("Unable to open Key \"%wZ\" Status 0x%lx\n", FullPath, Status);
|
|
SdbFree(FullPath->Buffer);
|
|
FullPath->Buffer = NULL;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
BOOL SdbpGetPermLayersInternal(PUNICODE_STRING FullPath, PWSTR pwszLayers, PDWORD pdwBytes, BOOL bMachine)
|
|
{
|
|
UNICODE_STRING FullKey;
|
|
ULONG ValueBuffer[(MAX_LAYER_LENGTH * sizeof(WCHAR) + sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG) - 1) / sizeof(ULONG)];
|
|
PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)ValueBuffer;
|
|
ULONG Length = 0;
|
|
HANDLE KeyHandle;
|
|
NTSTATUS Status;
|
|
|
|
Status = SdbpOpenKey(&FullKey, bMachine, KEY_QUERY_VALUE, &KeyHandle);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = NtQueryValueKey(KeyHandle, FullPath, KeyValuePartialInformation, PartialInfo, sizeof(ValueBuffer), &Length);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
StringCbCopyNW(pwszLayers, *pdwBytes, (PCWSTR)PartialInfo->Data, PartialInfo->DataLength);
|
|
*pdwBytes = PartialInfo->DataLength;
|
|
}
|
|
else
|
|
{
|
|
SHIM_INFO("Failed to read value info from Key \"%wZ\" Status 0x%lx\n", &FullKey, Status);
|
|
}
|
|
NtClose(KeyHandle);
|
|
SdbFree(FullKey.Buffer);
|
|
}
|
|
return NT_SUCCESS(Status);
|
|
}
|
|
|
|
BOOL SdbDeletePermLayerKeys(PCWSTR wszPath, BOOL bMachine)
|
|
{
|
|
UNICODE_STRING FullKey;
|
|
SDB_TMP_STR LongPath;
|
|
HANDLE KeyHandle;
|
|
NTSTATUS Status;
|
|
|
|
if (!SdbpResolvePath(&LongPath, wszPath))
|
|
return FALSE;
|
|
|
|
Status = SdbpOpenKey(&FullKey, bMachine, KEY_SET_VALUE, &KeyHandle);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = NtDeleteValueKey(KeyHandle, &LongPath.Str);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SHIM_INFO("Failed to delete value from Key \"%wZ\" Status 0x%lx\n", &FullKey, Status);
|
|
/* This is what we want, so if the key didnt exist, we should not fail :) */
|
|
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
NtClose(KeyHandle);
|
|
SdbFree(FullKey.Buffer);
|
|
}
|
|
SdbpFreeTempStr(&LongPath);
|
|
return NT_SUCCESS(Status);
|
|
}
|
|
|
|
BOOL SdbpMatchLayer(PCWSTR start, PCWSTR end, PCWSTR compare)
|
|
{
|
|
size_t len;
|
|
if (!end)
|
|
return !wcsicmp(start, compare);
|
|
len = end - start;
|
|
return wcslen(compare) == len && !_wcsnicmp(start, compare, len);
|
|
}
|
|
|
|
BOOL SdbpAppendLayer(PWSTR target, DWORD len, PCWSTR layer, PCWSTR end)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
if (target[0])
|
|
Status = StringCbCatW(target, len, SPACE_ONLY);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
if (end)
|
|
Status = StringCbCatNW(target, len, layer, (end - layer) * sizeof(WCHAR));
|
|
else
|
|
Status = StringCbCatW(target, len, layer);
|
|
}
|
|
|
|
return NT_SUCCESS(Status);
|
|
}
|
|
|
|
|
|
/**
|
|
* Determine if we allow permission layers to apply on this file.
|
|
*
|
|
* @param [in] Path Full pathname of the file, only the drive part is used.
|
|
*
|
|
* @return TRUE if we allow permission layer, FALSE if not.
|
|
*/
|
|
BOOL WINAPI AllowPermLayer(PCWSTR Path)
|
|
{
|
|
WCHAR tmp[] = { 'A',':','\\', 0 };
|
|
ULONG type;
|
|
if (!Path)
|
|
{
|
|
SHIM_ERR("Invalid argument\n");
|
|
return FALSE;
|
|
}
|
|
switch (Path[1])
|
|
{
|
|
case L':':
|
|
break;
|
|
case L'\\':
|
|
SHIM_INFO("\"%S\" is a network path.\n", Path);
|
|
return FALSE;
|
|
default:
|
|
SHIM_INFO("\"%S\" not a full path we can operate on.\n", Path);
|
|
return FALSE;
|
|
}
|
|
tmp[0] = Path[0];
|
|
type = GetDriveTypeW(tmp);
|
|
if (type == DRIVE_REMOTE)
|
|
{
|
|
/* The logging here indicates that it does not like a CDROM or removable media, but it only
|
|
seems to bail out on a media that reports it is remote...
|
|
I have included correct logging, I doubt anyone would parse the logging, so this shouldnt break anything. */
|
|
SHIM_INFO("\"%S\" is on a remote drive.\n", Path);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Read the layers specified for the application.
|
|
*
|
|
* @param [in] wszPath Full pathname of the file.
|
|
* @param [out] pwszLayers On return, the layers set on the file.
|
|
* @param pdwBytes The size of the pwszLayers buffer in bytes, and on return the size of
|
|
* the data written (in bytes)
|
|
* @param [in] dwFlags The flags, [GPLK_USER | GPLK_MACHINE].
|
|
*
|
|
* @return TRUE if it succeeds, FALSE if it fails.
|
|
*/
|
|
BOOL WINAPI SdbGetPermLayerKeys(PCWSTR wszPath, PWSTR pwszLayers, PDWORD pdwBytes, DWORD dwFlags)
|
|
{
|
|
BOOL Result = FALSE;
|
|
SDB_TMP_STR LongPath;
|
|
DWORD dwBytes, dwTotal = 0;
|
|
if (!wszPath || !pdwBytes)
|
|
{
|
|
SHIM_ERR("NULL parameter passed for wszPath or pdwBytes.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!SdbpResolvePath(&LongPath, wszPath))
|
|
return FALSE;
|
|
dwBytes = *pdwBytes;
|
|
if (dwFlags & GPLK_MACHINE)
|
|
{
|
|
if (SdbpGetPermLayersInternal(&LongPath.Str, pwszLayers, &dwBytes, TRUE))
|
|
{
|
|
Result = TRUE;
|
|
dwTotal = dwBytes - sizeof(WCHAR); /* Compensate for the nullterm. */
|
|
pwszLayers += dwTotal / sizeof(WCHAR);
|
|
dwBytes = *pdwBytes - dwBytes;
|
|
if (dwFlags & GPLK_USER)
|
|
{
|
|
*(pwszLayers++) = L' ';
|
|
*pwszLayers = L'\0';
|
|
dwBytes -= sizeof(WCHAR);
|
|
dwTotal += sizeof(WCHAR);
|
|
}
|
|
}
|
|
}
|
|
if (dwFlags & GPLK_USER)
|
|
{
|
|
if (SdbpGetPermLayersInternal(&LongPath.Str, pwszLayers, &dwBytes, FALSE))
|
|
{
|
|
Result = TRUE;
|
|
dwTotal += dwBytes - sizeof(WCHAR); /* Compensate for the nullterm. */
|
|
}
|
|
else if (dwTotal > 0 && pwszLayers[-1] == L' ')
|
|
{
|
|
pwszLayers[-1] = '\0';
|
|
dwTotal -= sizeof(WCHAR);
|
|
}
|
|
}
|
|
if (dwTotal)
|
|
dwTotal += sizeof(WCHAR);
|
|
*pdwBytes = dwTotal;
|
|
SdbpFreeTempStr(&LongPath);
|
|
return Result;
|
|
}
|
|
|
|
/**
|
|
* Set or clear the Layer key.
|
|
*
|
|
* @param [in] wszPath Full pathname of the file.
|
|
* @param [in] wszLayers The layers to add (space separated), or an empty string / NULL to
|
|
* remove all layers.
|
|
* @param [in] bMachine TRUE to machine.
|
|
*
|
|
* @return TRUE if it succeeds, FALSE if it fails.
|
|
*/
|
|
BOOL WINAPI SdbSetPermLayerKeys(PCWSTR wszPath, PCWSTR wszLayers, BOOL bMachine)
|
|
{
|
|
UNICODE_STRING FullKey;
|
|
SDB_TMP_STR LongPath;
|
|
HANDLE KeyHandle;
|
|
NTSTATUS Status;
|
|
|
|
if (!wszLayers || *wszLayers == '\0')
|
|
return SdbDeletePermLayerKeys(wszPath, bMachine);
|
|
|
|
if (!SdbpResolvePath(&LongPath, wszPath))
|
|
return FALSE;
|
|
|
|
Status = SdbpOpenKey(&FullKey, bMachine, KEY_SET_VALUE, &KeyHandle);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = NtSetValueKey(KeyHandle, &LongPath.Str, 0, REG_SZ, (PVOID)wszLayers, SdbpStrsize(wszLayers));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SHIM_INFO("Failed to write a value to Key \"%wZ\" Status 0x%lx\n", &FullKey, Status);
|
|
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
NtClose(KeyHandle);
|
|
SdbFree(FullKey.Buffer);
|
|
}
|
|
SdbpFreeTempStr(&LongPath);
|
|
return NT_SUCCESS(Status);
|
|
}
|
|
|
|
/**
|
|
* Adds or removes a single layer entry.
|
|
*
|
|
* @param [in] wszPath Full pathname of the file.
|
|
* @param [in] wszLayer The layer to add or remove.
|
|
* @param [in] dwFlags Additional flags to add / remove [LAYER_APPLY_TO_SYSTEM_EXES | ???].
|
|
* @param [in] bMachine When TRUE, the setting applies to all users, when FALSE only applies
|
|
* to the current user.
|
|
* @param [in] bEnable TRUE to enable, FALSE to disable a layer / flag specified.
|
|
*
|
|
* @return TRUE if it succeeds, FALSE if it fails.
|
|
*/
|
|
BOOL WINAPI SetPermLayerState(PCWSTR wszPath, PCWSTR wszLayer, DWORD dwFlags, BOOL bMachine, BOOL bEnable)
|
|
{
|
|
WCHAR fullLayer[MAX_LAYER_LENGTH] = { 0 };
|
|
WCHAR newLayer[MAX_LAYER_LENGTH] = { 0 };
|
|
DWORD dwBytes = sizeof(fullLayer), dwWriteFlags = 0;
|
|
PWSTR start, p;
|
|
|
|
if (!wszLayer)
|
|
{
|
|
SHIM_ERR("Invalid argument\n");
|
|
return FALSE;
|
|
}
|
|
if (dwFlags & ~(LAYER_APPLY_TO_SYSTEM_EXES | LAYER_UNK_FLAG2))
|
|
{
|
|
SHIM_ERR("Invalid flags\n");
|
|
return FALSE;
|
|
}
|
|
p = wcspbrk(wszLayer, DISALLOWED_LAYER_CHARS);
|
|
if (p)
|
|
{
|
|
switch (*p)
|
|
{
|
|
case ' ':
|
|
SHIM_ERR("Only one layer can be passed in at a time.\n");
|
|
return FALSE;
|
|
case '#':
|
|
case '!':
|
|
SHIM_ERR("Flags cannot be passed in with the layer name.\n");
|
|
return FALSE;
|
|
}
|
|
}
|
|
if (!SdbGetPermLayerKeys(wszPath, fullLayer, &dwBytes, bMachine ? GPLK_MACHINE : GPLK_USER))
|
|
{
|
|
fullLayer[0] = '\0';
|
|
dwBytes = sizeof(fullLayer);
|
|
}
|
|
|
|
start = fullLayer;
|
|
while (*start == '!' || *start == '#' || *start == ' ' || *start == '\t')
|
|
{
|
|
if (*start == '#')
|
|
dwWriteFlags |= LAYER_APPLY_TO_SYSTEM_EXES;
|
|
else if (*start == '!')
|
|
dwWriteFlags |= LAYER_UNK_FLAG2;
|
|
start++;
|
|
}
|
|
if (bEnable)
|
|
dwWriteFlags |= dwFlags;
|
|
else
|
|
dwWriteFlags &= ~dwFlags;
|
|
|
|
p = newLayer;
|
|
if (dwWriteFlags & LAYER_UNK_FLAG2)
|
|
*(p++) = '!';
|
|
if (dwWriteFlags & LAYER_APPLY_TO_SYSTEM_EXES)
|
|
*(p++) = '#';
|
|
|
|
do
|
|
{
|
|
while (*start == ' ' || *start == '\t')
|
|
++start;
|
|
|
|
if (*start == '\0')
|
|
break;
|
|
p = wcspbrk(start, LAYER_SEPARATORS);
|
|
if (!SdbpMatchLayer(start, p, wszLayer))
|
|
{
|
|
SdbpAppendLayer(newLayer, sizeof(newLayer), start, p);
|
|
}
|
|
start = p + 1;
|
|
} while (p);
|
|
|
|
if (bEnable && wszLayer[0])
|
|
{
|
|
SdbpAppendLayer(newLayer, sizeof(newLayer), wszLayer, NULL);
|
|
}
|
|
|
|
return SdbSetPermLayerKeys(wszPath, newLayer, bMachine);
|
|
}
|