stats_log.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 1996-2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Routines for cachefs logging.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <sys/types.h>
#include <sys/param.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <libintl.h>
#include <time.h>
#include <string.h>
#include <sys/fs/cachefs_fs.h>
#include <sys/fs/cachefs_log.h>
#include <malloc.h>
#include <limits.h>
#include "stats.h"
#include <assert.h>
/* forward declarations of statics */
static kstat_t *stats_log_kstat_read(stats_cookie_t *);
static char *stats_log_fmtfid(cfs_fid_t *);
static bool_t stats_xdr_loghead(XDR *, struct cachefs_log_logfile_header *);
static int stats_log_fi_comp(const void *a, const void *b);
int
stats_log_kernel_setname(stats_cookie_t *st, char *path)
{
int error = 0;
kstat_t *log;
cachefs_log_control_t *lc;
int exists = 0;
assert(stats_good(st));
if ((log = stats_log_kstat_read(st)) == NULL) {
error = stats_errno(st);
goto out;
}
lc = (cachefs_log_control_t *)log->ks_data;
/*
* the stats_ API allows a NULL or an empty path to turn off
* logging, but the kstat interface has the string buffered,
* so we need to make an empty string.
*/
if (path == NULL)
path = "";
if ((lc->lc_path[0] == 0) && (path[0] == 0))
goto out;
(void) strlcpy(lc->lc_path, path, sizeof (lc->lc_path));
if (path[0] != '\0') {
struct stat64 s;
int f;
exists = access(path, F_OK);
/* logfile will be <2GB */
f = open(path, O_WRONLY | O_CREAT, 0666);
if (f < 0) {
stats_perror(st, error = SE_FILE,
gettext("Cannot open/create logfile: %s"),
strerror(errno));
goto out;
}
if (fstat64(f, &s) < 0) {
stats_perror(st, error = SE_FILE,
gettext("Cannot stat logfile: %s"),
strerror(errno));
(void) close(f);
goto out;
}
/*
* the kernel will accept an empty file as a logfile. we must
* make sure that we created this empty file, i.e. that it's
* not an already existing file that happened to be empty.
*
* if we hand the kernel a nonempty file, it will check the
* magic number. thus, if they hand it something like
* /etc/passwd, the kernel should reject it. we just have to
* catch the cases of empty files we don't want to be
* logfiles.
*/
if ((exists == 0) && (s.st_size == 0LL)) {
stats_perror(st, error = SE_INVAL,
gettext(
"Cannot use existing empty file as a logfile"));
(void) close(f);
goto out;
}
(void) close(f);
}
if (kstat_write(st->st_kstat_cookie, log, NULL) < 0) {
stats_perror(st, error = SE_KERNEL,
gettext("Cannot set logfile path for this filesystem"));
goto out;
}
out:
if ((error != 0) && (path[0] != '\0') && (exists != 0))
(void) unlink(path);
return (error);
}
int
stats_log_which(stats_cookie_t *st, int which, int onoff)
{
int error = 0;
kstat_t *log;
cachefs_log_control_t *lc;
assert(stats_good(st));
if ((log = stats_log_kstat_read(st)) == NULL) {
error = stats_errno(st);
goto out;
}
lc = (cachefs_log_control_t *)log->ks_data;
if (onoff)
CACHEFS_LOG_SET(lc, which);
else
CACHEFS_LOG_CLEAR(lc, which);
if (kstat_write(st->st_kstat_cookie, log, NULL) < 0) {
stats_perror(st, error = SE_KERNEL,
gettext("Cannot set log bitmap for this filesystem"));
goto out;
}
out:
return (error);
}
char *
stats_log_kernel_getname(stats_cookie_t *st)
{
char *rc = NULL;
kstat_t *log;
cachefs_log_control_t *lc;
assert(stats_good(st));
if ((log = stats_log_kstat_read(st)) == NULL)
goto out;
lc = (cachefs_log_control_t *)log->ks_data;
rc = lc->lc_path; /* rc[0] will be '\0' if we're not logging */
out:
return (rc);
}
static kstat_t *
stats_log_kstat_read(stats_cookie_t *st)
{
kstat_t *rc;
assert(stats_good(st));
assert(st->st_flags & ST_BOUND);
if ((rc = kstat_lookup(st->st_kstat_cookie,
"cachefs", st->st_fsid, "log")) == NULL) {
/*
* XXX if st was created for a particular cachedir, we
* should scan for another st->st_fsid that'll get us
* the same cache.
*/
stats_perror(st, SE_KERNEL,
gettext("Cannot lookup kstats for this filesystem"));
goto out;
}
if (kstat_read(st->st_kstat_cookie, rc, NULL) < 0) {
stats_perror(st, SE_KERNEL,
gettext("Cannot read kstats for this filesystem"));
rc = NULL;
goto out;
}
out:
return (rc);
}
int
stats_log_logfile_open(stats_cookie_t *st, char *fname)
{
int rc = 0;
assert(stats_good(st));
if ((fname == NULL) || (fname[0] == '\0')) {
kstat_t *log;
cachefs_log_control_t *lc;
if ((log = stats_log_kstat_read(st)) == NULL) {
rc = -1;
goto out;
}
lc = (cachefs_log_control_t *)log->ks_data;
fname = lc->lc_path;
}
/* logfile will be <2GB */
if ((st->st_logstream = fopen(fname, "r")) == NULL) {
stats_perror(st, SE_FILE,
gettext("Cannot open logfile %s"), fname);
rc = -1;
goto out;
}
xdrstdio_create(&st->st_logxdr, st->st_logstream, XDR_DECODE);
if (! stats_xdr_loghead(&st->st_logxdr, &st->st_loghead)) {
stats_perror(st, SE_CORRUPT,
gettext("Cannot read header from logfile %s"), fname);
rc = -1;
goto out;
}
if (st->st_loghead.lh_magic != CACHEFS_LOG_MAGIC) {
stats_perror(st, SE_CORRUPT,
gettext("%s: Invalid log file header"), fname);
rc = -1;
goto out;
}
if (st->st_loghead.lh_revision > CACHEFS_LOG_FILE_REV) {
stats_perror(st, SE_CORRUPT,
gettext("%s: Revision too high"), fname);
rc = -1;
goto out;
}
st->st_flags |= ST_LFOPEN;
out:
if (rc != 0) {
if (st->st_logstream != NULL) {
(void) fclose(st->st_logstream);
st->st_logstream = NULL;
}
if (st->st_logxdr.x_ops != NULL) {
xdr_destroy(&st->st_logxdr);
st->st_logxdr.x_ops = NULL;
}
}
return (rc);
}
static bool_t
stats_xdr_loghead(XDR *xdrs, struct cachefs_log_logfile_header *lh)
{
if ((! xdr_u_int(xdrs, &lh->lh_magic)) ||
(! xdr_u_int(xdrs, &lh->lh_revision)) ||
(! xdr_int(xdrs, &lh->lh_errno)) ||
(! xdr_u_int(xdrs, &lh->lh_blocks)) ||
(! xdr_u_int(xdrs, &lh->lh_files)) ||
(! xdr_u_int(xdrs, &lh->lh_maxbsize)) ||
(! xdr_u_int(xdrs, &lh->lh_pagesize)))
return (FALSE);
return (TRUE);
}
void *
stats_log_logfile_read(stats_cookie_t *st, int *type)
{
void *rc = NULL;
size_t size;
int ttype;
XDR *xdrs;
char *string1, *string2;
assert(stats_good(st));
xdrs = &st->st_logxdr;
if (! (st->st_flags & ST_LFOPEN)) {
stats_perror(st, SE_INVAL,
gettext("Logfile was not open"));
goto out;
}
if (type == NULL)
type = &ttype;
if (! xdr_int(xdrs, type))
goto out;
switch (*type) {
struct cachefs_log_mount_record mount, *mountp;
struct cachefs_log_umount_record umount;
struct cachefs_log_getpage_record getpage;
struct cachefs_log_readdir_record readdir;
struct cachefs_log_readlink_record readlink;
struct cachefs_log_remove_record remove;
struct cachefs_log_rmdir_record rmdir;
struct cachefs_log_truncate_record truncate;
struct cachefs_log_putpage_record putpage;
struct cachefs_log_create_record create;
struct cachefs_log_mkdir_record mkdir;
struct cachefs_log_rename_record rename;
struct cachefs_log_symlink_record symlink;
struct cachefs_log_populate_record populate;
struct cachefs_log_csymlink_record csymlink;
struct cachefs_log_filldir_record filldir;
struct cachefs_log_mdcreate_record mdcreate;
struct cachefs_log_gpfront_record gpfront;
struct cachefs_log_rfdir_record rfdir;
struct cachefs_log_ualloc_record ualloc;
struct cachefs_log_calloc_record challoc;
struct cachefs_log_nocache_record nocache;
case CACHEFS_LOG_MOUNT:
if ((! xdr_int(xdrs, &mount.error)) ||
(! xdr_int(xdrs, (int *)&mount.time)) ||
(! xdr_opaque(xdrs, (caddr_t)&mount.vfsp,
sizeof (mount.vfsp))) ||
(! xdr_u_int(xdrs, &mount.flags)) ||
(! xdr_u_int(xdrs, &mount.popsize)) ||
(! xdr_u_int(xdrs, &mount.fgsize)) ||
(! xdr_u_short(xdrs, &mount.pathlen)) ||
(! xdr_u_short(xdrs, &mount.cacheidlen))) {
stats_perror(st, SE_CORRUPT,
gettext("Truncated mount record"));
goto out;
}
mount.type = *type;
size = sizeof (mount) + mount.pathlen + mount.cacheidlen -
CLPAD(cachefs_log_mount_record, path);
if ((rc = mountp =
(struct cachefs_log_mount_record *)
calloc(1, size)) == NULL) {
stats_perror(st, SE_NOMEM,
gettext("Cannot malloc record"));
goto out;
}
memcpy(rc, &mount, size);
string1 = mountp->path;
string2 = mountp->path + mount.pathlen + 1;
(void) xdr_wrapstring(xdrs, &string1);
(void) xdr_wrapstring(xdrs, &string2);
break;
case CACHEFS_LOG_UMOUNT:
if ((! xdr_int(xdrs, &umount.error)) ||
(! xdr_int(xdrs, (int *)&umount.time)) ||
(! xdr_opaque(xdrs, (caddr_t)&umount.vfsp,
sizeof (umount.vfsp)))) {
stats_perror(st, SE_CORRUPT,
gettext("Truncated umount record"));
goto out;
}
umount.type = *type;
size = sizeof (umount);
if ((rc = (caddr_t)calloc(1, size)) == NULL) {
stats_perror(st, SE_NOMEM,
gettext("Cannot malloc record"));
goto out;
}
memcpy(rc, &umount, size);
break;
case CACHEFS_LOG_GETPAGE:
if ((! xdr_int(xdrs, &getpage.error)) ||
(! xdr_int(xdrs, (int *)&getpage.time)) ||
(! xdr_opaque(xdrs, (caddr_t)&getpage.vfsp,
sizeof (getpage.vfsp))) ||
(! xdr_opaque(xdrs, (caddr_t)&getpage.fid,
sizeof (getpage.fid))) ||
(! xdr_u_longlong_t(xdrs,
(u_longlong_t *)&getpage.fileno)) ||
(! xdr_int(xdrs, (int *)&getpage.uid)) ||
(! xdr_u_longlong_t(xdrs,
(u_longlong_t *)&getpage.offset)) ||
(! xdr_u_int(xdrs, &getpage.len))) {
stats_perror(st, SE_CORRUPT,
gettext("Truncated getpage record"));
goto out;
}
getpage.type = *type;
size = sizeof (getpage);
if ((rc = (caddr_t)calloc(1, size)) == NULL) {
stats_perror(st, SE_NOMEM,
gettext("Cannot malloc record"));
goto out;
}
memcpy(rc, &getpage, size);
break;
case CACHEFS_LOG_READDIR:
if ((! xdr_int(xdrs, &readdir.error)) ||
(! xdr_int(xdrs, (int *)&readdir.time)) ||
(! xdr_opaque(xdrs, (caddr_t)&readdir.vfsp,
sizeof (readdir.vfsp))) ||
(! xdr_opaque(xdrs, (caddr_t)&readdir.fid,
sizeof (readdir.fid))) ||
(! xdr_u_longlong_t(xdrs,
(u_longlong_t *)&readdir.fileno)) ||
(! xdr_int(xdrs, (int *)&readdir.uid)) ||
(! xdr_u_longlong_t(xdrs,
(u_longlong_t *)&readdir.offset)) ||
(! xdr_int(xdrs, &readdir.eof))) {
stats_perror(st, SE_CORRUPT,
gettext("Truncated readdir record"));
goto out;
}
readdir.type = *type;
size = sizeof (readdir);
if ((rc = (caddr_t)calloc(1, size)) == NULL) {
stats_perror(st, SE_NOMEM,
gettext("Cannot malloc record"));
goto out;
}
memcpy(rc, &readdir, size);
break;
case CACHEFS_LOG_READLINK:
if ((! xdr_int(xdrs, &readlink.error)) ||
(! xdr_int(xdrs, (int *)&readlink.time)) ||
(! xdr_opaque(xdrs, (caddr_t)&readlink.vfsp,
sizeof (readlink.vfsp))) ||
(! xdr_opaque(xdrs, (caddr_t)&readlink.fid,
sizeof (readlink.fid))) ||
(! xdr_u_longlong_t(xdrs,
(u_longlong_t *)&readlink.fileno)) ||
(! xdr_int(xdrs, (int *)&readlink.uid)) ||
(! xdr_u_int(xdrs,
&readlink.length))) {
stats_perror(st, SE_CORRUPT,
gettext("Truncated readlink record"));
goto out;
}
readlink.type = *type;
size = sizeof (readlink);
if ((rc = (caddr_t)calloc(1, size)) == NULL) {
stats_perror(st, SE_NOMEM,
gettext("Cannot malloc record"));
goto out;
}
memcpy(rc, &readlink, size);
break;
case CACHEFS_LOG_REMOVE:
if ((! xdr_int(xdrs, &remove.error)) ||
(! xdr_int(xdrs, (int *)&remove.time)) ||
(! xdr_opaque(xdrs, (caddr_t)&remove.vfsp,
sizeof (remove.vfsp))) ||
(! xdr_opaque(xdrs, (caddr_t)&remove.fid,
sizeof (remove.fid))) ||
(! xdr_u_longlong_t(xdrs,
(u_longlong_t *)&remove.fileno)) ||
(! xdr_int(xdrs, (int *)&remove.uid))) {
stats_perror(st, SE_CORRUPT,
gettext("Truncated remove record"));
goto out;
}
remove.type = *type;
size = sizeof (remove);
if ((rc = (caddr_t)calloc(1, size)) == NULL) {
stats_perror(st, SE_NOMEM,
gettext("Cannot malloc record"));
goto out;
}
memcpy(rc, &remove, size);
break;
case CACHEFS_LOG_RMDIR:
if ((! xdr_int(xdrs, &rmdir.error)) ||
(! xdr_int(xdrs, (int *)&rmdir.time)) ||
(! xdr_opaque(xdrs, (caddr_t)&rmdir.vfsp,
sizeof (rmdir.vfsp))) ||
(! xdr_opaque(xdrs, (caddr_t)&rmdir.fid,
sizeof (rmdir.fid))) ||
(! xdr_u_longlong_t(xdrs, (u_longlong_t *)&rmdir.fileno)) ||
(! xdr_int(xdrs, (int *)&rmdir.uid))) {
stats_perror(st, SE_CORRUPT,
gettext("Truncated rmdir record"));
goto out;
}
rmdir.type = *type;
size = sizeof (rmdir);
if ((rc = (caddr_t)calloc(1, size)) == NULL) {
stats_perror(st, SE_NOMEM,
gettext("Cannot malloc record"));
goto out;
}
memcpy(rc, &rmdir, size);
break;
case CACHEFS_LOG_TRUNCATE:
if ((! xdr_int(xdrs, &truncate.error)) ||
(! xdr_int(xdrs, (int *)&truncate.time)) ||
(! xdr_opaque(xdrs, (caddr_t)&truncate.vfsp,
sizeof (truncate.vfsp))) ||
(! xdr_opaque(xdrs, (caddr_t)&truncate.fid,
sizeof (truncate.fid))) ||
(! xdr_u_longlong_t(xdrs,
(u_longlong_t *)&truncate.fileno)) ||
(! xdr_int(xdrs, (int *)&truncate.uid)) ||
(! xdr_u_longlong_t(xdrs,
(u_longlong_t *)&truncate.size))) {
stats_perror(st, SE_CORRUPT,
gettext("Truncated truncate record"));
goto out;
}
truncate.type = *type;
size = sizeof (truncate);
if ((rc = (caddr_t)calloc(1, size)) == NULL) {
stats_perror(st, SE_NOMEM,
gettext("Cannot malloc record"));
goto out;
}
memcpy(rc, &truncate, size);
break;
case CACHEFS_LOG_PUTPAGE:
if ((! xdr_int(xdrs, &putpage.error)) ||
(! xdr_int(xdrs, (int *)&putpage.time)) ||
(! xdr_opaque(xdrs, (caddr_t)&putpage.vfsp,
sizeof (putpage.vfsp))) ||
(! xdr_opaque(xdrs, (caddr_t)&putpage.fid,
sizeof (putpage.fid))) ||
(! xdr_u_longlong_t(xdrs,
(u_longlong_t *)&putpage.fileno)) ||
(! xdr_int(xdrs, (int *)&putpage.uid)) ||
(! xdr_u_longlong_t(xdrs,
(u_longlong_t *)&putpage.offset)) ||
(! xdr_u_int(xdrs, &putpage.len))) {
stats_perror(st, SE_CORRUPT,
gettext("Truncated putpage record"));
goto out;
}
putpage.type = *type;
size = sizeof (putpage);
if ((rc = (caddr_t)calloc(1, size)) == NULL) {
stats_perror(st, SE_NOMEM,
gettext("Cannot malloc record"));
goto out;
}
memcpy(rc, &putpage, size);
break;
case CACHEFS_LOG_CREATE:
if ((! xdr_int(xdrs, &create.error)) ||
(! xdr_int(xdrs, (int *)&create.time)) ||
(! xdr_opaque(xdrs, (caddr_t)&create.vfsp,
sizeof (create.vfsp))) ||
(! xdr_opaque(xdrs, (caddr_t)&create.fid,
sizeof (create.fid))) ||
(! xdr_u_longlong_t(xdrs,
(u_longlong_t *)&create.fileno)) ||
(! xdr_int(xdrs, (int *)&create.uid))) {
stats_perror(st, SE_CORRUPT,
gettext("Truncated create record"));
goto out;
}
create.type = *type;
size = sizeof (create);
if ((rc = (struct cachefs_log_create_record *)
calloc(1, size)) == NULL) {
stats_perror(st, SE_NOMEM,
gettext("Cannot malloc record"));
goto out;
}
memcpy(rc, &create, size);
break;
case CACHEFS_LOG_MKDIR:
if ((! xdr_int(xdrs, &mkdir.error)) ||
(! xdr_int(xdrs, (int *)&mkdir.time)) ||
(! xdr_opaque(xdrs, (caddr_t)&mkdir.vfsp,
sizeof (mkdir.vfsp))) ||
(! xdr_opaque(xdrs, (caddr_t)&mkdir.fid,
sizeof (mkdir.fid))) ||
(! xdr_u_longlong_t(xdrs, (u_longlong_t *)&mkdir.fileno)) ||
(! xdr_int(xdrs, (int *)&mkdir.uid))) {
stats_perror(st, SE_CORRUPT,
gettext("Truncated mkdir record"));
goto out;
}
mkdir.type = *type;
size = sizeof (mkdir);
if ((rc = (struct cachefs_log_mkdir_record *)
calloc(1, size)) == NULL) {
stats_perror(st, SE_NOMEM,
gettext("Cannot malloc record"));
goto out;
}
memcpy(rc, &mkdir, size);
break;
case CACHEFS_LOG_RENAME:
if ((! xdr_int(xdrs, &rename.error)) ||
(! xdr_int(xdrs, (int *)&rename.time)) ||
(! xdr_opaque(xdrs, (caddr_t)&rename.vfsp,
sizeof (rename.vfsp))) ||
(! xdr_opaque(xdrs, (caddr_t)&rename.gone,
sizeof (rename.gone))) ||
(! xdr_int(xdrs, &rename.removed)) ||
(! xdr_int(xdrs, (int *)&rename.uid))) {
stats_perror(st, SE_CORRUPT,
gettext("Truncated rename record"));
goto out;
}
rename.type = *type;
size = sizeof (rename);
if ((rc = (struct cachefs_log_rename_record *)
calloc(1, size)) == NULL) {
stats_perror(st, SE_NOMEM,
gettext("Cannot malloc record"));
goto out;
}
memcpy(rc, &rename, size);
break;
case CACHEFS_LOG_SYMLINK:
if ((! xdr_int(xdrs, &symlink.error)) ||
(! xdr_int(xdrs, (int *)&symlink.time)) ||
(! xdr_opaque(xdrs, (caddr_t)&symlink.vfsp,
sizeof (symlink.vfsp))) ||
(! xdr_opaque(xdrs, (caddr_t)&symlink.fid,
sizeof (symlink.fid))) ||
(! xdr_u_longlong_t(xdrs,
(u_longlong_t *)&symlink.fileno)) ||
(! xdr_int(xdrs, (int *)&symlink.uid)) ||
(! xdr_u_int(xdrs, &symlink.size))) {
stats_perror(st, SE_CORRUPT,
gettext("Truncated symlink record"));
goto out;
}
symlink.type = *type;
size = sizeof (symlink);
if ((rc = (struct cachefs_log_symlink_record *)
calloc(1, size)) == NULL) {
stats_perror(st, SE_NOMEM,
gettext("Cannot malloc record"));
goto out;
}
memcpy(rc, &symlink, size);
break;
case CACHEFS_LOG_POPULATE:
if ((! xdr_int(xdrs, &populate.error)) ||
(! xdr_int(xdrs, (int *)&populate.time)) ||
(! xdr_opaque(xdrs, (caddr_t)&populate.vfsp,
sizeof (populate.vfsp))) ||
(! xdr_opaque(xdrs, (caddr_t)&populate.fid,
sizeof (populate.fid))) ||
(! xdr_u_longlong_t(xdrs,
(u_longlong_t *)&populate.fileno)) ||
(! xdr_u_longlong_t(xdrs, (u_longlong_t *)&populate.off)) ||
(! xdr_u_int(xdrs, &populate.size))) {
stats_perror(st, SE_CORRUPT,
gettext("Truncated populate record"));
goto out;
}
populate.type = *type;
if ((rc = (struct cachefs_log_populate_record *)
calloc(1, sizeof (populate))) == NULL) {
stats_perror(st, SE_NOMEM,
gettext("Cannot malloc record"));
goto out;
}
memcpy(rc, &populate, sizeof (populate));
break;
case CACHEFS_LOG_CSYMLINK:
if ((! xdr_int(xdrs, &csymlink.error)) ||
(! xdr_int(xdrs, (int *)&csymlink.time)) ||
(! xdr_opaque(xdrs, (caddr_t)&csymlink.vfsp,
sizeof (csymlink.vfsp))) ||
(! xdr_opaque(xdrs, (caddr_t)&csymlink.fid,
sizeof (csymlink.fid))) ||
(! xdr_u_longlong_t(xdrs,
(u_longlong_t *)&csymlink.fileno)) ||
(! xdr_int(xdrs, &csymlink.size))) {
stats_perror(st, SE_CORRUPT,
gettext("Truncated csymlink record"));
goto out;
}
csymlink.type = *type;
if ((rc = (struct cachefs_log_csymlink_record *)
calloc(1, sizeof (csymlink))) == NULL) {
stats_perror(st, SE_NOMEM,
gettext("Cannot malloc record"));
goto out;
}
memcpy(rc, &csymlink, sizeof (csymlink));
break;
case CACHEFS_LOG_FILLDIR:
if ((! xdr_int(xdrs, &filldir.error)) ||
(! xdr_int(xdrs, (int *)&filldir.time)) ||
(! xdr_opaque(xdrs, (caddr_t)&filldir.vfsp,
sizeof (filldir.vfsp))) ||
(! xdr_opaque(xdrs, (caddr_t)&filldir.fid,
sizeof (filldir.fid))) ||
(! xdr_u_longlong_t(xdrs,
(u_longlong_t *)&filldir.fileno)) ||
(! xdr_int(xdrs, &filldir.size))) {
stats_perror(st, SE_CORRUPT,
gettext("Truncated filldir record"));
goto out;
}
filldir.type = *type;
if ((rc = (struct cachefs_log_filldir_record *)
calloc(1, sizeof (filldir))) == NULL) {
stats_perror(st, SE_NOMEM,
gettext("Cannot malloc record"));
goto out;
}
memcpy(rc, &filldir, sizeof (filldir));
break;
case CACHEFS_LOG_MDCREATE:
if ((! xdr_int(xdrs, &mdcreate.error)) ||
(! xdr_int(xdrs, (int *)&mdcreate.time)) ||
(! xdr_opaque(xdrs, (caddr_t)&mdcreate.vfsp,
sizeof (mdcreate.vfsp))) ||
(! xdr_opaque(xdrs, (caddr_t)&mdcreate.fid,
sizeof (mdcreate.fid))) ||
(! xdr_u_longlong_t(xdrs,
(u_longlong_t *)&mdcreate.fileno)) ||
(! xdr_u_int(xdrs, &mdcreate.count))) {
stats_perror(st, SE_CORRUPT,
gettext("Truncated mdcreate record"));
goto out;
}
mdcreate.type = *type;
if ((rc = (struct cachefs_log_mdcreate_record *)
calloc(1, sizeof (mdcreate))) == NULL) {
stats_perror(st, SE_NOMEM,
gettext("Cannot malloc record"));
goto out;
}
memcpy(rc, &mdcreate, sizeof (mdcreate));
break;
case CACHEFS_LOG_GPFRONT:
if ((! xdr_int(xdrs, &gpfront.error)) ||
(! xdr_int(xdrs, (int *)&gpfront.time)) ||
(! xdr_opaque(xdrs, (caddr_t)&gpfront.vfsp,
sizeof (gpfront.vfsp))) ||
(! xdr_opaque(xdrs, (caddr_t)&gpfront.fid,
sizeof (gpfront.fid))) ||
(! xdr_u_longlong_t(xdrs,
(u_longlong_t *)&gpfront.fileno)) ||
(! xdr_int(xdrs, (int *)&gpfront.uid)) ||
(! xdr_u_longlong_t(xdrs, (u_longlong_t *)&gpfront.off)) ||
(! xdr_u_int(xdrs, &gpfront.len))) {
stats_perror(st, SE_CORRUPT,
gettext("Truncated gpfront record"));
goto out;
}
gpfront.type = *type;
if ((rc = (struct cachefs_log_gpfront_record *)
calloc(1, sizeof (gpfront))) == NULL) {
stats_perror(st, SE_NOMEM,
gettext("Cannot malloc record"));
goto out;
}
memcpy(rc, &gpfront, sizeof (gpfront));
break;
case CACHEFS_LOG_RFDIR:
if ((! xdr_int(xdrs, &rfdir.error)) ||
(! xdr_int(xdrs, (int *)&rfdir.time)) ||
(! xdr_opaque(xdrs, (caddr_t)&rfdir.vfsp,
sizeof (rfdir.vfsp))) ||
(! xdr_opaque(xdrs, (caddr_t)&rfdir.fid,
sizeof (rfdir.fid))) ||
(! xdr_u_longlong_t(xdrs, (u_longlong_t *)&rfdir.fileno)) ||
(! xdr_int(xdrs, (int *)&rfdir.uid))) {
stats_perror(st, SE_CORRUPT,
gettext("Truncated rfdir record"));
goto out;
}
rfdir.type = *type;
if ((rc = (struct cachefs_log_rfdir_record *)
calloc(1, sizeof (rfdir))) == NULL) {
stats_perror(st, SE_NOMEM,
gettext("Cannot malloc record"));
goto out;
}
memcpy(rc, &rfdir, sizeof (rfdir));
break;
case CACHEFS_LOG_UALLOC:
if ((! xdr_int(xdrs, &ualloc.error)) ||
(! xdr_int(xdrs, (int *)&ualloc.time)) ||
(! xdr_opaque(xdrs, (caddr_t)&ualloc.vfsp,
sizeof (ualloc.vfsp))) ||
(! xdr_opaque(xdrs, (caddr_t)&ualloc.fid,
sizeof (ualloc.fid))) ||
(! xdr_u_longlong_t(xdrs,
(u_longlong_t *)&ualloc.fileno)) ||
(! xdr_u_longlong_t(xdrs, (u_longlong_t *)&ualloc.off)) ||
(! xdr_u_int(xdrs, &ualloc.len))) {
stats_perror(st, SE_CORRUPT,
gettext("Truncated ualloc record"));
goto out;
}
ualloc.type = *type;
if ((rc = (struct cachefs_log_ualloc_record *)
calloc(1, sizeof (ualloc))) == NULL) {
stats_perror(st, SE_NOMEM,
gettext("Cannot malloc record"));
goto out;
}
memcpy(rc, &ualloc, sizeof (ualloc));
break;
case CACHEFS_LOG_CALLOC:
if ((! xdr_int(xdrs, &challoc.error)) ||
(! xdr_int(xdrs, (int *)&challoc.time)) ||
(! xdr_opaque(xdrs, (caddr_t)&challoc.vfsp,
sizeof (challoc.vfsp))) ||
(! xdr_opaque(xdrs, (caddr_t)&challoc.fid,
sizeof (challoc.fid))) ||
(! xdr_u_longlong_t(xdrs,
(u_longlong_t *)&challoc.fileno)) ||
(! xdr_u_longlong_t(xdrs, (u_longlong_t *)&challoc.off)) ||
(! xdr_u_int(xdrs, &challoc.len))) {
stats_perror(st, SE_CORRUPT,
gettext("Truncated calloc record"));
goto out;
}
challoc.type = *type;
if ((rc = (struct cachefs_log_calloc_record *)
calloc(1, sizeof (challoc))) == NULL) {
stats_perror(st, SE_NOMEM,
gettext("Cannot malloc record"));
goto out;
}
memcpy(rc, &challoc, sizeof (challoc));
break;
case CACHEFS_LOG_NOCACHE:
if ((! xdr_int(xdrs, &nocache.error)) ||
(! xdr_int(xdrs, (int *)&nocache.time)) ||
(! xdr_opaque(xdrs, (caddr_t)&nocache.vfsp,
sizeof (nocache.vfsp))) ||
(! xdr_opaque(xdrs, (caddr_t)&nocache.fid,
sizeof (nocache.fid))) ||
(! xdr_u_longlong_t(xdrs,
(u_longlong_t *)&nocache.fileno))) {
stats_perror(st, SE_CORRUPT,
gettext("Truncated nocache record"));
goto out;
}
nocache.type = *type;
if ((rc = (struct cachefs_log_nocache_record *)
calloc(1, sizeof (nocache))) == NULL) {
stats_perror(st, SE_NOMEM,
gettext("Cannot malloc record"));
goto out;
}
memcpy(rc, &nocache, sizeof (nocache));
break;
default:
stats_perror(st, SE_CORRUPT,
gettext("Corrupt logfile (position %x)"),
ftell(st->st_logstream));
break;
}
out:
return (rc);
}
/*
* convert a logfile record (read by stats_log_logfile_read()) to
* ascii. probably not for end-user consumption, but this should be
* the official way to do it.
*/
char *
stats_log_record_toascii(stats_cookie_t *st, void *recp)
{
int rectype = *((int *)recp);
int recerror = *((int *)recp + 1);
time_t tt = *((time_t *)((int *)recp + 2));
struct tm *tm = localtime(&tt);
char buffy[BUFSIZ], *fidstr, *fidstr2, *fidstr3;
struct cachefs_log_mount_record *mountp;
struct cachefs_log_umount_record *umountp;
struct cachefs_log_getpage_record *getpagep;
struct cachefs_log_readdir_record *readdirp;
struct cachefs_log_readlink_record *readlinkp;
struct cachefs_log_remove_record *removep;
struct cachefs_log_rmdir_record *rmdirp;
struct cachefs_log_truncate_record *truncatep;
struct cachefs_log_putpage_record *putpagep;
struct cachefs_log_create_record *createp;
struct cachefs_log_mkdir_record *mkdirp;
struct cachefs_log_rename_record *renamep;
struct cachefs_log_symlink_record *symlinkp;
struct cachefs_log_populate_record *populatep;
struct cachefs_log_csymlink_record *csymlinkp;
struct cachefs_log_filldir_record *filldirp;
struct cachefs_log_mdcreate_record *mdcreatep;
struct cachefs_log_gpfront_record *gpfrontp;
struct cachefs_log_rfdir_record *rfdirp;
struct cachefs_log_ualloc_record *uallocp;
struct cachefs_log_calloc_record *callocp;
struct cachefs_log_nocache_record *nocachep;
assert(stats_good(st));
(void) sprintf(st->st_asciirec, "%2d/%-2d %2d:%.2d %2d",
tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min,
recerror);
switch (rectype) {
case CACHEFS_LOG_MOUNT:
mountp = (struct cachefs_log_mount_record *)recp;
(void) snprintf(buffy, sizeof (buffy),
" %-8s %llx %8x %d %d %s (%s)", "Mount", mountp->vfsp,
mountp->flags, mountp->popsize,
mountp->fgsize, mountp->path,
mountp->path + mountp->pathlen + 1);
(void) strlcat(st->st_asciirec, buffy,
sizeof (st->st_asciirec));
break;
case CACHEFS_LOG_UMOUNT:
umountp = (struct cachefs_log_umount_record *)recp;
(void) snprintf(buffy, sizeof (buffy), " %-8s %llx",
"Umount", umountp->vfsp);
(void) strlcat(st->st_asciirec, buffy,
sizeof (st->st_asciirec));
break;
case CACHEFS_LOG_GETPAGE:
getpagep = (struct cachefs_log_getpage_record *)recp;
(void) snprintf(buffy, sizeof (buffy),
" %-8s %llx %s %llu %ld %llu %u",
"Getpage",
getpagep->vfsp, fidstr = stats_log_fmtfid(&getpagep->fid),
getpagep->fileno,
getpagep->uid, getpagep->offset, getpagep->len);
(void) strlcat(st->st_asciirec, buffy,
sizeof (st->st_asciirec));
free(fidstr);
break;
case CACHEFS_LOG_READDIR:
readdirp = (struct cachefs_log_readdir_record *)recp;
(void) snprintf(buffy, sizeof (buffy),
" %-8s %llx %s %llu %d %llx %d", "Readdir",
readdirp->vfsp, fidstr = stats_log_fmtfid(&readdirp->fid),
readdirp->fileno,
readdirp->uid, readdirp->offset, readdirp->eof);
(void) strlcat(st->st_asciirec, buffy,
sizeof (st->st_asciirec));
free(fidstr);
break;
case CACHEFS_LOG_READLINK:
readlinkp = (struct cachefs_log_readlink_record *)recp;
(void) snprintf(buffy, sizeof (buffy),
" %-8s %llx %s %llu %d %u", "Readlink",
readlinkp->vfsp,
fidstr = stats_log_fmtfid(&readlinkp->fid),
readlinkp->fileno,
readlinkp->uid, readlinkp->length);
(void) strlcat(st->st_asciirec, buffy,
sizeof (st->st_asciirec));
free(fidstr);
break;
case CACHEFS_LOG_REMOVE:
removep = (struct cachefs_log_remove_record *)recp;
(void) snprintf(buffy, sizeof (buffy),
" %-8s %llx %s %llu %d", "Remove",
removep->vfsp, fidstr = stats_log_fmtfid(&removep->fid),
removep->fileno,
removep->uid);
(void) strlcat(st->st_asciirec, buffy,
sizeof (st->st_asciirec));
free(fidstr);
break;
case CACHEFS_LOG_RMDIR:
rmdirp = (struct cachefs_log_rmdir_record *)recp;
(void) snprintf(buffy, sizeof (buffy),
" %-8s %llx %s %llu %d", "Rmdir",
rmdirp->vfsp, fidstr = stats_log_fmtfid(&rmdirp->fid),
rmdirp->fileno,
rmdirp->uid);
(void) strlcat(st->st_asciirec, buffy,
sizeof (st->st_asciirec));
free(fidstr);
break;
case CACHEFS_LOG_TRUNCATE:
truncatep = (struct cachefs_log_truncate_record *)recp;
(void) snprintf(buffy, sizeof (buffy),
" %-8s %llx %s %llu %d %llu", "Truncate",
truncatep->vfsp,
fidstr = stats_log_fmtfid(&truncatep->fid),
truncatep->fileno,
truncatep->uid, truncatep->size);
(void) strlcat(st->st_asciirec, buffy,
sizeof (st->st_asciirec));
free(fidstr);
break;
case CACHEFS_LOG_PUTPAGE:
putpagep = (struct cachefs_log_putpage_record *)recp;
(void) snprintf(buffy, sizeof (buffy),
" %-8s %llx %s %llu %d %llu %u", "Putpage",
putpagep->vfsp, fidstr = stats_log_fmtfid(&putpagep->fid),
putpagep->fileno,
putpagep->uid, putpagep->offset, putpagep->len);
(void) strlcat(st->st_asciirec, buffy,
sizeof (st->st_asciirec));
free(fidstr);
break;
case CACHEFS_LOG_CREATE:
createp = (struct cachefs_log_create_record *)recp;
(void) snprintf(buffy, sizeof (buffy),
" %-8s %llx %s %llu %d", "Create",
createp->vfsp,
fidstr = stats_log_fmtfid(&createp->fid),
createp->fileno,
createp->uid);
(void) strlcat(st->st_asciirec, buffy,
sizeof (st->st_asciirec));
free(fidstr);
break;
case CACHEFS_LOG_MKDIR:
mkdirp = (struct cachefs_log_mkdir_record *)recp;
(void) snprintf(buffy, sizeof (buffy),
" %-8s %llx %s %llu %d", "Mkdir",
mkdirp->vfsp,
fidstr = stats_log_fmtfid(&mkdirp->fid),
mkdirp->fileno,
mkdirp->uid);
(void) strlcat(st->st_asciirec, buffy,
sizeof (st->st_asciirec));
free(fidstr);
break;
case CACHEFS_LOG_RENAME:
renamep = (struct cachefs_log_rename_record *)recp;
(void) snprintf(buffy, sizeof (buffy),
" %-8s %llx %s %llu %d %d", "Rename",
renamep->vfsp,
fidstr = stats_log_fmtfid(&renamep->gone),
renamep->fileno,
renamep->removed, renamep->uid);
(void) strlcat(st->st_asciirec, buffy,
sizeof (st->st_asciirec));
free(fidstr);
break;
case CACHEFS_LOG_SYMLINK:
symlinkp = (struct cachefs_log_symlink_record *)recp;
(void) snprintf(buffy, sizeof (buffy),
" %-8s %llx %s %llu %d %u", "Symlink",
symlinkp->vfsp,
fidstr = stats_log_fmtfid(&symlinkp->fid),
symlinkp->fileno,
symlinkp->uid, symlinkp->size);
(void) strlcat(st->st_asciirec, buffy,
sizeof (st->st_asciirec));
free(fidstr);
break;
case CACHEFS_LOG_POPULATE:
populatep = (struct cachefs_log_populate_record *)recp;
(void) snprintf(buffy, sizeof (buffy),
" %-8s %llx %s %llu %llu %d", "Populate",
populatep->vfsp,
fidstr = stats_log_fmtfid(&populatep->fid),
populatep->fileno,
populatep->off, populatep->size);
(void) strlcat(st->st_asciirec, buffy,
sizeof (st->st_asciirec));
free(fidstr);
break;
case CACHEFS_LOG_CSYMLINK:
csymlinkp = (struct cachefs_log_csymlink_record *)recp;
(void) snprintf(buffy, sizeof (buffy),
" %-8s %llx %s %llu %d", "Csymlink",
csymlinkp->vfsp,
fidstr = stats_log_fmtfid(&csymlinkp->fid),
csymlinkp->fileno,
csymlinkp->size);
(void) strlcat(st->st_asciirec, buffy,
sizeof (st->st_asciirec));
free(fidstr);
break;
case CACHEFS_LOG_FILLDIR:
filldirp = (struct cachefs_log_filldir_record *)recp;
(void) snprintf(buffy, sizeof (buffy),
" %-8s %llx %s %llu %d", "Filldir",
filldirp->vfsp,
fidstr = stats_log_fmtfid(&filldirp->fid),
filldirp->fileno,
filldirp->size);
(void) strlcat(st->st_asciirec, buffy,
sizeof (st->st_asciirec));
free(fidstr);
break;
case CACHEFS_LOG_MDCREATE:
mdcreatep = (struct cachefs_log_mdcreate_record *)recp;
(void) snprintf(buffy, sizeof (buffy),
" %-8s %llx %s %llu %u", "Mdcreate",
mdcreatep->vfsp,
fidstr = stats_log_fmtfid(&mdcreatep->fid),
mdcreatep->fileno, mdcreatep->count);
(void) strlcat(st->st_asciirec, buffy,
sizeof (st->st_asciirec));
free(fidstr);
break;
case CACHEFS_LOG_GPFRONT:
gpfrontp = (struct cachefs_log_gpfront_record *)recp;
(void) snprintf(buffy, sizeof (buffy),
" %-8s %llx %s %llu %d %llu %u", "Gpfront",
gpfrontp->vfsp,
fidstr = stats_log_fmtfid(&gpfrontp->fid),
gpfrontp->fileno,
gpfrontp->uid, gpfrontp->off, gpfrontp->len);
(void) strlcat(st->st_asciirec, buffy,
sizeof (st->st_asciirec));
free(fidstr);
break;
case CACHEFS_LOG_RFDIR:
rfdirp = (struct cachefs_log_rfdir_record *)recp;
(void) snprintf(buffy, sizeof (buffy),
" %-8s %llx %s %llu %d", "Rfdir",
rfdirp->vfsp,
fidstr = stats_log_fmtfid(&rfdirp->fid),
rfdirp->fileno,
rfdirp->uid);
(void) strlcat(st->st_asciirec, buffy,
sizeof (st->st_asciirec));
free(fidstr);
break;
case CACHEFS_LOG_UALLOC:
uallocp = (struct cachefs_log_ualloc_record *)recp;
(void) snprintf(buffy, sizeof (buffy),
" %-8s %llx %s %llu %llu %u", "Ualloc",
uallocp->vfsp,
fidstr = stats_log_fmtfid(&uallocp->fid),
uallocp->fileno,
uallocp->off, uallocp->len);
(void) strlcat(st->st_asciirec, buffy,
sizeof (st->st_asciirec));
free(fidstr);
break;
case CACHEFS_LOG_CALLOC:
callocp = (struct cachefs_log_calloc_record *)recp;
(void) snprintf(buffy, sizeof (buffy),
" %-8s %llx %s %llu %llu %u", "Calloc",
callocp->vfsp,
fidstr = stats_log_fmtfid(&callocp->fid),
callocp->fileno, callocp->off, callocp->len);
(void) strlcat(st->st_asciirec, buffy,
sizeof (st->st_asciirec));
free(fidstr);
break;
case CACHEFS_LOG_NOCACHE:
nocachep = (struct cachefs_log_nocache_record *)recp;
(void) snprintf(buffy, sizeof (buffy),
" %-8s %llx %s %llu", "Nocache",
nocachep->vfsp,
fidstr = stats_log_fmtfid(&nocachep->fid),
nocachep->fileno);
(void) strlcat(st->st_asciirec, buffy,
sizeof (st->st_asciirec));
free(fidstr);
break;
default:
stats_perror(st, SE_CORRUPT,
gettext(
"Attempt to format invalid log type=%d (position %x)"),
rectype, ftell(st->st_logstream));
return (NULL);
}
return (st->st_asciirec);
}
uint_t
stats_log_get_record_info(stats_cookie_t *sc,
void *recp, caddr_t *vfsp, cfs_fid_t **fidp, ino64_t *filenop,
u_offset_t *offp, u_offset_t *lenp)
{
int type = ((int *)recp)[0];
int error = ((int *)recp)[1];
uint_t rc = 0;
struct cachefs_log_getpage_record *getpagep;
struct cachefs_log_readdir_record *readdirp;
struct cachefs_log_readlink_record *readlinkp;
struct cachefs_log_remove_record *removep;
struct cachefs_log_rmdir_record *rmdirp;
struct cachefs_log_truncate_record *truncatep;
struct cachefs_log_putpage_record *putpagep;
struct cachefs_log_create_record *createp;
struct cachefs_log_mkdir_record *mkdirp;
struct cachefs_log_rename_record *renamep;
struct cachefs_log_symlink_record *symlinkp;
struct cachefs_log_populate_record *populatep;
struct cachefs_log_csymlink_record *csymlinkp;
struct cachefs_log_filldir_record *filldirp;
struct cachefs_log_mdcreate_record *mdcreatep;
struct cachefs_log_gpfront_record *gpfrontp;
struct cachefs_log_rfdir_record *rfdirp;
struct cachefs_log_ualloc_record *uallocp;
struct cachefs_log_calloc_record *callocp;
struct cachefs_log_nocache_record *nocachep;
switch (type) {
case CACHEFS_LOG_RFDIR:
if ((error == EINVAL) || (error == ENOENT))
error = 0;
break;
}
if (error != 0)
return (0);
switch (type) {
case CACHEFS_LOG_GETPAGE:
getpagep = (struct cachefs_log_getpage_record *)recp;
*fidp = &getpagep->fid;
*filenop = getpagep->fileno;
*vfsp = (caddr_t)getpagep->vfsp;
*offp = getpagep->offset;
*lenp = (u_offset_t)getpagep->len;
rc = (GRI_ADD | GRI_EXPENSIVE);
break;
case CACHEFS_LOG_READDIR:
readdirp = (struct cachefs_log_readdir_record *)recp;
*fidp = &readdirp->fid;
*filenop = readdirp->fileno;
*vfsp = (caddr_t)readdirp->vfsp;
*offp = readdirp->offset;
*lenp = (u_offset_t)sc->st_loghead.lh_maxbsize;
rc = (GRI_ADD | GRI_EXPENSIVE);
break;
case CACHEFS_LOG_READLINK:
readlinkp = (struct cachefs_log_readlink_record *)recp;
*fidp = &readlinkp->fid;
*filenop = readlinkp->fileno;
*vfsp = (caddr_t)readlinkp->vfsp;
*offp = 0LL;
*lenp = (u_offset_t)((readlinkp->length > C_FSL_SIZE) ?
readlinkp->length : 0);
rc = (GRI_ADD | GRI_EXPENSIVE);
break;
case CACHEFS_LOG_REMOVE:
removep = (struct cachefs_log_remove_record *)recp;
*fidp = &removep->fid;
*filenop = removep->fileno;
*vfsp = (caddr_t)removep->vfsp;
*offp = *lenp = 0LL;
rc = (GRI_TRUNC | GRI_MODIFY);
break;
case CACHEFS_LOG_RMDIR:
rmdirp = (struct cachefs_log_rmdir_record *)recp;
*fidp = &rmdirp->fid;
*filenop = rmdirp->fileno;
*vfsp = (caddr_t)rmdirp->vfsp;
*offp = *lenp = 0LL;
rc = (GRI_TRUNC | GRI_MODIFY);
break;
case CACHEFS_LOG_TRUNCATE:
truncatep = (struct cachefs_log_truncate_record *)recp;
*fidp = &truncatep->fid;
*filenop = truncatep->fileno;
*vfsp = (caddr_t)truncatep->vfsp;
*offp = 0LL;
*lenp = truncatep->size;
rc = (GRI_TRUNC | GRI_MODIFY);
break;
case CACHEFS_LOG_PUTPAGE:
putpagep = (struct cachefs_log_putpage_record *)recp;
*fidp = &putpagep->fid;
*filenop = putpagep->fileno;
*vfsp = (caddr_t)putpagep->vfsp;
*offp = putpagep->offset;
*lenp = (u_offset_t)putpagep->len;
rc = (GRI_ADD | GRI_MODIFY);
break;
case CACHEFS_LOG_CREATE:
createp = (struct cachefs_log_create_record *)recp;
*fidp = &createp->fid;
*filenop = createp->fileno;
*vfsp = (caddr_t)createp->vfsp;
*offp = *lenp = 0LL;
rc = (GRI_ADD | GRI_MODIFY);
break;
case CACHEFS_LOG_MKDIR:
mkdirp = (struct cachefs_log_mkdir_record *)recp;
*fidp = &mkdirp->fid;
*filenop = mkdirp->fileno;
*vfsp = (caddr_t)mkdirp->vfsp;
*offp = *lenp = 0LL;
rc = (GRI_ADD | GRI_MODIFY);
break;
case CACHEFS_LOG_RENAME:
renamep = (struct cachefs_log_rename_record *)recp;
*fidp = &renamep->gone;
*filenop = renamep->fileno;
*vfsp = (caddr_t)renamep->vfsp;
*offp = *lenp = 0LL;
rc = GRI_MODIFY;
if (renamep->removed)
rc |= GRI_TRUNC;
break;
case CACHEFS_LOG_SYMLINK:
symlinkp = (struct cachefs_log_symlink_record *)recp;
*fidp = &symlinkp->fid;
*filenop = symlinkp->fileno;
*vfsp = (caddr_t)symlinkp->vfsp;
*offp = 0LL;
*lenp = (u_offset_t)((symlinkp->size > C_FSL_SIZE) ?
symlinkp->size : 0);
rc = (GRI_ADD | GRI_MODIFY);
break;
case CACHEFS_LOG_POPULATE:
populatep = (struct cachefs_log_populate_record *)recp;
*fidp = &populatep->fid;
*filenop = populatep->fileno;
*vfsp = (caddr_t)populatep->vfsp;
*offp = populatep->off;
*lenp = (u_offset_t)populatep->size;
rc = GRI_ADD;
break;
case CACHEFS_LOG_CSYMLINK:
csymlinkp = (struct cachefs_log_csymlink_record *)recp;
*fidp = &csymlinkp->fid;
*filenop = csymlinkp->fileno;
*vfsp = (caddr_t)csymlinkp->vfsp;
*offp = 0LL;
*lenp = (u_offset_t)((csymlinkp->size > C_FSL_SIZE) ?
csymlinkp->size : 0);
rc = GRI_ADD;
break;
case CACHEFS_LOG_FILLDIR:
filldirp = (struct cachefs_log_filldir_record *)recp;
*fidp = &filldirp->fid;
*filenop = filldirp->fileno;
*vfsp = (caddr_t)filldirp->vfsp;
*offp = 0LL;
*lenp = (u_offset_t)(filldirp->size);
rc = GRI_ADD;
break;
case CACHEFS_LOG_MDCREATE:
mdcreatep = (struct cachefs_log_mdcreate_record *)recp;
*fidp = &mdcreatep->fid;
*filenop = mdcreatep->fileno;
*vfsp = (caddr_t)mdcreatep->vfsp;
*lenp = (u_offset_t)mdcreatep->count;
rc = GRI_METADATA;
break;
case CACHEFS_LOG_GPFRONT:
gpfrontp = (struct cachefs_log_gpfront_record *)recp;
*fidp = &gpfrontp->fid;
*filenop = gpfrontp->fileno;
*vfsp = (caddr_t)gpfrontp->vfsp;
*offp = gpfrontp->off;
*lenp = (u_offset_t)sc->st_loghead.lh_pagesize;
rc = (GRI_ADD | GRI_EXPENSIVE);
break;
case CACHEFS_LOG_RFDIR:
rfdirp = (struct cachefs_log_rfdir_record *)recp;
rfdirp->error = 0;
*fidp = &rfdirp->fid;
*filenop = rfdirp->fileno;
*vfsp = (caddr_t)rfdirp->vfsp;
*offp = 0LL;
*lenp = (u_offset_t)sc->st_loghead.lh_maxbsize;
rc = (GRI_ADD | GRI_EXPENSIVE);
break;
case CACHEFS_LOG_UALLOC:
uallocp = (struct cachefs_log_ualloc_record *)recp;
*fidp = &uallocp->fid;
*filenop = uallocp->fileno;
*vfsp = (caddr_t)uallocp->vfsp;
*offp = uallocp->off;
*lenp = (u_offset_t)uallocp->len;
rc = (GRI_ADD);
break;
case CACHEFS_LOG_CALLOC:
callocp = (struct cachefs_log_calloc_record *)recp;
*fidp = &callocp->fid;
*filenop = callocp->fileno;
*vfsp = (caddr_t)callocp->vfsp;
*offp = callocp->off;
*lenp = (u_offset_t)callocp->len;
rc = (GRI_ADD | GRI_EXPENSIVE);
break;
case CACHEFS_LOG_NOCACHE:
nocachep = (struct cachefs_log_nocache_record *)recp;
*fidp = &nocachep->fid;
*filenop = nocachep->fileno;
*vfsp = (caddr_t)nocachep->vfsp;
*offp = *lenp = 0LL;
rc = (GRI_TRUNC);
break;
}
return (rc);
}
/*
* ascii formatter for fids. returns a malloc()ed string -- it's up to
* the caller to free it.
*/
static char *
stats_log_fmtfid(cfs_fid_t *fidp)
{
char buffy[BUFSIZ], *rc;
(void) strcpy(buffy, "<fid>");
rc = strdup(buffy);
if (rc == NULL)
rc = "out of memory";
return (rc);
}
void
stats_log_fi_add(stats_cookie_t *st, fid_info *fip, u_offset_t off,
u_offset_t len)
{
int i, j;
u_offset_t iend, jend, tmp;
assert(stats_good(st));
assert(st->st_flags & ST_DBMOPEN);
assert(st->st_flags & ST_LFOPEN);
/* shortcut if we had some sort of zero-length thing */
if (len == 0LL)
return;
/* `smear' the offset and length to block boundaries */
/*
* pre-largefiles: iend = off & ~(st->st_loghead.lh_maxbsize - 1);
* largefiles: make sure that we ~ all bits in the 64 bit
* version of (st->st_loghead.lh_maxbsize - 1)
*/
tmp = (u_offset_t)(st->st_loghead.lh_maxbsize - 1);
iend = off & ~tmp;
jend = off + len;
jend += (u_offset_t)(st->st_loghead.lh_maxbsize - 1);
/*
* pre-largefiles: jend &= ~(st->st_loghead.lh_maxbsize - 1);
* largefiles: make sure that we ~ all bits in the 64 bit
* version of (st->st_loghead.lh_maxbsize - 1)
*/
jend &= ~tmp;
off = iend;
len = jend - off;
/* see if our offset falls within an existing chunk */
for (i = 0; i < fip->fi_ent_n; i++) {
iend = fip->fi_ent[i].offset + fip->fi_ent[i].len;
if ((fip->fi_ent[i].offset <= off) && (iend >= off))
break;
}
/* update the chunk, or make a new one */
if (i < fip->fi_ent_n) {
if ((off + len) > iend)
fip->fi_ent[i].len = off + len - fip->fi_ent[i].offset;
} else if (i < C_MAX_ALLOCINFO_SLOTS) {
fip->fi_ent_n = i + 1;
fip->fi_ent[i].offset = off;
fip->fi_ent[i].len = len;
} else {
/* cachefs does a nocache, so we'll immitate */
/*
* XXX we're free to grow again. assume we got
* inactivated right away -- the worst case!
*/
fip->fi_ent_n = 0;
fip->fi_total = 0LL;
}
/* quit for the trivial (hopefully the usual) case... */
if (fip->fi_ent_n <= 1) {
if (fip->fi_ent_n == 0)
fip->fi_total = 0LL;
else
fip->fi_total = fip->fi_ent[0].len;
return;
}
/*
* we have to see if we can consolidate any chunks. the
* chunks aren't guaranteed to be in any kind of order, so we
* do a qsort. otherwise, the consolidation would be N^2 (but
* we're probably close here).
*/
qsort(fip->fi_ent, fip->fi_ent_n, sizeof (fip->fi_ent[0]),
stats_log_fi_comp);
/* tag non-essential entries with offset == -1, and consolidate */
for (i = 0; i < fip->fi_ent_n - 1; i++) {
if ((offset_t)fip->fi_ent[i].offset < 0)
continue;
iend = fip->fi_ent[i].offset + fip->fi_ent[i].len;
for (j = i + 1; j < fip->fi_ent_n; j++) {
if (iend < fip->fi_ent[j].offset)
break;
jend = fip->fi_ent[j].offset + fip->fi_ent[j].len;
if (jend >= iend)
fip->fi_ent[i].len =
jend - fip->fi_ent[i].offset;
fip->fi_ent[j].offset = (u_offset_t)-1;
}
}
/* get rid of non-essential entries (without preserving order) */
for (i = 0; i < fip->fi_ent_n; i++)
if ((offset_t)fip->fi_ent[i].offset < 0)
fip->fi_ent[i--] = fip->fi_ent[--(fip->fi_ent_n)];
/* add up the new total size */
for (i = fip->fi_total = 0LL; i < fip->fi_ent_n; i++)
fip->fi_total += fip->fi_ent[i].len;
}
static int
stats_log_fi_comp(const void *a, const void *b)
{
struct fid_info_allocent *fa = (struct fid_info_allocent *)a;
struct fid_info_allocent *fb = (struct fid_info_allocent *)b;
if ((offset_t)(fa->offset - fb->offset) > 0)
return (1);
if ((offset_t)(fa->offset - fb->offset) < 0)
return (-1);
return (0);
}
void
stats_log_fi_trunc(stats_cookie_t *st, fid_info *fip, u_offset_t off,
u_offset_t len)
{
fip->fi_ent_n = 1;
fip->fi_ent[0].offset = off;
fip->fi_ent[0].len = len;
fip->fi_total = len;
}
struct cachefs_log_logfile_header *
stats_log_getheader(stats_cookie_t *st)
{
assert(stats_good(st));
assert(st->st_flags & ST_LFOPEN);
return (&st->st_loghead);
}
void
stats_log_compute_wssize(stats_cookie_t *st)
{
void *record;
int type;
struct cachefs_log_mount_record *mountp;
struct cachefs_log_umount_record *umountp;
datum key;
caddr_t vfsp;
mount_info *mi = NULL, *mip;
size_t len1, len2, maxlen;
char *string1, *string2;
uint_t rflags;
fid_info fi, *fip;
cfs_fid_t *fidp;
ino64_t fileno;
u_offset_t off;
u_offset_t len;
struct cachefs_log_logfile_header *lh = &st->st_loghead;
size_t delta;
assert(stats_good(st));
assert(st->st_flags & ST_LFOPEN);
assert(st->st_flags & ST_DBMOPEN);
/*
* The maximum size of a mount_info structure is the size of
* the structure less the space already defined for char mi_path[]
* plus the maximum size of mi_path.
*
* Additional space is allocated to mi_path at runtime using
* malloc(). The size needs to be calculated in-situ as ANSI C
* will only allow 'sizeof expression' or 'sizeof (type)'.
*/
mi = malloc(sizeof (*mi) - sizeof (mi->mi_path) + MI_MAX_MI_PATH);
if (mi == NULL) {
stats_perror(st, SE_NOMEM, gettext("Out of memory"));
goto out;
}
st->st_ws_init = st->st_loghead.lh_blocks;
while (record = stats_log_logfile_read(st, &type)) {
switch (type) {
case CACHEFS_LOG_MOUNT:
mountp = (struct cachefs_log_mount_record *)record;
if (mountp->error != 0)
break;
for (key = stats_dbm_firstkey(st);
key.dptr != NULL;
key = stats_dbm_nextkey(st)) {
if (key.dsize != sizeof (vfsp))
continue;
memcpy((caddr_t)&vfsp, key.dptr,
sizeof (vfsp));
mip = stats_dbm_fetch_byvfsp(st, vfsp);
if (mip == NULL)
continue;
len1 = strlen(mip->mi_path);
len2 = strlen(mip->mi_path + len1 + 1);
memcpy((caddr_t)mi, mip, sizeof (*mi) +
len1 + len2 - CLPAD(mount_info, mi_path));
free(mip);
string1 = mi->mi_path + len1 + 1;
string2 = mountp->path + mountp->pathlen + 1;
if (strcmp(string1, string2) == 0) {
stats_dbm_delete_byvfsp(st, vfsp);
break;
}
}
if (key.dptr == NULL) {
/* non-idempotent setup stuff */
memset(mi, '\0', sizeof (*mi));
mi->mi_flags = mountp->flags;
mi->mi_filegrp_size = mountp->fgsize;
}
/*
* idempotent setup stuff
*
* Careful string handling around mi_path
* is required as it contains two NULL
* terminated strings.
*/
mi->mi_mounted = 1;
maxlen = MI_MAX_MI_PATH - 1;
len1 = strlcpy(mi->mi_path, mountp->path, maxlen);
if (len1 >= maxlen) {
stats_perror(st, SE_CORRUPT,
gettext("Path too long in log file"));
break;
}
len1 = strlen(mi->mi_path);
maxlen = MI_MAX_MI_PATH - (len1 + 1);
len2 = strlcpy(mi->mi_path + len1 + 1,
mountp->path + mountp->pathlen + 1, maxlen);
if (len2 >= maxlen) {
stats_perror(st, SE_CORRUPT,
gettext("CacheID too long in log file"));
break;
}
stats_dbm_store_byvfsp(st, (caddr_t)mountp->vfsp, mi);
break;
case CACHEFS_LOG_UMOUNT:
umountp = (struct cachefs_log_umount_record *)record;
if (umountp->error != 0)
break;
mip = stats_dbm_fetch_byvfsp(st,
(caddr_t)umountp->vfsp);
if (mip == NULL)
break;
mip->mi_mounted = 0;
stats_dbm_store_byvfsp(st, (caddr_t)umountp->vfsp, mip);
free(mip);
break;
default:
rflags = stats_log_get_record_info(st, record,
&vfsp, &fidp, &fileno, &off, &len);
if (rflags == 0) /* shortcut */
break;
mip = stats_dbm_fetch_byvfsp(st, vfsp);
if (mip == NULL) /* hopefully very rare */
break;
fip = stats_dbm_fetch_byfid(st, fidp);
if (fip == NULL) {
fip = &fi;
memset(&fi, '\0', sizeof (fi));
fi.fi_vfsp = vfsp;
}
/* account for the creation of the fscache */
if (! mip->mi_used) {
mip->mi_used = 1;
/* account for the .cfs_option file */
mip->mi_current += (u_offset_t)lh->lh_maxbsize;
st->st_ws_current +=
(u_offset_t)lh->lh_maxbsize;
}
/*
* Add in the size-growth of the attrcache.
* len will be non-zero only for the record type
* CACHEFS_LOG_MDCREATE, and len can't be > 2GB because
* it refers to the number of entries in
* the attribute cache file.
*/
assert(len <= UINT_MAX);
delta = stats_dbm_attrcache_addsize(st, mip, fileno,
(type == CACHEFS_LOG_MDCREATE) ? (uint_t)len : 0);
st->st_ws_current += (u_offset_t)delta;
mip->mi_current += (u_offset_t)delta;
/* see if this is an `expensive' logfile */
if ((! st->st_ws_expensive) && (rflags & GRI_EXPENSIVE))
st->st_ws_expensive = 1;
/* subtract current frontfile size ... */
st->st_ws_current -= fip->fi_total;
mip->mi_current -= fip->fi_total;
/* compute new frontfile size */
if ((mip->mi_flags & CFS_WRITE_AROUND) &&
(rflags & GRI_MODIFY)) {
fip->fi_total = 0LL;
fip->fi_ent_n = 0;
} else if (rflags & GRI_ADD) {
stats_log_fi_add(st, fip, off, len);
} else if (rflags & GRI_TRUNC) {
stats_log_fi_trunc(st, fip, off, len);
}
if (rflags & GRI_METADATA)
fip->fi_flags |= FI_METADATA;
/* add back in new frontfile size */
mip->mi_current += fip->fi_total;
if (mip->mi_current > mip->mi_high)
mip->mi_high = mip->mi_current;
stats_dbm_store_byvfsp(st, vfsp, mip);
free(mip);
st->st_ws_current += fip->fi_total;
if (st->st_ws_current > st->st_ws_high)
st->st_ws_high = st->st_ws_current;
stats_dbm_store_byfid(st, fidp, fip);
if (fip != &fi)
free(fip);
break;
}
free(record);
if (stats_inerror(st))
break;
}
out:
if (mi != NULL)
free(mi);
if (! stats_inerror(st))
st->st_flags |= ST_WSCOMP;
}
int
stats_log_wssize_init(stats_cookie_t *st)
{
assert(stats_good(st));
assert(st->st_flags & ST_WSCOMP);
return (st->st_ws_init);
}
u_offset_t
stats_log_wssize_current(stats_cookie_t *st)
{
assert(stats_good(st));
assert(st->st_flags & ST_WSCOMP);
return (st->st_ws_current);
}
u_offset_t
stats_log_wssize_high(stats_cookie_t *st)
{
assert(stats_good(st));
assert(st->st_flags & ST_WSCOMP);
return (st->st_ws_high);
}
int
stats_log_wssize_expensive(stats_cookie_t *st)
{
assert(stats_good(st));
assert(st->st_flags & ST_WSCOMP);
return (st->st_ws_expensive);
}