⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pgpfiledb.c

📁 著名的加密软件的应用于电子邮件中
💻 C
📖 第 1 页 / 共 2 页
字号:
/*
* pgpFileDB.c -- Manage PGPKeyDBs based on RingFiles
*
* Copyright (C) 1996,1997 Pretty Good Privacy, Inc. All rights reserved.
*
* $Id: pgpFileDB.c,v 1.31.2.5 1997/06/07 09:50:22 mhw Exp $
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef HAVE_FCNTL_H
#include <fcntl.h>		/* For O_CREAT, O_EXCL */
#endif

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef UNIX
#include <sys/stat.h>
#endif

#ifndef W_OK
#define W_OK 2
#endif

#include "pgpKDBint.h"
#include "pgpFileRef.h"
#include "pgpFileNames.h"
#include "pgpDebug.h"
#include "pgpFile.h"
#include "pgpRngRead.h"

/* Private data for RingFile type of PGPKeyDB */
typedef struct RingDB {
	RingFile *rfile;			/* RingFile for keys */
	PgpFile *pfile;		 	/* PgpFile for keys */
	PGPFileRef *fileRef;		/* fileRef for keys */
	RingSet *rsmut;		 	/* Mutable ringset */
	RingSet const *rsimmut;		 /* Immutable ringset */
		RingSet	*rsfrozen;		 /* Frozen copy of rsmut */
		byte	 		*membuf;			/* Memory buffer for memory pfiles */
		Boolean	bwriteable;		 /* True if a writeable database */
		Boolean	bdirty;		 	/* True if rsmut has been changed */
		Boolean	bprivate;		/* True if a private keyring */
		Boolean	btrusted;		/* True if should get trust packets */
	Boolean			bfreebuf;	 	/* True if should free membuf on close */
} RingDB;



/***************************** File IO ****************************/


/*
* Create a temp key file which is not used by anyone else.
* Return a stream pointer to the file, and set the file reference in
* *tmpFileRef1. Base the file reference on the given file.
* The returned file reference needs to be free'd with pgpFreeFileRef, and of
* course the returned file handle needs to be closed too.
* The implementation of this tends to be OS (and possibly file system)
* dependent.
*/
static FILE *
tmpkeyfile (PGPFileRef const *fileRef, PGPFileRef **tmpFileRef1,
			PGPFileOpenFlags flags, Boolean isPrivate)
{
	char *				file = NULL;
	char *				tmp = NULL;
	char *				nfile = NULL;
	char *				nfilep = NULL;
	PGPFileRef *		tmpFileRef = NULL;
	FILE *				stdFile = NULL;
	
	if ((file = pgpGetFileRefName(fileRef)) == NULL)
		goto error;
	if ((tmp = pgpFileNameContract(file)) == NULL)
		goto error;
	if ((nfile = pgpFileNameExtend(tmp, ".pg0")) == NULL)
		goto error;
	nfilep = nfile + strlen(file) - 1;
	if ((tmpFileRef = pgpCopyFileRef(fileRef)) == NULL)
		goto error;
	for ( ; ; ) {
		if ((pgpSetFileRefName(&tmpFileRef, nfile)) != PGPERR_OK)
			goto error;
		if ((stdFile = pgpFileRefStdIOOpen(tmpFileRef, flags,
							isPrivate ? kPGPFileTypePrivRing
								: kPGPFileTypePubRing, NULL)) != NULL)
			break;
		if ((*nfilep)++ > 'z')
			goto error;
	}
error:
	pgpMemFree(file);
	pgpMemFree(tmp);
	pgpMemFree(nfile);
	if (stdFile != NULL)
		*tmpFileRef1 = tmpFileRef;
	else if (tmpFileRef != NULL)
		pgpFreeFileRef(tmpFileRef);
	return stdFile;
}

/*
* Error recovery routine for do_write, below. Called to re-open the
* original file due to some problem while writing out the updated version.
* The original file is back to its original filename. newfile is the
* tmp ringfile which we have succeeded in outputting. Use it to restore
* rsmut. Closing it will be done by our caller.
*/
static void
do_recover (RingDB *rdb, RingFile *newfile)
{
			FILE *f;
			PGPError error;
			RingPool *ringpool = ringSetPool (rdb->rsimmut);

		f = pgpFileRefStdIOOpen(rdb->fileRef,
				 		 		 	rdb->bwriteable ? kPGPFileOpenReadWritePerm
				 		 		 		 		 	: kPGPFileOpenReadPerm,
				 		 		 	kPGPFileTypeNone, &error);
			pgpAssert (f);
			rdb->pfile = pgpFileReadOpen (f, NULL, NULL);
			pgpAssert (rdb->pfile);
			rdb->rfile = ringFileOpen(ringpool, rdb->pfile, rdb->btrusted, &error);
			pgpAssert (rdb->rfile);
			rdb->rsimmut = ringFileSet (rdb->rfile);
			if (newfile) {
			rdb->rsmut = ringSetCreate (ringpool);
				 ringSetAddSet (rdb->rsmut, ringFileSet (newfile));
			}
}


