#include "MDSSession.h"
#include <Security/DbContext.h>
#include "MDSModule.h"
#include "MDSAttrParser.h"
#include "MDSAttrUtils.h"
#include <memory>
#include <Security/cssmerr.h>
#include <Security/utilities.h>
#include <Security/logging.h>
#include <Security/debugging.h>
#include <Security/mds_schema.h>
#include <sys/types.h>
#include <sys/param.h>
#include <dirent.h>
#include <fcntl.h>
#include <assert.h>
#include <time.h>
namespace Security
{
#define MDS_SYSTEM_PATH "/System/Library/Frameworks"
#define MDS_SYSTEM_FRAME "Security.framework"
#define MDS_BUNDLE_PATH "/System/Library/Security"
#define MDS_BUNDLE_EXTEN ".bundle"
#define MDS_SYSTEM_DB_DIR "/private/var/tmp/mds"
#define MDS_LOCK_FILE_NAME "mds.lock"
#define MDS_OBJECT_DB_NAME "mdsObject.db"
#define MDS_DIRECT_DB_NAME "mdsDirectory.db"
#define MDS_LOCK_FILE_PATH MDS_SYSTEM_DB_DIR "/" MDS_LOCK_FILE_NAME
#define MDS_OBJECT_DB_PATH MDS_SYSTEM_DB_DIR "/" MDS_OBJECT_DB_NAME
#define MDS_DIRECT_DB_PATH MDS_SYSTEM_DB_DIR "/" MDS_DIRECT_DB_NAME
#define MDS_USER_DB_DIR "Library/Security"
#define MDS_USER_BUNDLE "Library/Security"
#define DB_LOCK_TIMEOUT (2 * 1000)
#define MDS_SCAN_INTERVAL 10
#define START_FROM_SCRATCH 0
#define SKIP_FILE_LOCKING 0
#define SYSTEM_MDS_ROOT_ONLY 0
#define SYSTEM_DBS_VIA_USER 1
#define AUTO_COMMIT_OPT 1
static bool doFilesExist(
const char *objDbFile,
const char *directDbFile,
bool purge) {
struct stat sb;
bool objectExist = false;
bool directExist = false;
if (stat(objDbFile, &sb) == 0) {
if(!(sb.st_mode & S_IFREG)) {
MSDebug("deleting non-regular file %s", objDbFile);
if(purge && unlink(objDbFile)) {
MSDebug("unlink(%s) returned %d", objDbFile, errno);
CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR);
}
}
else {
objectExist = true;
}
}
if (stat(directDbFile, &sb) == 0) {
if(!(sb.st_mode & S_IFREG)) {
MSDebug("deleting non-regular file %s", directDbFile);
if(purge & unlink(directDbFile)) {
MSDebug("unlink(%s) returned %d", directDbFile, errno);
CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR);
}
}
directExist = true;
}
if(objectExist && directExist) {
return true;
}
else if(!purge) {
return false;
}
if(objectExist) {
if(unlink(objDbFile)) {
MSDebug("unlink(%s) returned %d", objDbFile, errno);
CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR);
}
}
if(directExist) {
if(unlink(directDbFile)) {
MSDebug("unlink(%s) returned %d", directDbFile, errno);
CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR);
}
}
return false;
}
static bool doesDirectExist(
const char *dirPath)
{
struct stat sb;
if (stat(dirPath, &sb)) {
return false;
}
if(!(sb.st_mode & S_IFDIR)) {
return false;
}
return true;
}
static int createDir(
const char *dirPath,
mode_t dirMode = 0)
{
if(doesDirectExist(dirPath)) {
return 0;
}
int rtn = mkdir(dirPath, 0755);
if(rtn) {
if(errno == EEXIST) {
rtn = 0;
}
else {
rtn = errno;
MSDebug("mkdir(%s) returned %d", dirPath, errno);
}
}
if((rtn == 0) && (dirMode != 0)) {
rtn = chmod(dirPath, dirMode);
if(rtn) {
MSDebug("chmod(%s) returned %d", dirPath, errno);
}
}
return rtn;
}
MDSSession::MDSSession (const Guid *inCallerGuid,
const CSSM_MEMORY_FUNCS &inMemoryFunctions) :
DatabaseSession(MDSModule::get().databaseManager()),
mCssmMemoryFunctions (inMemoryFunctions),
mModule(MDSModule::get()),
mLockFd(-1)
{
MSDebug("MDSSession::MDSSession");
#if START_FROM_SCRATCH
unlink(MDS_LOCK_FILE_PATH);
unlink(MDS_OBJECT_DB_PATH);
unlink(MDS_DIRECT_DB_PATH);
#endif
mCallerGuidPresent = inCallerGuid != nil;
if (mCallerGuidPresent)
mCallerGuid = *inCallerGuid;
}
MDSSession::~MDSSession ()
{
MSDebug("MDSSession::~MDSSession");
releaseLock(mLockFd);
}
void
MDSSession::terminate ()
{
MSDebug("MDSSession::terminate");
releaseLock(mLockFd);
closeAll();
}
void
MDSSession::install ()
{
if((getuid() != (uid_t)0) && SYSTEM_MDS_ROOT_ONLY) {
CssmError::throwMe(CSSMERR_DL_OS_ACCESS_DENIED);
}
int sysFdLock = -1;
try {
if(createDir(MDS_SYSTEM_DB_DIR, 01777)) {
MSDebug("Error creating system MDS dir; aborting.");
CssmError::throwMe(CSSMERR_DL_OS_ACCESS_DENIED);
}
if(!obtainLock(MDS_LOCK_FILE_PATH, sysFdLock, DB_LOCK_TIMEOUT)) {
CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR);
}
if(!systemDatabasesPresent(true)) {
bool created = createSystemDatabases();
if(created) {
DbFilesInfo dbFiles(*this, MDS_SYSTEM_DB_DIR);
#if AUTO_COMMIT_OPT
dbFiles.autoCommit(CSSM_FALSE);
#endif
dbFiles.updateSystemDbInfo(MDS_SYSTEM_PATH, MDS_BUNDLE_PATH);
}
}
}
catch(...) {
if(sysFdLock != -1) {
releaseLock(sysFdLock);
}
throw;
}
releaseLock(sysFdLock);
}
void
MDSSession::uninstall ()
{
CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
}
CSSM_DB_HANDLE MDSSession::dbOpen(
const char *dbName)
{
MSDebug("Opening %s", dbName);
CSSM_DB_HANDLE dbHand;
DatabaseSession::DbOpen(dbName,
NULL, CSSM_DB_ACCESS_READ,
NULL, NULL, dbHand);
return dbHand;
}
void MDSSession::DbOpen(const char *DbName,
const CSSM_NET_ADDRESS *DbLocation,
CSSM_DB_ACCESS_TYPE AccessRequest,
const AccessCredentials *AccessCred,
const void *OpenParameters,
CSSM_DB_HANDLE &DbHandle)
{
updateDataBases();
if(DbName == NULL) {
CssmError::throwMe(CSSMERR_DL_INVALID_DB_NAME);
}
const char *dbName;
if(!strcmp(DbName, MDS_OBJECT_DIRECTORY_NAME)) {
dbName = MDS_OBJECT_DB_NAME;
}
else if(!strcmp(DbName, MDS_CDSA_DIRECTORY_NAME)) {
dbName = MDS_DIRECT_DB_NAME;
}
else {
CssmError::throwMe(CSSMERR_DL_INVALID_DB_NAME);
}
char fullPath[MAXPATHLEN];
dbFullPath(dbName, fullPath);
DatabaseSession::DbOpen(fullPath, DbLocation, AccessRequest, AccessCred,
OpenParameters, DbHandle);
}
void
MDSSession::GetDbNames(CSSM_NAME_LIST_PTR &outNameList)
{
outNameList = new CSSM_NAME_LIST[1];
outNameList->NumStrings = 2;
outNameList->String = new (char *)[2];
outNameList->String[0] = MDSCopyCstring(MDS_OBJECT_DIRECTORY_NAME);
outNameList->String[1] = MDSCopyCstring(MDS_CDSA_DIRECTORY_NAME);
}
void
MDSSession::FreeNameList(CSSM_NAME_LIST &inNameList)
{
delete [] inNameList.String[0];
delete [] inNameList.String[1];
delete [] inNameList.String;
}
void MDSSession::GetDbNameFromHandle(CSSM_DB_HANDLE DBHandle,
char **DbName)
{
printf("GetDbNameFromHandle: code on demand\n");
CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR);
}
bool
MDSSession::obtainLock(
const char *lockFile, int &fd, int timeout) {
#if SKIP_FILE_LOCKING
return true;
#else
static const int kRetryDelay = 250;
fd = open(MDS_LOCK_FILE_PATH, O_CREAT | O_EXCL, 0544);
while (fd == -1 && timeout >= kRetryDelay) {
timeout -= kRetryDelay;
usleep(1000 * kRetryDelay);
mLockFd = open(MDS_LOCK_FILE_PATH, O_CREAT | O_EXCL, 0544);
}
return (fd != -1);
#endif
}
void
MDSSession::releaseLock(int &fd)
{
#if !SKIP_FILE_LOCKING
if (fd != -1) {
close(fd);
unlink(MDS_LOCK_FILE_PATH);
fd = -1;
}
#endif
}
void MDSSession::dbFullPath(
const char *dbName,
char fullPath[MAXPATHLEN+1])
{
mModule.getDbPath(fullPath);
assert(fullPath[0] != '\0');
strcat(fullPath, "/");
strcat(fullPath, dbName);
}
static bool isBundle(
const struct dirent *dp)
{
if(dp == NULL) {
return false;
}
switch(dp->d_type) {
case DT_UNKNOWN:
case DT_DIR:
break;
default:
return false;
}
int suffixLen = strlen(MDS_BUNDLE_EXTEN);
int len = strlen(dp->d_name);
return (len >= suffixLen) &&
!strcmp(dp->d_name + len - suffixLen, MDS_BUNDLE_EXTEN);
}
static bool checkUserBundles(
const char *bundlePath)
{
MSDebug("searching for user bundles in %s", bundlePath);
DIR *dir = opendir(bundlePath);
if (dir == NULL) {
return false;
}
struct dirent *dp;
bool rtn = false;
while ((dp = readdir(dir)) != NULL) {
if(isBundle(dp)) {
rtn = true;
break;
}
}
closedir(dir);
MSDebug("...%s bundle(s) found", rtn ? "" : "No");
return rtn;
}
#define COPY_BUF_SIZE 1024
static void safeCopyFile(
const char *fromPath,
const char *toPath)
{
int srcFd = open(fromPath, O_RDONLY, 0);
if(srcFd < 0) {
int error = errno;
MSDebug("Error %d opening system DB file %s\n", error, fromPath);
UnixError::throwMe(error);
}
struct flock fl;
fl.l_start = 0;
fl.l_len = 1;
fl.l_pid = getpid();
fl.l_type = F_RDLCK; fl.l_whence = SEEK_SET;
for (;;) {
if (::fcntl(srcFd, F_SETLKW, reinterpret_cast<int>(&fl)) == -1) {
int error = errno;
if (error == EINTR) {
continue;
}
MSDebug("Error %d locking system DB file %s\n", error, fromPath);
UnixError::throwMe(error);
}
else {
break;
}
}
int destFd = open(toPath, O_WRONLY | O_APPEND | O_CREAT | O_TRUNC | O_EXCL, 0644);
if(destFd < 0) {
int error = errno;
MSDebug("Error %d opening user DB file %s\n", error, toPath);
UnixError::throwMe(error);
}
char buf[COPY_BUF_SIZE];
while(1) {
int bytesRead = read(srcFd, buf, COPY_BUF_SIZE);
if(bytesRead == 0) {
break;
}
if(bytesRead < 0) {
int error = errno;
MSDebug("Error %d reading system DB file %s\n", error, fromPath);
UnixError::throwMe(error);
}
int bytesWritten = write(destFd, buf, bytesRead);
if(bytesWritten < 0) {
int error = errno;
MSDebug("Error %d writing user DB file %s\n", error, toPath);
UnixError::throwMe(error);
}
}
fl.l_type = F_UNLCK;
if (::fcntl(srcFd, F_SETLK, reinterpret_cast<int>(&fl)) == -1) {
MSDebug("Error %d unlocking system DB file %s\n", errno, fromPath);
}
close(srcFd);
close(destFd);
}
static void copySystemDbs(
const char *userDbFileDir)
{
char toPath[MAXPATHLEN+1];
sprintf(toPath, "%s/%s", userDbFileDir, MDS_OBJECT_DB_NAME);
safeCopyFile(MDS_OBJECT_DB_PATH, toPath);
sprintf(toPath, "%s/%s", userDbFileDir, MDS_DIRECT_DB_NAME);
safeCopyFile(MDS_DIRECT_DB_PATH, toPath);
}
void MDSSession::updateDataBases()
{
bool isRoot = (getuid() == (uid_t)0);
bool createdSystemDb = false;
if(!systemDatabasesPresent(false)) {
if(isRoot || SYSTEM_DBS_VIA_USER) {
install();
}
else {
assert(0);
}
createdSystemDb = true;
}
double delta = mModule.timeSinceLastScan();
if(delta < (double)MDS_SCAN_INTERVAL) {
return;
}
char userDbFileDir[MAXPATHLEN+1];
char userObjDbFilePath[MAXPATHLEN+1];
char userDirectDbFilePath[MAXPATHLEN+1];
char userBundlePath[MAXPATHLEN+1];
char userDbLockPath[MAXPATHLEN+1];
if(isRoot) {
strcat(userDbFileDir, MDS_SYSTEM_DB_DIR);
}
else {
char *userHome = getenv("HOME");
if(userHome == NULL) {
MSDebug("updateDataBases: no HOME");
userHome = "/";
}
sprintf(userBundlePath, "%s/%s", userHome, MDS_USER_BUNDLE);
sprintf(userDbFileDir, "%s/%d", MDS_SYSTEM_DB_DIR, (int)(getuid()));
}
sprintf(userObjDbFilePath, "%s/%s", userDbFileDir, MDS_OBJECT_DB_NAME);
sprintf(userDirectDbFilePath, "%s/%s", userDbFileDir, MDS_DIRECT_DB_NAME);
sprintf(userDbLockPath, "%s/%s", userDbFileDir, MDS_LOCK_FILE_NAME);
if(!isRoot) {
if(createDir(userDbFileDir)) {
Syslog::alert("Error creating %s", userDbFileDir);
MSDebug("Error creating user DBs; using system DBs");
mModule.setDbPath(MDS_SYSTEM_DB_DIR);
return;
}
}
if(!obtainLock(userDbLockPath, mLockFd, DB_LOCK_TIMEOUT)) {
CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR);
}
try {
if(!isRoot) {
if(createdSystemDb) {
unlink(userObjDbFilePath);
unlink(userDirectDbFilePath);
}
if(createdSystemDb || !doFilesExist(userObjDbFilePath, userDirectDbFilePath,
true)) {
MSDebug("copying system DBs to user at %s", userDbFileDir);
copySystemDbs(userDbFileDir);
}
else {
MSDebug("Using existing user DBs at %s", userDbFileDir);
}
}
else {
MSDebug("Using system DBs only");
}
DbFilesInfo dbFiles(*this, userDbFileDir);
if(!createdSystemDb) {
dbFiles.removeOutdatedPlugins();
dbFiles.updateSystemDbInfo(MDS_SYSTEM_PATH, MDS_BUNDLE_PATH);
}
if(!isRoot) {
if(checkUserBundles(userBundlePath)) {
dbFiles.updateForBundleDir(userBundlePath);
}
}
mModule.setDbPath(userDbFileDir);
}
catch(...) {
releaseLock(mLockFd);
throw;
}
mModule.lastScanIsNow();
releaseLock(mLockFd);
}
void MDSSession::removeRecordsForGuid(
const char *guid,
CSSM_DB_HANDLE dbHand)
{
CSSM_QUERY query;
CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
CSSM_HANDLE resultHand;
CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
CSSM_SELECTION_PREDICATE predicate;
CSSM_DATA predData;
recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_ANY;
recordAttrs.SemanticInformation = 0;
recordAttrs.NumberOfAttributes = 0;
recordAttrs.AttributeData = NULL;
predicate.DbOperator = CSSM_DB_EQUAL;
predicate.Attribute.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
predicate.Attribute.Info.Label.AttributeName = "ModuleID";
predicate.Attribute.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_STRING;
predData.Data = (uint8 *)guid;
predData.Length = strlen(guid) + 1;
predicate.Attribute.Value = &predData;
predicate.Attribute.NumberOfValues = 1;
query.RecordType = CSSM_DL_DB_RECORD_ANY;
query.Conjunctive = CSSM_DB_NONE;
query.NumSelectionPredicates = 1;
query.SelectionPredicate = &predicate;
query.QueryLimits.TimeLimit = 0; query.QueryLimits.SizeLimit = 1; query.QueryFlags = 0;
try {
for(;;) {
DLQuery perryQuery(query);
resultHand = DataGetFirst(dbHand,
&perryQuery,
&recordAttrs,
NULL, record);
if(resultHand) {
try {
MSDebug("...deleting a record for guid %s", guid);
DataDelete(dbHand, *record);
DataAbortQuery(dbHand, resultHand);
}
catch(...) {
MSDebug("exception (1) while deleting record for guid %s", guid);
}
}
else if(record) {
FreeUniqueRecord(dbHand, *record);
break;
}
}
}
catch (...) {
MSDebug("exception (2) while deleting record for guid %s", guid);
}
}
bool MDSSession::systemDatabasesPresent(bool purge)
{
bool rtn = false;
try {
if(doFilesExist(MDS_OBJECT_DB_PATH, MDS_DIRECT_DB_PATH, purge)) {
rtn = true;
}
}
catch(...) {
}
return rtn;
}
void
MDSSession::createSystemDatabase(
const char *dbName,
const RelationInfo *relationInfo,
unsigned numRelations,
CSSM_DB_HANDLE &dbHand) {
CSSM_DBINFO dbInfo;
CSSM_DBINFO_PTR dbInfoP = &dbInfo;
memset(dbInfoP, 0, sizeof(CSSM_DBINFO));
dbInfoP->NumberOfRecordTypes = numRelations;
dbInfoP->IsLocal = CSSM_TRUE; dbInfoP->AccessPath = NULL;
unsigned size = sizeof(CSSM_DB_PARSING_MODULE_INFO) * numRelations;
dbInfoP->DefaultParsingModules = (CSSM_DB_PARSING_MODULE_INFO_PTR)malloc(size);
memset(dbInfoP->DefaultParsingModules, 0, size);
size = sizeof(CSSM_DB_RECORD_ATTRIBUTE_INFO) * numRelations;
dbInfoP->RecordAttributeNames = (CSSM_DB_RECORD_ATTRIBUTE_INFO_PTR)malloc(size);
memset(dbInfoP->RecordAttributeNames, 0, size);
size = sizeof(CSSM_DB_RECORD_INDEX_INFO) * numRelations;
dbInfoP->RecordIndexes = (CSSM_DB_RECORD_INDEX_INFO_PTR)malloc(size);
memset(dbInfoP->RecordIndexes, 0, size);
unsigned relation;
for(relation=0; relation<numRelations; relation++) {
const struct RelationInfo *relp = &relationInfo[relation]; CSSM_DB_RECORD_ATTRIBUTE_INFO_PTR attrInfo =
&dbInfoP->RecordAttributeNames[relation]; CSSM_DB_RECORD_INDEX_INFO_PTR indexInfo =
&dbInfoP->RecordIndexes[relation];
attrInfo->DataRecordType = relp->DataRecordType;
attrInfo->NumberOfAttributes = relp->NumberOfAttributes;
attrInfo->AttributeInfo = (CSSM_DB_ATTRIBUTE_INFO_PTR)relp->AttributeInfo;
indexInfo->DataRecordType = relp->DataRecordType;
indexInfo->NumberOfIndexes = relp->NumberOfIndexes;
indexInfo->IndexInfo = (CSSM_DB_INDEX_INFO_PTR)relp->IndexInfo;
}
try {
DbCreate(dbName,
NULL, *dbInfoP,
CSSM_DB_ACCESS_READ | CSSM_DB_ACCESS_WRITE,
NULL, NULL, dbHand);
}
catch(...) {
MSDebug("Error on DbCreate");
free(dbInfoP->DefaultParsingModules);
free(dbInfoP->RecordAttributeNames);
free(dbInfoP->RecordIndexes);
throw;
}
free(dbInfoP->DefaultParsingModules);
free(dbInfoP->RecordAttributeNames);
free(dbInfoP->RecordIndexes);
}
bool MDSSession::createSystemDatabases()
{
CSSM_DB_HANDLE objectDbHand = 0;
CSSM_DB_HANDLE directoryDbHand = 0;
assert((getuid() == (uid_t)0) || !SYSTEM_MDS_ROOT_ONLY);
if(systemDatabasesPresent(true)) {
MSDebug("system DBs already exist");
return false;
}
MSDebug("Creating MDS DBs");
try {
createSystemDatabase(MDS_OBJECT_DB_PATH, &kObjectRelation, 1, objectDbHand);
DbClose(objectDbHand);
objectDbHand = 0;
createSystemDatabase(MDS_DIRECT_DB_PATH, kMDSRelationInfo, kNumMdsRelations,
directoryDbHand);
DbClose(directoryDbHand);
directoryDbHand = 0;
}
catch (...) {
MSDebug("Error creating MDS DBs - deleting both DB files");
unlink(MDS_OBJECT_DB_PATH);
unlink(MDS_DIRECT_DB_PATH);
throw;
}
return true;
}
MDSSession::DbFilesInfo::DbFilesInfo(
MDSSession &session,
const char *dbPath) :
mSession(session),
mObjDbHand(0),
mDirectDbHand(0),
mLaterTimestamp(0)
{
assert(strlen(dbPath) < MAXPATHLEN);
strcpy(mDbPath, dbPath);
char path[MAXPATHLEN];
sprintf(path, "%s/%s", mDbPath, MDS_OBJECT_DB_NAME);
struct stat sb;
int rtn = ::stat(path, &sb);
if(rtn) {
int error = errno;
MSDebug("Error %d statting DB file %s", error, path);
UnixError::throwMe(error);
}
mLaterTimestamp = sb.st_mtimespec.tv_sec;
sprintf(path, "%s/%s", mDbPath, MDS_DIRECT_DB_NAME);
rtn = ::stat(path, &sb);
if(rtn) {
int error = errno;
MSDebug("Error %d statting DB file %s", error, path);
UnixError::throwMe(error);
}
if(sb.st_mtimespec.tv_sec > mLaterTimestamp) {
mLaterTimestamp = sb.st_mtimespec.tv_sec;
}
}
#define AUTO_COMMIT_OFF_ON_CLOSE 1
MDSSession::DbFilesInfo::~DbFilesInfo()
{
if(mObjDbHand != 0) {
#if AUTO_COMMIT_OPT && AUTO_COMMIT_OFF_ON_CLOSE
mSession.PassThrough(mObjDbHand,
CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
reinterpret_cast<void *>(CSSM_TRUE),
NULL);
#endif
mSession.DbClose(mObjDbHand);
mObjDbHand = 0;
}
if(mDirectDbHand != 0) {
#if AUTO_COMMIT_OPT && AUTO_COMMIT_OFF_ON_CLOSE
mSession.PassThrough(mDirectDbHand,
CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
reinterpret_cast<void *>(CSSM_TRUE),
NULL);
#endif
mSession.DbClose(mDirectDbHand);
mDirectDbHand = 0;
}
}
CSSM_DB_HANDLE MDSSession::DbFilesInfo::objDbHand()
{
if(mObjDbHand != 0) {
return mObjDbHand;
}
char fullPath[MAXPATHLEN + 1];
sprintf(fullPath, "%s/%s", mDbPath, MDS_OBJECT_DB_NAME);
mObjDbHand = mSession.dbOpen(fullPath);
return mObjDbHand;
}
CSSM_DB_HANDLE MDSSession::DbFilesInfo::directDbHand()
{
if(mDirectDbHand != 0) {
return mDirectDbHand;
}
char fullPath[MAXPATHLEN + 1];
sprintf(fullPath, "%s/%s", mDbPath, MDS_DIRECT_DB_NAME);
mDirectDbHand = mSession.dbOpen(fullPath);
return mDirectDbHand;
}
void MDSSession::DbFilesInfo::updateSystemDbInfo(
const char *systemPath, const char *bundlePath) {
char fullPath[MAXPATHLEN];
sprintf(fullPath, "%s/%s", systemPath, MDS_SYSTEM_FRAME);
updateForBundle(fullPath);
updateForBundleDir(bundlePath);
}
MDSSession::DbFilesInfo::TbdRecord::TbdRecord(
const CSSM_DATA &guid)
{
assert(guid.Length <= MAX_GUID_LEN);
assert(guid.Length != 0);
memmove(mGuid, guid.Data, guid.Length);
if(mGuid[guid.Length - 1] != '\0') {
mGuid[guid.Length] = '\0';
}
}
void MDSSession::DbFilesInfo::checkOutdatedPlugin(
const CSSM_DATA &pathValue,
const CSSM_DATA &guidValue,
TbdVector &tbdVector)
{
struct stat sb;
bool obsolete = false;
int rtn = ::stat((char *)pathValue.Data, &sb);
if(rtn) {
obsolete = true;
}
else if(sb.st_mtimespec.tv_sec > mLaterTimestamp) {
obsolete = true;
}
if(obsolete) {
TbdRecord *tbdRecord = new TbdRecord(guidValue);
tbdVector.push_back(tbdRecord);
MSDebug("checkOutdatedPlugin: flagging %s obsolete", pathValue.Data);
}
}
void MDSSession::DbFilesInfo::removeOutdatedPlugins()
{
CSSM_QUERY query;
CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
CSSM_HANDLE resultHand;
CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
CSSM_DB_ATTRIBUTE_DATA theAttrs[2];
CSSM_DB_ATTRIBUTE_INFO_PTR attrInfo;
TbdVector tbdRecords;
recordAttrs.DataRecordType = MDS_OBJECT_RECORDTYPE;
recordAttrs.SemanticInformation = 0;
recordAttrs.NumberOfAttributes = 2;
recordAttrs.AttributeData = theAttrs;
attrInfo = &theAttrs[0].Info;
attrInfo->AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attrInfo->Label.AttributeName = "ModuleID";
attrInfo->AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_STRING;
theAttrs[0].NumberOfValues = 0;
theAttrs[0].Value = NULL;
attrInfo = &theAttrs[1].Info;
attrInfo->AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attrInfo->Label.AttributeName = "Path";
attrInfo->AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_STRING;
theAttrs[1].NumberOfValues = 0;
theAttrs[1].Value = NULL;
query.RecordType = MDS_OBJECT_RECORDTYPE;
query.Conjunctive = CSSM_DB_NONE;
query.NumSelectionPredicates = 0;
query.SelectionPredicate = NULL;
query.QueryLimits.TimeLimit = 0; query.QueryLimits.SizeLimit = 1; query.QueryFlags = 0;
DLQuery perryQuery(query);
try {
resultHand = mSession.DataGetFirst(objDbHand(),
&perryQuery,
&recordAttrs,
NULL, record);
}
catch(...) {
MSDebug("removeOutdatedPlugins: DataGetFirst threw");
return; }
if(record) {
mSession.FreeUniqueRecord(mObjDbHand, *record);
}
if(resultHand) {
if(theAttrs[0].NumberOfValues && theAttrs[1].NumberOfValues) {
checkOutdatedPlugin(*theAttrs[1].Value, *theAttrs[0].Value,
tbdRecords);
}
else {
MSDebug("removeOutdatedPlugins: incomplete record found (1)!");
}
for(unsigned dex=0; dex<2; dex++) {
if(theAttrs[dex].Value) {
if(theAttrs[dex].Value->Data) {
mSession.free(theAttrs[dex].Value->Data);
}
mSession.free(theAttrs[dex].Value);
}
}
}
else {
MSDebug("removeOutdatedPlugins: empty object DB");
return;
}
for(;;) {
bool brtn = mSession.DataGetNext(objDbHand(),
resultHand,
&recordAttrs,
NULL,
record);
if(!brtn) {
break;
}
if(record) {
mSession.FreeUniqueRecord(mObjDbHand, *record);
}
if(theAttrs[0].NumberOfValues && theAttrs[1].NumberOfValues) {
checkOutdatedPlugin(*theAttrs[1].Value,
*theAttrs[0].Value,
tbdRecords);
}
else {
MSDebug("removeOutdatedPlugins: incomplete record found (2)!");
}
for(unsigned dex=0; dex<2; dex++) {
if(theAttrs[dex].Value) {
if(theAttrs[dex].Value->Data) {
mSession.free(theAttrs[dex].Value->Data);
}
mSession.free(theAttrs[dex].Value);
}
}
}
unsigned numRecords = tbdRecords.size();
for(unsigned i=0; i<numRecords; i++) {
TbdRecord *tbdRecord = tbdRecords[i];
mSession.removeRecordsForGuid(tbdRecord->guid(), objDbHand());
mSession.removeRecordsForGuid(tbdRecord->guid(), directDbHand());
}
for(unsigned i=0; i<numRecords; i++) {
delete tbdRecords[i];
}
}
void MDSSession::DbFilesInfo::updateForBundleDir(
const char *bundleDirPath)
{
MSDebug("...updating DBs for dir %s", bundleDirPath);
DIR *dir = opendir(bundleDirPath);
if (dir == NULL) {
MSDebug("updateForBundleDir: error %d opening %s", errno, bundleDirPath);
return;
}
struct dirent *dp;
char fullPath[MAXPATHLEN];
while ((dp = readdir(dir)) != NULL) {
if(isBundle(dp)) {
sprintf(fullPath, "%s/%s", bundleDirPath, dp->d_name);
updateForBundle(fullPath);
}
}
closedir(dir);
}
bool MDSSession::DbFilesInfo::lookupForPath(
const char *path)
{
CSSM_QUERY query;
CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
CSSM_HANDLE resultHand = 0;
CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
CSSM_DB_ATTRIBUTE_DATA theAttr;
CSSM_DB_ATTRIBUTE_INFO_PTR attrInfo = &theAttr.Info;
CSSM_SELECTION_PREDICATE predicate;
CSSM_DATA predData;
recordAttrs.DataRecordType = MDS_OBJECT_RECORDTYPE;
recordAttrs.SemanticInformation = 0;
recordAttrs.NumberOfAttributes = 1;
recordAttrs.AttributeData = &theAttr;
attrInfo->AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attrInfo->Label.AttributeName = "Path";
attrInfo->AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_STRING;
theAttr.NumberOfValues = 0;
theAttr.Value = NULL;
predicate.DbOperator = CSSM_DB_EQUAL;
predicate.Attribute.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
predicate.Attribute.Info.Label.AttributeName = "Path";
predicate.Attribute.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_STRING;
predData.Data = (uint8 *)path;
predData.Length = strlen(path) + 1;
predicate.Attribute.Value = &predData;
predicate.Attribute.NumberOfValues = 1;
query.RecordType = MDS_OBJECT_RECORDTYPE;
query.Conjunctive = CSSM_DB_NONE;
query.NumSelectionPredicates = 1;
query.SelectionPredicate = &predicate;
query.QueryLimits.TimeLimit = 0; query.QueryLimits.SizeLimit = 1; query.QueryFlags = 0;
bool ourRtn = true;
try {
DLQuery perryQuery(query);
resultHand = mSession.DataGetFirst(objDbHand(),
&perryQuery,
&recordAttrs,
NULL, record);
}
catch (...) {
ourRtn = false;
}
if(record) {
mSession.FreeUniqueRecord(mObjDbHand, *record);
}
else {
ourRtn = false;
}
if(resultHand && ourRtn) {
try {
mSession.DataAbortQuery(mObjDbHand, resultHand);
}
catch(...) {
MSDebug("exception on DataAbortQuery in lookupForPath");
}
}
if(theAttr.Value) {
if(theAttr.Value->Data) {
mSession.free(theAttr.Value->Data);
}
mSession.free(theAttr.Value);
}
return ourRtn;
}
void MDSSession::DbFilesInfo::updateForBundle(
const char *bundlePath)
{
MSDebug("...updating DBs for bundle %s", bundlePath);
if(lookupForPath(bundlePath)) {
return;
}
MDSAttrParser parser(bundlePath,
mSession,
objDbHand(),
directDbHand());
parser.parseAttrs();
}
void MDSSession::DbFilesInfo::autoCommit(CSSM_BOOL val)
{
try {
mSession.PassThrough(objDbHand(),
CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
reinterpret_cast<void *>(val),
NULL);
mSession.PassThrough(directDbHand(),
CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
reinterpret_cast<void *>(val),
NULL);
}
catch (...) {
MSDebug("DbFilesInfo::autoCommit error!");
}
}
}