RetroZilla/security/nss/cmd/modutil/install.c
roytam1 44b7f056d9 cherry-picked mozilla NSS upstream changes (to rev bad5fd065fa1, which is on par with 3.20):
bug1001332, 56b691c003ad, bug1086145, bug1054069, bug1155922, bug991783, bug1125025, bug1162521, bug1162644, bug1132941, bug1164364, bug1166205, bug1166163, bug1166515, bug1138554, bug1167046, bug1167043, bug1169451, bug1172128, bug1170322, bug102794, bug1128184, bug557830, bug1174648, bug1180244, bug1177784, bug1173413, bug1169174, bug1084669, bug951455, bug1183395, bug1177430, bug1183827, bug1160139, bug1154106, bug1142209, bug1185033, bug1193467, bug1182667(with sha512 changes backed out, which breaks VC6 compilation), bug1158489, bug337796
2018-07-14 21:22:32 +08:00

959 lines
23 KiB
C

/* 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)) 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;
}