/*
* This writes out a RingSet which is a replacement for a RingFile
* which is open. We do it carefully since on some OS's we can't
* rename open files. This closes the RingFile but leaves the
* RingSet open and pointing to the filename.
* Renames existing file.ext to file.bak.
* Variables with suffix "1" refer to original file; suffix "2" are
* for a tmp file; suffix "3" are for the updated file.
* The reason the tmp file is used is to have some backing store so
* we can close and rename the original file before outputting the
* updated version.
*
* There are some tricky issues here re error recovery. Especially once
* we get rid of the original key file, we may not be able to get it back
* while we have the other one open. So we will require that the .bak file
* renaming must work, so that we can rename the .bak file back if necessary.
* Otherwise we have problems when we run out of disk space midway. This
* means that the write will fail if the .bak file renaming fails, such as if
* there is a .bak file but it is not writeable or deletable.
*/
static PGPError
do_write (RingDB *rdb)
	{
			char	*file;						/* Filename for keyring */
			char	*bak,						/* .bak filename for keyring */
			*tmp;						/* Temp to help create .bak */
			PGPFileRef	*tmpFileRef;					/* Temp file reference */
			PGPFileRef	*bakFileRef;					/* Bak file reference */
			RingSet		*set;					/* RingSet we are writing out */
			RingFile		*rfile1,					/* Original RingFile for keyring */
					*rfile2,					/* RingFile for temp file */
				 				*rfile3;					/* RingFile for updated keyring file */
		PgpFile		*pfp1,			 		/* Original PgpFile for keyring */
					*pfp2,					/* PgpFile for temp file */
				 				*pfp3;					/* PgpFile for updated keyring file */
			FILE		*fp2,					/* Stream handle for temp file */
					*fp3;					/* Stream handle for updated keyring */
	int 	flags;
	int error = 0, err1;

	set = rdb->rsmut;
	rfile1 = rdb->rfile;
	pfp1 = rdb->pfile;
	flags = (rdb->btrusted && !rdb->bprivate) ? PGP_WRITETRUST_PUB : 0;

	file = pgpGetFileRefName(rdb->fileRef);

	tmp = pgpFileNameContract (file);
#if MACINTOSH
	bak = pgpFileNameExtend (tmp, " (backup)");
#else
	bak = pgpFileNameExtend (tmp, ".bak");
#endif
	bakFileRef = pgpCopyFileRef(rdb->fileRef);
	pgpSetFileRefName(&bakFileRef, bak);
	pgpFree(bak);
	pgpFree(tmp);
	pgpFree(file);
	fp2 = tmpkeyfile (rdb->fileRef, &tmpFileRef, kPGPFileOpenStdUpdateFlags,
						rdb->bprivate);
	if (!fp2) {
		pgpFreeFileRef (tmpFileRef);
		pgpFreeFileRef (bakFileRef);
		return PGPERR_NO_FILE;
	}
	/* Write out new data to tmpfile2 */
	pfp2 = pgpFileWriteOpen (fp2, NULL);
	error = ringSetWrite (set, pfp2, &rfile2, PGPVERSION_3, flags);
	pgpFileFlush (pfp2);
	if (error) {
		pgpFileClose (pfp2);
		pgpDeleteFile (tmpFileRef);
		pgpFreeFileRef (tmpFileRef);
		pgpFreeFileRef (bakFileRef);
		return error;
	}

	/* Must close set in order to be able to cleanly close rfile1 */
	ringSetDestroy(set);
	rdb->rsmut = NULL;
	if (ringFileClose (rfile1) != 0) {
		/* Have a problem here, it should have closed */
		/* Probably due to a pesky RingSet leak! */
		/* Actually happens a lot with pgp3, it doesn't care */
/*		fprintf (stderr, "Warning: %s didn't close!\n", file); */
		pgpAssert (0);
	}
	/* Close the PgpFile regardless */
	pgpFileClose (pfp1);

	/*
	* Convert temp PgpFile from write mode to read mode now, it will
	* serve as the backing store for the next step.
	*/
	error = pgpFileWrite2Read (pfp2);
	pgpAssert (error == 0);

	/* Now rename the old file */
	pgpDeleteFile (bakFileRef);
 error = pgpRenameFile (rdb->fileRef, bakFileRef);

#if 0
/*
	* The software is operating in a mode where this rename often fails.
	* If another process has the keyring file open under Windows, the file
	* can't be renamed. What happens in that case is we proceed with the
	* write and then the app posts a message to the other app(s) telling
	* it to re-read the keyring file. So we will not call that an error
	* at this point.
*/
	if (error) {
		/* Failed to rename, we cannot proceed. Get orig file open again */
		do_recover (rdb, rfile2);
		ringFileClose (rfile2);
		pgpFileClose (pfp2);
		pgpDeleteFile (tmpFileRef);
		pgpFreeFileRef (tmpFileRef);
		pgpFreeFileRef (bakFileRef);
		return PGPERR_NO_FILE;
	}
#endif

	/* Output data again, this time to original filename */
	fp3 = pgpFileRefStdIOOpen (rdb->fileRef, kPGPFileOpenStdUpdateFlags,
								rdb->bprivate ? kPGPFileTypePrivRing
											: kPGPFileTypePubRing, NULL);
	pfp3 = pgpFileWriteOpen (fp3, NULL);
	if (!pfp3) {
			/*
		* Failure to open output file. Should not happen since we just
		* had one and renamed it to .bak. Try to recover anyway.
		* Rename .bak file back to original and re-open it.
			*/
		pgpDeleteFile (rdb->fileRef);
		error = pgpRenameFile (bakFileRef, rdb->fileRef);
		pgpAssert (error == PGPERR_OK); /* Else we are lost */
		do_recover (rdb, rfile2);
		ringFileClose (rfile2);
		pgpFileClose (pfp2);
		pgpDeleteFile (tmpFileRef);
		pgpFreeFileRef (tmpFileRef);
		pgpFreeFileRef (bakFileRef);
		return PGPERR_NO_FILE;
	}
	error = ringSetWrite (ringFileSet(rfile2), pfp3, &rfile3,
		PGPVERSION_3, flags);
	pgpFileFlush (pfp3);
	if (error) {
		/* Try to recover by restoring .bak file */
		pgpFileClose (pfp3);
		pgpDeleteFile (rdb->fileRef);
		err1 = pgpRenameFile (bakFileRef, rdb->fileRef);
		pgpAssert (err1 == PGPERR_OK);	/* Else we are lost */
		do_recover (rdb, rfile2);
		ringFileClose (rfile2);
		pgpFileClose (pfp2);
		pgpDeleteFile (tmpFileRef);
		pgpFreeFileRef (tmpFileRef);
		pgpFreeFileRef (bakFileRef);
		return error;
}

	/* Delete tmp file now */
	err1 = ringFileClose (rfile2);
	pgpAssert (err1==0);
	pgpFileClose (pfp2);
	pgpDeleteFile (tmpFileRef);
	pgpFreeFileRef (tmpFileRef);

	/*
	* Convert final PgpFile from write mode to read mode now, it will
	* serve as the backing store from now on.
	*/
	error = pgpFileWrite2Read (pfp3);
	pgpAssert (error == PGPERR_OK);

	pgpFreeFileRef (bakFileRef);

	rdb->rfile = rfile3;
	rdb->pfile = pfp3;
	return PGPERR_OK;
}

