summaryrefslogtreecommitdiff
path: root/usr/src/lib/libkmsagent/common/SYSCommon.c
diff options
context:
space:
mode:
authorWyllys Ingersoll <Wyllys.Ingersoll@Sun.COM>2010-06-28 16:04:11 -0700
committerWyllys Ingersoll <Wyllys.Ingersoll@Sun.COM>2010-06-28 16:04:11 -0700
commit4f14b0f29aa144cc03efdde5508ae126ae197acf (patch)
tree5292d99c7ec8374d412d58116e7bbc7279e72067 /usr/src/lib/libkmsagent/common/SYSCommon.c
parent1e49577a7fcde812700ded04431b49d67cc57d6d (diff)
downloadillumos-joyent-4f14b0f29aa144cc03efdde5508ae126ae197acf.tar.gz
PSARC 2010/195 PKCS11 KMS Provider
6944296 Solaris needs a PKCS#11 provider to allow access to KMS keystore functionality
Diffstat (limited to 'usr/src/lib/libkmsagent/common/SYSCommon.c')
-rw-r--r--usr/src/lib/libkmsagent/common/SYSCommon.c2307
1 files changed, 2307 insertions, 0 deletions
diff --git a/usr/src/lib/libkmsagent/common/SYSCommon.c b/usr/src/lib/libkmsagent/common/SYSCommon.c
new file mode 100644
index 0000000000..dd418fec4b
--- /dev/null
+++ b/usr/src/lib/libkmsagent/common/SYSCommon.c
@@ -0,0 +1,2307 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/*---------------------------------------------------------------------------
+ * Module: SYSCommon.c
+ *-------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include "SYSCommon.h"
+#include <time.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <signal.h>
+
+#ifndef WIN32
+#include <unistd.h>
+#endif
+
+#ifdef WIN32
+#include <io.h>
+#include <stdlib.h> /* for malloc, calloc, and free */
+#elif defined K_LINUX_PLATFORM
+#include <unistd.h> /* it includes usleep(us) */
+#include <sys/time.h>
+#include <fts.h>
+#else
+/*
+ * Directory traversal code is not yet available for Solaris.
+ * If such code will need to be written, then it will probably use ftw.h.
+ */
+#endif
+
+#ifdef K_SOLARIS_PLATFORM
+/* For K_AdjustLocalClock */
+#include <unistd.h>
+/* For K_SetRootPassword */
+#define __EXTENSIONS__ /* to expose flockfile and friends in stdio.h */
+#include <errno.h>
+#include <libgen.h>
+#include <malloc.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <stropts.h>
+#include <unistd.h>
+#include <termio.h>
+#include <security/pam_appl.h>
+#include <widec.h>
+#endif
+
+#ifdef K_LINUX_PLATFORM
+extern int pthread_mutexattr_settype __P ((pthread_mutexattr_t *__attr,
+ int __kind));
+#endif
+
+#ifdef K_HPUX_PLATFORM
+int64 atoll(const char *str)
+{
+ int64 tmp = 0;
+ sscanf(str, "%lld", &tmp);
+ return tmp;
+}
+
+#endif
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_CreateThread
+ *
+ * Description:
+ * Thread creation function "CreateThread" takes a thread function
+ * and its parameter to create a thread. It also has a Boolean
+ * parameter to indicate if the thread is detached or joinable.
+ * A new thread's handle is returned through the output parameter.
+ *
+ * Input
+ * -----
+ * i_pFunc Function pointer of the thread function
+ * i_pvData The point of the parameter passed to the thread function
+ * i_bIsDetached The thread is detached or not
+ * (Note: It is not supported on Win32)
+ *
+ * Output
+ * ------
+ * o_pNewThread The Thread handle
+ *
+ * Return value Error code
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_CreateThread(K_ThreadFunc i_pFunc,
+ void *i_pvData,
+ int i_bIsDetached,
+ K_THREAD_HANDLE *o_pNewThread)
+{
+ int iOK = K_SYS_OK;
+ int iReturn = 0;
+
+#ifdef WIN32
+
+ {
+ unsigned id;
+
+ *o_pNewThread = (HANDLE)_beginthreadex(NULL,
+ 0,
+ (int (_stdcall *) (void *vpData))i_pFunc,
+ i_pvData,
+ 0,
+ &id);
+
+
+ if(*o_pNewThread == 0)
+ {
+#ifdef SYS_DEBUG
+ printf(" (%s, %d): error creating pthread, error = %d\n",
+ __FILE__, __LINE__, iReturn);
+#endif
+ return K_SYS_ERR_CREATE_THREAD;
+ }
+
+ return K_SYS_OK;
+ }
+
+#else
+ pthread_attr_t attr;
+
+ iReturn = pthread_attr_init(&attr);
+
+ if ( iReturn == 0 )
+ {
+ iReturn = pthread_attr_setdetachstate(&attr, (i_bIsDetached) ?
+ PTHREAD_CREATE_DETACHED :
+ PTHREAD_CREATE_JOINABLE);
+ }
+
+#ifdef UNIX
+ if ( iReturn == 0 )
+ {
+ iReturn = pthread_attr_setstacksize(&attr, 1024*1024);
+ }
+#endif
+
+ if ( iReturn == 0 )
+ {
+ iReturn = pthread_create(o_pNewThread, &attr, (void *(*)(void *)) i_pFunc, i_pvData);
+ }
+
+ if ( iReturn == 0 )
+ {
+ iReturn = pthread_attr_destroy(&attr);
+ }
+
+ // TODO: Log error?
+ if ( iReturn )
+ {
+#ifdef SYS_DEBUG
+ printf(" (%s, %d): error creating pthread, error = %d\n",
+ __FILE__, __LINE__, iReturn);
+#endif
+
+ iOK = K_SYS_ERR_CREATE_THREAD;
+ }
+
+ return iOK;
+#endif
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_JoinThread
+ *
+ * Description:
+ * Thread joining function is called when the current thread
+ * waits another thread to terminate.
+ *
+ * Input
+ * -----
+ * i_hThread The thread handle of the to-be-joined thread
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value Error code
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_JoinThread(K_THREAD_HANDLE i_hThread)
+{
+ int iOK = K_SYS_OK;
+#ifdef WIN32
+
+ WaitForSingleObject(i_hThread, INFINITE);
+
+#else
+ {
+ int iReturn;
+ iReturn = pthread_join(i_hThread, NULL);
+
+ if ( iReturn )
+ {
+
+#ifdef SYS_DEBUG
+ printf(" (%s, %d): error creating pthread, error = %d\n",
+ __FILE__, __LINE__, iReturn);
+#endif
+ iOK = K_SYS_ERR_JOIN_THREAD;
+ }
+ }
+
+#endif
+ return iOK;
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_GetCurrentThreadId
+ *
+ * Description:
+ * Returns the thread ID of the current thread.
+ *
+ * Input
+ * -----
+ * (none)
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value The thread ID
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_GetCurrentThreadId()
+{
+#ifdef WIN32
+ return GetCurrentThreadId();
+#else
+ return pthread_self();
+#endif
+
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_CreateMutex
+ *
+ * Description:
+ * The mutex creation function creates a mutex according to the given
+ * mutex type, and returns the mutex handle to the output parameter.
+ *
+ * Input
+ * -----
+ * i_bIsRecursive Indication whether the mutex can be entered recursively
+ *
+ * Output
+ * ------
+ * o_phandle the handle pointer to the mutex
+ *
+ * Return value Error Code
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_CreateMutex( K_MUTEX_HANDLE *o_phandle )
+{
+ int iOK = K_SYS_OK;
+ BOOL bIsRecursive = 1; // this used to be an input -- but why do we want this to be optional?
+
+#ifdef WIN32
+ {
+ *o_phandle = (WIN32Mutex *)malloc(sizeof(WIN32Mutex));
+ if(*o_phandle == NULL)
+ {
+ return K_SYS_ERR_NO_MEMORY;
+ }
+ (*o_phandle)->m_bIsRecursive = bIsRecursive;
+ if(bIsRecursive)
+ {
+ InitializeCriticalSection(&((*o_phandle)->m_stCriticalSection));
+ }
+ else
+ {
+ (*o_phandle)->m_handle = CreateMutex(NULL, FALSE, NULL);
+ }
+
+ }
+#else
+ {
+ int iType;
+ pthread_mutexattr_t attr;
+
+ if ( pthread_mutexattr_init(&attr) )
+ {
+ return K_SYS_ERR_COND;
+ }
+
+ if(bIsRecursive)
+ {
+ iType =
+#ifdef K_LINUX_PLATFORM
+ PTHREAD_MUTEX_RECURSIVE_NP;
+#else
+ PTHREAD_MUTEX_RECURSIVE;
+#endif
+
+ if ( pthread_mutexattr_settype(&attr, iType) )
+ {
+ return K_SYS_ERR_COND;
+ }
+ }
+
+ *o_phandle = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
+ if(*o_phandle == NULL)
+ {
+ return K_SYS_ERR_NO_MEMORY;
+ }
+
+ if ( pthread_mutex_init(*o_phandle, &attr) )
+ {
+ return K_SYS_ERR_COND;
+ }
+
+ if ( pthread_mutexattr_destroy(&attr) )
+ {
+ return K_SYS_ERR_COND;
+ }
+ }
+#endif
+
+ return iOK;
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_LockMutex
+ *
+ * Description:
+ * K_LockMutex is used to lock the mutex, and K_UnlockMutex is
+ * used to unlock it.
+ *
+ * Input
+ * -----
+ * i_handle the mutex handle
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value Error Code
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_LockMutex(K_MUTEX_HANDLE i_handle)
+{
+ int iOK = K_SYS_OK;
+#ifdef WIN32
+
+ if(i_handle->m_bIsRecursive)
+ {
+ EnterCriticalSection(&(i_handle->m_stCriticalSection));
+ }
+ else
+ {
+ WaitForSingleObject(i_handle->m_handle, INFINITE);
+ }
+
+#else
+
+ if ( pthread_mutex_lock(i_handle) )
+ {
+ return K_SYS_ERR_COND;
+ }
+
+#endif
+ return iOK; // TODO: better error handling
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_UnlockMutex
+ *
+ * Description:
+ * K_UnlockMutex is used to unlock the lock.
+ *
+ * Input
+ * -----
+ * i_handle the mutex handle
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value Error Code
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_UnlockMutex(K_MUTEX_HANDLE i_handle)
+{
+ int iOK = K_SYS_OK;
+
+#ifdef WIN32
+ if(i_handle->m_bIsRecursive)
+ {
+ LeaveCriticalSection(&(i_handle->m_stCriticalSection));
+ }
+ else
+ {
+ ReleaseMutex(i_handle->m_handle);
+ }
+
+#else
+
+ if ( pthread_mutex_unlock(i_handle) )
+ {
+ return K_SYS_ERR_COND;
+ }
+#endif
+
+ return iOK; // TODO: better error handling
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_DestroyMutex
+ *
+ * Description:
+ * When a mutex is no longer needed, K_DestroyMutex must be called
+ * to destroy it.
+ *
+ * Input
+ * -----
+ * i_handle the mutex handle
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value Error Code
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_DestroyMutex(K_MUTEX_HANDLE i_handle)
+{
+
+ int iOK = K_SYS_OK;
+
+#ifdef WIN32
+
+ if(i_handle->m_bIsRecursive)
+ {
+ DeleteCriticalSection(&(i_handle->m_stCriticalSection));
+ }
+ else
+ {
+ CloseHandle(i_handle->m_handle);
+ }
+ free(i_handle);
+
+#else
+ pthread_mutex_destroy(i_handle);
+ free(i_handle);
+#endif
+ return iOK; // TODO: better error handling
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_InitConditionalVariable
+ *
+ * Description:
+ * This function initializes a conditional variable. Upon successful
+ * completion, the new condition variable is returned via the condition
+ * parameter, and 0 is returned. Otherwise, an error code is returned.
+ *
+ * Input
+ * -----
+ * i_pCond the pointer to the conditional variable which is to be
+ * initialized
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value Error Code
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_InitConditionalVariable (K_ConditionalVariable * i_pCond)
+{
+ int iOK = K_SYS_OK;
+#ifdef WIN32
+
+ i_pCond->m_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ i_pCond->m_hMutex = CreateMutex(NULL, FALSE, NULL);
+ i_pCond->m_iSignalAll = 0;
+ i_pCond->m_iNumWaiting = 0;
+
+#else
+
+ if ( pthread_cond_init(i_pCond, NULL) )
+ {
+ return K_SYS_ERR_COND;
+ }
+
+#endif
+
+ return iOK;
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_DestroyConditionalVariable
+ *
+ * Description:
+ * This function destroys a conditional variable. Upon successful
+ * completion, the condition variable is destroyed, and 0 is returned.
+ * Otherwise, an error code is returned.
+ * After deletion of the condition variable, the condition parameter
+ * is not valid until it is initialized again by a call to the
+ * K_InitConditionalVariable subroutine.
+ *
+ * Input
+ * -----
+ * i_pCond the pointer to the conditional variable which is to be
+ * destroyed
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value Error Code
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_DestroyConditionalVariable(K_ConditionalVariable * i_pCond)
+{
+ int iOK = K_SYS_OK;
+#ifdef WIN32
+ CloseHandle(i_pCond->m_hMutex);
+ CloseHandle(i_pCond->m_hEvent);
+#else
+
+ if ( pthread_cond_destroy(i_pCond) )
+ {
+ return K_SYS_ERR_COND;
+ }
+
+#endif
+ return iOK;
+
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_WaitConditionalVariable
+ *
+ * Description:
+ * This function is used to block on a condition variable.
+ * They are called with mutex locked by the calling thread or undefined
+ * behaviour will result.
+ *
+ * Input
+ * -----
+ * i_pCond the pointer to the conditional variable
+ * i_handle the companion mutex handle
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value Error Code
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_WaitConditionalVariable(K_ConditionalVariable * i_pCond,
+ K_MUTEX_HANDLE i_handle)
+{
+
+ int iOK = K_SYS_OK;
+#ifdef WIN32
+ DWORD res;
+
+ while (1)
+ {
+ iOK = WaitForSingleObject(i_pCond->m_hMutex, INFINITE);
+ if (iOK != WAIT_OBJECT_0)
+ {
+ return K_SYS_ERR_COND;
+ }
+ i_pCond->m_iNumWaiting++;
+ ReleaseMutex(i_pCond->m_hMutex);
+
+ K_UnlockMutex(i_handle);
+ res = WaitForSingleObject(i_pCond->m_hEvent, INFINITE);
+ i_pCond->m_iNumWaiting--;
+
+ if (res != WAIT_OBJECT_0)
+ {
+ ReleaseMutex(i_pCond->m_hMutex);
+ return K_SYS_ERR_COND;
+ }
+
+ if (i_pCond->m_iSignalAll)
+ {
+ if (i_pCond->m_iNumWaiting == 0)
+ {
+ ResetEvent(i_pCond->m_hEvent);
+ }
+ break;
+ }
+
+ if (i_pCond->m_iSignalled)
+ {
+ i_pCond->m_iSignalled = 0;
+ ResetEvent(i_pCond->m_hEvent);
+ break;
+ }
+ ReleaseMutex(i_pCond->m_hMutex);
+ }
+
+ K_LockMutex(i_handle);
+
+ return K_SYS_OK;
+#else
+
+ if ( pthread_cond_wait(i_pCond, i_handle) )
+ {
+ return K_SYS_ERR_COND;
+ }
+
+#endif
+ return iOK; // TODO: better error handling
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_SignalConditionalVariable
+ *
+ * Description:
+ * This function is used to restart one of the threads that are waiting on
+ * the condition variable. If no threads are waiting on it, nothing happens.
+ * If several threads are waiting on it, exactly one is restarted.
+ *
+ * Input
+ * -----
+ * i_pCond the pointer to the conditional variable
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value Error Code
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_SignalConditionalVariable(K_ConditionalVariable * i_pCond)
+{
+ int iOK = K_SYS_OK;
+#ifdef WIN32
+
+ int iReturn;
+
+ iReturn = WaitForSingleObject(i_pCond->m_hMutex, INFINITE);
+ if (iReturn != WAIT_OBJECT_0)
+ {
+ return K_SYS_ERR_COND;
+ }
+
+ i_pCond->m_iSignalled = 1;
+
+ iReturn = SetEvent(i_pCond->m_hEvent);
+ if (iReturn == 0)
+ {
+ iOK = K_SYS_ERR_COND;
+ }
+ ReleaseMutex(i_pCond->m_hMutex);
+
+ return iOK;
+#else
+
+ if ( pthread_cond_signal(i_pCond) )
+ {
+ return K_SYS_ERR_COND;
+ }
+
+#endif
+ return iOK;
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_BroadcastConditionalVariable
+ *
+ * Description:
+ * This function is used to restart all threads that are waiting on
+ * the condition variable.
+ *
+ * Input
+ * -----
+ * i_pCond the pointer to the conditional variable
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value Error Code
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_BroadcastConditionalVariable(K_ConditionalVariable * i_pCond)
+{
+
+ int iOK = K_SYS_OK;
+
+#ifdef WIN32
+
+ int iReturn;
+
+ iReturn = WaitForSingleObject(i_pCond->m_hMutex, INFINITE);
+ if (iReturn != WAIT_OBJECT_0)
+ {
+ return K_SYS_ERR_COND;
+ }
+ i_pCond->m_iSignalled = 1;
+ i_pCond->m_iSignalAll = 1;
+
+ iReturn = SetEvent(i_pCond->m_hEvent);
+
+ if (iReturn == 0)
+ {
+ iOK = K_SYS_ERR_COND;
+ }
+
+ ReleaseMutex(i_pCond->m_hMutex);
+
+ return iOK;
+
+#else
+
+ if ( pthread_cond_broadcast(i_pCond) )
+ {
+ return K_SYS_ERR_COND;
+ }
+
+#endif
+ return iOK;
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_Sleep
+ *
+ * Description:
+ * Sleep for a given period in given milliseconds.
+ *
+ * Input
+ * -----
+ * i_ms milliseconds
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value (none)
+ *
+ *--------------------------------------------------------------------------*/
+
+void K_Sleep(int i_ms)
+{
+#ifdef WIN32
+ Sleep(i_ms);
+#else
+ usleep(i_ms * 1000);
+#endif
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_GetTickCount
+ *
+ * Description:
+ * The K_GetTickCount function retrieves the number of
+ * milliseconds that have elapsed since the system was started.
+ *
+ * Input
+ * -----
+ * (none)
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value the elasped milliseconds since the system was started
+ *
+ *--------------------------------------------------------------------------*/
+
+unsigned int K_GetTickCount()
+{
+#ifdef WIN32
+ return (unsigned int)GetTickCount();
+#else
+ {
+ struct timeval tv;
+ gettimeofday( &tv, NULL );
+ /* this will rollover ~ every 49.7 days
+ dont surprise when it returns negative values, since we are only interested
+ in using sth like "tickCount2 - tickCount1" to get the time interval
+ */
+ return ( tv.tv_sec * 1000 ) + ( tv.tv_usec / 1000 );
+ }
+#endif
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_AdjustClock
+ *
+ * Description:
+ * The K_AdjustClock function immediately adjusts the system clock by
+ * the given number of seconds. A positive number adjusts the system
+ * clock forward; a negative number adjusts the system clock backward.
+ *
+ * Input
+ * -----
+ * i_iAdjustmentInSeconds Number of seconds by which to adjust the
+ * system clock
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value 1 if successful, 0 on error
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_AdjustClock( long i_iAdjustmentInSeconds )
+{
+#ifndef WIN32
+ struct timeval stDateTime;
+ if ( 0 != gettimeofday(&stDateTime, NULL) )
+ {
+ return FALSE;
+ }
+
+ stDateTime.tv_sec += i_iAdjustmentInSeconds;
+
+ if ( 0 != settimeofday(&stDateTime, NULL) )
+ {
+ return FALSE;
+ }
+#else
+ // TODO: implement for Windows
+ return FALSE;
+#endif
+
+ return TRUE;
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_IsLittleEndian
+ *
+ * Description:
+ * Checks to see whether this platform uses little endian integer
+ * representation.
+ *
+ * Input
+ * -----
+ * (none)
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value 1 for little endian
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_IsLittleEndian()
+{
+ short iWord = 0x4321;
+ return ((*(unsigned char*)&iWord) == 0x21);
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_FileLength32
+ *
+ * Description:
+ * Gets the size in bytes of the file associated with the given FILE pointer.
+ *
+ * Input
+ * -----
+ * i_fpFile File handle
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value File size in bytes, or -1L on error
+ *
+ *--------------------------------------------------------------------------*/
+
+long K_FileLength32( FILE* i_fpFile )
+{
+#ifdef WIN32
+ int iFileDescriptor = _fileno( i_fpFile );
+ struct _stat stStat;
+
+ if ( _fstat(iFileDescriptor, &stStat) != 0)
+ {
+ // error
+ return -1L;
+ }
+
+#else
+ int iFileDescriptor = fileno( i_fpFile );
+ struct stat stStat;
+
+ if ( fstat(iFileDescriptor, &stStat) != 0)
+ {
+ // error
+ return -1L;
+ }
+
+#endif
+
+ return stStat.st_size;
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_StringCompareNoCase
+ *
+ * Description:
+ * Compares the two given strings insensitive to case.
+ *
+ * Input
+ * -----
+ * i_sString1 First string
+ * i_sString2 Second string
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value 0 if identical, -1 if first string is less than second
+ * string, or 1 if first string is greater than second
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_StringCompareNoCase( const char* i_sString1, const char* i_sString2 )
+{
+#ifdef WIN32
+ return _stricmp( i_sString1, i_sString2 );
+#else
+ return strcasecmp( i_sString1, i_sString2 );
+#endif
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_StringCompareNoCaseWide
+ *
+ * Description:
+ * Compares the two given wide strings insensitive to case.
+ *
+ * Input
+ * -----
+ * i_wsString1 First wide string
+ * i_wsString2 Second wide string
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value 0 if identical, -1 if first string is less than second
+ * string, or 1 if first string is greater than second
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_StringCompareNoCaseWide( const wchar_t* i_wsString1, const wchar_t* i_wsString2 )
+{
+#ifdef WIN32
+ return _wcsicmp( i_wsString1, i_wsString2 );
+#elif defined K_SOLARIS_PLATFORM
+ return wscasecmp( i_wsString1, i_wsString2 );
+#else
+ return wcscasecmp( i_wsString1, i_wsString2 );
+#endif
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_CreateDirectory
+ *
+ * Description:
+ * Creates a directory with the given path name.
+ *
+ * Input
+ * -----
+ * i_sDirectoryName Directory name
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value 0 on success, -1 on failure
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_CreateDirectory( const char* i_sDirectoryName )
+{
+ // TODO: make this build all parent directories as well.
+
+#ifdef WIN32
+ if ( CreateDirectoryA( i_sDirectoryName, NULL ) )
+ {
+ return 0;
+ }
+ else
+ {
+ DWORD dwError = GetLastError();
+ return ( dwError == ERROR_ALREADY_EXISTS ) ? 0 : (dwError ? dwError : -1);
+ }
+#else
+ if ( mkdir( i_sDirectoryName, S_IRWXU ) == 0 )
+ {
+ return 0;
+ }
+ else
+ {
+ return ( errno == EEXIST ) ? 0 : (errno ? errno : -1);
+ }
+#endif
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_DeleteFile
+ *
+ * Description:
+ * Deletes the given file.
+ *
+ * Input
+ * -----
+ * i_sFilename Name of file to delete
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value 0 on success, errno on failure
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_DeleteFile( const char* i_sFilename )
+{
+ int bSuccess = 0;
+
+ bSuccess =
+#ifdef WIN32
+ _unlink(
+#else
+ unlink(
+#endif
+ i_sFilename ) == 0;
+
+ return bSuccess ? 0 : errno;
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_ReadFile
+ *
+ * Description:
+ * Reads from the given file and passes the bytes read back to the output
+ * parameter. The caller must deallocate o_ppFileData using free().
+ *
+ * Input
+ * -----
+ * i_sFilename Name of file from which to read
+ *
+ * Output
+ * ------
+ * o_ppFileData Pointer to bytes read
+ *
+ * Return value Number of bytes read on success, -1 on failure
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_ReadFile( const char* i_sFilename, unsigned char** o_ppFileData )
+{
+ FILE* pFile = 0;
+ long iFileSize = 0;
+
+ if ( !i_sFilename || (strlen(i_sFilename) <= 0) || !o_ppFileData )
+ {
+ return -1;
+ }
+
+ *o_ppFileData = 0;
+
+ // Open the file
+
+ pFile = fopen( i_sFilename, "rb" );
+ if ( !pFile )
+ {
+ return -1;
+ }
+
+ // Determine the file size
+
+ if ( fseek( pFile, 0, SEEK_END ) )
+ {
+ (void) fclose( pFile );
+ return -1;
+ }
+
+ iFileSize = ftell( pFile );
+ if ( iFileSize < 0 )
+ {
+ (void) fclose( pFile );
+ return -1;
+ }
+ else if ( iFileSize == 0 )
+ {
+ (void) fclose( pFile );
+ return 0;
+ }
+
+ if ( fseek( pFile, 0, SEEK_SET ) )
+ {
+ (void) fclose( pFile );
+ return -1;
+ }
+
+ *o_ppFileData = (unsigned char*)malloc( iFileSize );
+ if ( !*o_ppFileData )
+ {
+ // Out of memory.
+ (void) fclose( pFile );
+ return -1;
+ }
+
+ if ( iFileSize != (long)fread( *o_ppFileData, 1, iFileSize, pFile ) )
+ {
+ free( *o_ppFileData );
+ *o_ppFileData = 0;
+ (void) fclose( pFile );
+ return -1;
+ }
+
+ (void) fclose( pFile );
+
+ return iFileSize;
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_ReadFileString
+ *
+ * Description:
+ * Reads from the given file and passes the bytes read back to the output
+ * parameter, appending these bytes with a null terminator. There is no
+ * guarantee that there are no non-text characters in the returned "string".
+ * The caller must deallocate o_ppFileData using free().
+ *
+ * Input
+ * -----
+ * i_sFilename Name of file from which to read
+ *
+ * Output
+ * ------
+ * o_psFileDataString Pointer to bytes read
+ *
+ * Return value Number of bytes read (including null terminator) on
+ * success (0 if file is empty), -1 on failure
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_ReadFileString( const char* i_sFilename, char** o_psFileDataString )
+{
+ unsigned char* pFileData = 0;
+ int iFileSize = 0;
+
+ *o_psFileDataString = 0;
+
+ iFileSize = K_ReadFile( i_sFilename, &pFileData );
+
+ if ( iFileSize <= 0 )
+ {
+ return iFileSize;
+ }
+
+ *o_psFileDataString = (char*)malloc( iFileSize+1 );
+
+ if ( !*o_psFileDataString )
+ {
+ // Out of memory.
+ if ( pFileData )
+ {
+ free( pFileData );
+ }
+ return -1;
+ }
+
+ memcpy( *o_psFileDataString, pFileData, iFileSize );
+
+ (*o_psFileDataString)[iFileSize] = '\0';
+
+ if ( pFileData )
+ {
+ free( pFileData );
+ }
+
+ return iFileSize+1;
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_WriteFile
+ *
+ * Description:
+ * Writes the given bytes to the given file.
+ *
+ * Input
+ * -----
+ * i_sFilename Name of file to which to write
+ * i_pFileData Bytes to write
+ * i_iFileDataSize Number of bytes to write
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value 0 on success, errno or -1 (generic error) on failure
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_WriteFile( const char* i_sFilename, const unsigned char* i_pFileData, int i_iFileDataSize )
+{
+ FILE* pFile = 0;
+
+ if ( !i_sFilename || (strlen(i_sFilename) <= 0) || (!i_pFileData && (i_iFileDataSize > 0)) || (i_iFileDataSize < 0) )
+ {
+ return -1;
+ }
+
+ pFile = fopen( i_sFilename, "wb" );
+ if ( !pFile )
+ {
+ int iError = errno;
+ return (iError != 0) ? iError : -1;
+ }
+
+ if ( i_iFileDataSize > 0 )
+ {
+ if ( i_iFileDataSize != (int)fwrite( i_pFileData, 1, i_iFileDataSize, pFile ) )
+ {
+ int iError = ferror( pFile );
+ (void) fclose( pFile );
+ return (iError != 0) ? iError : -1;
+ }
+ }
+
+ (void) fclose( pFile );
+
+ return 0;
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_WriteFileString
+ *
+ * Description:
+ * Writes the given null-terminated bytes to the given file. The null
+ * terminator itself is not written to the file.
+ *
+ * Input
+ * -----
+ * i_sFilename Name of file to which to write
+ * i_sFileData Bytes to write
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value 0 on success, errno or -1 (generic error) on failure
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_WriteFileString( const char* i_sFilename, const char* i_sFileData )
+{
+ if ( !i_sFilename || (strlen(i_sFilename) <= 0) || !i_sFileData || (strlen(i_sFileData) <= 0) )
+ {
+ return -1;
+ }
+
+ return K_WriteFile( i_sFilename, (const unsigned char*)i_sFileData, strlen(i_sFileData) );
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_FileExists
+ *
+ * Description:
+ * Checks to see whehter the given file exists.
+ *
+ * Input
+ * -----
+ * i_sFilename Name of file to check
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value 1 if file exists, 0 if not, -1 on failure
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_FileExists( const char* i_sFilename )
+{
+ FILE* pFile = 0;
+
+ if ( !i_sFilename || (strlen(i_sFilename) <= 0) )
+ {
+ return -1;
+ }
+
+ pFile = fopen( i_sFilename, "r+" );
+
+ if ( !pFile )
+ {
+ if ( errno == ENOENT )
+ {
+ return 0;
+ }
+
+ return -1;
+ }
+
+ (void) fclose( pFile );
+
+ return 1;
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_CopyFile
+ *
+ * Description:
+ * Reads from the given source file and writes these bytes to the given
+ * destination file.
+ *
+ * Input
+ * -----
+ * i_sSrcFilename Name of file from which to read
+ * i_sDestFilename Name of file to which to write
+ *
+ * Output
+ * ------
+ * o_pbFileExists Non-zero if the destination file already exists
+ *
+ * Return value 0 on success, errno or -1 (generic error) on failure
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_CopyFile( const char* i_sSrcFilename, const char* i_sDestFilename, int* o_pbFileExists )
+{
+ unsigned char* pFileData = 0;
+ int iFileSize = 0;
+ int iError, iFileExists;
+
+ if ( !i_sSrcFilename || (strlen(i_sSrcFilename) <= 0)
+ || !i_sDestFilename || (strlen(i_sDestFilename) <= 0)
+ || !o_pbFileExists )
+ {
+ return -1;
+ }
+
+ *o_pbFileExists = 0;
+
+ iFileExists = K_FileExists( i_sDestFilename );
+
+ if ( iFileExists < 0 )
+ {
+ iError = errno;
+ return (iError == 0) ? -1 : iError;
+ }
+ else if ( iFileExists > 0 )
+ {
+ *o_pbFileExists = 1;
+ return -1;
+ }
+
+ iFileSize = K_ReadFile( i_sSrcFilename, &pFileData );
+ if ( iFileSize < 0 )
+ {
+ iError = errno;
+ return (iError == 0) ? -1 : iError;
+ }
+
+ iError = K_WriteFile( i_sDestFilename, pFileData, iFileSize );
+
+ if ( pFileData )
+ {
+ free( pFileData );
+ }
+
+ return iError;
+}
+
+
+#ifdef K_LINUX_PLATFORM
+static int fts_compare( const FTSENT** i_ppF1, const FTSENT** i_ppF2 )
+{
+ return strcmp( (*i_ppF1)->fts_name, (*i_ppF2)->fts_name );
+}
+#else
+/*
+ * Directory traversal code is not yet available for Solaris.
+ * If such code will need to be written, then it will probably use ftw.h.
+ */
+#endif
+
+
+/*
+ * TODO: Set up functions for platform-specific find-file operations to
+ * help clean up the code below.
+ */
+
+typedef struct K_FindInfo
+{
+#ifdef WIN32
+ struct _finddata_t m_stFindData;
+ long m_hFile;
+#elif defined K_LINUX_PLATFORM
+ FTS* m_pFTS;
+ FTSENT* m_pFTSENT;
+#else
+/*
+ * Directory traversal code is not yet available for Solaris.
+ * If such code will need to be written, then it will probably use ftw.h.
+ */
+ int unused;
+#endif
+} K_FindInfo;
+
+// Memory for filename is held in i_pFindInfo.
+const char* K_GetFilenameFromInfo( const K_FindInfo* i_pFindInfo )
+{
+ if( !i_pFindInfo )
+ {
+ return 0;
+ }
+
+#ifdef WIN32
+ return i_pFindInfo->m_stFindData.name;
+#elif defined K_LINUX_PLATFORM
+ return i_pFindInfo->m_pFTSENT->fts_name;
+#else
+/*
+ * Directory traversal code is not yet available for Solaris.
+ * If such code will need to be written, then it will probably use ftw.h.
+ */
+ FATAL_ASSERT( 0 );
+ return 0;
+#endif
+}
+
+// Forward declarations
+int K_FindFileNext( K_FindInfo* io_pFindInfo );
+void K_FindFileClose( K_FindInfo* io_pFindInfo );
+
+// Returns 0 if successful, 1 if not found, -1 if error.
+// If not error, K_FindFileClose must be called.
+// o_pFindInfo must not be null.
+int K_FindFileFirst( const char* i_sDirectoryName, K_FindInfo* o_pFindInfo )
+{
+#ifdef WIN32
+ char* sSearchString = 0;
+ int iSearchStringIndex = 0;
+#endif
+
+ if ( !i_sDirectoryName || (strlen(i_sDirectoryName) <= 0) || !o_pFindInfo )
+ {
+ return -1;
+ }
+
+#ifdef WIN32
+ memset( o_pFindInfo, 0, sizeof(K_FindInfo) );
+
+ iSearchStringIndex = strlen(i_sDirectoryName);
+ if ( i_sDirectoryName[iSearchStringIndex-1] == PATH_SEPARATOR )
+ {
+ iSearchStringIndex += 2;
+ }
+ else
+ {
+ iSearchStringIndex += 3;
+ }
+
+ sSearchString = (char*)calloc( iSearchStringIndex, 1 );
+ if ( !sSearchString )
+ {
+ return -1;
+ }
+
+ strcpy( sSearchString, i_sDirectoryName );
+ iSearchStringIndex--;
+ sSearchString[iSearchStringIndex] = '\0';
+ iSearchStringIndex--;
+ sSearchString[iSearchStringIndex] = '*';
+ iSearchStringIndex--;
+ sSearchString[iSearchStringIndex] = PATH_SEPARATOR;
+
+ o_pFindInfo->m_hFile = _findfirst( sSearchString, &o_pFindInfo->m_stFindData );
+ free( sSearchString );
+ if ( o_pFindInfo->m_hFile == -1 )
+ {
+ if ( errno == ENOENT )
+ {
+ return 1;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+#elif defined K_LINUX_PLATFORM
+ memset( o_pFindInfo, 0, sizeof(K_FindInfo) );
+
+ o_pFindInfo->m_pFTS = fts_open( aPath, FTS_PHYSICAL | FTS_NOSTAT, fts_compare );
+ if ( !o_pFindInfo->m_pFTS )
+ {
+ return -1;
+ }
+
+ o_pFindInfo->m_pFTSENT = fts_read( o_pFindInfo->m_pFTS );
+ if ( !o_pFindInfo->m_pFTSENT )
+ {
+ if ( errno == 0 )
+ {
+ return 1;
+ }
+ else
+ {
+ fts_close( o_pFindInfo->m_pFTS );
+ return -1;
+ }
+ }
+#else
+/*
+ * Directory traversal code is not yet available for Solaris.
+ * If such code will need to be written, then it will probably use ftw.h.
+ */
+#endif
+
+ // If what we found is not actually a file, get the next hit.
+#ifdef WIN32
+ if ( (o_pFindInfo->m_stFindData.attrib & _A_SUBDIR) )
+#elif defined K_LINUX_PLATFORM
+ if ( !(o_pFindInfo->m_pFTSENT->fts_info & FTS_F) )
+#else
+/*
+ * Directory traversal code is not yet available for Solaris.
+ * If such code will need to be written, then it will probably use ftw.h.
+ */
+#endif
+ {
+ int iNextReturn = K_FindFileNext( o_pFindInfo );
+ if ( iNextReturn < 0 )
+ {
+ K_FindFileClose( o_pFindInfo );
+ return -1;
+ }
+ else
+ {
+ return iNextReturn;
+ }
+ }
+
+#if defined(WIN32) || defined(K_LINUX_PLATFORM)
+ return 0;
+#endif
+}
+
+// Returns 0 if successful, 1 if not found, -1 if error.
+int K_FindFileNext( K_FindInfo* io_pFindInfo )
+{
+ if ( !io_pFindInfo )
+ {
+ return -1;
+ }
+
+#ifdef WIN32
+ if ( _findnext( io_pFindInfo->m_hFile, &io_pFindInfo->m_stFindData ) != 0 )
+ {
+ return (errno == ENOENT) ? 1 : -1;
+ }
+#elif defined K_LINUX_PLATFORM
+ io_pFindInfo->m_pFTSENT = fts_read( io_pFindInfo->m_pFTS );
+ if ( !io_pFindInfo->m_pFTSENT )
+ {
+ return (errno == 0) ? 1 : -1;
+ }
+#else
+/*
+ * Directory traversal code is not yet available for Solaris.
+ * If such code will need to be written, then it will probably use ftw.h.
+ */
+#endif
+
+ // If what we found is not actually a file, get the next hit.
+#ifdef WIN32
+ if ( (io_pFindInfo->m_stFindData.attrib & _A_SUBDIR) )
+#elif defined K_LINUX_PLATFORM
+ if ( !(io_pFindInfo->m_pFTSENT->fts_info & FTS_F) )
+#else
+/*
+ * Directory traversal code is not yet available for Solaris.
+ * If such code will need to be written, then it will probably use ftw.h.
+ */
+#endif
+ {
+ return K_FindFileNext( io_pFindInfo );
+ }
+
+#if defined(WIN32) || defined(K_LINUX_PLATFORM)
+ return 0;
+#endif
+}
+
+void K_FindFileClose( K_FindInfo* io_pFindInfo )
+{
+ if ( !io_pFindInfo )
+ {
+ return;
+ }
+
+#ifdef WIN32
+ _findclose( io_pFindInfo->m_hFile );
+#elif defined K_LINUX_PLATFORM
+ fts_close( io_pFindInfo->m_pFTS );
+#else
+/*
+ * Directory traversal code is not yet available for Solaris.
+ * If such code will need to be written, then it will probably use ftw.h.
+ */
+#endif
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_GetFilenamesInDirectoryCount
+ *
+ * Description:
+ * Reads the given directory and returns the number of files that it contains.
+ *
+ * Input
+ * -----
+ * i_sDirectoryName Name of directory
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value Number of files on success, -1 on failure
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_GetFilenamesInDirectoryCount( const char* i_sDirectoryName )
+{
+ K_FindInfo stFindInfo;
+ int iCurrentFile = 0;
+ int iError = 0;
+
+ if ( !i_sDirectoryName || (strlen(i_sDirectoryName) <= 0) )
+ {
+ return -1;
+ }
+
+ iError = K_FindFileFirst( i_sDirectoryName, &stFindInfo );
+ if ( iError < 0 )
+ {
+ // error
+ return -1;
+ }
+ else if ( iError > 0 )
+ {
+ // no files found
+ K_FindFileClose( &stFindInfo );
+ return 0;
+ }
+
+ while ( 1 )
+ {
+ iCurrentFile++;
+
+ iError = K_FindFileNext( &stFindInfo );
+ if ( iError < 0 )
+ {
+ // error
+ K_FindFileClose( &stFindInfo );
+ return -1;
+ }
+ else if ( iError > 0 )
+ {
+ // no more files found
+ break;
+ }
+ }
+
+ K_FindFileClose( &stFindInfo );
+
+ return iCurrentFile;
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_GetFilenamesInDirectory
+ *
+ * Description:
+ * Reads the given directory and returns an array of names of files that it
+ * contains. A null pointer appears at the last item in the array. The
+ * caller must deallocate o_pasFilenames by using K_FreeFilenames or by
+ * calling free() for each file name and then calling free() on the array
+ * itself.
+ *
+ * Input
+ * -----
+ * i_sDirectoryName Name of directory
+ *
+ * Output
+ * ------
+ * o_pasFilenames Array of names of files found in this directory
+ *
+ * Return value Number of files on success, -1 on failure
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_GetFilenamesInDirectory(
+ const char* i_sDirectoryName,
+ char*** o_pasFilenames )
+{
+ // Note that we iterate through the filenames twice -- once to get the count
+ // (K_GetFilenamesInDirectoryCount) and then once to get all the names. But
+ // it may happen that the count changes between these calls. So we'll retrieve
+ // at most the number of files that's returned in the first pass.
+
+ K_FindInfo stFindInfo;
+ int iFilenameCount = 0, iCurrentFile = 0;
+ int iError = 0;
+
+ if ( !i_sDirectoryName || (strlen(i_sDirectoryName) <= 0) || !o_pasFilenames )
+ {
+ return -1;
+ }
+
+ *o_pasFilenames = 0;
+
+ iFilenameCount = K_GetFilenamesInDirectoryCount( i_sDirectoryName );
+
+ if ( iFilenameCount < 0 )
+ {
+ return -1;
+ }
+
+ iError = K_FindFileFirst( i_sDirectoryName, &stFindInfo );
+ if ( iError < 0 )
+ {
+ // error
+ return -1;
+ }
+ else if ( iError > 0 )
+ {
+ // No files found
+ K_FindFileClose( &stFindInfo );
+ return 0;
+ }
+
+ *o_pasFilenames = (char**)calloc( (iFilenameCount+1), sizeof(char*) ); // +1 for the null last one
+ if ( !*o_pasFilenames )
+ {
+ // Out of memory
+ K_FindFileClose( &stFindInfo );
+ return -1;
+ }
+
+ while ( 1 )
+ {
+ const char* sFilename = K_GetFilenameFromInfo( &stFindInfo );
+
+ size_t iFilenameLength = sFilename ? strlen( sFilename ) : 0;
+
+ if ( iFilenameLength <= 0 )
+ {
+ K_FreeFilenames( *o_pasFilenames );
+ K_FindFileClose( &stFindInfo );
+ return -1;
+ }
+
+ (*o_pasFilenames)[iCurrentFile] = (char*)calloc( (iFilenameLength+1), sizeof(char) );
+ if ( !(*o_pasFilenames)[iCurrentFile] )
+ {
+ K_FreeFilenames( *o_pasFilenames );
+ K_FindFileClose( &stFindInfo );
+ return -1;
+ }
+
+ strncpy( (*o_pasFilenames)[iCurrentFile], sFilename, iFilenameLength );
+ (*o_pasFilenames)[iCurrentFile][iFilenameLength] = '\0';
+
+ iCurrentFile++;
+
+ if ( iCurrentFile >= iFilenameCount )
+ {
+ break;
+ }
+
+ iError = K_FindFileNext( &stFindInfo );
+ if ( iError < 0 )
+ {
+ // error
+ K_FindFileClose( &stFindInfo );
+ return -1;
+ }
+ else if ( iError > 0 )
+ {
+ // no more files found
+ break;
+ }
+ }
+
+ K_FindFileClose( &stFindInfo );
+
+ return iCurrentFile;
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_FreeFilenames
+ *
+ * Description:
+ * Deallocates the memory allocated in a successful call to
+ * K_GetFilenamesInDirectory.
+ *
+ * Input
+ * -----
+ * i_asFilenames Array of names of files
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value (none)
+ *
+ *--------------------------------------------------------------------------*/
+
+void K_FreeFilenames( char** i_asFilenames )
+{
+ int i;
+
+ if ( !i_asFilenames )
+ {
+ return;
+ }
+
+ for ( i = 0; (i_asFilenames[i] != 0); i++ )
+ {
+ free( i_asFilenames[i] );
+ i_asFilenames[i] = 0;
+ }
+
+ free( i_asFilenames );
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_AdjustLocalClock
+ *
+ * Description:
+ * The K_AdjustLocalClock function gradually adjusts the system clock by
+ * the given number of seconds. A positive number adjusts the system
+ * clock forward; a negative number adjusts the system clock backward.
+ *
+ * Input
+ * -----
+ * i_iAdjustmentInSeconds Number of seconds by which to adjust the
+ * system clock
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value 1 if successful, 0 on error
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_AdjustLocalClock( int i_iNumberOfSeconds )
+{
+ struct timeval delta, lastchange;
+
+#ifndef K_SOLARIS_PLATFORM
+ /* Only supported/tested on Solaris at the moment */
+
+ return -1;
+#else
+ /* WARNING: uses standard C time functions with Year 2038 limitations */
+ time_t now;
+
+ if ( (now = time(NULL)) == ((time_t)-1) )
+ {
+ return -1;
+ }
+
+ delta.tv_sec = i_iNumberOfSeconds;
+ delta.tv_usec = 0;
+
+ return adjtime(&delta, &lastchange);
+#endif
+}
+
+
+#ifdef K_SOLARIS_PLATFORM
+static int pam_tty_conv(
+ int num_msg,
+ struct pam_message** mess,
+ struct pam_response** resp,
+ void* my_data)
+{
+ // Following code implements a console-based PAM "conversation" function
+ // (based sample code from Solaris 10 Software Developer Collection >>
+ // Solaris Security for Developers Guide >>
+ // 3. Writing PAM Applications and Services)
+
+ struct pam_message* m = *mess;
+ struct pam_response* r;
+ int i, j;
+ const char* sPassword = (const char*)my_data;
+ int error = PAM_CONV_ERR;
+
+ if (num_msg <= 0 || num_msg >= PAM_MAX_NUM_MSG)
+ {
+ (void) fprintf(stderr, "PAM error: bad number of messages");
+ *resp = NULL;
+ return (PAM_CONV_ERR);
+ }
+
+ if ((*resp = r = calloc(num_msg, sizeof (struct pam_response))) == NULL)
+ {
+ return (PAM_BUF_ERR);
+ }
+
+ // Loop through messages
+ for (i = 0; i < num_msg; i++) {
+
+ // bad message from service module
+ if (m->msg == NULL)
+ {
+ (void) fprintf(stderr, "PAM error: bad message");
+ goto err;
+ }
+
+ // fix up final newline: removed for prompts, added back for messages
+ if (m->msg[strlen(m->msg)] == '\n')
+ {
+ m->msg[strlen(m->msg)] = '\0';
+ }
+
+ // Since the KMA has its own password prompts and enforces its own rule checks, we already have the
+ // new password in memory. So instead of displaying PAM prompts and collecting user responses, we
+ // "automate" by assuming that the prompts correspond to the standard sequence of "New password:"
+ // followed by "Confirm password:" and so in each case we immediately return the password we already
+ // have in memory. This violates the PAM "conversation" function instructions (which say, basically,
+ // not to assume any particular sequence of prompts since there could be any number of underlying
+ // password managers), but since the KMA is running on an appliance with a fixed password manager,
+ // our assumptions should hold.
+
+ r->resp = NULL;
+ r->resp_retcode = 0;
+ switch (m->msg_style)
+ {
+ case PAM_PROMPT_ECHO_OFF:
+ case PAM_PROMPT_ECHO_ON:
+ // Assume the prompt asked for New/Confirm password, so return password.
+ if ( (r->resp = strdup(sPassword)) == NULL )
+ {
+ error = PAM_BUF_ERR;
+ goto err;
+ }
+ break;
+
+ case PAM_ERROR_MSG:
+ // Assuming the system is configured properly and the KMA password prompts enforce password strength rules,
+ // there should not be errors because of weak passwords, etc. Still, print errors so users/support can
+ // diagnose problems.
+ (void) fputs(m->msg, stderr);
+ (void) fputc('\n', stderr);
+ break;
+
+ case PAM_TEXT_INFO:
+ // Supress prompts (again, making assumptions).
+ break;
+
+ default:
+ (void) fprintf(stderr, "PAM error: unknown message");
+ goto err;
+ }
+ if (errno == EINTR)
+ {
+ goto err;
+ }
+
+ // next message/response
+ m++;
+ r++;
+ }
+ return (PAM_SUCCESS);
+
+err:
+ // Service modules do not clean up responses if an error is returned.
+ // Free responses here.
+ for (j = 0; j < i; j++, r++)
+ {
+ if (r->resp)
+ {
+ // clear before freeing -- may be a password
+ bzero(r->resp, strlen(r->resp));
+ free(r->resp);
+ r->resp = NULL;
+ }
+ }
+ free(r);
+ *resp = NULL;
+ return error;
+}
+#endif
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_SetRootPassword
+ *
+ * Description:
+ * The K_SetRootPassword function sets the password for the root user via
+ * Pluggable Authentication Module (PAM). This function is interactive.
+ *
+ * Input
+ * -----
+ * i_sPassword Password to set
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value 0 if successful, -1 on error
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_SetRootPassword( const char* i_sPassword )
+{
+ // Only supported/tested on Solaris at the moment
+#ifndef K_SOLARIS_PLATFORM
+ return -1;
+#else
+ // Based on sample code from Solaris 10 Software Developer Collection >>
+ // Solaris Security for Developers Guide >>
+ // 3. Writing PAM Applications and Services
+
+ // TODO: Return PAM error codes (to be logged) instead of emitting
+ // messages to screen?
+
+ struct pam_conv conv;
+ pam_handle_t *pamh;
+ int err;
+
+ conv.conv = pam_tty_conv;
+ conv.appdata_ptr = (void*)i_sPassword;
+
+ // Initialize PAM framework
+ err = pam_start("KeyMgr", "root", &conv, &pamh);
+ if (err != PAM_SUCCESS)
+ {
+ fprintf(stderr, "PAM error: %s\n", pam_strerror(pamh, err));
+ return -1;
+ }
+
+ // Change password
+ err = pam_chauthtok(pamh, 0);
+ if (err != PAM_SUCCESS)
+ {
+ fprintf(stderr, "PAM error: %s\n", pam_strerror(pamh, err));
+ // fall through to cleanup
+ }
+
+ // Cleanup session
+ pam_end(pamh, 0);
+
+ return (err == PAM_SUCCESS) ? 0 : -1;
+#endif
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_Alarm
+ *
+ * Description:
+ * Calls alarm(2) on Unix in order to cause the operating system to generate
+ * a SIGALRM signal for this process after the given number of real-time
+ * seconds. Does nothing on Windows.
+ *
+ * Input
+ * -----
+ * i_iSeconds Number of seconds after which to generate a SIGALRM
+ * signal
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value If a previous alarm request is pending, then it returns
+ * the number of seconds until this previous request would
+ * have generated a SIGALRM signal. Otherwise, returns 0.
+ *
+ *--------------------------------------------------------------------------*/
+
+unsigned int K_Alarm( unsigned int i_iSeconds )
+{
+#ifndef WIN32
+ return alarm( i_iSeconds );
+#else
+ return 0;
+#endif
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_GetExtendedVersionFromBase
+ *
+ * Description:
+ * This KMS-specific function prepends the timestamp value to the specified
+ * base replication schema version and returns this value as an extended
+ * replication schema version.
+ *
+ * Input
+ * -----
+ * i_iBaseSchemaVersion Base replication schema version
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value Extended replication schema version
+ *
+ *--------------------------------------------------------------------------*/
+
+unsigned int K_GetExtendedVersionFromBase( unsigned int i_iBaseSchemaVersion )
+{
+ // seconds since 1970, force to 32-bit
+#ifdef WIN32
+ INT32 iTimeStamp = (INT32) time(NULL);
+#else
+ int32_t iTimeStamp = (int32_t) time(NULL);
+#endif
+ // minutes since 1970
+ iTimeStamp = iTimeStamp / 60;
+ // minutes since 2000 (approximately)
+ iTimeStamp -= (30*365*24*60);
+ // shift 8 bits to clear out room for schema version #
+ iTimeStamp = iTimeStamp << 8;
+ // add schema version # to lower end
+ iTimeStamp |= i_iBaseSchemaVersion;
+
+ return (unsigned int) iTimeStamp;
+
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_ParseTimestampFromExtendedVersion
+ *
+ * Description:
+ * This KMS-specific function parses the timestamp value from the given
+ * extended replication schema version and returns this timestamp value.
+ *
+ * Input
+ * -----
+ * i_iExtendedSchemaVersion Extended replication schema version
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value Timestamp value
+ *
+ *--------------------------------------------------------------------------*/
+
+unsigned int K_ParseTimestampFromExtendedVersion(
+ unsigned int i_iExtendedSchemaVersion )
+{
+ unsigned int iTimeStamp = i_iExtendedSchemaVersion >> 8;
+
+ return iTimeStamp;
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_ParseBaseFromExtendedVersion
+ *
+ * Description:
+ * This KMS-specific function parses the base replication schema value from
+ * the given extended replication schema version and returns this base value.
+ *
+ * Input
+ * -----
+ * i_iExtendedSchemaVersion Extended replication schema version
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value Base replication schema value
+ *
+ *--------------------------------------------------------------------------*/
+
+unsigned int K_ParseBaseFromExtendedVersion(
+ unsigned int i_iExtendedSchemaVersion )
+{
+ unsigned int iBaseSchemaVersion = i_iExtendedSchemaVersion & 0x000000FF;
+
+ return iBaseSchemaVersion;
+}
+
+
+/*---------------------------------------------------------------------------
+ * Function: K_System
+ *
+ * Description:
+ * This function is a thread-safe replacement for the unsafe system(3C) call.
+ * See the popen(3C) man page for more information.
+ *
+ * Input
+ * -----
+ * i_sCmd Command to execute
+ *
+ * Output
+ * ------
+ * (none)
+ *
+ * Return value Termination status of the command language interpreter
+ * if successful, -1 on failure
+ *
+ *--------------------------------------------------------------------------*/
+
+int K_System( const char *i_sCmd )
+{
+#ifndef WIN32
+ FILE *p;
+ int rc;
+ struct sigaction sOldAction;
+
+ // Save signal handler
+ sigaction( SIGCHLD, NULL, &sOldAction );
+
+ // Use default child signal handler
+ sigset( SIGCHLD, SIG_DFL );
+
+ p = popen( i_sCmd, "w" );
+ if ( p == NULL )
+ {
+ rc = -1;
+ }
+ else
+ {
+ rc = pclose( p );
+ }
+
+ // Reset signal handler
+ sigset( SIGCHLD, sOldAction.sa_handler );
+
+ return rc;
+#else
+ return system( i_sCmd );
+#endif
+}
+