freebsd-dev/stand/ficl/fileaccess.c
Simon J. Gerraty 91cd69ee2c veloader use vectx API for kernel and modules
The vectx API, computes the hash for verifying a file as it is read.
This avoids the overhead of reading files twice - once to verify, then
again to load.

For doing an install via loader, avoiding the need to rewind
large files is critical.

This API is only used for modules, kernel and mdimage as these are the
biggest files read by the loader.
The reduction in boot time depends on how expensive the I/O is
on any given platform.  On a fast VM we see 6% improvement.

For install via loader the first file to be verified is likely to be the
kernel, so some of the prep work (finding manifest etc) done by
verify_file() needs to be factored so it can be reused for
vectx_open().

For missing or unrecognized fingerprint entries, we fail
in vectx_open() unless verifying is disabled.

Otherwise fingerprint check happens in vectx_close() and
since this API is only used for files which must be verified
(VE_MUST) we panic if we get an incorrect hash.

Reviewed by:	imp,tsoome
MFC after:	1 week
Sponsored by:	Juniper Networks
Differential Revision:	https://reviews.freebsd.org//D23827
2020-03-09 16:02:54 +00:00

433 lines
10 KiB
C

/* $FreeBSD$ */
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <sys/stat.h>
#include "ficl.h"
#if FICL_WANT_FILE
/*
**
** fileaccess.c
**
** Implements all of the File Access word set that can be implemented in portable C.
**
*/
static void pushIor(FICL_VM *pVM, int success)
{
int ior;
if (success)
ior = 0;
else
ior = errno;
stackPushINT(pVM->pStack, ior);
}
static void ficlFopen(FICL_VM *pVM, char *writeMode) /* ( c-addr u fam -- fileid ior ) */
{
int fam = stackPopINT(pVM->pStack);
int length = stackPopINT(pVM->pStack);
void *address = (void *)stackPopPtr(pVM->pStack);
char mode[4];
FILE *f;
char *filename = (char *)alloca(length + 1);
memcpy(filename, address, length);
filename[length] = 0;
*mode = 0;
switch (FICL_FAM_OPEN_MODE(fam))
{
case 0:
stackPushPtr(pVM->pStack, NULL);
stackPushINT(pVM->pStack, EINVAL);
return;
case FICL_FAM_READ:
strcat(mode, "r");
break;
case FICL_FAM_WRITE:
strcat(mode, writeMode);
break;
case FICL_FAM_READ | FICL_FAM_WRITE:
strcat(mode, writeMode);
strcat(mode, "+");
break;
}
strcat(mode, (fam & FICL_FAM_BINARY) ? "b" : "t");
f = fopen(filename, mode);
if (f == NULL)
stackPushPtr(pVM->pStack, NULL);
else
#ifdef LOADER_VERIEXEC
if (*mode == 'r' &&
verify_file(fileno(f), filename, 0, VE_GUESS, __func__) < 0) {
fclose(f);
stackPushPtr(pVM->pStack, NULL);
} else
#endif
{
ficlFILE *ff = (ficlFILE *)malloc(sizeof(ficlFILE));
strcpy(ff->filename, filename);
ff->f = f;
stackPushPtr(pVM->pStack, ff);
fseek(f, 0, SEEK_SET);
}
pushIor(pVM, f != NULL);
}
static void ficlOpenFile(FICL_VM *pVM) /* ( c-addr u fam -- fileid ior ) */
{
ficlFopen(pVM, "a");
}
static void ficlCreateFile(FICL_VM *pVM) /* ( c-addr u fam -- fileid ior ) */
{
ficlFopen(pVM, "w");
}
static int closeFiclFILE(ficlFILE *ff) /* ( fileid -- ior ) */
{
FILE *f = ff->f;
free(ff);
return !fclose(f);
}
static void ficlCloseFile(FICL_VM *pVM) /* ( fileid -- ior ) */
{
ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
pushIor(pVM, closeFiclFILE(ff));
}
static void ficlDeleteFile(FICL_VM *pVM) /* ( c-addr u -- ior ) */
{
int length = stackPopINT(pVM->pStack);
void *address = (void *)stackPopPtr(pVM->pStack);
char *filename = (char *)alloca(length + 1);
memcpy(filename, address, length);
filename[length] = 0;
pushIor(pVM, !unlink(filename));
}
static void ficlRenameFile(FICL_VM *pVM) /* ( c-addr1 u1 c-addr2 u2 -- ior ) */
{
int length;
void *address;
char *from;
char *to;
length = stackPopINT(pVM->pStack);
address = (void *)stackPopPtr(pVM->pStack);
to = (char *)alloca(length + 1);
memcpy(to, address, length);
to[length] = 0;
length = stackPopINT(pVM->pStack);
address = (void *)stackPopPtr(pVM->pStack);
from = (char *)alloca(length + 1);
memcpy(from, address, length);
from[length] = 0;
pushIor(pVM, !rename(from, to));
}
static void ficlFileStatus(FICL_VM *pVM) /* ( c-addr u -- x ior ) */
{
struct stat statbuf;
int length = stackPopINT(pVM->pStack);
void *address = (void *)stackPopPtr(pVM->pStack);
char *filename = (char *)alloca(length + 1);
memcpy(filename, address, length);
filename[length] = 0;
if (stat(filename, &statbuf) == 0)
{
/*
** the "x" left on the stack is implementation-defined.
** I push the file's access mode (readable, writeable, is directory, etc)
** as defined by ANSI C.
*/
stackPushINT(pVM->pStack, statbuf.st_mode);
stackPushINT(pVM->pStack, 0);
}
else
{
stackPushINT(pVM->pStack, -1);
stackPushINT(pVM->pStack, ENOENT);
}
}
static void ficlFilePosition(FICL_VM *pVM) /* ( fileid -- ud ior ) */
{
ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
long ud = ftell(ff->f);
stackPushINT(pVM->pStack, ud);
pushIor(pVM, ud != -1);
}
static long fileSize(FILE *f)
{
struct stat statbuf;
statbuf.st_size = -1;
if (fstat(fileno(f), &statbuf) != 0)
return -1;
return statbuf.st_size;
}
static void ficlFileSize(FICL_VM *pVM) /* ( fileid -- ud ior ) */
{
ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
long ud = fileSize(ff->f);
stackPushINT(pVM->pStack, ud);
pushIor(pVM, ud != -1);
}
#define nLINEBUF 256
static void ficlIncludeFile(FICL_VM *pVM) /* ( i*x fileid -- j*x ) */
{
ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
CELL id = pVM->sourceID;
int result = VM_OUTOFTEXT;
long currentPosition, totalSize;
long size;
pVM->sourceID.p = (void *)ff;
currentPosition = ftell(ff->f);
totalSize = fileSize(ff->f);
size = totalSize - currentPosition;
if ((totalSize != -1) && (currentPosition != -1) && (size > 0))
{
char *buffer = (char *)malloc(size);
long got = fread(buffer, 1, size, ff->f);
if (got == size)
result = ficlExecC(pVM, buffer, size);
}
#if 0
ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
CELL id = pVM->sourceID;
char cp[nLINEBUF];
int nLine = 0;
int keepGoing;
int result;
pVM->sourceID.p = (void *)ff;
/* feed each line to ficlExec */
keepGoing = TRUE;
while (keepGoing && fgets(cp, nLINEBUF, ff->f))
{
int len = strlen(cp) - 1;
nLine++;
if (len <= 0)
continue;
if (cp[len] == '\n')
cp[len] = '\0';
result = ficlExec(pVM, cp);
switch (result)
{
case VM_OUTOFTEXT:
case VM_USEREXIT:
break;
default:
pVM->sourceID = id;
keepGoing = FALSE;
break;
}
}
#endif /* 0 */
/*
** Pass an empty line with SOURCE-ID == -1 to flush
** any pending REFILLs (as required by FILE wordset)
*/
pVM->sourceID.i = -1;
ficlExec(pVM, "");
pVM->sourceID = id;
closeFiclFILE(ff);
}
static void ficlReadFile(FICL_VM *pVM) /* ( c-addr u1 fileid -- u2 ior ) */
{
ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
int length = stackPopINT(pVM->pStack);
void *address = (void *)stackPopPtr(pVM->pStack);
int result;
clearerr(ff->f);
result = fread(address, 1, length, ff->f);
stackPushINT(pVM->pStack, result);
pushIor(pVM, ferror(ff->f) == 0);
}
static void ficlReadLine(FICL_VM *pVM) /* ( c-addr u1 fileid -- u2 flag ior ) */
{
ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
int length = stackPopINT(pVM->pStack);
char *address = (char *)stackPopPtr(pVM->pStack);
int error;
int flag;
if (feof(ff->f))
{
stackPushINT(pVM->pStack, -1);
stackPushINT(pVM->pStack, 0);
stackPushINT(pVM->pStack, 0);
return;
}
clearerr(ff->f);
*address = 0;
fgets(address, length, ff->f);
error = ferror(ff->f);
if (error != 0)
{
stackPushINT(pVM->pStack, -1);
stackPushINT(pVM->pStack, 0);
stackPushINT(pVM->pStack, error);
return;
}
length = strlen(address);
flag = (length > 0);
if (length && ((address[length - 1] == '\r') || (address[length - 1] == '\n')))
length--;
stackPushINT(pVM->pStack, length);
stackPushINT(pVM->pStack, flag);
stackPushINT(pVM->pStack, 0); /* ior */
}
static void ficlWriteFile(FICL_VM *pVM) /* ( c-addr u1 fileid -- ior ) */
{
ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
int length = stackPopINT(pVM->pStack);
void *address = (void *)stackPopPtr(pVM->pStack);
clearerr(ff->f);
fwrite(address, 1, length, ff->f);
pushIor(pVM, ferror(ff->f) == 0);
}
static void ficlWriteLine(FICL_VM *pVM) /* ( c-addr u1 fileid -- ior ) */
{
ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
size_t length = (size_t)stackPopINT(pVM->pStack);
void *address = (void *)stackPopPtr(pVM->pStack);
clearerr(ff->f);
if (fwrite(address, 1, length, ff->f) == length)
fwrite("\n", 1, 1, ff->f);
pushIor(pVM, ferror(ff->f) == 0);
}
static void ficlRepositionFile(FICL_VM *pVM) /* ( ud fileid -- ior ) */
{
ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
size_t ud = (size_t)stackPopINT(pVM->pStack);
pushIor(pVM, fseek(ff->f, ud, SEEK_SET) == 0);
}
static void ficlFlushFile(FICL_VM *pVM) /* ( fileid -- ior ) */
{
ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
pushIor(pVM, fflush(ff->f) == 0);
}
#if FICL_HAVE_FTRUNCATE
static void ficlResizeFile(FICL_VM *pVM) /* ( ud fileid -- ior ) */
{
ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
size_t ud = (size_t)stackPopINT(pVM->pStack);
pushIor(pVM, ftruncate(fileno(ff->f), ud) == 0);
}
#endif /* FICL_HAVE_FTRUNCATE */
#endif /* FICL_WANT_FILE */
void ficlCompileFile(FICL_SYSTEM *pSys)
{
#if FICL_WANT_FILE
FICL_DICT *dp = pSys->dp;
assert(dp);
dictAppendWord(dp, "create-file", ficlCreateFile, FW_DEFAULT);
dictAppendWord(dp, "open-file", ficlOpenFile, FW_DEFAULT);
dictAppendWord(dp, "close-file", ficlCloseFile, FW_DEFAULT);
dictAppendWord(dp, "include-file", ficlIncludeFile, FW_DEFAULT);
dictAppendWord(dp, "read-file", ficlReadFile, FW_DEFAULT);
dictAppendWord(dp, "read-line", ficlReadLine, FW_DEFAULT);
dictAppendWord(dp, "write-file", ficlWriteFile, FW_DEFAULT);
dictAppendWord(dp, "write-line", ficlWriteLine, FW_DEFAULT);
dictAppendWord(dp, "file-position", ficlFilePosition, FW_DEFAULT);
dictAppendWord(dp, "file-size", ficlFileSize, FW_DEFAULT);
dictAppendWord(dp, "reposition-file", ficlRepositionFile, FW_DEFAULT);
dictAppendWord(dp, "file-status", ficlFileStatus, FW_DEFAULT);
dictAppendWord(dp, "flush-file", ficlFlushFile, FW_DEFAULT);
dictAppendWord(dp, "delete-file", ficlDeleteFile, FW_DEFAULT);
dictAppendWord(dp, "rename-file", ficlRenameFile, FW_DEFAULT);
#ifdef FICL_HAVE_FTRUNCATE
dictAppendWord(dp, "resize-file", ficlResizeFile, FW_DEFAULT);
ficlSetEnv(pSys, "file", FICL_TRUE);
ficlSetEnv(pSys, "file-ext", FICL_TRUE);
#endif /* FICL_HAVE_FTRUNCATE */
#else
(void)pSys;
#endif /* FICL_WANT_FILE */
}