/* Editor Settings: expandtabs and use 4 spaces for indentation * ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: * * -*- mode: c, c-basic-offset: 4 -*- */ /* * Copyright Likewise Software 2004-2008 * All rights reserved. * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the license, or (at * your option) any later version. * * This library 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 Lesser * General Public License for more details. You should have received a copy * of the GNU Lesser General Public License along with this program. If * not, see . * * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING * TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU * LESSER GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT * license@likewisesoftware.com */ /* * Copyright (C) Likewise Software. All rights reserved. * * Module Name: * * fileutils.c * * Abstract: * * Registry File Utilities * * Authors: Krishna Ganugapati (krishnag@likewisesoftware.com) * Sriram Nambakam (snambakam@likewisesoftware.com) * Marc Guy (mguy@likewisesoftware.com) * */ #include "includes.h" static DWORD RegCreateDirectoryRecursive( PSTR pszCurDirPath, PSTR pszTmpPath, PSTR *ppszTmp, DWORD dwFileMode, DWORD dwWorkingFileMode, int iPart ); DWORD RegRemoveFile( PCSTR pszPath ) { DWORD dwError = 0; while (1) { if (unlink(pszPath) < 0) { if (errno == EINTR) { continue; } dwError = errno; BAIL_ON_REG_ERROR(dwError); } else { break; } } error: return dwError; } DWORD RegCheckFileExists( PCSTR pszPath, PBOOLEAN pbFileExists ) { DWORD dwError = 0; struct stat statbuf; memset(&statbuf, 0, sizeof(struct stat)); while (1) { if (stat(pszPath, &statbuf) < 0) { if (errno == EINTR) { continue; } else if (errno == ENOENT) { *pbFileExists = 0; break; } dwError = errno; BAIL_ON_REG_ERROR(dwError); } else { *pbFileExists = 1; break; } } error: return dwError; } DWORD RegCheckSockExists( PSTR pszPath, PBOOLEAN pbSockExists ) { DWORD dwError = 0; struct stat statbuf; memset(&statbuf, 0, sizeof(struct stat)); while (1) { if (stat(pszPath, &statbuf) < 0) { if (errno == EINTR) { continue; } else if (errno == ENOENT || errno == ENOTDIR) { *pbSockExists = 0; break; } dwError = errno; BAIL_ON_REG_ERROR(dwError); } else { *pbSockExists = (((statbuf.st_mode & S_IFMT) == S_IFSOCK) ? TRUE : FALSE); break; } } error: return dwError; } DWORD RegMoveFile( PCSTR pszSrcPath, PCSTR pszDstPath ) { DWORD dwError = 0; if (rename(pszSrcPath, pszDstPath) < 0) { dwError = errno; } return dwError; } DWORD RegChangePermissions( PCSTR pszPath, mode_t dwFileMode ) { DWORD dwError = 0; while (1) { if (chmod(pszPath, dwFileMode) < 0) { if (errno == EINTR) { continue; } dwError = errno; BAIL_ON_REG_ERROR(dwError); } else { break; } } error: return dwError; } DWORD RegChangeOwner( PCSTR pszPath, uid_t uid, gid_t gid ) { DWORD dwError = 0; struct stat statbuf = {0}; if (lstat(pszPath, &statbuf) < 0) { dwError = errno; BAIL_ON_REG_ERROR(dwError); } while (1) { if (S_ISLNK(statbuf.st_mode)) { if (lchown(pszPath, uid, gid) < 0) { if (errno == EINTR) { continue; } dwError = errno; BAIL_ON_REG_ERROR(dwError); } else { break; } } else { if (chown(pszPath, uid, gid) < 0) { if (errno == EINTR) { continue; } dwError = errno; BAIL_ON_REG_ERROR(dwError); } else { break; } } } error: return dwError; } DWORD RegChangeDirectory( PSTR pszPath ) { if (pszPath == NULL || *pszPath == '\0') return EINVAL; if (chdir(pszPath) < 0) return errno; return 0; } /* // TODO: Check access and removability before actual deletion */ DWORD RegRemoveDirectory( PSTR pszPath ) { DWORD dwError = 0; DIR* pDir = NULL; struct dirent* pDirEntry = NULL; struct stat statbuf; CHAR szBuf[PATH_MAX+1]; if ((pDir = opendir(pszPath)) == NULL) { dwError = errno; BAIL_ON_REG_ERROR(dwError); } while ((pDirEntry = readdir(pDir)) != NULL) { if (!strcmp(pDirEntry->d_name, "..") || !strcmp(pDirEntry->d_name, ".")) continue; sprintf(szBuf, "%s/%s", pszPath, pDirEntry->d_name); memset(&statbuf, 0, sizeof(struct stat)); if (stat(szBuf, &statbuf) < 0) { dwError = errno; BAIL_ON_REG_ERROR(dwError); } if ((statbuf.st_mode & S_IFMT) == S_IFDIR) { dwError = RegRemoveDirectory(szBuf); BAIL_ON_REG_ERROR(dwError); if (rmdir(szBuf) < 0) { dwError = errno; BAIL_ON_REG_ERROR(dwError); } } else { dwError = RegRemoveFile(szBuf); BAIL_ON_REG_ERROR(dwError); } } error: if (pDir) closedir(pDir); return dwError; } DWORD RegCheckDirectoryExists( PCSTR pszPath, PBOOLEAN pbDirExists ) { DWORD dwError = 0; struct stat statbuf; while (1) { memset(&statbuf, 0, sizeof(struct stat)); if (stat(pszPath, &statbuf) < 0) { if (errno == EINTR) { continue; } else if (errno == ENOENT || errno == ENOTDIR) { *pbDirExists = FALSE; break; } dwError = errno; BAIL_ON_REG_ERROR(dwError); } /* The path exists. Is it a directory? */ *pbDirExists = (((statbuf.st_mode & S_IFMT) == S_IFDIR) ? TRUE : FALSE); break; } error: return dwError; } 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; } DWORD RegCreateDirectory( PCSTR pszPath, mode_t dwFileMode ) { DWORD dwError = 0; PSTR pszCurDirPath = NULL; PSTR pszTmpPath = NULL; PSTR pszTmp = NULL; mode_t dwWorkingFileMode; if (pszPath == NULL || *pszPath == '\0') { dwError = EINVAL; BAIL_ON_REG_ERROR(dwError); } dwWorkingFileMode = dwFileMode; if (!(dwFileMode & S_IXUSR)) { /* * This is so that we can navigate the folders * when we are creating the subfolders */ dwWorkingFileMode |= S_IXUSR; } dwError = RegGetCurrentDirectoryPath(&pszCurDirPath); BAIL_ON_REG_ERROR(dwError); dwError = RegCStringDuplicate(&pszTmpPath, pszPath); BAIL_ON_REG_ERROR(dwError); if (*pszPath == '/') { dwError = RegChangeDirectory("/"); BAIL_ON_REG_ERROR(dwError); dwError = RegCreateDirectoryRecursive("/", pszTmpPath, &pszTmp, dwFileMode, dwWorkingFileMode, 0); BAIL_ON_REG_ERROR(dwError); } else { dwError = RegCreateDirectoryRecursive(pszCurDirPath, pszTmpPath, &pszTmp, dwFileMode, dwWorkingFileMode, 0); BAIL_ON_REG_ERROR(dwError); } error: if (pszCurDirPath) { RegChangeDirectory(pszCurDirPath); RegMemoryFree(pszCurDirPath); } if (pszTmpPath) { RegMemoryFree(pszTmpPath); } return dwError; } 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 RegGetOwnerAndPermissions( PCSTR pszSrcPath, uid_t * uid, gid_t * gid, mode_t * mode ) { DWORD dwError = 0; struct stat statbuf; memset(&statbuf, 0, sizeof(struct stat)); if (stat(pszSrcPath, &statbuf) < 0) { dwError = errno; BAIL_ON_REG_ERROR(dwError); } *uid = statbuf.st_uid; *gid = statbuf.st_gid; *mode = statbuf.st_mode; error: return dwError; } DWORD RegCopyFileWithPerms( PCSTR pszSrcPath, PCSTR pszDstPath, mode_t dwPerms ) { DWORD dwError = 0; PCSTR pszTmpSuffix = ".tmp_likewise_lsass"; PSTR pszTmpPath = NULL; BOOLEAN bRemoveFile = FALSE; CHAR szBuf[1024+1]; int iFd = -1; int oFd = -1; DWORD dwBytesRead = 0; if (IsNullOrEmptyString(pszSrcPath) || IsNullOrEmptyString(pszDstPath)) { dwError = EINVAL; BAIL_ON_REG_ERROR(dwError); } dwError = RegAllocateMemory(strlen(pszDstPath)+strlen(pszTmpSuffix)+2, (PVOID*)&pszTmpPath); BAIL_ON_REG_ERROR(dwError); strcpy(pszTmpPath, pszDstPath); strcat(pszTmpPath, pszTmpSuffix); if ((iFd = open(pszSrcPath, O_RDONLY, S_IRUSR)) < 0) { dwError = errno; BAIL_ON_REG_ERROR(dwError); } if ((oFd = open(pszTmpPath, O_WRONLY|O_TRUNC|O_CREAT, S_IRUSR|S_IWUSR)) < 0) { dwError = errno; BAIL_ON_REG_ERROR(dwError); } bRemoveFile = TRUE; while (1) { if ((dwBytesRead = read(iFd, szBuf, 1024)) < 0) { if (errno == EINTR) continue; dwError = errno; BAIL_ON_REG_ERROR(dwError); } if (dwBytesRead == 0) break; if (write(oFd, szBuf, dwBytesRead) != dwBytesRead) { if (errno == EINTR) continue; dwError = errno; BAIL_ON_REG_ERROR(dwError); } } close(iFd); iFd = -1; close(oFd); oFd = -1; dwError = RegMoveFile(pszTmpPath, pszDstPath); BAIL_ON_REG_ERROR(dwError); bRemoveFile = FALSE; dwError = RegChangePermissions(pszDstPath, dwPerms); BAIL_ON_REG_ERROR(dwError); error: if (iFd >= 0) close(iFd); if (oFd >= 0) close(oFd); if (bRemoveFile) { RegRemoveFile(pszTmpPath); } LWREG_SAFE_FREE_STRING(pszTmpPath); return dwError; } DWORD RegChangeOwnerAndPermissions( PCSTR pszPath, uid_t uid, gid_t gid, mode_t dwFileMode ) { DWORD dwError = 0; dwError = RegChangeOwner(pszPath, uid, gid); BAIL_ON_REG_ERROR(dwError); dwError = RegChangePermissions(pszPath, dwFileMode); BAIL_ON_REG_ERROR(dwError); error: return dwError; } DWORD RegCopyFileWithOriginalPerms( PCSTR pszSrcPath, PCSTR pszDstPath ) { DWORD dwError = 0; uid_t uid; gid_t gid; mode_t mode; dwError = RegGetOwnerAndPermissions(pszSrcPath, &uid, &gid, &mode); BAIL_ON_REG_ERROR(dwError); dwError = RegCopyFileWithPerms(pszSrcPath, pszDstPath, mode); BAIL_ON_REG_ERROR(dwError); dwError = RegChangeOwnerAndPermissions(pszDstPath, uid, gid, mode); BAIL_ON_REG_ERROR(dwError); error: return dwError; } DWORD RegBackupFile( PCSTR pszPath ) { DWORD dwError = 0; BOOLEAN bExists = FALSE; CHAR szBackupPath[PATH_MAX]; dwError = RegCheckFileExists(pszPath, &bExists); BAIL_ON_REG_ERROR(dwError); if (!bExists) { /* Do not need to backup, since the file does not yet exist. */ goto done; } sprintf(szBackupPath, "%s.likewise_lsass.orig", pszPath); dwError = RegCheckFileExists(szBackupPath, &bExists); BAIL_ON_REG_ERROR(dwError); if (bExists) { sprintf(szBackupPath, "%s.likewise_lsass.bak", pszPath); } dwError = RegCopyFileWithOriginalPerms(pszPath, szBackupPath); BAIL_ON_REG_ERROR(dwError); done: error: return dwError; } 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; } DWORD RegCreateSymlink( PCSTR pszOldPath, PCSTR pszNewPath ) { return ((symlink(pszOldPath, pszNewPath) < 0) ? errno : 0); } DWORD RegCopyDirectory( PCSTR pszSourceDirPath, uid_t ownerUid, gid_t ownerGid, PCSTR pszDestDirPath ) { DWORD dwError = 0; DIR* pDir = NULL; struct dirent* pDirEntry = NULL; struct stat statbuf = {0}; CHAR szSrcPath[PATH_MAX+1]; CHAR szDstPath[PATH_MAX+1]; PSTR pszTargetPath = NULL; if (NULL == (pDir = opendir(pszSourceDirPath))) { dwError = errno; BAIL_ON_REG_ERROR(dwError); } while (NULL != (pDirEntry = readdir(pDir))) { if (!strcmp(pDirEntry->d_name, ".") || !strcmp(pDirEntry->d_name, "..")|| !strcmp(pDirEntry->d_name, "lost+found")) continue; memset(&statbuf, 0, sizeof(statbuf)); sprintf(szSrcPath, "%s/%s", pszSourceDirPath, pDirEntry->d_name); if (lstat(szSrcPath, &statbuf) < 0) { dwError = errno; BAIL_ON_REG_ERROR(dwError); } sprintf(szDstPath, "%s/%s", pszDestDirPath, pDirEntry->d_name); if (S_ISDIR(statbuf.st_mode)) { dwError = RegCreateDirectory( szDstPath, statbuf.st_mode); BAIL_ON_REG_ERROR(dwError); dwError = RegChangeOwner( szDstPath, ownerUid, ownerGid); BAIL_ON_REG_ERROR(dwError); dwError = RegCopyDirectory( szSrcPath, ownerUid, ownerGid, szDstPath); BAIL_ON_REG_ERROR(dwError); } else if (S_ISREG(statbuf.st_mode)) { dwError = RegCopyFileWithOriginalPerms( szSrcPath, szDstPath); BAIL_ON_REG_ERROR(dwError); dwError = RegChangeOwner( szDstPath, ownerUid, ownerGid); BAIL_ON_REG_ERROR(dwError); } else if (S_ISLNK(statbuf.st_mode)) { dwError = RegGetSymlinkTarget( szSrcPath, &pszTargetPath); BAIL_ON_REG_ERROR(dwError); dwError = RegCreateSymlink( pszTargetPath, szDstPath); BAIL_ON_REG_ERROR(dwError); dwError = RegChangeOwner( szDstPath, ownerUid, ownerGid); BAIL_ON_REG_ERROR(dwError); } } cleanup: if (pDir) { closedir(pDir); } LWREG_SAFE_FREE_STRING(pszTargetPath); return dwError; error: goto cleanup; } DWORD RegGetMatchingFilePathsInFolder( PCSTR pszDirPath, PCSTR pszFileNameRegExp, PSTR** pppszHostFilePaths, PDWORD pdwNPaths ) { typedef struct __PATHNODE { PSTR pszPath; struct __PATHNODE *pNext; } PATHNODE, *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; } DWORD RegCheckLinkExists( PSTR pszPath, PBOOLEAN pbLinkExists ) { DWORD dwError = 0; BOOLEAN bExists = FALSE; struct stat statbuf; memset(&statbuf, 0, sizeof(struct stat)); while (1) { if (stat(pszPath, &statbuf) < 0) { if (errno == EINTR) { continue; } else if (errno == ENOENT || errno == ENOTDIR) { *pbLinkExists = 0; break; } dwError = errno; BAIL_ON_REG_ERROR(dwError); } else { if (((statbuf.st_mode & S_IFMT) == S_IFLNK)) { bExists = TRUE; } break; } } error: *pbLinkExists = bExists; return dwError; } DWORD RegCheckFileOrLinkExists( PSTR pszPath, PBOOLEAN pbExists ) { DWORD dwError = 0; BOOLEAN bExists = FALSE; struct stat statbuf; memset(&statbuf, 0, sizeof(struct stat)); while (1) { if (stat(pszPath, &statbuf) < 0) { if (errno == EINTR) { continue; } else if (errno == ENOENT || errno == ENOTDIR) { break; } dwError = errno; BAIL_ON_REG_ERROR(dwError); } else { if (((statbuf.st_mode & S_IFMT) == S_IFLNK) || ((statbuf.st_mode & S_IFMT) == S_IFREG)) { bExists = TRUE; } break; } } error: *pbExists = bExists; return dwError; } static DWORD RegCreateDirectoryRecursive( PSTR pszCurDirPath, PSTR pszTmpPath, PSTR *ppszTmp, DWORD dwFileMode, DWORD dwWorkingFileMode, int iPart ) { DWORD dwError = 0; PSTR pszDirPath = NULL; BOOLEAN bDirCreated = FALSE; BOOLEAN bDirExists = FALSE; CHAR szDelimiters[] = "/"; PSTR pszToken = strtok_r((iPart ? NULL : pszTmpPath), szDelimiters, ppszTmp); if (pszToken != NULL) { dwError = RegAllocateMemory(strlen(pszCurDirPath)+strlen(pszToken)+2, (PVOID*)&pszDirPath); BAIL_ON_REG_ERROR(dwError); sprintf(pszDirPath, "%s/%s", (!strcmp(pszCurDirPath, "/") ? "" : pszCurDirPath), pszToken); dwError = RegCheckDirectoryExists(pszDirPath, &bDirExists); BAIL_ON_REG_ERROR(dwError); if (!bDirExists) { if (mkdir(pszDirPath, dwWorkingFileMode) < 0) { dwError = errno; BAIL_ON_REG_ERROR(dwError); } bDirCreated = TRUE; } dwError = RegChangeDirectory(pszDirPath); BAIL_ON_REG_ERROR(dwError); dwError = RegCreateDirectoryRecursive( pszDirPath, pszTmpPath, ppszTmp, dwFileMode, dwWorkingFileMode, iPart+1 ); BAIL_ON_REG_ERROR(dwError); } if (bDirCreated && (dwFileMode != dwWorkingFileMode)) { dwError = RegChangePermissions(pszDirPath, dwFileMode); BAIL_ON_REG_ERROR(dwError); } if (pszDirPath) { RegMemoryFree(pszDirPath); } return dwError; error: if (bDirCreated) { RegRemoveDirectory(pszDirPath); } if (pszDirPath) { RegMemoryFree(pszDirPath); } return dwError; }