/*
* Open the specified ringfile. If writeable flag is clear, it will be
* forced read-only, otherwise whether it is writeable will depend on
* permissions. Return 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
do_open (PGPFileRef const *fileRef1, int trusted, int private, int writeable,
	RingPool *ringpool, RingDB *rdb)
{
		PGPFileRef	*fileRef;
		FILE	*f;
		PgpFile	*pfile;
		RingFile	*rfile;
	RingSet const *rsimmut;
	RingSet *rsmut;
	int error;

	rsmut = ringSetCreate (ringpool);
	if (!rsmut) {
		return PGPERR_NOMEM;
	}

	fileRef = pgpCopyFileRef(fileRef1);

	f = pgpFileRefStdIOOpen(fileRef, writeable ? kPGPFileOpenReadWritePerm
												: kPGPFileOpenReadPerm,
							kPGPFileTypeNone, &error);
	if (!f) {
		/* Try creating file if it did not exist */
		f = pgpFileRefStdIOOpen(fileRef, kPGPFileOpenStdUpdateFlags,
								private ? kPGPFileTypePrivRing
									: kPGPFileTypePubRing, &error);
		if (!f) {
			ringSetDestroy (rsmut);
			pgpFreeFileRef(fileRef);
			return error;
		}
	}

	pfile = pgpFileReadOpen (f, NULL, NULL);
	if (!pfile) {
		pgpStdIOClose (f);
		ringSetDestroy (rsmut);
		pgpFreeFileRef(fileRef);
		return PGPERR_NOMEM;
	}

	rfile = ringFileOpen (ringpool, pfile, trusted, &error);
	if (!rfile) {
		pgpFileClose (pfile);
		ringSetDestroy (rsmut);
		pgpFreeFileRef(fileRef);
		return error;
	}

	rsimmut = ringFileSet (rfile);
	ringSetAddSet (rsmut, rsimmut);

		rdb->fileRef	= fileRef;
		rdb->pfile	= pfile;
		rdb->rfile	= rfile;
		rdb->rsimmut	= rsimmut;
		rdb->rsmut	= rsmut;
	rdb->bwriteable = writeable;
	rdb->bdirty = ringFileIsDirty (rfile);
	rdb->btrusted = trusted;
	rdb->bprivate = private;

	return PGPERR_OK;
}


/* Close the open keyfile object */
static PGPError
do_close (RingDB *rdb)
{

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -