📄 pgpfiledb.c
字号:
/*
* pgpFileDB.c
* Manage PGPKeyDB's based on RingFiles
*
* Copyright (C) 1996,1997 Network Associates Inc. and affiliated companies.
* All rights reserved
*
* $Id: pgpFileDB.c,v 1.81.6.2 1999/06/10 23:08:59 hal Exp $
*/
#include <stdio.h>
#include <string.h>
#include "pgpConfig.h"
#include "pgpMemoryMgr.h"
#if HAVE_FCNTL_H
#include <fcntl.h> /* For O_CREAT, O_EXCL */
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifndef W_OK
#define W_OK 2
#endif
#include "pgpKDBInt.h"
#include "pgpFileRef.h"
#include "pgpFileSpec.h"
#include "pgpFileUtilities.h"
#include "pgpStrings.h"
#include "pgpFileNames.h"
#include "pgpDebug.h"
#include "pgpFile.h"
#include "pgpRngRead.h"
#include "pgpContext.h"
/* Private data for RingFile type of PGPKeyDBRef */
typedef struct RingDB {
PGPContextRef context;
RingFile * ringFile; /* RingFile for keys */
PGPFile * pgpFile; /* PGPFile for keys */
PFLFileSpecRef mainFileRef; /* Main keyring filename */
PFLFileSpecRef readFileRef; /* Filename of keyring backing store */
FILE * mainStdFile; /* Main keyring FILE (for locking only) */
RingSet * mutableSet; /* Mutable ringset */
/* mutableSet will be NULL if !bwriteable */
RingSet const * immutableSet; /* Immutable ringset */
RingSet * frozenSet; /* Frozen copy of mutableSet */
PGPBoolean bwriteable; /* True if a writeable database */
PGPBoolean bdirty; /* True if mutableSet has been changed */
PGPBoolean bprivate; /* True if a private keyring */
PGPBoolean btrusted; /* True if should get trust packets */
PGPBoolean bmembuf; /* True if memory-buffer based */
PGPBoolean btempfile; /* True if readFileRef is a temp file */
PGPKeyRingOpenFlags openFlags; /* Flags used to open keyring */
DEBUG_STRUCT_CONSTRUCTOR( RingDB )
} RingDB;
/***************************** File IO ****************************/
/*
* Readers and writers can safely access the same key ring simultaneously.
* The only constraint is that at most one writer can access the file at
* the same time.
*
* This wasn't possible before because the bulk of the key data is loaded
* on demand using an in-memory index which is built on open. This requires
* that the file used for backing store never change. Our solution is to
* never use the main ring file for backing store, but instead use a static
* copy of the file.
*
* But copying the file is a lot of overhead to suffer every time the key
* ring is opened. So we leave the copy lying around so the next user will
* be spared the overhead, amortizing the cost over several accesses.
*
* These static copies are also useful as backups, and that's how they're
* named on disk. Every time a key ring is closed, old backups are purged.
* Currently, all but the most recent 4 backups are removed.
*/
static const char *
GetSeparator(
PGPSize maxFileLength )
{
#if PGP_MACINTOSH
(void)maxFileLength; /* Avoid warning */
return " Backup ";
#else
if ( maxFileLength >= 18 )
return "-bak-";
else
return "-";
#endif
}
static PGPError
FindMatchingBackup(
PFLFileSpecRef mainFileRef,
PFLFileSpecRef * readFileRef )
{
PGPError err = kPGPError_NoErr;
PGPSize maxFileLength;
char * mainFileName = NULL;
PFLFileInfo mainFileInfo;
PFLDirectoryIterRef dirIter = kInvalidPFLDirectoryIterRef;
PFLFileSpecRef fileRef = kInvalidPFLFileSpecRef;
char * fileName = NULL;
const char * separator;
PFLFileInfo fileInfo;
PFLFileSpecRef newestFileRef = kInvalidPFLFileSpecRef;
time_t newestFileModTime = 0; /* Avoid warning */
PGPBoolean valid;
PGPUInt32 number;
*readFileRef = NULL;
err = PFLGetFileSpecName( mainFileRef, &mainFileName );
if ( IsPGPError( err ) )
goto cleanup;
err = PFLGetMaxFileSpecNameLength( mainFileRef, &maxFileLength );
if ( IsPGPError( err ) )
goto cleanup;
separator = GetSeparator( maxFileLength );
err = PFLGetFileInfo( mainFileRef, &mainFileInfo );
if ( IsPGPError( err ) )
goto cleanup;
err = PFLGetParentDirectory( mainFileRef, &fileRef );
if ( IsPGPError( err ) )
goto cleanup;
err = PFLNewDirectoryIter( fileRef, &dirIter );
if ( IsPGPError( err ) )
goto cleanup;
PFLFreeFileSpec( fileRef );
fileRef = NULL;
while ( IsntPGPError( err = PFLNextFileInDirectory( dirIter, &fileRef ) ) )
{
err = PFLGetFileSpecName( fileRef, &fileName );
if ( IsPGPError( err ) )
goto cleanup;
err = PGPVerifyNumberFileName( mainFileName, separator, fileName,
maxFileLength + 1, &valid, &number );
if ( IsPGPError( err ) )
goto cleanup;
if ( valid )
{
err = PFLGetFileInfo( fileRef, &fileInfo );
if ( IsPGPError( err ) )
goto cleanup;
/*
* XXX: We may want to rethink this heuristic.
* In particular, what happens if a backup file
* exists which is newer than the current time?
* Also, it would be nice to check file meta
* info (like creator/type on the Mac) to make
* sure it matches.
*/
if ( ( fileInfo.flags & kPGPFileInfo_IsPlainFile ) &&
fileInfo.dataLength == mainFileInfo.dataLength &&
fileInfo.modificationTime >= mainFileInfo.modificationTime &&
( newestFileRef == kInvalidPFLFileSpecRef ||
newestFileModTime < fileInfo.modificationTime ) )
{
if ( newestFileRef != kInvalidPFLFileSpecRef )
PFLFreeFileSpec( newestFileRef );
newestFileRef = fileRef;
fileRef = kInvalidPFLFileSpecRef;
newestFileModTime = fileInfo.modificationTime;
}
}
if ( fileRef != kInvalidPFLFileSpecRef )
{
PFLFreeFileSpec( fileRef );
fileRef = NULL;
}
PGPFreeData( fileName );
fileName = NULL;
}
if ( err != kPGPError_EndOfIteration )
goto cleanup;
if ( newestFileRef == kInvalidPFLFileSpecRef )
{
err = kPGPError_FileNotFound;
goto cleanup;
}
/* XXX: Maybe check file contents here, just to be sure? */
err = kPGPError_NoErr;
*readFileRef = newestFileRef;
/* We're no longer responsible for this */
newestFileRef = kInvalidPFLFileSpecRef;
cleanup:
if ( fileRef != kInvalidPFLFileSpecRef )
PFLFreeFileSpec( fileRef );
if ( newestFileRef != kInvalidPFLFileSpecRef )
PFLFreeFileSpec( newestFileRef );
if ( dirIter != kInvalidPFLDirectoryIterRef )
PFLFreeDirectoryIter( dirIter );
if ( IsntNull( mainFileName ) )
PGPFreeData( mainFileName );
if ( IsntNull( fileName ) )
PGPFreeData( fileName );
return err;
}
static PGPError
CopyStdFile(
PGPMemoryMgrRef memoryMgr,
FILE * origFile,
FILE * destFile )
{
int stdErr;
PGPError err = kPGPError_NoErr;
char * buffer = NULL;
PGPSize bufferSize = 16 * 1024L;
PGPSize length;
rewind( origFile );
rewind( destFile );
buffer = (char *)PGPNewData( memoryMgr, bufferSize, 0 );
if ( IsNull( buffer ) )
{
err = kPGPError_OutOfMemory;
goto cleanup;
}
while ( ( length = fread( buffer, 1, bufferSize, origFile ) ) > 0 )
{
if ( length != fwrite( buffer, 1, length, destFile ) )
{
stdErr = ferror( destFile );
if (
0
#ifdef ENOSPC
|| stdErr == ENOSPC
#endif
#ifdef EFBIG
|| stdErr == EFBIG
#endif
)
{
err = kPGPError_DiskFull;
}
else
{
err = kPGPError_WriteFailed;
}
goto cleanup;
}
}
if ( !feof( origFile ) )
{
err = kPGPError_ReadFailed;
goto cleanup;
}
cleanup:
if ( IsntNull( buffer ) )
PGPFreeData( buffer );
return err;
}
/* Copy permissions, group and owner from main keyring file */
/* Only implemented for Unix at present */
static PGPError
pgpCopyFilePerms( PFLFileSpecRef oldFileRef, PFLFileSpecRef newFileRef )
{
#if !PGP_UNIX
(void) oldFileRef;
(void) newFileRef;
return kPGPError_NoErr;
#else
char *oldName;
char *newName;
struct stat oldStat;
PGPError err;
err = PFLGetFullPathFromFileSpec( oldFileRef, &oldName );
if ( IsPGPError( err ) )
return err;
err = PFLGetFullPathFromFileSpec( newFileRef, &newName );
if ( IsPGPError( err ) ) {
PGPFreeData( oldName );
return err;
}
if (stat (oldName, &oldStat) < 0) {
err = kPGPError_FileOpFailed;
PGPFreeData( oldName );
PGPFreeData( newName );
return err;
}
/* Try to set newup file perms, group, owner; ignore errors */
chmod (newName, oldStat.st_mode);
chown (newName, -1, oldStat.st_gid);
chown (newName, oldStat.st_uid, -1);
PGPFreeData( oldName );
PGPFreeData( newName );
return kPGPError_NoErr;
#endif
}
static PGPError
MakeMatchingBackup(
PFLFileSpecRef mainFileRef,
FILE * mainStdFile,
PGPFileType fileType,
PFLFileSpecRef * readFileRef )
{
PGPError err = kPGPError_NoErr;
PGPMemoryMgrRef memoryMgr = PFLGetFileSpecMemoryMgr( mainFileRef );
PFLFileSpecRef fileRef = kInvalidPFLFileSpecRef;
PFLFileSpecRef tempFileRef = kInvalidPFLFileSpecRef;
char * mainFileName = NULL;
char * fileName = NULL;
PGPSize maxFileLength;
const char * separator;
PGPUInt32 number;
PGPBoolean exists;
PGPBoolean weOpenedMainFile = FALSE;
FILE * stdFile = NULL;
*readFileRef = NULL;
/* First copy the ring to a temp file in the backup directory */
err = PFLFileSpecGetUniqueSpec( mainFileRef, &tempFileRef );
if ( IsPGPError( err ) )
goto cleanup;
err = pgpFileRefStdIOOpen( tempFileRef, kPGPFileOpenStdWriteFlags,
fileType, &stdFile );
if ( IsPGPError( err ) )
goto cleanup;
err = pgpCopyFilePerms( mainFileRef, tempFileRef );
if ( IsPGPError( err ) )
goto cleanup;
if ( IsNull( mainStdFile ) )
{
err = pgpFileRefStdIOOpen( mainFileRef, kPGPFileOpenReadPerm,
kPGPFileTypeNone, &mainStdFile );
if ( IsPGPError( err ) )
goto cleanup;
weOpenedMainFile = TRUE;
}
err = CopyStdFile( memoryMgr, mainStdFile, stdFile );
if ( IsPGPError( err ) )
goto cleanup;
pgpStdIOClose( stdFile );
stdFile = NULL;
/* Next, we find a new backup name to rename to */
err = PFLGetMaxFileSpecNameLength( mainFileRef, &maxFileLength );
if ( IsPGPError( err ) )
goto cleanup;
err = PFLCopyFileSpec( mainFileRef, &fileRef );
if ( IsPGPError( err ) )
goto cleanup;
err = PFLGetFileSpecName( mainFileRef, &mainFileName );
if ( IsPGPError( err ) )
goto cleanup;
separator = GetSeparator( maxFileLength );
fileName = (char *)PGPNewData( memoryMgr, maxFileLength + 1, 0 );
if ( IsNull( fileName ) )
{
err = kPGPError_OutOfMemory;
goto cleanup;
}
for (number = 1; ; number++ )
{
err = PGPNewNumberFileName( mainFileName, separator, number,
maxFileLength + 1, fileName );
if ( IsPGPError( err ) )
goto cleanup;
err = PFLSetFileSpecName( fileRef, fileName );
if ( IsPGPError( err ) )
goto cleanup;
err = PFLFileSpecExists( fileRef, &exists );
if ( IsPGPError( err ) )
goto cleanup;
if ( !exists )
{
err = PFLFileSpecRename( tempFileRef, fileName );
if ( IsntPGPError( err ) )
break;
else if ( err != kPGPError_ItemNotFound )
goto cleanup;
}
}
*readFileRef = fileRef;
fileRef = NULL; /* We're no longer responsible for it */
cleanup:
if ( tempFileRef != kInvalidPFLFileSpecRef )
PFLFreeFileSpec( tempFileRef );
if ( fileRef != kInvalidPFLFileSpecRef )
PFLFreeFileSpec( fileRef );
if ( IsntNull( mainFileName ) )
PGPFreeData( mainFileName );
if ( IsntNull( fileName ) )
PGPFreeData( fileName );
if ( IsntNull( stdFile ) )
pgpStdIOClose( stdFile );
if ( weOpenedMainFile && IsntNull( mainStdFile ) )
pgpStdIOClose( mainStdFile );
return err;
}
static PGPError
MakeTempCopy(
PFLFileSpecRef mainFileRef,
FILE * mainStdFile,
PGPFileType fileType,
PFLFileSpecRef * readFileRef )
{
PGPError err = kPGPError_NoErr;
PGPMemoryMgrRef context = PFLGetFileSpecMemoryMgr( mainFileRef );
PFLFileSpecRef fileRef = kInvalidPFLFileSpecRef;
FILE * stdFile = NULL;
PGPBoolean weOpenedMainFile = FALSE;
err = PFLGetTempFileSpec( context, kInvalidPFLFileSpecRef, &fileRef );
if ( IsPGPError( err ) )
goto cleanup;
err = pgpFileRefStdIOOpen( fileRef, kPGPFileOpenStdWriteFlags,
fileType, &stdFile );
if ( IsPGPError( err ) )
goto cleanup;
if ( IsNull( mainStdFile ) )
{
err = pgpFileRefStdIOOpen( mainFileRef, kPGPFileOpenReadPerm,
kPGPFileTypeNone, &mainStdFile );
if ( IsPGPError( err ) )
goto cleanup;
weOpenedMainFile = TRUE;
}
err = CopyStdFile( context, mainStdFile, stdFile );
if ( IsPGPError( err ) )
goto cleanup;
*readFileRef = fileRef;
fileRef = NULL; /* We're no longer responsible for it */
cleanup:
if ( fileRef != kInvalidPFLFileSpecRef )
PFLFreeFileSpec( fileRef );
if ( IsntNull( stdFile ) )
pgpStdIOClose( stdFile );
if ( weOpenedMainFile && IsntNull( mainStdFile ) )
pgpStdIOClose( mainStdFile );
return err;
}
/* This struct is only used by PurgeOldBackups() */
typedef struct
{
PFLFileSpecRef fileRef;
time_t modTime;
} PGPFileTimeInfo;
static PGPError
PurgeOldBackups(
PFLFileSpecRef mainFileRef )
{
PGPError err = kPGPError_NoErr;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -