📄 fileio.c
字号:
/*____________________________________________________________________________
fileio.c
Copyright(C) 1998,1999 Network Associates, Inc.
All rights reserved.
PGP 6.5 Command Line
I/O routines for PGP
PGP: Pretty Good(tm) Privacy - public key cryptography for the masses.
Modified 16 Apr 92 - HAJK
Mods for support of VAX/VMS file system
Modified 17 Nov 92 - HAJK
Change to temp file stuff for VMS.
$Id: fileio.c,v 1.19 1999/05/12 21:01:03 sluu Exp $
____________________________________________________________________________*/
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#if PGP_UNIX
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#ifdef _BSD
#include <sys/param.h>
#endif
extern int errno;
#endif /* PGP_UNIX */
#ifdef VMS
#include <file.h>
#include <assert.h>
#endif
#include "pgpBase.h"
#include "pgpErrors.h"
#include "pgpContext.h"
#include "pgpDiskWiper.h"
#include "pgpClientErrors.h"
#include "config.h"
#include "usuals.h"
//#include "stubs.h"
#include "globals.h"
#include "fileio.h"
#include "language.h"
#include "pgp.h"
#if PGP_WIN32 || defined(MSDOS) || defined(OS2)
#include <io.h>
#include <fcntl.h>
#endif
#ifndef F_OK
#define F_OK 0
#define X_OK 1
#define W_OK 2
#define R_OK 4
#endif /* !F_OK */
/*
* DIRSEPS is a string of possible directory-separation characters
* The first one is the preferred one, which goes in between
* PGPPATH and the file name if PGPPATH is not terminated with a
* directory separator.
*/
#if defined(MSDOS) || defined(__MSDOS__) || defined(OS2)
static char const DIRSEPS[] = "\\/:";
#if PGP_WIN32
#define MULTIPLE_DOTS
#endif
#define BSLASH
#elif defined(ATARI)
static char const DIRSEPS[] = "\\/:";
#define BSLASH
#elif defined(PGP_UNIX)
static char const DIRSEPS[] = "/";
#define MULTIPLE_DOTS
#elif defined(AMIGA)
static char const DIRSEPS[] = "/:";
#define MULTIPLE_DOTS
#elif defined(VMS)
static char const DIRSEPS[] = "]:"; /* Any more? */
#else
/* #error is not portable, this has the same effect */
#include "Unknown OS"
#endif
/* 1st character of temporary file extension */
#define TMP_EXT '$' /* extensions are '.$##' */
/* The PGPPATH environment variable */
static char PGPPATH[] = "PGPPATH";
/* Disk buffers, used here and in crypto.c */
static PGPByte textbuf[DISKBUFSIZE];
static unsigned textbuf2[2 * DISKBUFSIZE / sizeof(unsigned)];
#if 0
/* Don't forget to change 'pgp60' whenever you update relVersion past 6.0 */
static char const *const manual_dirs[] =
{
#if defined(VMS)
"$PGPPATH", "", "[pgp]", "[pgp60]", "[pgp600]",
PGP_SYSTEM_DIR, "SYS$LOGIN:", "SYS$LOGIN:[pgp]",
"SYS$LOGIN:[pgp60]", "SYS$LOGIN:[pgp600]", "[-]",
#elif defined(PGP_UNIX)
"$PGPPATH", "", "pgp", "pgp60", "pgp600", PGP_SYSTEM_DIR,
"$HOME/.pgp", "$HOME", "$HOME/pgp", "$HOME/pgp60", "..",
#elif defined(AMIGA)
"$PGPPATH", "", "pgp", "pgp60", ":pgp", ":pgp60", ":pgp600",
":", "/",
#else /* MSDOS or ATARI */
"$PGPPATH", "", "pgp", "pgp60", "\\pgp", "\\pgp60", "\\pgp600",
"\\", "..", "c:\\pgp", "c:\\pgp60",
#endif
0};
#endif
static struct pgpfileBones _fileBones;
void initFileBones(struct pgpmainBones *mainbPtr)
{
struct pgpfileBones *filebPtr = &_fileBones;
filebPtr->HLP_EXTENSION = ".hlp";
filebPtr->PGP_EXTENSION = ".pgp";
filebPtr->ASC_EXTENSION = ".asc";
filebPtr->SIG_EXTENSION = ".sig";
filebPtr->BAK_EXTENSION = ".bak";
filebPtr->PKR_EXTENSION = ".pkr";
filebPtr->SKR_EXTENSION = ".skr";
filebPtr->HELP_FILENAME = "pgp.hlp";
filebPtr->CONSOLE_FILENAME = "_CONSOLE";
filebPtr->moreon=FALSE;
filebPtr->piping=FALSE;
filebPtr->savepgpout=NULL;
filebPtr->envbPtr = mainbPtr->envbPtr;
filebPtr->mainbPtr = mainbPtr;
mainbPtr->filebPtr = filebPtr;
}
PGPBoolean fileExists(char *filename)
/* Returns TRUE iff file exists. */
{
return access(filename, F_OK) == 0;
} /* file_exists */
static PGPBoolean isRegularFile(char *filename)
{
#ifdef S_ISREG
struct stat st;
return stat(filename, &st) != -1 && S_ISREG(st.st_mode);
#else
return TRUE;
#endif
}
/*
* This wipes a file using the improved wiping strategy. The purpose of
* this is to make sure no sensitive information is left on the disk.
*
* Note that the file MUST be open for read/write.
*
* It may not work to eliminate everything from non-volatile storage
* if the OS you're using does its own paging or swapping. Then
* it's an issue of how the OS's paging device is wiped, and you can
* only hope that the space will be reused within a few seconds.
*
* Also, some file systems (in particular, the Logging File System
* for Sprite) do not write new data in the same place as old data,
* defeating this wiping entirely. Fortunately, such systems
* usually don't need a swap file, and for small temp files, they
* do write-behind, so if you create and delete a file fast enough,
* it never gets written to disk at all.
*/
static void wipeout( struct pgpfileBones *filebPtr, FILE *fp )
{
PGPContextRef context = filebPtr->mainbPtr->pgpContext;
PGPEnv *env = pgpContextGetEnvironment( context );
PGPError err = kPGPError_NoErr;
PGPDiskWipeRef wipeRef = kPGPInvalidRef;
PGPInt32 passes = kPGPNumPatterns;
PGPInt32 buffer[256];
#if PGP_DEBUG
PGPInt32 pri;
#endif /* PGP_DEBUG */
err = PGPCreateDiskWiper( context, &wipeRef, passes);
if( IsntPGPError(err) )
{
while( IsntPGPError( PGPGetDiskWipeBuffer(wipeRef, buffer) ) )
{
#if HAVE_FTELLO
PGPInt64 len;
#else
PGPInt32 len;
#endif
int i = 1;
#if PGP_DEBUG
if (pgpenvGetInt( env, PGPENV_VERBOSE, &pri, &err ))
fprintf(filebPtr->pgpout, LANG("Pass #%d\r\n"), i++);
#endif /* PGP_DEBUG */
/*
Write pattern to disk here, repeating until end of the
file. We should round the file size up to next page
boundary to be safe. Also make sure to sync the file
after writing each pattern so that they actually get
written to disk and not just an IO buffer.
*/
/* Get the file size */
fseek(fp, 0L, SEEK_END);
#if HAVE_FTELLO
len = ftello(fp);
/* instead of wiping very long files you really should
degauss the media, but we aim to please. */
#else
len = ftell(fp);
#endif
rewind(fp);
while (len > 0) {
/* Write it out - yes, we're ignoring errors */
fwrite((char const *) buffer, sizeof(buffer), 1, fp);
len -= sizeof(buffer);
}
fflush( fp );
/* try to sync it to the disk.. */
#ifdef PGP_UNIX
sync();
#endif
}
err = PGPDestroyDiskWiper(wipeRef);
pgpAssertNoErr(err);
}
if(IsPGPError(err))
{
char buf[256];
PGPGetClientErrorString( err,256, buf );
fprintf( filebPtr->pgpout, LANG("Error!!!\r\n%s\r\n"), buf);
}
}
/*
* Completely overwrite and erase file, so that no sensitive
* information is left on the disk.
*/
int wipefile(struct pgpfileBones *filebPtr, char *filename)
{
FILE *fp;
#if PGP_DEBUG
fprintf( filebPtr->pgpout, LANG("wiping file %s"),filename);
#endif
/* open file f for read/write, in binary (not text) mode... */
if ((fp = fopen(filename, FOPRWBIN)) == NULL)
return -1; /* error - file can't be opened */
wipeout(filebPtr, fp);
fclose(fp);
return 0; /* normal return */
} /* wipefile */
/*
Returns the part of a filename after all directory specifiers.
Note: some functions depend on the side-effect that the returned
pointer rests _within_ the source string.
*/
char *fileTail(char *filename)
{
char *p;
char const *s = DIRSEPS;
while (*s) {
p = strrchr(filename, *s);
if (p)
filename = p + 1;
s++;
}
return filename;
}
PGPBoolean dropFilename(char *filename)
{
char *p = NULL;
char const *s = DIRSEPS;
p = strrchr(filename, *s);
if(p)
{
p[1] = '\0';
return TRUE;
}
return FALSE;
}
/* return TRUE if extension matches the end of filename */
PGPBoolean hasExtension(char *filename, char *extension)
{
int lf = strlen(filename);
int lx = strlen(extension);
if (lf <= lx)
return FALSE;
return !strcmp(filename + lf - lx, extension);
}
/* return TRUE if path is a filename created by tempFile() */
/* Filename matches "*.$[0-9][0-9]" */
PGPBoolean isTempFile( struct pgpfileBones *filebPtr, char *path)
{
char *p = strrchr(path, '.');
return p != NULL && p[1] == TMP_EXT &&
isdigit(p[2]) && isdigit(p[3]) && p[4] == '\0';
}
/*
* Returns TRUE if user left off file extension, allowing default.
* Note that the name is misleading if multiple dots are allowed.
* not_pgp_extension or something would be better.
*/
PGPBoolean noExtension( struct pgpfileBones *filebPtr, char *filename )
{
#ifdef MULTIPLE_DOTS /* filename can have more than one dot */
if (hasExtension(filename, filebPtr->ASC_EXTENSION) ||
hasExtension(filename, filebPtr->PGP_EXTENSION) ||
hasExtension(filename, filebPtr->PKR_EXTENSION) ||
hasExtension(filename, filebPtr->SKR_EXTENSION) ||
hasExtension(filename, filebPtr->SIG_EXTENSION) ||
isTempFile(filebPtr, filename))
return FALSE;
else
return TRUE;
#else
filename = fileTail(filename);
return strrchr(filename, '.') == NULL;
#endif
} /* no_extension */
/* deletes trailing ".xxx" file extension after the period. */
void dropExtension(struct pgpfileBones *filebPtr, char *filename )
{
if (!noExtension( filebPtr, filename ))
*strrchr(filename, '.') = '\0';
} /* drop_extension */
/* append filename extension if there isn't one already. */
void defaultExtension( struct pgpfileBones *filebPtr, char *filename,
char *extension )
{
if (noExtension( filebPtr, filename ))
strcat(filename, extension);
} /* default_extension */
#ifndef MAX_NAMELEN
#if defined(AMIGA)||defined(NeXT)||(defined(BSD)&&BSD>41)\
||(defined(sun)&&defined(i386))
#define MAX_NAMELEN 255
#else
#include <limits.h>
#endif
#endif
/* truncate the filename so that an extension can be tacked on. */
static void truncateName( struct pgpfileBones *filebPtr, char *path,
int ext_len )
{
struct pgpenvBones *envbPtr = filebPtr->envbPtr;
PGPEnv *env = envbPtr->m_env;
#ifdef PGP_UNIX /* for other systems this is a no-op */
PGPInt32 pri;
PGPError err;
char *p;
#ifdef MAX_NAMELEN /* overrides the use of pathconf() */
int namemax = MAX_NAMELEN;
#else
int namemax;
#ifdef _PC_NAME_MAX
char dir[MAX_PATH];
strcpy(dir, path);
if ((p = strrchr(dir, '/')) == NULL) {
strcpy(dir, ".");
} else {
if (p == dir)
++p;
*p = '\0';
}
if ((namemax = pathconf(dir, _PC_NAME_MAX)) <= ext_len)
return;
#else
#ifdef NAME_MAX
namemax = NAME_MAX;
#else
namemax = 14;
#endif /* NAME_MAX */
#endif /* _PC_NAME_MAX */
#endif /* MAX_NAMELEN */
if ((p = strrchr(path, '/')) == NULL)
p = path;
else
++p;
if (strlen(p) > namemax - ext_len) {
if (pgpenvGetInt( env, PGPENV_VERBOSE, &pri, &err ))
fprintf(filebPtr->pgpout, LANG("Truncating filename '%s' "),
path);
p[namemax - ext_len] = '\0';
if (pgpenvGetInt( env, PGPENV_VERBOSE, &pri, &err ))
fprintf(filebPtr->pgpout, LANG("to '%s'\n"), path);
}
#endif /* PGP_UNIX */
}
/* change the filename extension. */
void forceExtension( struct pgpfileBones *filebPtr, char *filename,
char *extension )
{
dropExtension( filebPtr, filename ); /* out with the old */
truncateName( filebPtr, filename, strlen(extension) );
strcat(filename, extension); /* in with the new */
} /* force_extension */
/*
* Get yes/no answer from user, returns TRUE for yes, FALSE for no.
* First the translations are checked, if they don't match 'y' and 'n'
* are tried.
*/
PGPBoolean getyesno(struct pgpfileBones *filebPtr, char default_answer,
PGPBoolean batchmode)
{
char buf[8];
static char yes[8], no[8];
FILE *promptfp;
if(filebPtr)
promptfp=filebPtr->pgpout;
else
promptfp=stderr;
if (yes[0] == '\0') {
strncpy(yes, LANG("y"), 7);
strncpy(no, LANG("n"), 7);
}
if (!batchmode) { /* return default answer in batchmode */
pgpTtyGetString(buf, 6, promptfp); /* echo keyboard input */
strlwr(buf);
if (!strncmp(buf, no, strlen(no)))
return FALSE;
if (!strncmp(buf, yes, strlen(yes)))
return TRUE;
if (buf[0] == 'n')
return FALSE;
if (buf[0] == 'y')
return TRUE;
}
return default_answer == 'y' ? TRUE : FALSE;
}/* getyesno */
/* if user consents to it, change the filename extension. */
char *maybeForceExtension( struct pgpfileBones *filebPtr, char *filename,
char *extension )
{
struct pgpenvBones *envbPtr = filebPtr->envbPtr;
PGPEnv *env = envbPtr->m_env;
PGPInt32 pri;
PGPError err;
static char newname[MAX_PATH];
if (!hasExtension(filename, extension)) {
strcpy(newname, filename);
forceExtension( filebPtr, newname, extension );
if (!fileExists(newname)) {
fprintf(filebPtr->pgpout,
LANG("\nShould '%s' be renamed to '%s' [Y/n]? "),
filename, newname);
if (getyesno(filebPtr, 'y', (PGPBoolean)pgpenvGetInt( env,
PGPENV_BATCHMODE, &pri, &err )))
return newname;
}
}
return NULL;
}/* maybe_force_extension */
/*
* Add a trailing directory separator to a name, if absent.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -