/*
* Copyright (c) 1985, 1989 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that: (1) source distributions retain this entire copyright
* notice and comment, and (2) distributions including binaries display
* the following acknowledgement: ``This product includes software
* developed by the University of California, Berkeley and its contributors''
* in the documentation or other materials provided with the distribution
* and in all advertising materials mentioning features or use of this
* software. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
static char Version[] = "@(#)file.c e07@nikhef.nl (Eric Wassenaar) 991529";
#endif
#include "host.h"
#define MAXCACHENAME (4+2+MAXDNAME) /* NNNN/Xdomainname */
static char cachefilebuf[MAXCACHENAME+1];
static char *cachefile = NULL; /* full name of real cache file */
static char tempcachebuf[MAXCACHENAME+1];
static char *tempcache = NULL; /* full name of temporary cache file */
static int cachefd = -1; /* cache file descriptor */
time_t cachetime = 0; /* time of last cache modification */
/*
** CACHENAME -- Construct full name of cache file
** ----------------------------------------------
**
** Returns:
** Pointer to specified buffer.
**
** Cache files are organized in a hashed directory structure,
** with the 4-digit hash value as the name of the particular
** directory. A single character prefixes the file name.
** The cache is relative to the current working directory.
*/
char *
cachename(name, buf, prefix)
input char *name; /* name of zone to process */
output char *buf; /* where to store the file name */
input char prefix; /* single char prefix for file name */
{
register unsigned int hfunc;
register char *p, *q;
register char c;
/*
* Construct the name of the hash directory.
*/
for (hfunc = 0, p = name; (c = *p) != '\0'; p++)
{
hfunc = ((hfunc << 1) ^ (lowercase(c) & 0377)) % HASHSIZE;
}
(void) sprintf(buf, "%04u/%c", hfunc, prefix);
/*
* Construct the full name of the cache file.
* Make sure there are only reasonable characters in the file name.
* Obviously there should be no further slashes in the name.
*/
for (q = &buf[strlen(buf)], p = name; (c = *p) != '\0'; p++)
{
if (!is_alnum(c) && !in_string("-._", c))
c = '?';
*q++ = lowercase(c);
}
*q = '\0';
return(buf);
}
/*
** CACHE_OPEN -- Open cache file for either read or write
** ------------------------------------------------------
**
** Returns:
** File descriptor if successfully opened.
** -1 in case of errors.
**
** Outputs:
** Sets ``cachefd'' to the cache file descriptor.
** Sets ``cachefile'' to the real cache file name.
** Sets ``tempcache'' to the temp cache name in case
** a fresh cache must be created, NULL for read only.
*/
int
cache_open(name, create)
input char *name; /* name of zone to process */
input bool create; /* set to create fresh file */
{
struct stat st;
register char *p;
/* construct the name of the associated cache file */
cachefile = cachename(name, cachefilebuf, 'C');
/* temporary cache file is not needed when read only */
tempcache = create ? cachename(name, tempcachebuf, 'T') : NULL;
/*
* If selected for read only, the cache file must just exist.
*/
if (!create)
{
cachefd = open(cachefile, O_RDONLY, 0);
if (cachefd < 0)
{
if (errno != ENOENT)
cache_perror("Cannot open", cachefile);
return(-1);
}
/* save last modification time for later reference */
cachetime = (fstat(cachefd, &st) >= 0) ? st.st_mtime : 0;
return(cachefd);
}
/*
* If necessary, create the intermediate cache directories.
*/
p = index(cachefile, '/');
while (p != NULL)
{
if (p > cachefile)
{
*p = '\0';
if (stat(cachefile, &st) < 0)
{
if (errno != ENOENT)
{
cache_perror("Cannot stat", cachefile);
return(-1);
}
if (mkdir(cachefile, 0755) < 0)
{
if (errno == EEXIST)
continue;
cache_perror("Cannot mkdir", cachefile);
return(-1);
}
}
else if ((st.st_mode & S_IFMT) != S_IFDIR)
{
seterrno(ENOTDIR);
cache_perror("Cannot create", cachefile);
return(-1);
}
*p = '/';
}
p = index(p+1, '/');
}
/*
* Create a fresh temporary cache file. It will be renamed to the
* real cache file after processing has been completed successfully.
*/
cachefd = open(tempcache, O_WRONLY|O_CREAT|O_TRUNC, 0644);
if (cachefd < 0)
{
cache_perror("Cannot open", tempcache);
return(-1);
}
/* last modification time unused during cache write */
cachetime = 0;
return(cachefd);
}
/*
** CACHE_CLOSE -- Terminate cache file processing
** ----------------------------------------------
**
** Returns:
** Status result of close or rename, if appropriate.
** 0 on success, -1 on errors.
*/
int
cache_close(create)
input bool create; /* set to create fresh file */
{
int save_errno = errno; /* preserve state */
int status = 0; /* result status */
/* we must have a valid file */
if (cachefd >= 0)
{
/* close the cache; this must succeed when writing */
status = close(cachefd);
if (status < 0 && tempcache != NULL)
{
cache_perror("Cannot close", tempcache);
/* preserve existing cache; prevent rename */
create = FALSE;
}
/* move temp cache to real cache if so requested */
if ((tempcache != NULL) && create)
{
status = rename(tempcache, cachefile);
if (status < 0)
{
cache_perror("Cannot rename", tempcache);
}
}
else if (tempcache != NULL)
{
if (unlink(tempcache) < 0)
{
cache_perror("Cannot unlink", tempcache);
}
}
}
tempcache = NULL;
cachefile = NULL;
cachefd = -1;
/* restore state */
seterrno(save_errno);
return(status);
}
/*
** CACHE_WRITE -- Write an answer buffer to the local disk cache
** -------------------------------------------------------------
**
** Returns:
** Length of buffer if successfully written.
** -1 in case of failure (error message is issued).
**
** The buffer is written in two steps: first a single word with
** the length of the buffer, followed by the buffer itself.
** Anticipate partial writes, probably not strictly necessary.
*/
int
cache_write(buf, bufsize)
input char *buf; /* location of raw answer buffer */
input int bufsize; /* length of answer buffer */
{
u_short len;
char *buffer;
int buflen;
register int n;
/* we must have a valid file */
if (cachefd < 0)
return(0);
/*
* Write the length of the answer buffer.
*/
/* len = htons((u_short)bufsize); */
putshort((u_short)bufsize, (u_char *)&len);
buffer = (char *)&len;
buflen = INT16SZ;
while (buflen > 0 && (n = write(cachefd, buffer, buflen)) > 0)
{
buffer += n;
buflen -= n;
}
if (buflen != 0)
{
if (errno == 0) seterrno(EIO);
cache_perror("Cannot write answer length", tempcache);
return(-1);
}
/*
* Write the answer buffer itself.
*/
buffer = buf;
buflen = bufsize;
while (buflen > 0 && (n = write(cachefd, buffer, buflen)) > 0)
{
buffer += n;
buflen -= n;
}
if (buflen != 0)
{
if (errno == 0) seterrno(EIO);
cache_perror("Cannot write answer", tempcache);
return(-1);
}
return(bufsize);
}
/*
** CACHE_READ -- Read an answer buffer from the local disk cache
** -------------------------------------------------------------
**
** Returns:
** Length of (untruncated) answer if successfully read.
** -1 in case of failure (error message is issued).
**
** The answer is read in two steps: first a single word which
** gives the length of the buffer, followed by the buffer itself.
** If the answer is too long to fit into the supplied buffer,
** only the portion that fits will be stored, the residu will be
** flushed, and the truncation flag will be set.
**
** Note. The returned length is that of the *un*truncated answer,
** however, and not the amount of data that is actually available.
** This may give the caller a hint about new buffer reallocation.
*/
int
cache_read(buf, bufsize)
output char *buf; /* location of buffer to store answer */
input int bufsize; /* maximum size of answer buffer */
{
u_short len;
char *buffer;
int buflen;
int reslen;
register int n;
/* we must have a valid file */
if (cachefd < 0)
return(0);
/*
* Read the length of answer buffer.
*/
buffer = (char *)&len;
buflen = INT16SZ;
while (buflen > 0 && (n = read(cachefd, buffer, buflen)) > 0)
{
buffer += n;
buflen -= n;
}
if (buflen != 0)
{
if (errno == 0) seterrno(EIO);
cache_perror("Cannot read answer length", cachefile);
return(-1);
}
/*
* Terminate if length is zero.
*/
/* len = ntohs(len); */
len = _getshort((u_char *)&len);
if (len == 0)
return(0);
/*
* Check for truncation.
* Do not chop the returned length in case of buffer overflow.
*/
reslen = 0;
if ((int)len > bufsize)
{
reslen = len - bufsize;
/* len = bufsize; */
}
/*
* Read the answer buffer itself.
* Truncate the answer is the supplied buffer is not big enough.
*/
buffer = buf;
buflen = (reslen > 0) ? bufsize : len;
while (buflen > 0 && (n = read(cachefd, buffer, buflen)) > 0)
{
buffer += n;
buflen -= n;
}
if (buflen != 0)
{
if (errno == 0) seterrno(EIO);
cache_perror("Cannot read answer", cachefile);
return(-1);
}
/*
* Discard the residu to keep subsequent reads in sync.
*/
if (reslen > 0)
{
HEADER *bp = (HEADER *)buf;
char resbuf[PACKETSZ];
buffer = resbuf;
buflen = (reslen < sizeof(resbuf)) ? reslen : sizeof(resbuf);
while (reslen > 0 && (n = read(cachefd, buffer, buflen)) > 0)
{
reslen -= n;
buflen = (reslen < sizeof(resbuf)) ? reslen : sizeof(resbuf);
}
if (reslen != 0)
{
if (errno == 0) seterrno(EIO);
cache_perror("Cannot read residu", cachefile);
return(-1);
}
/* set truncation flag */
bp->tc = 1;
}
return(len);
}
/*
** CACHE_PERROR -- Issue perror message including file info
** --------------------------------------------------------
**
** Returns:
** None.
*/
void
cache_perror(message, filename)
input char *message; /* extra message string */
input char *filename; /* file name for perror */
{
int save_errno = errno; /* preserve state */
/* prepend extra message */
if (message != NULL)
(void) fprintf(stderr, "%s ", message);
/* issue actual message */
seterrno(save_errno);
perror(filename);
/* restore state */
seterrno(save_errno);
}
syntax highlighted by Code2HTML, v. 0.9.1