diff options
Diffstat (limited to '')
-rw-r--r-- | security/nss/cmd/modutil/install.c | 941 |
1 files changed, 941 insertions, 0 deletions
diff --git a/security/nss/cmd/modutil/install.c b/security/nss/cmd/modutil/install.c new file mode 100644 index 0000000000..91bcca928e --- /dev/null +++ b/security/nss/cmd/modutil/install.c @@ -0,0 +1,941 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "install.h" +#include "install-ds.h" +#include <prerror.h> +#include <prlock.h> +#include <prio.h> +#include <prmem.h> +#include <prprf.h> +#include <prsystem.h> +#include <prproces.h> + +#ifdef XP_UNIX +/* for chmod */ +#include <sys/types.h> +#include <sys/stat.h> +#endif + +/*extern "C" {*/ +#include <jar.h> +/*}*/ + +extern /*"C"*/ + int + Pk11Install_AddNewModule(char *moduleName, char *dllPath, + unsigned long defaultMechanismFlags, + unsigned long cipherEnableFlags); +extern /*"C"*/ + short + Pk11Install_UserVerifyJar(JAR *jar, PRFileDesc *out, + PRBool query); +extern /*"C"*/ + const char * + mySECU_ErrorString(PRErrorCode errnum); +extern int Pk11Install_yyparse(); + +#define INSTALL_METAINFO_TAG "Pkcs11_install_script" +#define SCRIPT_TEMP_FILE "pkcs11inst.tmp" +#define ROOT_MARKER "%root%" +#define TEMP_MARKER "%temp%" +#define PRINTF_ROOT_MARKER "%%root%%" +#define TEMPORARY_DIRECTORY_NAME "pk11inst.dir" +#define JAR_BASE_END (JAR_BASE + 100) + +static PRLock *errorHandlerLock = NULL; +static Pk11Install_ErrorHandler errorHandler = NULL; +static char *PR_Strdup(const char *str); +static int rm_dash_r(char *path); +static int make_dirs(char *path, int file_perms); +static int dir_perms(int perms); + +static Pk11Install_Error DoInstall(JAR *jar, const char *installDir, + const char *tempDir, Pk11Install_Platform *platform, + PRFileDesc *feedback, PRBool noverify); + +static char *errorString[] = { + "Operation was successful", /* PK11_INSTALL_NO_ERROR */ + "Directory \"%s\" does not exist", /* PK11_INSTALL_DIR_DOESNT_EXIST */ + "File \"%s\" does not exist", /* PK11_INSTALL_FILE_DOESNT_EXIST */ + "File \"%s\" is not readable", /* PK11_INSTALL_FILE_NOT_READABLE */ + "%s", /* PK11_INSTALL_ERROR_STRING */ + "Error in JAR file %s: %s", /* PK11_INSTALL_JAR_ERROR */ + "No Pkcs11_install_script specified in JAR metainfo file", + /* PK11_INSTALL_NO_INSTALLER_SCRIPT */ + "Could not delete temporary file \"%s\"", + /*PK11_INSTALL_DELETE_TEMP_FILE */ + "Could not open temporary file \"%s\"", /*PK11_INSTALL_OPEN_SCRIPT_FILE*/ + "%s: %s", /* PK11_INSTALL_SCRIPT_PARSE */ + "Error in script: %s", + "Unable to obtain system platform information", + "Installer script has no information about the current platform (%s)", + "Relative directory \"%s\" does not contain " PRINTF_ROOT_MARKER, + "Module File \"%s\" not found", + "Error occurred installing module \"%s\" into database", + "Error extracting \"%s\" from JAR file: %s", + "Directory \"%s\" is not writeable", + "Could not create directory \"%s\"", + "Could not remove directory \"%s\"", + "Unable to execute \"%s\"", + "Unable to wait for process \"%s\"", + "\"%s\" returned error code %d", + "User aborted operation", + "Unspecified error" +}; + +enum { + INSTALLED_FILE_MSG = 0, + INSTALLED_MODULE_MSG, + INSTALLER_SCRIPT_NAME, + MY_PLATFORM_IS, + USING_PLATFORM, + PARSED_INSTALL_SCRIPT, + EXEC_FILE_MSG, + EXEC_SUCCESS, + INSTALLATION_COMPLETE_MSG, + USER_ABORT +}; + +static char *msgStrings[] = { + "Installed file %s to %s\n", + "Installed module \"%s\" into module database\n", + "Using installer script \"%s\"\n", + "Current platform is %s\n", + "Using installation parameters for platform %s\n", + "Successfully parsed installation script\n", + "Executing \"%s\"...\n", + "\"%s\" executed successfully\n", + "\nInstallation completed successfully\n", + "\nAborting...\n" +}; + +/************************************************************************** + * S t r i n g N o d e + */ +typedef struct StringNode_str { + char *str; + struct StringNode_str *next; +} StringNode; + +StringNode * +StringNode_new() +{ + StringNode *new_this; + new_this = (StringNode *)PR_Malloc(sizeof(StringNode)); + PORT_Assert(new_this != NULL); + new_this->str = NULL; + new_this->next = NULL; + return new_this; +} + +void +StringNode_delete(StringNode *s) +{ + if (s->str) { + PR_Free(s->str); + s->str = NULL; + } +} + +/************************************************************************* + * S t r i n g L i s t + */ +typedef struct StringList_str { + StringNode *head; + StringNode *tail; +} StringList; + +void +StringList_new(StringList *list) +{ + list->head = NULL; + list->tail = NULL; +} + +void +StringList_delete(StringList *list) +{ + StringNode *tmp; + while (list->head) { + tmp = list->head; + list->head = list->head->next; + StringNode_delete(tmp); + } +} + +void +StringList_Append(StringList *list, char *str) +{ + if (!str) { + return; + } + + if (!list->tail) { + /* This is the first element */ + list->head = list->tail = StringNode_new(); + } else { + list->tail->next = StringNode_new(); + list->tail = list->tail->next; + } + + list->tail->str = PR_Strdup(str); + list->tail->next = NULL; /* just to be sure */ +} + +/************************************************************************** + * + * P k 1 1 I n s t a l l _ S e t E r r o r H a n d l e r + * + * Sets the error handler to be used by the library. Returns the current + * error handler function. + */ +Pk11Install_ErrorHandler +Pk11Install_SetErrorHandler(Pk11Install_ErrorHandler handler) +{ + Pk11Install_ErrorHandler old; + + if (!errorHandlerLock) { + errorHandlerLock = PR_NewLock(); + } + + PR_Lock(errorHandlerLock); + + old = errorHandler; + errorHandler = handler; + + PR_Unlock(errorHandlerLock); + + return old; +} + +/************************************************************************** + * + * P k 1 1 I n s t a l l _ I n i t + * + * Does initialization that otherwise would be done on the fly. Only + * needs to be called by multithreaded apps, before they make any calls + * to this library. + */ +void +Pk11Install_Init() +{ + if (!errorHandlerLock) { + errorHandlerLock = PR_NewLock(); + } +} + +/************************************************************************** + * + * P k 1 1 I n s t a l l _ R e l e a s e + * + * Releases static data structures used by the library. Don't use the + * library after calling this, unless you call Pk11Install_Init() + * first. This function doesn't have to be called at all unless you're + * really anal about freeing memory before your program exits. + */ +void +Pk11Install_Release() +{ + if (errorHandlerLock) { + PR_Free(errorHandlerLock); + errorHandlerLock = NULL; + } +} + +/************************************************************************* + * + * e r r o r + * + * Takes an error code and its arguments, creates the error string, + * and sends the string to the handler function if it exists. + */ + +#include <stdarg.h> + +static void +error(PRErrorCode errcode, ...) +{ + + va_list ap; + char *errstr; + Pk11Install_ErrorHandler handler; + + if (!errorHandlerLock) { + errorHandlerLock = PR_NewLock(); + } + + PR_Lock(errorHandlerLock); + + handler = errorHandler; + + PR_Unlock(errorHandlerLock); + + if (handler) { + va_start(ap, errcode); + errstr = PR_vsmprintf(errorString[errcode], ap); + handler(errstr); + PR_smprintf_free(errstr); + va_end(ap); + } +} + +/************************************************************************* + * + * j a r _ c a l l b a c k + */ +static int +jar_callback(int status, JAR *foo, const char *bar, char *pathname, + char *errortext) +{ + char *string; + + string = PR_smprintf("JAR error %d: %s in file %s\n", status, errortext, + pathname); + error(PK11_INSTALL_ERROR_STRING, string); + PR_smprintf_free(string); + return 0; +} + +/************************************************************************* + * + * P k 1 1 I n s t a l l _ D o I n s t a l l + * + * jarFile is the path of a JAR in the PKCS #11 module JAR format. + * installDir is the directory relative to which files will be + * installed. + */ +Pk11Install_Error +Pk11Install_DoInstall(char *jarFile, const char *installDir, + const char *tempDir, PRFileDesc *feedback, short force, PRBool noverify) +{ + JAR *jar; + char *installer; + unsigned long installer_len; + int status; + Pk11Install_Error ret; + PRBool made_temp_file; + Pk11Install_Info installInfo; + Pk11Install_Platform *platform; + char *errMsg; + char sysname[SYS_INFO_BUFFER_LENGTH], release[SYS_INFO_BUFFER_LENGTH], + arch[SYS_INFO_BUFFER_LENGTH]; + char *myPlatform; + + jar = NULL; + ret = PK11_INSTALL_UNSPECIFIED; + made_temp_file = PR_FALSE; + errMsg = NULL; + Pk11Install_Info_init(&installInfo); + + /* + printf("Inside DoInstall, jarFile=%s, installDir=%s, tempDir=%s\n", + jarFile, installDir, tempDir); + */ + + /* + * Check out jarFile and installDir for validity + */ + if (PR_Access(installDir, PR_ACCESS_EXISTS) != PR_SUCCESS) { + error(PK11_INSTALL_DIR_DOESNT_EXIST, installDir); + return PK11_INSTALL_DIR_DOESNT_EXIST; + } + if (!tempDir) { + tempDir = "."; + } + if (PR_Access(tempDir, PR_ACCESS_EXISTS) != PR_SUCCESS) { + error(PK11_INSTALL_DIR_DOESNT_EXIST, tempDir); + return PK11_INSTALL_DIR_DOESNT_EXIST; + } + if (PR_Access(tempDir, PR_ACCESS_WRITE_OK) != PR_SUCCESS) { + error(PK11_INSTALL_DIR_NOT_WRITEABLE, tempDir); + return PK11_INSTALL_DIR_NOT_WRITEABLE; + } + if ((PR_Access(jarFile, PR_ACCESS_EXISTS) != PR_SUCCESS)) { + error(PK11_INSTALL_FILE_DOESNT_EXIST, jarFile); + return PK11_INSTALL_FILE_DOESNT_EXIST; + } + if (PR_Access(jarFile, PR_ACCESS_READ_OK) != PR_SUCCESS) { + error(PK11_INSTALL_FILE_NOT_READABLE, jarFile); + return PK11_INSTALL_FILE_NOT_READABLE; + } + + /* + * Extract the JAR file + */ + jar = JAR_new(); + JAR_set_callback(JAR_CB_SIGNAL, jar, jar_callback); + + if (noverify) { + status = JAR_pass_archive_unverified(jar, jarArchGuess, jarFile, "url"); + } else { + status = JAR_pass_archive(jar, jarArchGuess, jarFile, "url"); + } + if ((status < 0) || (jar->valid < 0)) { + if (status >= JAR_BASE && status <= JAR_BASE_END) { + error(PK11_INSTALL_JAR_ERROR, jarFile, JAR_get_error(status)); + } else { + error(PK11_INSTALL_JAR_ERROR, jarFile, + mySECU_ErrorString(PORT_GetError())); + } + ret = PK11_INSTALL_JAR_ERROR; + goto loser; + } + /*printf("passed the archive\n");*/ + + /* + * Show the user security information, allow them to abort or continue + */ + if (Pk11Install_UserVerifyJar(jar, PR_STDOUT, + force ? PR_FALSE + : PR_TRUE) && + !force) { + if (feedback) { + PR_fprintf(feedback, msgStrings[USER_ABORT]); + } + ret = PK11_INSTALL_USER_ABORT; + goto loser; + } + + /* + * Get the name of the installation file + */ + if (JAR_get_metainfo(jar, NULL, INSTALL_METAINFO_TAG, (void **)&installer, + (unsigned long *)&installer_len)) { + error(PK11_INSTALL_NO_INSTALLER_SCRIPT); + ret = PK11_INSTALL_NO_INSTALLER_SCRIPT; + goto loser; + } + if (feedback) { + PR_fprintf(feedback, msgStrings[INSTALLER_SCRIPT_NAME], installer); + } + + /* + * Extract the installation file + */ + if (PR_Access(SCRIPT_TEMP_FILE, PR_ACCESS_EXISTS) == PR_SUCCESS) { + if (PR_Delete(SCRIPT_TEMP_FILE) != PR_SUCCESS) { + error(PK11_INSTALL_DELETE_TEMP_FILE, SCRIPT_TEMP_FILE); + ret = PK11_INSTALL_DELETE_TEMP_FILE; + goto loser; + } + } + if (noverify) { + status = JAR_extract(jar, installer, SCRIPT_TEMP_FILE); + } else { + status = JAR_verified_extract(jar, installer, SCRIPT_TEMP_FILE); + } + if (status) { + if (status >= JAR_BASE && status <= JAR_BASE_END) { + error(PK11_INSTALL_JAR_EXTRACT, installer, JAR_get_error(status)); + } else { + error(PK11_INSTALL_JAR_EXTRACT, installer, + mySECU_ErrorString(PORT_GetError())); + } + ret = PK11_INSTALL_JAR_EXTRACT; + goto loser; + } else { + made_temp_file = PR_TRUE; + } + + /* + * Parse the installation file into a syntax tree + */ + Pk11Install_FD = PR_Open(SCRIPT_TEMP_FILE, PR_RDONLY, 0); + if (!Pk11Install_FD) { + error(PK11_INSTALL_OPEN_SCRIPT_FILE, SCRIPT_TEMP_FILE); + ret = PK11_INSTALL_OPEN_SCRIPT_FILE; + goto loser; + } + if (Pk11Install_yyparse()) { + error(PK11_INSTALL_SCRIPT_PARSE, installer, + Pk11Install_yyerrstr ? Pk11Install_yyerrstr : ""); + ret = PK11_INSTALL_SCRIPT_PARSE; + goto loser; + } + +#if 0 + /* for debugging */ + Pk11Install_valueList->Print(0); +#endif + + /* + * From the syntax tree, build a semantic structure + */ + errMsg = Pk11Install_Info_Generate(&installInfo, Pk11Install_valueList); + if (errMsg) { + error(PK11_INSTALL_SEMANTIC, errMsg); + ret = PK11_INSTALL_SEMANTIC; + goto loser; + } +#if 0 + installInfo.Print(0); +#endif + + if (feedback) { + PR_fprintf(feedback, msgStrings[PARSED_INSTALL_SCRIPT]); + } + + /* + * Figure out which platform to use + */ + { + sysname[0] = release[0] = arch[0] = '\0'; + + if ((PR_GetSystemInfo(PR_SI_SYSNAME, sysname, SYS_INFO_BUFFER_LENGTH) != + PR_SUCCESS) || + (PR_GetSystemInfo(PR_SI_RELEASE, release, SYS_INFO_BUFFER_LENGTH) != + PR_SUCCESS) || + (PR_GetSystemInfo(PR_SI_ARCHITECTURE, arch, SYS_INFO_BUFFER_LENGTH) != + PR_SUCCESS)) { + error(PK11_INSTALL_SYSINFO); + ret = PK11_INSTALL_SYSINFO; + goto loser; + } + myPlatform = PR_smprintf("%s:%s:%s", sysname, release, arch); + platform = Pk11Install_Info_GetBestPlatform(&installInfo, myPlatform); + if (!platform) { + error(PK11_INSTALL_NO_PLATFORM, myPlatform); + PR_smprintf_free(myPlatform); + ret = PK11_INSTALL_NO_PLATFORM; + goto loser; + } + if (feedback) { + PR_fprintf(feedback, msgStrings[MY_PLATFORM_IS], myPlatform); + PR_fprintf(feedback, msgStrings[USING_PLATFORM], + Pk11Install_PlatformName_GetString(&platform->name)); + } + PR_smprintf_free(myPlatform); + } + + /* Run the install for that platform */ + ret = DoInstall(jar, installDir, tempDir, platform, feedback, noverify); + if (ret) { + goto loser; + } + + ret = PK11_INSTALL_SUCCESS; +loser: + if (Pk11Install_valueList) { + Pk11Install_ValueList_delete(Pk11Install_valueList); + Pk11Install_valueList = NULL; + } + if (jar) { + JAR_destroy(jar); + } + if (made_temp_file) { + PR_Delete(SCRIPT_TEMP_FILE); + } + if (errMsg) { + PR_smprintf_free(errMsg); + } + return ret; +} + +/* +///////////////////////////////////////////////////////////////////////// +// actually run the installation, copying files to and fro +*/ +static Pk11Install_Error +DoInstall(JAR *jar, const char *installDir, const char *tempDir, + Pk11Install_Platform *platform, PRFileDesc *feedback, PRBool noverify) +{ + Pk11Install_File *file; + Pk11Install_Error ret; + char *modDest; + char *cp; + int i; + int status; + char *tempname, *temp; + StringList executables; + StringNode *execNode; + PRProcessAttr *attr; + PRProcess *proc; + char *argv[2]; + char *envp[1]; + int errcode; + + ret = PK11_INSTALL_UNSPECIFIED; + modDest = NULL; + tempname = NULL; + + StringList_new(&executables); + /* + // Create Temporary directory + */ + tempname = PR_smprintf("%s/%s", tempDir, TEMPORARY_DIRECTORY_NAME); + if (PR_Access(tempname, PR_ACCESS_EXISTS) == PR_SUCCESS) { + /* Left over from previous run? Delete it. */ + rm_dash_r(tempname); + } + if (PR_MkDir(tempname, 0700) != PR_SUCCESS) { + error(PK11_INSTALL_CREATE_DIR, tempname); + ret = PK11_INSTALL_CREATE_DIR; + goto loser; + } + + /* + // Install all the files + */ + for (i = 0; i < platform->numFiles; i++) { + char *dest; + file = &platform->files[i]; + + if (file->relativePath) { + PRBool foundMarker = PR_FALSE; + char *reldir = PR_Strdup(file->relativePath); + + if (!reldir) { + error(PK11_INSTALL_UNSPECIFIED); + goto loser; + } + + /* Replace all the markers with the directories for which they stand */ + while (1) { + if ((cp = PL_strcasestr(reldir, ROOT_MARKER))) { + /* Has a %root% marker */ + *cp = '\0'; + temp = PR_smprintf("%s%s%s", reldir, installDir, + cp + strlen(ROOT_MARKER)); + PR_Free(reldir); + reldir = temp; + foundMarker = PR_TRUE; + } else if ((cp = PL_strcasestr(reldir, TEMP_MARKER))) { + /* Has a %temp% marker */ + *cp = '\0'; + temp = PR_smprintf("%s%s%s", reldir, tempname, + cp + strlen(TEMP_MARKER)); + PR_Free(reldir); + reldir = temp; + foundMarker = PR_TRUE; + } else { + break; + } + } + if (!foundMarker) { + /* Has no markers...this isn't really a relative directory */ + error(PK11_INSTALL_BOGUS_REL_DIR, file->relativePath); + ret = PK11_INSTALL_BOGUS_REL_DIR; + PR_Free(reldir); + goto loser; + } + dest = reldir; + } else if (file->absolutePath) { + dest = PR_Strdup(file->absolutePath); + } else { + error(PK11_INSTALL_UNSPECIFIED); + goto loser; + } + + /* Remember if this is the module file, we'll need to add it later */ + if (i == platform->modFile) { + modDest = PR_Strdup(dest); + } + + /* Remember is this is an executable, we'll need to run it later */ + if (file->executable) { + StringList_Append(&executables, dest); + /*executables.Append(dest);*/ + } + + /* Make sure the directory we are targetting exists */ + if (make_dirs(dest, file->permissions)) { + ret = PK11_INSTALL_CREATE_DIR; + goto loser; + } + + /* Actually extract the file onto the filesystem */ + if (noverify) { + status = JAR_extract(jar, (char *)file->jarPath, dest); + } else { + status = JAR_verified_extract(jar, (char *)file->jarPath, dest); + } + if (status) { + if (status >= JAR_BASE && status <= JAR_BASE_END) { + error(PK11_INSTALL_JAR_EXTRACT, file->jarPath, + JAR_get_error(status)); + } else { + error(PK11_INSTALL_JAR_EXTRACT, file->jarPath, + mySECU_ErrorString(PORT_GetError())); + } + ret = PK11_INSTALL_JAR_EXTRACT; + goto loser; + } + if (feedback) { + PR_fprintf(feedback, msgStrings[INSTALLED_FILE_MSG], + file->jarPath, dest); + } + +/* no NSPR command to change permissions? */ +#ifdef XP_UNIX + (void)chmod(dest, file->permissions); +#endif + + PR_Free(dest); + } + /* Make sure we found the module file */ + if (!modDest) { + /* Internal problem here, since every platform is supposed to have + a module file */ + error(PK11_INSTALL_NO_MOD_FILE, platform->moduleName); + ret = PK11_INSTALL_NO_MOD_FILE; + goto loser; + } + + /* + // Execute any executable files + */ + { + argv[1] = NULL; + envp[0] = NULL; + for (execNode = executables.head; execNode; execNode = execNode->next) { + attr = PR_NewProcessAttr(); + argv[0] = PR_Strdup(execNode->str); + + /* Announce our intentions */ + if (feedback) { + PR_fprintf(feedback, msgStrings[EXEC_FILE_MSG], execNode->str); + } + + /* start the process */ + if (!(proc = PR_CreateProcess(execNode->str, argv, envp, attr))) { + PR_Free(argv[0]); + PR_DestroyProcessAttr(attr); + error(PK11_INSTALL_EXEC_FILE, execNode->str); + ret = PK11_INSTALL_EXEC_FILE; + goto loser; + } + + /* wait for it to finish */ + if (PR_WaitProcess(proc, &errcode) != PR_SUCCESS) { + PR_Free(argv[0]); + PR_DestroyProcessAttr(attr); + error(PK11_INSTALL_WAIT_PROCESS, execNode->str); + ret = PK11_INSTALL_WAIT_PROCESS; + goto loser; + } + + /* What happened? */ + if (errcode) { + /* process returned an error */ + error(PK11_INSTALL_PROC_ERROR, execNode->str, errcode); + } else if (feedback) { + /* process ran successfully */ + PR_fprintf(feedback, msgStrings[EXEC_SUCCESS], execNode->str); + } + + PR_Free(argv[0]); + PR_DestroyProcessAttr(attr); + } + } + + /* + // Add the module + */ + status = Pk11Install_AddNewModule((char *)platform->moduleName, + (char *)modDest, platform->mechFlags, platform->cipherFlags); + + if (status != SECSuccess) { + error(PK11_INSTALL_ADD_MODULE, platform->moduleName); + ret = PK11_INSTALL_ADD_MODULE; + goto loser; + } + if (feedback) { + PR_fprintf(feedback, msgStrings[INSTALLED_MODULE_MSG], + platform->moduleName); + } + + if (feedback) { + PR_fprintf(feedback, msgStrings[INSTALLATION_COMPLETE_MSG]); + } + + ret = PK11_INSTALL_SUCCESS; + +loser: + if (modDest) { + PR_Free(modDest); + } + if (tempname) { + PRFileInfo info; + if (PR_GetFileInfo(tempname, &info) == PR_SUCCESS) { + if (info.type == PR_FILE_DIRECTORY) { + /* Recursively remove temporary directory */ + if (rm_dash_r(tempname)) { + error(PK11_INSTALL_REMOVE_DIR, + tempname); + ret = PK11_INSTALL_REMOVE_DIR; + } + } + } + PR_Free(tempname); + } + StringList_delete(&executables); + return ret; +} + +/* +////////////////////////////////////////////////////////////////////////// +*/ +static char * +PR_Strdup(const char *str) +{ + char *tmp = (char *)PR_Malloc(strlen(str) + 1); + strcpy(tmp, str); + return tmp; +} + +/* + * r m _ d a s h _ r + * + * Remove a file, or a directory recursively. + * + */ +static int +rm_dash_r(char *path) +{ + PRDir *dir; + PRDirEntry *entry; + PRFileInfo fileinfo; + char filename[240]; + + if (PR_GetFileInfo(path, &fileinfo) != PR_SUCCESS) { + /*fprintf(stderr, "Error: Unable to access %s\n", filename);*/ + return -1; + } + if (fileinfo.type == PR_FILE_DIRECTORY) { + + dir = PR_OpenDir(path); + if (!dir) { + return -1; + } + + /* Recursively delete all entries in the directory */ + while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH)) != NULL) { + snprintf(filename, sizeof(filename), "%s/%s", path, entry->name); + if (rm_dash_r(filename)) { + PR_CloseDir(dir); + return -1; + } + } + + if (PR_CloseDir(dir) != PR_SUCCESS) { + return -1; + } + + /* Delete the directory itself */ + if (PR_RmDir(path) != PR_SUCCESS) { + return -1; + } + } else { + if (PR_Delete(path) != PR_SUCCESS) { + return -1; + } + } + return 0; +} + +/*************************************************************************** + * + * m a k e _ d i r s + * + * Ensure that the directory portion of the path exists. This may require + * making the directory, and its parent, and its parent's parent, etc. + */ +static int +make_dirs(char *path, int file_perms) +{ + char *Path; + char *start; + char *sep; + int ret = 0; + PRFileInfo info; + + if (!path) { + return 0; + } + + Path = PR_Strdup(path); + start = strpbrk(Path, "/\\"); + if (!start) { + return 0; + } + start++; /* start right after first slash */ + + /* Each time through the loop add one more directory. */ + while ((sep = strpbrk(start, "/\\"))) { + *sep = '\0'; + + if (PR_GetFileInfo(Path, &info) != PR_SUCCESS) { + /* No such dir, we have to create it */ + if (PR_MkDir(Path, dir_perms(file_perms)) != PR_SUCCESS) { + error(PK11_INSTALL_CREATE_DIR, Path); + ret = PK11_INSTALL_CREATE_DIR; + goto loser; + } + } else { + /* something exists by this name, make sure it's a directory */ + if (info.type != PR_FILE_DIRECTORY) { + error(PK11_INSTALL_CREATE_DIR, Path); + ret = PK11_INSTALL_CREATE_DIR; + goto loser; + } + } + + /* If this is the lowest directory level, make sure it is writeable */ + if (!strpbrk(sep + 1, "/\\")) { + if (PR_Access(Path, PR_ACCESS_WRITE_OK) != PR_SUCCESS) { + error(PK11_INSTALL_DIR_NOT_WRITEABLE, Path); + ret = PK11_INSTALL_DIR_NOT_WRITEABLE; + goto loser; + } + } + + start = sep + 1; /* start after the next slash */ + *sep = '/'; + } + +loser: + PR_Free(Path); + return ret; +} + +/************************************************************************* + * d i r _ p e r m s + * + * Guesses the desired permissions on a directory based on the permissions + * of a file that will be stored in it. Give read, write, and + * execute to the owner (so we can create the file), read and + * execute to anyone who has read permissions on the file, and write + * to anyone who has write permissions on the file. + */ +static int +dir_perms(int perms) +{ + int ret = 0; + + /* owner */ + ret |= 0700; + + /* group */ + if (perms & 0040) { + /* read on the file -> read and execute on the directory */ + ret |= 0050; + } + if (perms & 0020) { + /* write on the file -> write on the directory */ + ret |= 0020; + } + + /* others */ + if (perms & 0004) { + /* read on the file -> read and execute on the directory */ + ret |= 0005; + } + if (perms & 0002) { + /* write on the file -> write on the directory */ + ret |= 0002; + } + + return ret; +} |