diff options
Diffstat (limited to 'nss/cmd/modutil/install.c')
-rw-r--r-- | nss/cmd/modutil/install.c | 961 |
1 files changed, 961 insertions, 0 deletions
diff --git a/nss/cmd/modutil/install.c b/nss/cmd/modutil/install.c new file mode 100644 index 0000000..bcc7c7e --- /dev/null +++ b/nss/cmd/modutil/install.c @@ -0,0 +1,961 @@ +/* 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. + */ + +#ifdef OSF1 +/* stdarg has already been pulled in from NSPR */ +#undef va_start +#undef va_end +#undef va_arg +#include <varargs.h> +#else +#include <stdarg.h> +#endif + +#ifdef OSF1 +static void +error(long va_alist, ...) +#else +static void +error(Pk11Install_Error errcode, ...) +#endif +{ + + va_list ap; + char *errstr; + Pk11Install_ErrorHandler handler; + + if(!errorHandlerLock) { + errorHandlerLock = PR_NewLock(); + } + + PR_Lock(errorHandlerLock); + + handler = errorHandler; + + PR_Unlock(errorHandlerLock); + + if(handler) { +#ifdef OSF1 + va_start(ap); + errstr = PR_vsmprintf(errorString[va_arg(ap, Pk11Install_Error)], ap); +#else + va_start(ap, errcode); + errstr = PR_vsmprintf(errorString[errcode], ap); +#endif + 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); + PR_Free(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 *reldir; + char *dest; + 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; + reldir=NULL; + dest=NULL; + 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++) { + file = &platform->files[i]; + + if(file->relativePath) { + PRBool foundMarker = PR_FALSE; + reldir = PR_Strdup(file->relativePath); + + /* 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; + goto loser; + } + dest = reldir; + reldir = NULL; + } else if(file->absolutePath) { + dest = PR_Strdup(file->absolutePath); + } + + /* 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 + chmod(dest, file->permissions); +#endif + + /* Memory clean-up tasks */ + if(reldir) { + PR_Free(reldir); + reldir = NULL; + } + if(dest) { + PR_Free(dest); + dest = NULL; + } + } + /* 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(reldir) { + PR_Free(reldir); + } + if(dest) { + PR_Free(dest); + } + 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) { + sprintf(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; +} |