DWORD RegGetErrorMessageForLoggingEvent( DWORD dwErrCode, PSTR* ppszErrorMsg ) { DWORD dwErrorBufferSize = 0; DWORD dwError = 0; DWORD dwLen = 0; PSTR pszErrorMsg = NULL; PSTR pszErrorBuffer = NULL; dwErrorBufferSize = LwRegGetErrorString(dwErrCode, NULL, 0); if (!dwErrorBufferSize) goto cleanup; dwError = LW_RTL_ALLOCATE((PVOID*)&pszErrorBuffer, CHAR, sizeof(*pszErrorBuffer) * dwErrorBufferSize); BAIL_ON_REG_ERROR(dwError); dwLen = LwRegGetErrorString(dwErrCode, pszErrorBuffer, dwErrorBufferSize); if ((dwLen == dwErrorBufferSize) && !IsNullOrEmptyString(pszErrorBuffer)) { dwError = RegCStringAllocatePrintf( &pszErrorMsg, "Error: %s [error code: %d]", pszErrorBuffer, dwErrCode); BAIL_ON_REG_ERROR(dwError); } *ppszErrorMsg = pszErrorMsg; cleanup: LWREG_SAFE_FREE_STRING(pszErrorBuffer); return dwError; error: LWREG_SAFE_FREE_STRING(pszErrorMsg); *ppszErrorMsg = NULL; goto cleanup; }
NTSTATUS RegByteArrayToHexStr( IN UCHAR* pucByteArray, IN DWORD dwByteArrayLength, OUT PSTR* ppszHexString ) { NTSTATUS status = 0; DWORD i = 0; PSTR pszHexString = NULL; status = LW_RTL_ALLOCATE((PVOID*)&pszHexString, CHAR, sizeof(*pszHexString)* (dwByteArrayLength*2 + 1)); BAIL_ON_NT_STATUS(status); for (i = 0; i < dwByteArrayLength; i++) { sprintf(pszHexString+(2*i), "%.2X", pucByteArray[i]); } *ppszHexString = pszHexString; cleanup: return status; error: LWREG_SAFE_FREE_STRING(pszHexString); *ppszHexString = NULL; goto cleanup; }
DWORD RegStrndup( PCSTR pszInputString, size_t size, PSTR * ppszOutputString ) { DWORD dwError = 0; size_t copylen = 0; PSTR pszOutputString = NULL; if (!pszInputString || !ppszOutputString){ dwError = EINVAL; BAIL_ON_REG_ERROR(dwError); } for (copylen = 0; copylen < size && pszInputString[copylen]; copylen++); dwError = RegAllocateMemory(copylen+1, (PVOID*)&pszOutputString); BAIL_ON_REG_ERROR(dwError); memcpy(pszOutputString, pszInputString, copylen); pszOutputString[copylen] = 0; *ppszOutputString = pszOutputString; cleanup: return dwError; error: LWREG_SAFE_FREE_STRING(pszOutputString); goto cleanup; }
DWORD RegGetCurrentDirectoryPath( PSTR* ppszPath ) { DWORD dwError = 0; CHAR szBuf[PATH_MAX+1]; PSTR pszPath = NULL; if (getcwd(szBuf, PATH_MAX) == NULL) { dwError = errno; BAIL_ON_REG_ERROR(dwError); } dwError = RegCStringDuplicate(&pszPath, szBuf); BAIL_ON_REG_ERROR(dwError); *ppszPath = pszPath; return dwError; error: LWREG_SAFE_FREE_STRING(pszPath); return dwError; }
VOID RegPrintError( IN OPTIONAL PCSTR pszErrorPrefix, IN DWORD dwError ) { PCSTR pszUseErrorPrefix = NULL; size_t size = 0; PSTR pszErrorString = NULL; PCSTR pszWinError = NULL; if (dwError) { pszUseErrorPrefix = pszErrorPrefix; if (!pszUseErrorPrefix) { pszUseErrorPrefix = "LWREG ERROR: "; } size = LwRegGetErrorString(dwError, NULL, 0); if (size) { pszErrorString = malloc(size); if (pszErrorString) { LwRegGetErrorString(dwError, pszErrorString, size); } } pszWinError = LW_PRINTF_STRING(RegWin32ExtErrorToName(dwError)); if (!pszWinError) { pszWinError = ""; } if (LW_IS_NULL_OR_EMPTY_STR(pszErrorString)) { fprintf(stderr, "%s (error = %u%s%s)\n", pszUseErrorPrefix, dwError, *pszWinError ? " - " : "", pszWinError); } else { fprintf(stderr, "%s (error = %u%s%s)\n%s\n", pszUseErrorPrefix, dwError, *pszWinError ? " - " : "", pszWinError, pszErrorString); } } LWREG_SAFE_FREE_STRING(pszErrorString); }
VOID RegFreeSysLogInfo( PREG_SYS_LOG pSysLog ) { if (pSysLog->bOpened) { /* close connection to syslog */ closelog(); } LWREG_SAFE_FREE_STRING(pSysLog->pszIdentifier); RegMemoryFree(pSysLog); }
DWORD RegGetDirectoryFromPath( IN PCSTR pszPath, OUT PSTR* ppszDir ) { PCSTR pszLastSlash = NULL; PSTR pszDir = NULL; DWORD dwError = 0; BAIL_ON_INVALID_POINTER(pszPath); pszLastSlash = strrchr(pszPath, '/'); if (pszLastSlash == pszPath) { //Include the trailing / since this is the root directory ( / ) pszLastSlash++; } if (pszLastSlash == NULL) { dwError = RegCStringDuplicate( &pszDir, "."); BAIL_ON_REG_ERROR(dwError); } else { dwError = RegStrndup( pszPath, pszLastSlash - pszPath, &pszDir); BAIL_ON_REG_ERROR(dwError); } *ppszDir = pszDir; cleanup: return dwError; error: LWREG_SAFE_FREE_STRING(pszDir); *ppszDir = NULL; goto cleanup; }
DWORD RegGetSymlinkTarget( PCSTR pszPath, PSTR* ppszTargetPath ) { DWORD dwError = 0; CHAR szBuf[PATH_MAX+1]; PSTR pszTargetPath = NULL; memset(szBuf, 0, sizeof(szBuf)); while (1) { if (readlink(pszPath, szBuf, PATH_MAX) < 0) { if (errno == EINTR) continue; dwError = errno; BAIL_ON_REG_ERROR(dwError); } break; } dwError = RegCStringDuplicate( &pszTargetPath, szBuf); BAIL_ON_REG_ERROR(dwError); *ppszTargetPath = pszTargetPath; cleanup: return dwError; error: *ppszTargetPath = NULL; LWREG_SAFE_FREE_STRING(pszTargetPath); goto cleanup; }
static DWORD ProcessSubKeys( HANDLE hReg, PREG_EXPORT_STATE pExportState, HKEY hKey, PCSTR pszFullKeyName, DWORD dwNumSubKeys, DWORD dwMaxSubKeyLen ) { DWORD dwError = 0; int iCount = 0; DWORD dwSubKeyLen = dwMaxSubKeyLen+1; PWSTR pwszSubKey = NULL; // buffer for subkey name PSTR pszSubKey = NULL; PSTR pszFullSubKeyName = NULL; HKEY hSubKey = NULL; DWORD dwNumSubSubKeys = 0; DWORD dwMaxSubSubKeyLen = 0; // Get the subkeys and values under this key from registry for (iCount = 0; iCount < dwNumSubKeys; iCount++) { dwSubKeyLen = dwMaxSubKeyLen+1; dwError = RegAllocateMemory(sizeof(*pwszSubKey) * dwSubKeyLen, (PVOID*)&pwszSubKey); BAIL_ON_REG_ERROR(dwError); dwError = RegEnumKeyExW((HANDLE)hReg, hKey, iCount, pwszSubKey, &dwSubKeyLen, NULL, NULL, NULL, NULL); BAIL_ON_REG_ERROR(dwError); dwError = RegOpenKeyExW( hReg, hKey, pwszSubKey, 0, KEY_READ, &hSubKey); BAIL_ON_REG_ERROR(dwError); dwError = RegQueryInfoKeyA( (HANDLE) hReg, hSubKey, NULL, NULL, NULL, &dwNumSubSubKeys, &dwMaxSubSubKeyLen, NULL, NULL, NULL, NULL, NULL, NULL); BAIL_ON_REG_ERROR(dwError); // Get the pszFullSubKeyName dwError = RegCStringAllocateFromWC16String(&pszSubKey, pwszSubKey); BAIL_ON_REG_ERROR(dwError); dwError = RegCStringAllocatePrintf( &pszFullSubKeyName, "%s\\%s", pszFullKeyName, pszSubKey); BAIL_ON_REG_ERROR(dwError); dwError = RegShellUtilExport( hReg, pExportState, hSubKey, pszFullSubKeyName, dwNumSubSubKeys, dwMaxSubSubKeyLen); BAIL_ON_REG_ERROR(dwError); if (hSubKey) { dwError = RegCloseKey((HANDLE) hReg, hSubKey); BAIL_ON_REG_ERROR(dwError); hSubKey = NULL; } LWREG_SAFE_FREE_STRING(pszFullSubKeyName); LWREG_SAFE_FREE_STRING(pszSubKey); LWREG_SAFE_FREE_MEMORY(pwszSubKey); dwNumSubSubKeys = 0; dwMaxSubSubKeyLen = 0; pszFullSubKeyName = NULL; pszSubKey = NULL; pwszSubKey = NULL; } cleanup: if (hSubKey) { RegCloseKey((HANDLE) hReg, hSubKey); hSubKey = NULL; } LWREG_SAFE_FREE_STRING(pszFullSubKeyName); LWREG_SAFE_FREE_STRING(pszSubKey); LWREG_SAFE_FREE_MEMORY(pwszSubKey); dwNumSubKeys = 0; return dwError; error: goto cleanup; }
static DWORD ProcessExportedKeyInfo( IN HANDLE hReg, PREG_EXPORT_STATE pExportState, IN HKEY hKey, IN PCSTR pszFullKeyName, IN OPTIONAL PCSTR pszSddlCstring, IN OUT PREG_DATA_TYPE pPrevType ) { DWORD dwError = 0; DWORD dwError2 = 0; DWORD dwValueNameLen = MAX_KEY_LENGTH; WCHAR pwszValueName[MAX_KEY_LENGTH]; // buffer for subkey name WCHAR format PSTR pszValueName = NULL; // buffer for subkey name PSTR pszValueNameEscaped = NULL; // buffer for subkey name PSTR pszValue = NULL; REG_DATA_TYPE dataType = REG_NONE; BYTE *value = NULL; DWORD dwValueLen = 0; DWORD dwValueNameEscapedLen = 0; DWORD dwValueLenMax = 0; int iCount = 0; DWORD dwValuesCount = 0; PVOID pValue = NULL; PLWREG_VALUE_ATTRIBUTES pValueAttributes = NULL; PSTR pszAttrDump = NULL; DWORD pszAttrDumpLen = 0; PLWREG_CURRENT_VALUEINFO pCurrValueInfo = NULL; REG_PARSE_ITEM regItem = {0}; FILE *fp = pExportState->fp; BOOLEAN bValueSet = FALSE; dwError = PrintToRegFile( fp, pszFullKeyName, pszSddlCstring, REG_KEY, NULL, REG_KEY, FALSE, NULL, 0, pPrevType); BAIL_ON_REG_ERROR(dwError); dwError = RegQueryInfoKeyW( hReg, hKey, NULL, NULL, NULL, NULL, NULL, NULL, &dwValuesCount, NULL, &dwValueLenMax, NULL, NULL); BAIL_ON_REG_ERROR(dwError); if (!dwValuesCount) { goto cleanup; } dwError = RegAllocateMemory(dwValueLenMax, (PVOID*)&value); BAIL_ON_REG_ERROR(dwError); for (iCount = 0; iCount < dwValuesCount; iCount++) { memset(pwszValueName, 0, MAX_KEY_LENGTH); dwValueNameLen = MAX_KEY_LENGTH; memset(value, 0, dwValueLenMax); dwValueLen = dwValueLenMax; RegSafeFreeCurrentValueInfo(&pCurrValueInfo); dwError = RegEnumValueW((HANDLE)hReg, hKey, iCount, pwszValueName, &dwValueNameLen, NULL, &dataType, value, &dwValueLen); BAIL_ON_REG_ERROR(dwError); LWREG_SAFE_FREE_STRING(pszValueName); LWREG_SAFE_FREE_STRING(pszValueNameEscaped); dwError = RegCStringAllocateFromWC16String( &pszValueName, pwszValueName); BAIL_ON_REG_ERROR(dwError); dwError = RegShellUtilEscapeString( pszValueName, &pszValueNameEscaped, &dwValueNameEscapedLen); BAIL_ON_REG_ERROR(dwError); if (dataType == REG_SZ) { LWREG_SAFE_FREE_STRING(pszValue); dwError2 = RegCStringAllocateFromWC16String( &pszValue, (PWSTR) value); BAIL_ON_REG_ERROR(dwError2); pValue = pszValue; } else { pValue = value; } LWREG_SAFE_FREE_MEMORY(pszAttrDump); dwError = LwRegGetValueAttributesW( hReg, hKey, NULL, pwszValueName, &pCurrValueInfo, &pValueAttributes); if (pExportState->dwExportFormat == REGSHELL_EXPORT_LEGACY) { bValueSet = pCurrValueInfo || (dwError && dwValueLen) ? FALSE : TRUE; /* Export "legacy" format */ dwError = PrintToRegFile( fp, pszFullKeyName, NULL, REG_SZ, pszValueNameEscaped, dataType, bValueSet, pValue, dwValueLen, pPrevType); } else if (pExportState->dwExportFormat == REGSHELL_EXPORT_VALUES) { /* Export only values that override default */ if ((dwError == 0 && pCurrValueInfo) || (dwError && dwValueLen)) { memset(®Item, 0, sizeof(regItem)); regItem.type = REG_SZ; regItem.valueName = pszValueNameEscaped; if (pCurrValueInfo) { dataType = pCurrValueInfo->dwType; pValue = pCurrValueInfo->pvData; dwValueLen = pCurrValueInfo->cbData; } else { pValue = value; } regItem.valueType = dataType; regItem.regAttr.ValueType = regItem.valueType; if (dataType == REG_SZ) { if (pValue) { dwError = RegCStringAllocateFromWC16String( (PSTR *) ®Item.value, (PWSTR) pValue); BAIL_ON_REG_ERROR(dwError); regItem.valueLen = strlen(regItem.value); } pValue = regItem.value; dwValueLen = strlen((PSTR) value); } else { regItem.value = pValue; regItem.valueLen = dwValueLen; } /* Export "legacy" format */ dwError = PrintToRegFile( fp, pszFullKeyName, NULL, REG_SZ, pszValueNameEscaped, dataType, FALSE, pValue, dwValueLen, pPrevType); } else { /* * Don't care if there isn't a schema entry when * exporting values, so ignore error from * RegGetValueAttributesW(). */ dwError = 0; } } else if (dwError == 0) { /* Export value and attribute information */ memset(®Item, 0, sizeof(regItem)); regItem.type = REG_SZ; regItem.valueName = pszValueNameEscaped; regItem.valueType = dataType; if (pValueAttributes) { regItem.regAttr = *pValueAttributes; } /* Might have to do something with MULTI_SZ */ if (dataType == REG_SZ) { if (regItem.regAttr.pDefaultValue) { dwError = RegCStringAllocateFromWC16String( (PSTR *) ®Item.regAttr.pDefaultValue, (PWSTR) regItem.regAttr.pDefaultValue); BAIL_ON_REG_ERROR(dwError); regItem.regAttr.DefaultValueLen = strlen(regItem.regAttr.pDefaultValue); regItem.value = pCurrValueInfo ? pValue : NULL; } } else { regItem.value = pCurrValueInfo ? value : NULL; regItem.valueLen = dwValueLen; } dwError = RegExportAttributeEntries( ®Item, &pszAttrDump, &pszAttrDumpLen); BAIL_ON_REG_ERROR(dwError); LWREG_SAFE_FREE_STRING(regItem.regAttr.pDefaultValue); fprintf(fp, "%*s\n", pszAttrDumpLen, pszAttrDump); } else { /* * Create a "fake" PITEM, populate it with legacy * valueName=dataValue information, and use * RegExportAttributeEntries() to generate the export data. */ memset(®Item, 0, sizeof(regItem)); regItem.type = REG_SZ; regItem.valueName = pszValueNameEscaped; regItem.valueType = dataType; regItem.regAttr.ValueType = dataType; regItem.value = pValue; regItem.valueLen = dwValueLen; dwError = RegExportAttributeEntries( ®Item, &pszAttrDump, &pszAttrDumpLen); BAIL_ON_REG_ERROR(dwError); fprintf(fp, "%*s\n", pszAttrDumpLen, pszAttrDump); } } cleanup: LWREG_SAFE_FREE_MEMORY(pszAttrDump); LWREG_SAFE_FREE_MEMORY(pszValue); LWREG_SAFE_FREE_STRING(pszValueName); LWREG_SAFE_FREE_STRING(pszValueNameEscaped); LWREG_SAFE_FREE_STRING(pszValue); LWREG_SAFE_FREE_STRING(regItem.regAttr.pDefaultValue); RegSafeFreeCurrentValueInfo(&pCurrValueInfo); memset(value, 0, dwValueLenMax); LWREG_SAFE_FREE_MEMORY(value); return dwError; error: goto cleanup; }
DWORD RegSrvStartListenThread( void ) { PSTR pszCachePath = NULL; PSTR pszCommPath = NULL; BOOLEAN bDirExists = FALSE; DWORD dwError = 0; static LWMsgTime idleTimeout = {30, 0}; dwError = RegSrvGetCachePath(&pszCachePath); BAIL_ON_REG_ERROR(dwError); dwError = RegCheckDirectoryExists(pszCachePath, &bDirExists); BAIL_ON_REG_ERROR(dwError); if (!bDirExists) { // Directory should be RWX for root and accessible to all // (so they can see the socket. mode_t mode = S_IRWXU | S_IRGRP| S_IXGRP | S_IROTH | S_IXOTH; dwError = RegCreateDirectory(pszCachePath, mode); BAIL_ON_REG_ERROR(dwError); } dwError = LwRtlCStringAllocatePrintf(&pszCommPath, "%s/%s", pszCachePath, REG_SERVER_FILENAME); BAIL_ON_REG_ERROR(dwError); dwError = MAP_LWMSG_ERROR(lwmsg_context_new(NULL, &gpContext)); BAIL_ON_REG_ERROR(dwError); lwmsg_context_set_log_function(gpContext, RegSrvLogIpc, NULL); /* Set up IPC protocol object */ dwError = MAP_LWMSG_ERROR(lwmsg_protocol_new(gpContext, &gpProtocol)); BAIL_ON_REG_ERROR(dwError); dwError = MAP_LWMSG_ERROR(lwmsg_protocol_add_protocol_spec( gpProtocol, RegIPCGetProtocolSpec())); BAIL_ON_REG_ERROR(dwError); /* Set up IPC server object */ dwError = MAP_LWMSG_ERROR(lwmsg_peer_new(gpContext, gpProtocol, &gpServer)); BAIL_ON_REG_ERROR(dwError); dwError = MAP_LWMSG_ERROR(lwmsg_peer_add_dispatch_spec( gpServer, RegSrvGetDispatchSpec())); BAIL_ON_REG_ERROR(dwError); dwError = MAP_LWMSG_ERROR(lwmsg_peer_add_listen_endpoint( gpServer, LWMSG_ENDPOINT_DIRECT, "lwreg", 0)); BAIL_ON_REG_ERROR(dwError); dwError = MAP_LWMSG_ERROR(lwmsg_peer_add_listen_endpoint( gpServer, LWMSG_ENDPOINT_LOCAL, pszCommPath, 0666)); BAIL_ON_REG_ERROR(dwError); dwError = MAP_LWMSG_ERROR(lwmsg_peer_set_max_listen_clients( gpServer, MAX_CLIENTS)); BAIL_ON_REG_ERROR(dwError); dwError = MAP_LWMSG_ERROR(lwmsg_peer_set_max_listen_backlog( gpServer, REG_MAX(5, MAX_CLIENTS / 4))); BAIL_ON_REG_ERROR(dwError); dwError = MAP_LWMSG_ERROR(lwmsg_peer_set_timeout( gpServer, LWMSG_TIMEOUT_IDLE, &idleTimeout)); BAIL_ON_REG_ERROR(dwError); dwError = MAP_LWMSG_ERROR(lwmsg_peer_set_listen_session_functions( gpServer, RegSrvIpcConstructSession, RegSrvIpcDestructSession, NULL)); BAIL_ON_REG_ERROR(dwError); dwError = MAP_LWMSG_ERROR(lwmsg_peer_start_listen(gpServer)); error: LWREG_SAFE_FREE_STRING(pszCachePath); LWREG_SAFE_FREE_STRING(pszCommPath); if (dwError) { if (gpServer) { lwmsg_peer_stop_listen(gpServer); lwmsg_peer_delete(gpServer); gpServer = NULL; } } return dwError; }
DWORD RegGetMatchingFilePathsInFolder( PCSTR pszDirPath, PCSTR pszFileNameRegExp, PSTR** pppszHostFilePaths, PDWORD pdwNPaths ) { typedef struct __PATHNODE { PSTR pszPath; struct __PATHNODE *pNext; } *PPATHNODE; DWORD dwError = 0; DIR* pDir = NULL; struct dirent* pDirEntry = NULL; regex_t rx; BOOLEAN rxAllocated = FALSE; regmatch_t* pResult = NULL; size_t nMatch = 1; DWORD dwNPaths = 0; DWORD iPath = 0; PSTR* ppszHostFilePaths = NULL; CHAR szBuf[PATH_MAX+1]; struct stat statbuf; PPATHNODE pPathList = NULL; PPATHNODE pPathNode = NULL; BOOLEAN bDirExists = FALSE; dwError = RegCheckDirectoryExists(pszDirPath, &bDirExists); BAIL_ON_REG_ERROR(dwError); if(!bDirExists) { dwError = ENOENT; BAIL_ON_REG_ERROR(dwError); } if (regcomp(&rx, pszFileNameRegExp, REG_EXTENDED) != 0) { dwError = LWREG_ERROR_REGEX_COMPILE_FAILED; BAIL_ON_REG_ERROR(dwError); } rxAllocated = TRUE; dwError = RegAllocateMemory(sizeof(*pResult), (PVOID*)&pResult); BAIL_ON_REG_ERROR(dwError); pDir = opendir(pszDirPath); if (!pDir) { dwError = errno; BAIL_ON_REG_ERROR(dwError); } while ((pDirEntry = readdir(pDir)) != NULL) { int copied = snprintf( szBuf, sizeof(szBuf), "%s/%s", pszDirPath, pDirEntry->d_name); if (copied >= sizeof(szBuf)) { //Skip pathnames that are too long continue; } memset(&statbuf, 0, sizeof(struct stat)); if (lstat(szBuf, &statbuf) < 0) { if(errno == ENOENT) { //This occurs when there is a symbolic link pointing to a //location that doesn't exist, because stat looks at the final //file, not the link. Since this file doesn't exist anyway, //just skip it. continue; } dwError = errno; BAIL_ON_REG_ERROR(dwError); } /* * For now, we are searching only for regular files * This may be enhanced in the future to support additional * file system entry types */ if (((statbuf.st_mode & S_IFMT) == S_IFREG) && (regexec(&rx, pDirEntry->d_name, nMatch, pResult, 0) == 0)) { dwNPaths++; dwError = RegAllocateMemory(sizeof(*pPathNode), (PVOID*)&pPathNode); BAIL_ON_REG_ERROR(dwError); dwError = RegCStringDuplicate(&pPathNode->pszPath, szBuf); BAIL_ON_REG_ERROR(dwError); pPathNode->pNext = pPathList; pPathList = pPathNode; pPathNode = NULL; } } if (pPathList) { dwError = RegAllocateMemory(sizeof(*ppszHostFilePaths)*dwNPaths, (PVOID*)&ppszHostFilePaths); BAIL_ON_REG_ERROR(dwError); /* * The linked list is in reverse. * Assign values in reverse to get it right */ iPath = dwNPaths-1; pPathNode = pPathList; while (pPathNode) { *(ppszHostFilePaths+iPath) = pPathNode->pszPath; pPathNode->pszPath = NULL; pPathNode = pPathNode->pNext; iPath--; } } *pppszHostFilePaths = ppszHostFilePaths; *pdwNPaths = dwNPaths; cleanup: if (pPathNode) { LWREG_SAFE_FREE_STRING(pPathNode->pszPath); RegMemoryFree(pPathNode); } while(pPathList) { pPathNode = pPathList; pPathList = pPathList->pNext; LWREG_SAFE_FREE_STRING(pPathNode->pszPath); RegMemoryFree(pPathNode); } if (rxAllocated) { regfree(&rx); } LWREG_SAFE_FREE_MEMORY(pResult); if (pDir) { closedir(pDir); } return dwError; error: if (ppszHostFilePaths) { RegFreeStringArray(ppszHostFilePaths, dwNPaths); } goto cleanup; }