1
0
Files
2022-09-29 17:59:04 +03:00

1235 lines
27 KiB
C

/* -----------------------------------------------------------------
*
* subr.c
*
* Common subroutines used by the programs in these subdirectories.
*/
/* #pragma ident "@(#)subr.c 1.6 94/02/10 SMI" */
/*
* (c) 1992 Sun Microsystems, Inc
*/
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <sys/mount.h>
#include <sys/statvfs.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdarg.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <wait.h>
#include <ctype.h>
#include <fcntl.h>
#include <mntent.h>
#include <sys/mman.h>
#include <cachefs/cachefs_fs.h>
#include <syslog.h>
#include <dirent.h>
#include "subr.h"
#define XMAXPATH (PATH_MAX + MAXNAMELEN + 2)
extern int errno;
extern char *Progname;
/* -----------------------------------------------------------------
*
* cachefs_dir_lock
*
* Description:
* Gets a lock on the cache directory.
* To release the lock, call cachefs_dir_unlock
* with the returned value.
* Arguments:
* cachedirp name of the cache directory
* shared 1 if shared, 0 if not
* Returns:
* Returns -1 if the lock cannot be obtained immediatly.
* If the lock is obtained, returns a value >= 0.
* Preconditions:
* precond(cachedirp)
*/
int
cachefs_dir_lock(char *cachedirp, int shared)
{
int fd;
int xx;
char buf[MAXPATHLEN];
char *lockp = CACHELABEL_NAME;
struct flock fl;
/* see if path name is too long */
xx = strlen(cachedirp) + strlen(lockp) + 3;
if (xx >= MAXPATHLEN) {
pr_err(gettext("Cache directory name %s is too long"),
cachedirp);
return (-1);
}
/* make a path to the cache directory lock file */
sprintf(buf, "%s/%s", cachedirp, lockp);
/* create and open the cache directory lock file */
fd = open(buf, O_RDWR | O_CREAT, 0700);
if (fd == -1) {
pr_err(gettext("Cannot open lock file %s"), buf);
return (-1);
}
/* try to set the lock */
fl.l_type = (shared == 1) ? F_RDLCK : F_WRLCK;
fl.l_whence = 0;
fl.l_start = 0;
fl.l_len = 0;
fl.l_sysid = 0;
fl.l_pid = 0;
xx = fcntl(fd, F_SETLKW, &fl);
if (xx == -1) {
if (errno == EAGAIN) {
pr_err(gettext("Cannot gain access to the "
"cache directory %s."), cachedirp);
} else {
pr_err(gettext("Unexpected failure on lock file %s %s"),
buf, strerror(errno));
}
close(fd);
return (-1);
}
/* return the file descriptor which can be used to release the lock */
return (fd);
}
/* -----------------------------------------------------------------
*
* cachefs_dir_unlock
*
* Description:
* Releases an advisory lock on the cache directory.
* Arguments:
* fd cookie returned by cachefs_dir_lock
* Returns:
* Returns -1 if the lock cannot be released or 0 for success.
* Preconditions:
*/
int
cachefs_dir_unlock(int fd)
{
struct flock fl;
int error = 0;
int xx;
/* release the lock */
fl.l_type = F_UNLCK;
fl.l_whence = 0;
fl.l_start = 0;
fl.l_len = 0;
fl.l_sysid = 0;
fl.l_pid = 0;
xx = fcntl(fd, F_SETLK, &fl);
if (xx == -1) {
pr_err(gettext("Unexpected failure releasing lock file %s"),
strerror(errno));
error = -1;
}
/* close the lock file */
close(fd);
return (error);
}
/* -----------------------------------------------------------------
*
* cachefs_label_file_get
*
* Description:
* Gets the contents of a cache label file.
* Performs error checking on the file.
* Arguments:
* filep name of the cache label file
* clabelp where to put the file contents
* Returns:
* Returns 0 for success or -1 if an error occurs.
* Preconditions:
* precond(filep)
* precond(clabelp)
*/
int
cachefs_label_file_get(char *filep, struct cache_label *clabelp)
{
int xx;
int fd;
struct stat statinfo;
/* get info on the file */
xx = lstat(filep, &statinfo);
if (xx == -1) {
if (errno != ENOENT) {
pr_err(gettext("Cannot stat file %s: %s"),
filep, strerror(errno));
} else {
pr_err(gettext("File %s does not exist."), filep);
}
return (-1);
}
/* if the file is the wrong type */
if (!S_ISREG(statinfo.st_mode)) {
pr_err(gettext("Cache label file %s corrupted"), filep);
return (-1);
}
/* if the file is the wrong size */
if (statinfo.st_size != sizeof (struct cache_label)) {
pr_err(gettext("Cache label file %s wrong size"), filep);
return (-1);
}
/* open the cache label file */
fd = open(filep, O_RDONLY);
if (fd == -1) {
pr_err(gettext("Error opening %s: %s"), filep,
strerror(errno));
return (-1);
}
/* read the current set of parameters */
xx = read(fd, clabelp, sizeof (struct cache_label));
if (xx != sizeof (struct cache_label)) {
pr_err(gettext("Reading %s failed: %s\n"), filep,
strerror(errno));
close(fd);
return (-1);
}
close(fd);
/* check for an invalid version number */
if (clabelp->cl_cfsversion != CFSVERSION) {
pr_err(gettext("Invalid cache version"), filep);
return (-1);
}
/* return success */
return (0);
}
/* -----------------------------------------------------------------
*
* cachefs_label_file_put
*
* Description:
* Outputs the contents of a cache label object to a file.
* Arguments:
* filep name of the cache label file
* clabelp where to get the file contents
* Returns:
* Returns 0 for success or -1 if an error occurs.
* Preconditions:
* precond(filep)
* precond(clabelp)
*/
int
cachefs_label_file_put(char *filep, struct cache_label *clabelp)
{
int xx;
int fd;
struct dioattr frontdio;
/* check for an invalid version number */
if (clabelp->cl_cfsversion != CFSVERSION) {
pr_err(gettext("Invalid cache version"), filep);
return (-1);
}
/* get rid of the file if it already exists */
xx = unlink(filep);
if ((xx == -1) && (errno != ENOENT)) {
pr_err(gettext("Could not remove %s: %s"), filep, strerror(errno));
return (-1);
}
/* open the cache label file */
/*
* create the label file and open it for direct I/O first
* open for direct I/O so that F_DIOINFO will work
*/
fd = open(filep, O_CREAT | O_RDWR | O_DIRECT, 0600);
if (fd == -1) {
pr_err(gettext("Error creating %s: %s"), filep, strerror(errno));
return (-1);
}
/*
* get the direct I/O information for the front file system
*/
if (fcntl(fd, F_DIOINFO, &frontdio) == -1) {
pr_err(gettext("Error getting direct I/O info for %s: %s"), filep,
strerror(errno));
close(fd);
return (-1);
}
/*
* set the minimum I/O transfer size
*/
clabelp->cl_minio = frontdio.d_miniosz;
clabelp->cl_align = frontdio.d_mem;
/*
* close and reopen the label file for normal I/O
*/
close(fd);
fd = open(filep, O_RDWR);
if (fd == -1) {
pr_err(gettext("Error reopening %s: %s"), filep, strerror(errno));
return (-1);
}
/* write out the cache label object */
xx = write(fd, clabelp, sizeof (struct cache_label));
if (xx != sizeof (struct cache_label)) {
pr_err(gettext("Writing %s failed: %s"), filep, strerror(errno));
close(fd);
return (-1);
}
/* make sure the contents get to disk */
if (fsync(fd) != 0) {
pr_err(gettext("Writing %s failed on sync: %s"), filep,
strerror(errno));
close(fd);
return (-1);
}
close(fd);
/* return success */
return (0);
}
/* -----------------------------------------------------------------
*
* cachefs_inuse
*
* Description:
* Tests whether or not the cache directory is in use by
* the cache file system.
* Arguments:
* cachedirp name of the file system cache directory
* Returns:
* Returns 1 if the cache is in use or an error, 0 if not.
* Preconditions:
* precond(cachedirp)
*/
int
cachefs_inuse(char *cachedirp)
{
int fd;
int xx;
char buf[MAXPATHLEN];
char *lockp = CACHELABEL_NAME;
struct flock fl;
/* see if path name is too long */
xx = strlen(cachedirp) + strlen(lockp) + 3;
if (xx >= MAXPATHLEN) {
pr_err(gettext("Cache directory name %s is too long"),
cachedirp);
return (1);
}
/* make a path to the cache directory lock file */
sprintf(buf, "%s/%s", cachedirp, lockp);
/* open the kernel in use lock file */
fd = open(buf, O_RDWR, 0700);
if (fd == -1) {
pr_err(gettext("Cannot open lock file %s"), buf);
return (1);
}
/* test the lock status */
fl.l_type = F_WRLCK;
fl.l_whence = 0;
fl.l_start = 0;
fl.l_len = 1024;
fl.l_sysid = 0;
fl.l_pid = 0;
xx = fcntl(fd, F_GETLK, &fl);
if (xx == -1) {
pr_err(gettext("Unexpected failure on lock file %s %s"),
buf, strerror(errno));
close(fd);
return (1);
}
close(fd);
if (fl.l_type == F_UNLCK)
xx = 0;
else
xx = 1;
/* return whether or not the cache is in use */
return (xx);
}
/* -----------------------------------------------------------------
*
* pr_err
*
* Description:
* Prints an error message to stderr.
* Arguments:
* fmt printf style format
* ... arguments for fmt
* Returns:
* Preconditions:
* precond(fmt)
*/
void
pr_err(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
(void) fprintf(stderr, gettext("%s: "), Progname);
(void) vfprintf(stderr, fmt, ap);
(void) fprintf(stderr, "\n");
va_end(ap);
}
/* -----------------------------------------------------------------
*
* log_err
*
* Description:
* Prints an error message to syslog.
* Arguments:
* fmt printf style format
* ... arguments for fmt
* Returns:
* Preconditions:
* precond(fmt)
*/
void
log_err(char *fmt, ...)
{
va_list ap;
char *log_fmt;
log_fmt = malloc(strlen(Progname) + 2 + strlen(fmt) + 3);
if (!log_fmt) {
syslog(LOG_ERR, "log_err: out of memory\n");
return;
}
va_start(ap, fmt);
(void) sprintf(log_fmt, "%s: %s\n", Progname, fmt);
(void) vsyslog(LOG_ERR, log_fmt, ap);
va_end(ap);
}
/* -----------------------------------------------------------------
*
* delete_file
*
* Description:
* Remove a file or directory; called by nftw().
* Arguments:
* namep pathname of the file
* statp stat info about the file
* flg info about file
* ftwp depth information
* Returns:
* Returns 0 for success, -1 for failure.
* Preconditions:
* precond(namep)
*/
int
delete_file(const char *namep, const struct stat *statp, int flg,
struct FTW *ftwp)
{
/* ignore . and .. */
if (!strcmp(namep, ".") || !strcmp(namep, ".."))
return (0);
switch (flg) {
case FTW_F: /* files */
case FTW_SL:
if (unlink(namep) == -1) {
pr_err(gettext("unlink %s failed: %s"),
namep, strerror(errno));
return (-1);
}
break;
case FTW_DP: /* directories that have their children processed */
if (rmdir(namep) == -1) {
pr_err(gettext("rmdir %s failed: %s"),
namep, strerror(errno));
return (-1);
}
break;
case FTW_D: /* ignore directories if children not processed */
break;
default:
pr_err(gettext("failure on file %s, flg %d."),
namep, flg);
return (-1);
}
/* return success */
return (0);
}
/* -----------------------------------------------------------------
*
* convert_cl2uv
*
* Description:
* Copies the contents of a cache_label object into a
* user_values object, performing the necessary conversions.
* Arguments:
* clp cache_label to copy from
* uvp user_values to copy into
* dirp cache directory
* Returns:
* Returns 0 for success, -1 for an error.
* Preconditions:
* precond(clp)
* precond(uvp)
* precond(dirp)
*/
int
convert_cl2uv(const struct cache_label *clp, struct user_values *uvp,
const char *dirp)
{
struct statvfs fs;
int xx;
long long ltmp;
/* get file system information */
xx = statvfs(dirp, &fs);
if (xx == -1) {
pr_err(gettext("statvfs %s failed: %s"), dirp,
strerror(errno));
return (-1);
}
#define BOUND(yy) \
yy = (yy < 0) ? 0 : yy; \
yy = (yy > 100) ? 100 : yy;
xx = ((double)clp->cl_maxblks / (double)fs.f_blocks) * 100. + .5;
BOUND(xx);
uvp->uv_maxblocks = xx;
xx = ((double)clp->cl_maxfiles / (double)fs.f_files) * 100. + .5;
BOUND(xx);
uvp->uv_maxfiles = xx;
xx = ((double)clp->cl_blkhiwat / (double)fs.f_blocks) * 100. + .5;
BOUND(xx);
uvp->uv_hiblocks = xx;
xx = ((double)clp->cl_blklowat / (double)fs.f_blocks) * 100. + .5;
BOUND(xx);
uvp->uv_lowblocks = xx;
xx = ((double)clp->cl_filehiwat / (double)fs.f_files) * 100. + .5;
BOUND(xx);
uvp->uv_hifiles = xx;
xx = ((double)clp->cl_filelowat / (double)fs.f_files) * 100. + .5;
BOUND(xx);
uvp->uv_lowfiles = xx;
/* return success */
return (0);
}
/* -----------------------------------------------------------------
*
* convert_uv2cl
*
* Description:
* Copies the contents of a user_values object into a
* cache_label object, performing the necessary conversions.
* Arguments:
* uvp user_values to copy from
* clp cache_label to copy into
* dirp cache directory
* Returns:
* Returns 0 for success, -1 for an error.
* Preconditions:
* precond(uvp)
* precond(clp)
* precond(dirp)
*/
int
convert_uv2cl(const struct user_values *uvp, struct cache_label *clp,
const char *dirp)
{
struct statvfs fs;
int xx;
/* get file system information */
xx = statvfs(dirp, &fs);
if (xx == -1) {
pr_err(gettext("statvfs %s failed: %s"), dirp,
strerror(errno));
return (-1);
}
clp->cl_maxblks = uvp->uv_maxblocks / 100.0 * fs.f_blocks + .5;
clp->cl_maxfiles = uvp->uv_maxfiles / 100.0 * fs.f_files + .5;
clp->cl_blkhiwat = uvp->uv_hiblocks / 100.0 * fs.f_blocks + .5;
clp->cl_blklowat = uvp->uv_lowblocks / 100.0 * fs.f_blocks + .5;
clp->cl_filehiwat = uvp->uv_hifiles / 100.0 * fs.f_files + .5;
clp->cl_filelowat = uvp->uv_lowfiles / 100.0 * fs.f_files + .5;
/* return success */
return (0);
}
/* -----------------------------------------------------------------
*
* create_cache
*
* Description:
* Creates the specified cache directory and populates it as
* needed by CFS.
* Arguments:
* dirp the name of the cache directory
* uvp user values
* Returns:
* Returns 0 for success or:
* -1 for an error
* -2 for an error and cache directory partially created
* Preconditions:
* precond(dirp)
* precond(uvp)
*/
int
create_cache(char *dirp, struct user_values *uvp)
{
struct cache_label clabel;
int xx;
char path[XMAXPATH];
int fd;
void *bufp;
int cnt;
int lockid;
/* verify options are reasonable */
xx = check_user_values_for_sanity(uvp);
if (xx)
return (-1);
/* make sure cache dir name is not too long */
if (strlen(dirp) > (size_t)PATH_MAX) {
pr_err(gettext("path name %s is too long."), dirp);
return (-1);
}
/* make the directory */
if (mkdir(dirp, 0) == -1) {
switch (errno) {
case EEXIST:
if (chmod(dirp, (mode_t)0) == -1) {
pr_err(gettext("Unable to change %s to mode 0."), dirp);
return(-1);
}
break;
default:
pr_err(gettext("mkdir %s failed: %s"),
dirp, strerror(errno));
return (-1);
}
}
/* convert user values to a cache_label */
xx = convert_uv2cl(uvp, &clabel, dirp);
if (xx)
return (-1);
/*
* set the label version
*/
clabel.cl_cfsversion = CFSVERSION;
clabel.cl_cfslongsize = 0;
clabel.cl_cfsmetasize = 0;
/* lock the cache directory non-shared */
lockid = cachefs_dir_lock(dirp, 0);
if (lockid == -1) {
/* quit if could not get the lock */
return (-2);
}
/* make the directory for the back file system mount points */
/* note: we do not count this directory in the resources */
sprintf(path, "%s/%s", dirp, BACKMNT_NAME);
if (mkdir(path, 0700) == -1) {
if (errno != EEXIST) {
pr_err(gettext("mkdir %s failed: %s"), path,
strerror(errno));
cachefs_dir_unlock(lockid);
return (-2);
} else {
cachefs_dir_unlock(lockid);
return(0);
}
}
/* create the cache label file */
sprintf(path, "%s/%s", dirp, CACHELABEL_NAME);
xx = cachefs_label_file_put(path, &clabel);
if (xx == -1) {
pr_err(gettext("creating %s failed."), path);
cachefs_dir_unlock(lockid);
return (-2);
}
/* create the cache label duplicate file */
sprintf(path, "%s/%s.dup", dirp, CACHELABEL_NAME);
xx = cachefs_label_file_put(path, &clabel);
if (xx == -1) {
pr_err(gettext("creating %s failed."), path);
cachefs_dir_unlock(lockid);
return (-2);
}
/* release the lock on the cache */
cachefs_dir_unlock(lockid);
/* return success */
return (0);
}
/* -----------------------------------------------------------------
*
* check_user_values_for_sanity
*
* Description:
* Check the user_values for sanity.
* Arguments:
* uvp user_values object to check
* Returns:
* Returns 0 if okay, -1 if not.
* Preconditions:
* precond(uvp)
*/
int
check_user_values_for_sanity(const struct user_values *uvp)
{
int ret;
ret = 0;
if (uvp->uv_lowblocks >= uvp->uv_hiblocks) {
pr_err(gettext("lowblocks can't be >= hiblocks."));
ret = -1;
}
if (uvp->uv_lowfiles >= uvp->uv_hifiles) {
pr_err(gettext("lowfiles can't be >= hifiles."));
ret = -1;
}
/* XXX more conditions to check here? */
/* XXX make sure thresh values are between min and max values */
/* return status */
return (ret);
}
/* -----------------------------------------------------------------
*
* user_values_defaults
*
* Description:
* Sets default values in the user_values object.
* Arguments:
* uvp user_values object to set values for
* Returns:
* Preconditions:
* precond(uvp)
*/
void
user_values_defaults(struct user_values *uvp)
{
uvp->uv_maxblocks = 90;
uvp->uv_maxfiles = 90;
uvp->uv_hiblocks = 85;
uvp->uv_lowblocks = 75;
uvp->uv_hifiles = 85;
uvp->uv_lowfiles = 75;
}
/* -----------------------------------------------------------------
*
* delete_cache
*
* Description:
* Deletes the specified file system cache.
* Arguments:
* dirp cache directory name
* namep file system cache directory to delete
* Returns:
* Returns 0 for success, -1 for failure.
* Preconditions:
* precond(dirp)
* precond(namep)
*/
int
delete_cache(char *dirp, char *namep)
{
char command[XMAXPATH + 8];
int status;
/* make sure cache dir name is not too long */
if (strlen(dirp) > (size_t)PATH_MAX) {
pr_err(gettext("path name %s is too long."),
dirp);
return (-1);
}
/* construct the path name of the file system cache directory */
sprintf(command, "rm -rf %s/%s", dirp, namep);
if ((status = system(command)) == -1) {
pr_err("Could not remove %s/%s: %s\n", dirp, namep, strerror(errno));
return(-1);
} else if (WIFEXITED(status)) {
if (WEXITSTATUS(status)) {
pr_err("Could not remove %s/%s: exit status %d\n", dirp, namep,
WEXITSTATUS(status));
return(-1);
}
} else if (WIFSIGNALED(status)) {
pr_err("Could not remove %s/%s: signal %d\n", dirp, namep,
WTERMSIG(status));
return(-1);
} else {
pr_err("Could not remove %s/%s: status 0x%x\n", dirp, namep, status);
return(-1);
}
/* return success */
return (0);
}
/* -----------------------------------------------------------------
*
* delete_all_cache
*
* Description:
* Delete all caches in cache directory.
* Arguments:
* dirp the pathname of of the cache directory to delete
* Returns:
* Returns 0 for success or -1 for an error.
* Preconditions:
* precond(dirp)
*/
int
delete_all_cache(char *dirp)
{
int status;
DIR *dp;
struct dirent *dep;
char command[XMAXPATH + 8];
struct stat statinfo;
/* make sure cache dir name is not too long */
if (strlen(dirp) > (size_t)PATH_MAX) {
pr_err(gettext("path name %s is too long."),
dirp);
return (-1);
}
/* delete the back FS mount point directory if it exists */
sprintf(command, "rm -rf %s/%s", dirp, BACKMNT_NAME);
if ((status = system(command)) == -1) {
pr_err(gettext("Could not remove %s/%s: %s"), dirp, BACKMNT_NAME,
strerror(errno));
return (-1);
} else if (WIFEXITED(status)) {
if (WEXITSTATUS(status)) {
pr_err("Could not remove %s/%s: exit status %d\n", dirp,
BACKMNT_NAME, WEXITSTATUS(status));
return(-1);
}
} else if (WIFSIGNALED(status)) {
pr_err("Could not remove %s/%s: signal %d\n", dirp, BACKMNT_NAME,
WTERMSIG(status));
return(-1);
} else {
pr_err("Could not remove %s/%s: status 0x%x\n", dirp, BACKMNT_NAME,
status);
return(-1);
}
/* open the cache directory specified */
if ((dp = opendir(dirp)) == NULL) {
pr_err(gettext("cannot open cache directory %s: %s"),
dirp, strerror(errno));
return (-1);
}
/*
* read the file names in the cache directory
* remove only directories with names beginning with CACHEID_PREFIX
* all cache ID directory names begin with CACHEID_PREFIX
*/
while ((dep = readdir(dp)) != NULL) {
if (strncmp(dep->d_name, CACHEID_PREFIX, CACHEID_PREFIX_LEN) == 0) {
sprintf(command, "%s/%s", dirp, dep->d_name);
if (lstat(command, &statinfo) == -1) {
pr_err("Could not lstat %s: %s", command, strerror(errno));
} else if (S_ISDIR(statinfo.st_mode)) {
/* delete the file system cache directory */
if (delete_cache(dirp, dep->d_name)) {
closedir(dp);
return (-1);
}
}
}
}
closedir(dp);
/* remove the cache label file */
sprintf(command, "%s/%s", dirp, CACHELABEL_NAME);
if ((unlink(command) == -1) && (errno != ENOENT)) {
pr_err(gettext("unlink %s failed: %s"), command,
strerror(errno));
return (-1);
}
/* remove the cache label duplicate file */
sprintf(command, "%s/%s.dup", dirp, CACHELABEL_NAME);
if ((unlink(command) == -1) && (errno != ENOENT)) {
pr_err(gettext("unlink %s failed: %s"), command,
strerror(errno));
return (-1);
}
/* remove the directory */
if (rmdir(dirp) == -1) {
pr_err(gettext("rmdir %s failed: %s"), dirp,
strerror(errno));
return (-1);
}
/* return success */
return (0);
}
/* -----------------------------------------------------------------
*
* Calculate a 16 bit ones-complement checksum for a single buffer.
* This routine always adds even address bytes to the high order 8 bits
* of the 16 bit checksum and odd address bytes are added to the low
* order 8 bits of the 16 bit checksum. The caller must swap bytes in
* the sum to make this correct.
*
* The caller must ensure that the length is not zero or > 32K.
*/
static u_int
cksum(ushort *src, /* first byte */
int len) /* # of bytes */
{
u_int ck = 0;
/* do 64 byte blocks for the 128-byte cache line of the IP17 */
while (len >= 64) {
ck += src[0]; ck += src[1]; ck += src[2]; ck += src[3];
ck += src[4]; ck += src[5]; ck += src[6]; ck += src[7];
ck += src[8]; ck += src[9]; ck += src[10]; ck += src[11];
ck += src[12]; ck += src[13]; ck += src[14]; ck += src[15];
ck += src[16]; ck += src[17]; ck += src[18]; ck += src[19];
ck += src[20]; ck += src[21]; ck += src[22]; ck += src[23];
ck += src[24]; ck += src[25]; ck += src[26]; ck += src[27];
ck += src[28]; ck += src[29]; ck += src[30]; ck += src[31];
src += 32;
len -= 64;
}
/* we have < 64 bytes remaining */
if (0 != (len&32)) {
ck += src[0]; ck += src[1]; ck += src[2]; ck += src[3];
ck += src[4]; ck += src[5]; ck += src[6]; ck += src[7];
ck += src[8]; ck += src[9]; ck += src[10]; ck += src[11];
ck += src[12]; ck += src[13]; ck += src[14]; ck += src[15];
src += 16;
}
if (0 != (len&16)) {
ck += src[0]; ck += src[1]; ck += src[2]; ck += src[3];
ck += src[4]; ck += src[5]; ck += src[6]; ck += src[7];
src += 8;
}
if (0 != (len&8)) {
ck += src[0]; ck += src[1]; ck += src[2]; ck += src[3];
src += 4;
}
if (0 != (len&4)) {
ck += src[0]; ck += src[1];
src += 2;
}
if (0 != (len&2)) {
ck += src[0];
src += 1;
}
if (0 != (len&1)) {
#ifdef _MIPSEL
ck += *(unchar*)src;
#else
ck += *(unchar*)src << 8;
#endif
}
ck = (ck & 0xffff) + (ck >> 16);
return(0xffff & (ck + (ck >> 16)));
}
/* -----------------------------------------------------------------
*
* read_file_header
*
* Description:
* Read the file header from a cache file.
* Arguments:
* path full path for the file
* fileheader place to put the file header
* Returns:
* fileheader the file header is placed into the location pointed to
* by fileheader
* The return value of the function is a Boolean indicating whether or
* not the file header was successfully read.
* Preconditions:
* precond(path)
* precond(fileheader)
*/
int
read_file_header(const char *path, char *fileheader)
{
u_long header_sum = 0;
u_long calculated_sum = 0;
char *bp = fileheader;
int bytes = 0;
int resid = FILEHEADER_SIZE;
int status = 1;
int fd;
fileheader_t *fhp = (fileheader_t *)fileheader;
if ((fd = open(path, O_RDONLY, CACHEFS_FILEMODE)) == -1) {
return(0);
}
do {
bytes = read(fd, bp, resid);
switch (bytes) {
case 0:
status = 0;
break;
case -1:
status = 0;
break;
default:
resid -= bytes;
bp += bytes;
}
} while (status && (bytes < resid) && (bytes > 0));
close(fd);
if (status) {
if (fhp->fh_metadata.md_checksum == 0) {
status = 0;
} else {
header_sum = (u_long)fhp->fh_metadata.md_checksum;
fhp->fh_metadata.md_checksum = 0;
calculated_sum = cksum((ushort *)fileheader,
sizeof(fileheader_t));
if (calculated_sum != header_sum) {
status = 0;
}
}
}
return(status);
}
char *
make_filename(char *path)
{
char *name = strdup(path);
char *cp;
if (name) {
cp = name;
while ((cp = strchr(cp, '/')) != NULL)
*cp = '_';
}
return(name);
}
/*
*
* get_cacheid
*
* Description:
* Determines an identifier for the front file system cache.
* The length of the returned string is < PATH_MAX.
* The cache ID is of the form CACHEID_PREFIX<fsid>:<mntp> where fsid
* is the back file system FS name and mntp is the mount point name.
* Arguments:
* fsidp back file system id
* Returns:
* Returns a pointer to the string identifier, or NULL if the
* identifier was overflowed.
* Preconditions:
* precond(fsidp)
*/
char *
get_cacheid(char *fsidp, char *mntp)
{
char *c1;
char *buf;
int len = strlen(mntp) + strlen(fsidp) + strlen(CACHEID_PREFIX) + 1;
if (len >= (size_t) PATH_MAX)
return (NULL);
buf = malloc(len + 1);
if (!buf)
return(NULL);
strcpy(buf, CACHEID_PREFIX);
strcat(buf, fsidp);
strcat(buf, ":");
strcat(buf, mntp);
c1 = buf;
while ((c1 = strchr(c1, '/')) != NULL)
*c1 = '_';
return (buf);
}
/*
*
* get_mount_point
*
* Description:
* Makes a suitable mount point for the back file system.
* The name of the mount point created is stored in a malloced
* buffer in pathpp
* Arguments:
* cachedirp the name of the cache directory
* cacheid the cache ID for the file system
* pathpp where to store the mount point
* Returns:
* Returns 0 for success, -1 for an error, -2 for busy.
* Preconditions:
* precond(cachedirp)
* precond(cacheid)
* precond(pathpp)
*/
int
get_mount_point(char *cachedirp, char *cacheid, char **pathpp, int validate)
{
char *strp;
char *namep;
struct stat stat1;
struct stat stat2;
int xx;
/* get some space for the path name */
strp = malloc(MAXPATHLEN);
if (strp == NULL) {
pr_err(gettext("out of memory"));
return (-1);
}
/* see if the mount directory is valid */
sprintf(strp, "%s/%s", cachedirp, BACKMNT_NAME);
xx = stat(strp, &stat1);
if ((xx == -1) || !S_ISDIR(stat1.st_mode)) {
pr_err(gettext("%s is not a valid cache."), strp);
return (-1);
}
/* find a directory name we can use */
/* construct a directory name to consider */
namep = strp + strlen(strp);
sprintf(namep, "/%s", cacheid);
if (validate) {
/* stat the directory */
xx = stat(strp, &stat2);
/* if the stat failed */
if (xx == -1) {
/* if the dir does not exist we can use it */
if (errno == ENOENT) {
/* create the directory */
if (mkdir(strp, 0755) == -1) {
pr_err(gettext("could not make directory %s"), strp);
return (-1);
}
/* return success */
*pathpp = strp;
return (0);
}
/* any other error we are hosed */
pr_err(gettext("could not stat %s %s"),
strp, strerror(errno));
return (-1);
}
/* if it is a dir that is not being used, then we can use it */
if (S_ISDIR(stat2.st_mode) && (stat1.st_dev == stat2.st_dev)) {
*pathpp = strp;
return (0);
}
}
/* the mount point is in use */
*pathpp = strp;
return (-2);
}