📄 pgpfiledb.c
字号:
const char * separator;
PGPSize maxFileLength;
char * mainFileName = NULL;
PFLFileInfo mainFileInfo;
PFLDirectoryIterRef dirIter = kInvalidPFLDirectoryIterRef;
PFLFileSpecRef fileRef = kInvalidPFLFileSpecRef;
char * fileName = NULL;
PFLFileInfo fileInfo;
PGPFileTimeInfo newestFiles[4];
int maxNewest = sizeof(newestFiles) /
sizeof(newestFiles[0]);
PGPFileTimeInfo olderFiles[2][2];
long olderFileAges[2] = { 60 * 60L, 24 * 60 * 60L };
int maxOlder = sizeof(olderFiles) / sizeof(olderFiles[0]);
PFLFileSpecRef filesToDelete[32];
int maxFilesToDelete = sizeof(filesToDelete) /
sizeof(filesToDelete[0]);
int numFilesToDelete = 0;
int i;
time_t now = time( NULL );
PGPBoolean valid;
PGPBoolean deleteThisFile;
PGPUInt32 number;
pgpAssert( maxOlder == sizeof(olderFileAges) / sizeof(olderFileAges[0]) );
(void)olderFileAges;
for (i = 0; i < maxNewest; i++)
newestFiles[i].fileRef = kInvalidPFLFileSpecRef;
for (i = 0; i < maxOlder; i++)
olderFiles[i][0].fileRef = olderFiles[i][1].fileRef =
kInvalidPFLFileSpecRef;
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;
deleteThisFile = TRUE;
if ( fileInfo.modificationTime <= now )
{
i = 0;
while ( i < maxNewest &&
newestFiles[i].fileRef != kInvalidPFLFileSpecRef &&
newestFiles[i].modTime > fileInfo.modificationTime )
i++;
if ( i < maxNewest )
{
if ( newestFiles[ maxNewest - 1 ].fileRef
!= kInvalidPFLFileSpecRef )
{
if ( numFilesToDelete >= maxFilesToDelete )
goto overflow;
filesToDelete[ numFilesToDelete++ ] =
newestFiles[ maxNewest - 1 ].fileRef;
}
pgpCopyMemory( newestFiles + i, newestFiles + i + 1,
( maxNewest - i - 1 )
* sizeof( newestFiles[0] ) );
newestFiles[i].modTime = fileInfo.modificationTime;
err = PFLCopyFileSpec( fileRef,
&newestFiles[ i ].fileRef );
if ( IsPGPError( err ) )
goto cleanup;
deleteThisFile = FALSE;
}
}
if ( deleteThisFile )
{
if ( numFilesToDelete >= maxFilesToDelete )
goto overflow;
err = PFLCopyFileSpec( fileRef,
&filesToDelete[ numFilesToDelete ] );
if ( IsPGPError( err ) )
goto cleanup;
numFilesToDelete++;
}
}
if ( fileRef != kInvalidPFLFileSpecRef )
{
PFLFreeFileSpec( fileRef );
fileRef = NULL;
}
PGPFreeData( fileName );
fileName = NULL;
}
if ( err != kPGPError_EndOfIteration )
goto cleanup;
overflow: /* Overflowing is currently ignored, we'll delete the rest later */
err = kPGPError_NoErr;
goto cleanup;
cleanup:
if ( fileRef != kInvalidPFLFileSpecRef )
PFLFreeFileSpec( fileRef );
if ( dirIter != kInvalidPFLDirectoryIterRef )
PFLFreeDirectoryIter( dirIter );
if ( IsntNull( mainFileName ) )
PGPFreeData( mainFileName );
if ( IsntNull( fileName ) )
PGPFreeData( fileName );
for ( i = 0; i < maxNewest; i++ )
if ( newestFiles[i].fileRef != kInvalidPFLFileSpecRef )
PFLFreeFileSpec( newestFiles[i].fileRef );
for ( i = 0; i < numFilesToDelete; i++ )
{
if ( filesToDelete[i] != kInvalidPFLFileSpecRef )
{
PFLFileSpecDelete( filesToDelete[i] );
PFLFreeFileSpec( filesToDelete[i] );
}
}
return err;
}
/*
* Open the specified ringfile. If writeable flag is clear, it will be
* forced read-only, otherwise whether it is writeable will depend on
* permissions. Returns a filled-in rdb structure.
* Note that "private" is not a concept of the underlying library, but
* is used in the add function to decide which objects to accept.
*/
static PGPError
OpenKeyRing (
PGPContextRef cdkContext,
PFLConstFileSpecRef fileRef, PGPKeyRingOpenFlags openFlags,
RingPool *ringpool, RingDB *rdb)
{
PFLFileSpecRef mainFileRef = kInvalidPFLFileSpecRef; /* Main ring */
PFLFileSpecRef readFileRef = kInvalidPFLFileSpecRef; /* Latest backup */
FILE * mainStdFile = NULL; /* Stdio file of main ring */
FILE * readStdFile = NULL; /* Stdio file of latest backup */
PGPFile * pgpFile = NULL;
RingFile * ringFile = NULL;
RingSet const * immutableSet = NULL;
RingSet * mutableSet = NULL;
PGPError err = kPGPError_NoErr;
PGPBoolean fileExists;
PGPBoolean trusted = !!( openFlags & kPGPKeyRingOpenFlags_Trusted );
PGPBoolean priv = !!( openFlags & kPGPKeyRingOpenFlags_Private );
PGPBoolean writeable = !!( openFlags & kPGPKeyRingOpenFlags_Mutable );
PGPFileType fileType = priv ? kPGPFileTypePrivRing : kPGPFileTypePubRing;
if( ( openFlags & kPGPKeyRingOpenFlags_Create ) != 0 && ! writeable )
{
pgpDebugMsg( "OpenKeyRing(): Cannot create read-only keyring" );
err = kPGPError_BadParams;
goto cleanup;
}
err = PFLFileSpecExists( (PFLFileSpecRef) fileRef, &fileExists );
if( IsntPGPError( err ) && ! fileExists )
{
if( ( openFlags & kPGPKeyRingOpenFlags_Create ) == 0 )
{
err = kPGPError_FileNotFound;
}
}
if ( IsPGPError( err ) )
goto cleanup;
err = PFLCopyFileSpec(fileRef, &mainFileRef);
if ( IsPGPError( err ) )
goto cleanup;
if ( writeable )
{
PGPFileOpenFlags fileOpenFlags;
mutableSet = ringSetCreate (ringpool);
if ( IsNull( mutableSet ) )
{
err = kPGPError_OutOfMemory;
goto cleanup;
}
/* Lock the main keyring file to ensure exclusive access */
fileOpenFlags = kPGPFileOpenReadWritePerm | kPGPFileOpenLockFile;
err = pgpFileRefStdIOOpen( mainFileRef, fileOpenFlags, fileType,
&mainStdFile );
if ( IsPGPError( err ) )
{
if ( err == kPGPError_FileNotFound &&
( openFlags & kPGPKeyRingOpenFlags_Create ) )
{
err = pgpCreateFile( mainFileRef, fileType );
if ( IsPGPError( err ) )
goto cleanup;
err = pgpFileRefStdIOOpen( mainFileRef, fileOpenFlags,
fileType, &mainStdFile );
}
if ( IsPGPError( err ) )
goto cleanup;
}
}
else
{
/* Immutable keydb's don't need mutableSet */
mutableSet = NULL;
}
err = FindMatchingBackup( mainFileRef, &readFileRef );
if ( IsPGPError( err ) )
{
err = MakeMatchingBackup( mainFileRef, mainStdFile, fileType,
&readFileRef );
if ( err == kPGPError_FileLocked )
goto cleanup;
}
if ( IsPGPError( err ) || ( !writeable && PFL_PLATFORM_UNSAFE_DELETE ) )
{
/*
* If we failed to create a matching backup file, or
* if deletes aren't safe on this platform and we're not a writer,
* then we need to make a local copy of the keyring as backing store.
*/
err = MakeTempCopy( mainFileRef, mainStdFile, fileType, &readFileRef );
if ( IsPGPError( err ) )
goto cleanup;
rdb->btempfile = TRUE;
}
else
{
rdb->btempfile = FALSE;
}
/* Open the read-only file which will be used for backing store */
err = pgpFileRefStdIOOpen( readFileRef, kPGPFileOpenReadPerm,
kPGPFileTypeNone, &readStdFile );
if ( IsPGPError( err ) )
goto cleanup;
pgpFile = pgpFileReadOpen ( cdkContext, readStdFile, NULL, NULL );
if ( IsNull( pgpFile ) )
{
err = kPGPError_OutOfMemory; /* XXX: Return better error */
goto cleanup;
}
readStdFile = NULL; /* pgpFile is now responsible for closing it */
ringFile = ringFileOpen( ringpool, pgpFile, trusted, &err );
if ( IsNull( ringFile ) )
goto cleanup;
immutableSet = ringFileSet( ringFile );
if ( IsntNull( mutableSet ) )
ringSetAddSet( mutableSet, immutableSet );
rdb->mainFileRef = mainFileRef;
rdb->mainStdFile = mainStdFile;
rdb->readFileRef = readFileRef;
rdb->pgpFile = pgpFile;
rdb->ringFile = ringFile;
rdb->immutableSet = immutableSet;
rdb->mutableSet = mutableSet;
rdb->openFlags = openFlags;
rdb->bwriteable = writeable;
rdb->bdirty = ringFileIsDirty( ringFile );
rdb->btrusted = trusted;
rdb->bprivate = priv;
return kPGPError_NoErr;
cleanup:
if ( IsntNull( mutableSet ) )
ringSetDestroy( mutableSet );
if ( IsntNull( ringFile ) )
{
if( IsPGPError( ringFileClose( ringFile ) ) )
pgpDebugMsg( "Failed to close ringFile" );
}
if ( IsntNull( pgpFile ) )
pgpFileClose( pgpFile );
if ( IsntNull( readStdFile ) )
pgpStdIOClose( readStdFile );
if ( readFileRef != kInvalidPFLFileSpecRef )
PFLFreeFileSpec( readFileRef );
if ( IsntNull( mainStdFile ) )
pgpStdIOClose( mainStdFile );
if ( mainFileRef != kInvalidPFLFileSpecRef )
PFLFreeFileSpec( mainFileRef );
return err;
}
static PGPError
WriteKeyRing( RingDB * rdb )
{
PGPContextRef cdkContext = rdb->context;
PGPError err = kPGPError_NoErr;
PGPFile * oldPGPFile = rdb->pgpFile;
RingFile * oldRingFile = rdb->ringFile;
PFLFileSpecRef tempFileRef = kInvalidPFLFileSpecRef;
FILE * tempStdFile = NULL; /* Stdio file for temp file */
PGPFile * tempPGPFile = NULL; /* PGPFile for temp file */
PFLFileSpecRef newFileRef = kInvalidPFLFileSpecRef;
FILE * newStdFile = NULL; /* Stdio file for new backup */
PGPFile * newPGPFile = NULL; /* PGPFile for new backup file */
RingFile * newRingFile = NULL; /* RingFile for new backup file */
char * mainFileName = NULL;
PGPFileType fileType = rdb->bprivate ? kPGPFileTypePrivRing
: kPGPFileTypePubRing;
int flags = ( rdb->btrusted && !rdb->bprivate )
? PGP_RINGSETWRITE_PUBTRUST : 0;
/* Find a unique temporary file name */
err = PFLFileSpecGetUniqueSpec( rdb->mainFileRef, &tempFileRef );
if ( IsPGPError( err ) )
goto cleanup;
/* Create and open the temp file */
err = pgpFileRefStdIOOpen( tempFileRef,
kPGPFileOpenStdUpdateFlags
| kPGPFileOpenLockFile,
fileType,
&tempStdFile );
if ( IsPGPError( err ) )
goto cleanup;
err = pgpCopyFilePerms( rdb->mainFileRef, tempFileRef );
if ( IsPGPError( err ) )
goto cleanup;
/* Create a PGPFile object for the temp file */
tempPGPFile = pgpFileWriteOpen( cdkContext, tempStdFile, NULL );
if ( IsNull( tempPGPFile ) )
{
pgpStdIOClose( tempStdFile );
err = kPGPError_OutOfMemory; /* XXX: Return better error */
goto cleanup;
}
/* Write the key ring to the temp file, creating newRingFile */
err = ringSetWrite( rdb->mutableSet, tempPGPFile, &newRingFile,
PGPVERSION_4, flags );
if ( IsPGPError( err ) )
goto cleanup;
/* Make sure the data is written to disk */
err = pgpFileFlush( tempPGPFile );
if ( IsPGPError( err ) )
goto cleanup;
/*
* Make a new read-only backup from the newly written temp file
*/
err = MakeMatchingBackup( rdb->mainFileRef, tempStdFile, fileType,
&newFileRef );
if ( IsPGPError( err ) )
goto cleanup;
/* Open the new backup file which will be used for backing store */
err = pgpFileRefStdIOOpen( newFileRef, kPGPFileOpenReadPerm,
kPGPFileTypeNone, &newStdFile );
if ( IsPGPError( err ) )
goto cleanup;
/* Create a PGPFile object for the new backup file */
newPGPFile = pgpFileReadOpen( cdkContext, newStdFile, NULL, NULL );
if ( IsNull( newPGPFile ) )
{
pgpStdIOClose( newStdFile );
err = kPGPError_OutOfMemory; /* XXX: Return better error */
goto cleanup;
}
/*
* Switch the ringfile's backing store
* from the temp file to the new backup file.
*/
ringFileSwitchFile( newRingFile, newPGPFile );
pgpFileClose( tempPGPFile );
tempPGPFile = NULL;
/*
* Delete the main ring file and move the temp file into its place.
*
* XXX: Race condition here, we must release the lock momentarily
*/
{
/* We'll need this for renaming */
err = PFLGetFileSpecName( rdb->mainFileRef, &mainFileName );
if ( IsPGPError( err ) )
goto cleanup;
/* Close the main ring file, releasing our lock */
if ( IsntNull( rdb->mainStdFile ) )
{
err = pgpStdIOClose( rdb->mainStdFile );
if ( IsPGPError( err ) )
goto cleanup;
rdb->mainStdFile = NULL;
}
/* Try to delete the main ring file, ignoring errors */
(void)PFLFileSpecDelete( rdb->mainFileRef );
/* Move the temp file into the main ring's place */
err = PFLFileSpecRename( tempFileRef, mainFileName );
if ( IsPGPError( err ) )
goto cleanup;
/* Relock the main keyring file to ensure exclusive access */
err = pgpFileRefStdIOOpen( rdb->mainFileRef,
kPGPFileOpenReadWritePerm |
kPGPFileOpenLockFile,
fileType,
&rdb->mainStdFile );
if ( IsPGPError( err ) )
goto cleanup;
/* XXX: We should swap fileIDs here on the Mac */
}
/* We must close mutableSet in order to cleanly close oldRingFile */
ringSetDestroy( rdb->mutableSet );
rdb->mutableSet = NULL;
/* Now close the old backup or temp file */
err = ringFileClose( oldRingFile );
pgpFileClose( oldPGPFile ); /* Close the PGPFile regardless */
if ( IsPGPError( err ) )
{
/*
* We shouldn't get here. Probably due to a pesky RingSet leak!
*/
pgpDebugMsg( "Failed to close oldRingFile" );
}
/* If it was a local temp file, delete it immediately */
if ( rdb->btempfile )
{
PFLFileSpecDelete( rdb->readFileRef );
rdb->btempfile = FALSE;
}
PGPFreeData( mainFileName );
PFLFreeFileSpec( rdb->readFileRef );
rdb->readFileRef = newFileRef;
newFileRef = NULL;
PFLFreeFileSpec( tempFileRef );
tempFileRef = NULL;
rdb->ringFile = newRingFile;
rdb->pgpFile = newPGPFile;
PurgeOldBackups( rdb->mainFileRef );
return kPGPError_NoErr;
cleanup:
if ( IsNull( rdb->mainStdFile ) )
{
/* If we lost our exclusive lock, we must forfeit write access */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -