1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 /*
  30  * Routines for cachefs logging.
  31  */
  32 
  33 #include <stdio.h>
  34 #include <stdlib.h>
  35 #include <stddef.h>
  36 #include <sys/types.h>
  37 #include <sys/param.h>
  38 #include <errno.h>
  39 #include <sys/stat.h>
  40 #include <fcntl.h>
  41 #include <unistd.h>
  42 #include <libintl.h>
  43 #include <time.h>
  44 #include <string.h>
  45 #include <sys/fs/cachefs_fs.h>
  46 #include <sys/fs/cachefs_log.h>
  47 #include <malloc.h>
  48 #include <limits.h>
  49 #include "stats.h"
  50 #include <assert.h>
  51 
  52 /* forward declarations of statics */
  53 static kstat_t *stats_log_kstat_read(stats_cookie_t *);
  54 static char *stats_log_fmtfid(cfs_fid_t *);
  55 static bool_t stats_xdr_loghead(XDR *, struct cachefs_log_logfile_header *);
  56 static int stats_log_fi_comp(const void *a, const void *b);
  57 
  58 int
  59 stats_log_kernel_setname(stats_cookie_t *st, char *path)
  60 {
  61         int error = 0;
  62         kstat_t *log;
  63         cachefs_log_control_t *lc;
  64         int exists = 0;
  65 
  66         assert(stats_good(st));
  67 
  68         if ((log = stats_log_kstat_read(st)) == NULL) {
  69                 error = stats_errno(st);
  70                 goto out;
  71         }
  72 
  73         lc = (cachefs_log_control_t *)log->ks_data;
  74 
  75         /*
  76          * the stats_ API allows a NULL or an empty path to turn off
  77          * logging, but the kstat interface has the string buffered,
  78          * so we need to make an empty string.
  79          */
  80 
  81         if (path == NULL)
  82                 path = "";
  83         if ((lc->lc_path[0] == 0) && (path[0] == 0))
  84                 goto out;
  85 
  86         (void) strlcpy(lc->lc_path, path, sizeof (lc->lc_path));
  87 
  88         if (path[0] != '\0') {
  89                 struct stat64 s;
  90                 int f;
  91 
  92                 exists = access(path, F_OK);
  93                 /* logfile will be <2GB */
  94                 f = open(path, O_WRONLY | O_CREAT, 0666);
  95                 if (f < 0) {
  96                         stats_perror(st, error = SE_FILE,
  97                             gettext("Cannot open/create logfile: %s"),
  98                             strerror(errno));
  99                         goto out;
 100                 }
 101 
 102                 if (fstat64(f, &s) < 0) {
 103                         stats_perror(st, error = SE_FILE,
 104                             gettext("Cannot stat logfile: %s"),
 105                             strerror(errno));
 106                         (void) close(f);
 107                         goto out;
 108                 }
 109 
 110                 /*
 111                  * the kernel will accept an empty file as a logfile.  we must
 112                  * make sure that we created this empty file, i.e. that it's
 113                  * not an already existing file that happened to be empty.
 114                  *
 115                  * if we hand the kernel a nonempty file, it will check the
 116                  * magic number.  thus, if they hand it something like
 117                  * /etc/passwd, the kernel should reject it.  we just have to
 118                  * catch the cases of empty files we don't want to be
 119                  * logfiles.
 120                  */
 121 
 122                 if ((exists == 0) && (s.st_size == 0LL)) {
 123                         stats_perror(st, error = SE_INVAL,
 124                             gettext(
 125                             "Cannot use existing empty file as a logfile"));
 126                         (void) close(f);
 127                         goto out;
 128                 }
 129 
 130                 (void) close(f);
 131         }
 132 
 133         if (kstat_write(st->st_kstat_cookie, log, NULL) < 0) {
 134                 stats_perror(st, error = SE_KERNEL,
 135                     gettext("Cannot set logfile path for this filesystem"));
 136                 goto out;
 137         }
 138 
 139 out:
 140         if ((error != 0) && (path[0] != '\0') && (exists != 0))
 141                 (void) unlink(path);
 142 
 143         return (error);
 144 }
 145 
 146 int
 147 stats_log_which(stats_cookie_t *st, int which, int onoff)
 148 {
 149         int error = 0;
 150         kstat_t *log;
 151         cachefs_log_control_t *lc;
 152 
 153         assert(stats_good(st));
 154 
 155         if ((log = stats_log_kstat_read(st)) == NULL) {
 156                 error = stats_errno(st);
 157                 goto out;
 158         }
 159 
 160         lc = (cachefs_log_control_t *)log->ks_data;
 161 
 162         if (onoff)
 163                 CACHEFS_LOG_SET(lc, which);
 164         else
 165                 CACHEFS_LOG_CLEAR(lc, which);
 166 
 167         if (kstat_write(st->st_kstat_cookie, log, NULL) < 0) {
 168                 stats_perror(st, error = SE_KERNEL,
 169                     gettext("Cannot set log bitmap for this filesystem"));
 170                 goto out;
 171         }
 172 
 173 out:
 174         return (error);
 175 }
 176 
 177 char *
 178 stats_log_kernel_getname(stats_cookie_t *st)
 179 {
 180         char *rc = NULL;
 181         kstat_t *log;
 182         cachefs_log_control_t *lc;
 183 
 184         assert(stats_good(st));
 185 
 186         if ((log = stats_log_kstat_read(st)) == NULL)
 187                 goto out;
 188 
 189         lc = (cachefs_log_control_t *)log->ks_data;
 190 
 191         rc = lc->lc_path; /* rc[0] will be '\0' if we're not logging */
 192 
 193 out:
 194         return (rc);
 195 }
 196 
 197 static kstat_t *
 198 stats_log_kstat_read(stats_cookie_t *st)
 199 {
 200         kstat_t *rc;
 201 
 202         assert(stats_good(st));
 203         assert(st->st_flags & ST_BOUND);
 204 
 205         if ((rc = kstat_lookup(st->st_kstat_cookie,
 206             "cachefs", st->st_fsid, "log")) == NULL) {
 207                 /*
 208                  * XXX if st was created for a particular cachedir, we
 209                  * should scan for another st->st_fsid that'll get us
 210                  * the same cache.
 211                  */
 212                 stats_perror(st, SE_KERNEL,
 213                     gettext("Cannot lookup kstats for this filesystem"));
 214                 goto out;
 215         }
 216         if (kstat_read(st->st_kstat_cookie, rc, NULL) < 0) {
 217                 stats_perror(st, SE_KERNEL,
 218                     gettext("Cannot read kstats for this filesystem"));
 219                 rc = NULL;
 220                 goto out;
 221         }
 222 
 223 out:
 224         return (rc);
 225 }
 226 
 227 int
 228 stats_log_logfile_open(stats_cookie_t *st, char *fname)
 229 {
 230         int rc = 0;
 231 
 232         assert(stats_good(st));
 233 
 234         if ((fname == NULL) || (fname[0] == '\0')) {
 235                 kstat_t *log;
 236                 cachefs_log_control_t *lc;
 237 
 238                 if ((log = stats_log_kstat_read(st)) == NULL) {
 239                         rc = -1;
 240                         goto out;
 241                 }
 242                 lc = (cachefs_log_control_t *)log->ks_data;
 243                 fname = lc->lc_path;
 244         }
 245 
 246         /* logfile will be <2GB */
 247         if ((st->st_logstream = fopen(fname, "r")) == NULL) {
 248                 stats_perror(st, SE_FILE,
 249                     gettext("Cannot open logfile %s"), fname);
 250                 rc = -1;
 251                 goto out;
 252         }
 253         xdrstdio_create(&st->st_logxdr, st->st_logstream, XDR_DECODE);
 254 
 255         if (! stats_xdr_loghead(&st->st_logxdr, &st->st_loghead)) {
 256                 stats_perror(st, SE_CORRUPT,
 257                     gettext("Cannot read header from logfile %s"), fname);
 258                 rc = -1;
 259                 goto out;
 260         }
 261         if (st->st_loghead.lh_magic != CACHEFS_LOG_MAGIC) {
 262                 stats_perror(st, SE_CORRUPT,
 263                     gettext("%s: Invalid log file header"), fname);
 264                 rc = -1;
 265                 goto out;
 266         }
 267         if (st->st_loghead.lh_revision > CACHEFS_LOG_FILE_REV) {
 268                 stats_perror(st, SE_CORRUPT,
 269                     gettext("%s: Revision too high"), fname);
 270                 rc = -1;
 271                 goto out;
 272         }
 273 
 274         st->st_flags |= ST_LFOPEN;
 275 
 276 out:
 277         if (rc != 0) {
 278                 if (st->st_logstream != NULL) {
 279                         (void) fclose(st->st_logstream);
 280                         st->st_logstream = NULL;
 281                 }
 282                 if (st->st_logxdr.x_ops != NULL) {
 283                         xdr_destroy(&st->st_logxdr);
 284                         st->st_logxdr.x_ops = NULL;
 285                 }
 286         }
 287         return (rc);
 288 }
 289 
 290 static bool_t
 291 stats_xdr_loghead(XDR *xdrs, struct cachefs_log_logfile_header *lh)
 292 {
 293         if ((! xdr_u_int(xdrs, &lh->lh_magic)) ||
 294             (! xdr_u_int(xdrs, &lh->lh_revision)) ||
 295             (! xdr_int(xdrs, &lh->lh_errno)) ||
 296             (! xdr_u_int(xdrs, &lh->lh_blocks)) ||
 297             (! xdr_u_int(xdrs, &lh->lh_files)) ||
 298             (! xdr_u_int(xdrs, &lh->lh_maxbsize)) ||
 299             (! xdr_u_int(xdrs, &lh->lh_pagesize)))
 300                 return (FALSE);
 301 
 302         return (TRUE);
 303 }
 304 
 305 void *
 306 stats_log_logfile_read(stats_cookie_t *st, int *type)
 307 {
 308         void *rc = NULL;
 309         size_t size;
 310         int ttype;
 311         XDR *xdrs;
 312         char *string1, *string2;
 313 
 314         assert(stats_good(st));
 315 
 316         xdrs = &st->st_logxdr;
 317 
 318         if (! (st->st_flags & ST_LFOPEN)) {
 319                 stats_perror(st, SE_INVAL,
 320                     gettext("Logfile was not open"));
 321                 goto out;
 322         }
 323 
 324         if (type == NULL)
 325                 type = &ttype;
 326 
 327         if (! xdr_int(xdrs, type))
 328                 goto out;
 329 
 330         switch (*type) {
 331                 struct cachefs_log_mount_record mount, *mountp;
 332                 struct cachefs_log_umount_record umount;
 333                 struct cachefs_log_getpage_record getpage;
 334                 struct cachefs_log_readdir_record readdir;
 335                 struct cachefs_log_readlink_record readlink;
 336                 struct cachefs_log_remove_record remove;
 337                 struct cachefs_log_rmdir_record rmdir;
 338                 struct cachefs_log_truncate_record truncate;
 339                 struct cachefs_log_putpage_record putpage;
 340                 struct cachefs_log_create_record create;
 341                 struct cachefs_log_mkdir_record mkdir;
 342                 struct cachefs_log_rename_record rename;
 343                 struct cachefs_log_symlink_record symlink;
 344                 struct cachefs_log_populate_record populate;
 345                 struct cachefs_log_csymlink_record csymlink;
 346                 struct cachefs_log_filldir_record filldir;
 347                 struct cachefs_log_mdcreate_record mdcreate;
 348                 struct cachefs_log_gpfront_record gpfront;
 349                 struct cachefs_log_rfdir_record rfdir;
 350                 struct cachefs_log_ualloc_record ualloc;
 351                 struct cachefs_log_calloc_record challoc;
 352                 struct cachefs_log_nocache_record nocache;
 353 
 354         case CACHEFS_LOG_MOUNT:
 355                 if ((! xdr_int(xdrs, &mount.error)) ||
 356                     (! xdr_int(xdrs, (int *)&mount.time)) ||
 357                     (! xdr_opaque(xdrs, (caddr_t)&mount.vfsp,
 358                     sizeof (mount.vfsp))) ||
 359                     (! xdr_u_int(xdrs, &mount.flags)) ||
 360                     (! xdr_u_int(xdrs, &mount.popsize)) ||
 361                     (! xdr_u_int(xdrs, &mount.fgsize)) ||
 362                     (! xdr_u_short(xdrs, &mount.pathlen)) ||
 363                     (! xdr_u_short(xdrs, &mount.cacheidlen))) {
 364                         stats_perror(st, SE_CORRUPT,
 365                             gettext("Truncated mount record"));
 366                         goto out;
 367                 }
 368                 mount.type = *type;
 369                 size = sizeof (mount) + mount.pathlen + mount.cacheidlen -
 370                         CLPAD(cachefs_log_mount_record, path);
 371                 if ((rc = mountp =
 372                     (struct cachefs_log_mount_record *)
 373                     calloc(1, size)) == NULL) {
 374                         stats_perror(st, SE_NOMEM,
 375                             gettext("Cannot malloc record"));
 376                         goto out;
 377                 }
 378                 memcpy(rc, &mount, size);
 379                 string1 = mountp->path;
 380                 string2 = mountp->path + mount.pathlen + 1;
 381                 (void) xdr_wrapstring(xdrs, &string1);
 382                 (void) xdr_wrapstring(xdrs, &string2);
 383                 break;
 384 
 385         case CACHEFS_LOG_UMOUNT:
 386                 if ((! xdr_int(xdrs, &umount.error)) ||
 387                     (! xdr_int(xdrs, (int *)&umount.time)) ||
 388                     (! xdr_opaque(xdrs, (caddr_t)&umount.vfsp,
 389                     sizeof (umount.vfsp)))) {
 390                         stats_perror(st, SE_CORRUPT,
 391                             gettext("Truncated umount record"));
 392                         goto out;
 393                 }
 394                 umount.type = *type;
 395                 size = sizeof (umount);
 396                 if ((rc = (caddr_t)calloc(1, size)) == NULL) {
 397                         stats_perror(st, SE_NOMEM,
 398                             gettext("Cannot malloc record"));
 399                         goto out;
 400                 }
 401                 memcpy(rc, &umount, size);
 402                 break;
 403 
 404         case CACHEFS_LOG_GETPAGE:
 405                 if ((! xdr_int(xdrs, &getpage.error)) ||
 406                     (! xdr_int(xdrs, (int *)&getpage.time)) ||
 407                     (! xdr_opaque(xdrs, (caddr_t)&getpage.vfsp,
 408                     sizeof (getpage.vfsp))) ||
 409                     (! xdr_opaque(xdrs, (caddr_t)&getpage.fid,
 410                     sizeof (getpage.fid))) ||
 411                     (! xdr_u_longlong_t(xdrs,
 412                     (u_longlong_t *)&getpage.fileno)) ||
 413                     (! xdr_int(xdrs, (int *)&getpage.uid)) ||
 414                     (! xdr_u_longlong_t(xdrs,
 415                     (u_longlong_t *)&getpage.offset)) ||
 416                     (! xdr_u_int(xdrs, &getpage.len))) {
 417                         stats_perror(st, SE_CORRUPT,
 418                             gettext("Truncated getpage record"));
 419                         goto out;
 420                 }
 421                 getpage.type = *type;
 422                 size = sizeof (getpage);
 423                 if ((rc = (caddr_t)calloc(1, size)) == NULL) {
 424                         stats_perror(st, SE_NOMEM,
 425                             gettext("Cannot malloc record"));
 426                         goto out;
 427                 }
 428                 memcpy(rc, &getpage, size);
 429                 break;
 430 
 431         case CACHEFS_LOG_READDIR:
 432                 if ((! xdr_int(xdrs, &readdir.error)) ||
 433                     (! xdr_int(xdrs, (int *)&readdir.time)) ||
 434                     (! xdr_opaque(xdrs, (caddr_t)&readdir.vfsp,
 435                     sizeof (readdir.vfsp))) ||
 436                     (! xdr_opaque(xdrs, (caddr_t)&readdir.fid,
 437                     sizeof (readdir.fid))) ||
 438                     (! xdr_u_longlong_t(xdrs,
 439                     (u_longlong_t *)&readdir.fileno)) ||
 440                     (! xdr_int(xdrs, (int *)&readdir.uid)) ||
 441                     (! xdr_u_longlong_t(xdrs,
 442                     (u_longlong_t *)&readdir.offset)) ||
 443                     (! xdr_int(xdrs, &readdir.eof))) {
 444                         stats_perror(st, SE_CORRUPT,
 445                             gettext("Truncated readdir record"));
 446                         goto out;
 447                 }
 448                 readdir.type = *type;
 449                 size = sizeof (readdir);
 450                 if ((rc = (caddr_t)calloc(1, size)) == NULL) {
 451                         stats_perror(st, SE_NOMEM,
 452                             gettext("Cannot malloc record"));
 453                         goto out;
 454                 }
 455                 memcpy(rc, &readdir, size);
 456                 break;
 457 
 458         case CACHEFS_LOG_READLINK:
 459                 if ((! xdr_int(xdrs, &readlink.error)) ||
 460                     (! xdr_int(xdrs, (int *)&readlink.time)) ||
 461                     (! xdr_opaque(xdrs, (caddr_t)&readlink.vfsp,
 462                     sizeof (readlink.vfsp))) ||
 463                     (! xdr_opaque(xdrs, (caddr_t)&readlink.fid,
 464                     sizeof (readlink.fid))) ||
 465                     (! xdr_u_longlong_t(xdrs,
 466                     (u_longlong_t *)&readlink.fileno)) ||
 467                     (! xdr_int(xdrs, (int *)&readlink.uid)) ||
 468                     (! xdr_u_int(xdrs,
 469                     &readlink.length))) {
 470                         stats_perror(st, SE_CORRUPT,
 471                             gettext("Truncated readlink record"));
 472                         goto out;
 473                 }
 474                 readlink.type = *type;
 475                 size = sizeof (readlink);
 476                 if ((rc = (caddr_t)calloc(1, size)) == NULL) {
 477                         stats_perror(st, SE_NOMEM,
 478                             gettext("Cannot malloc record"));
 479                         goto out;
 480                 }
 481                 memcpy(rc, &readlink, size);
 482                 break;
 483 
 484         case CACHEFS_LOG_REMOVE:
 485                 if ((! xdr_int(xdrs, &remove.error)) ||
 486                     (! xdr_int(xdrs, (int *)&remove.time)) ||
 487                     (! xdr_opaque(xdrs, (caddr_t)&remove.vfsp,
 488                     sizeof (remove.vfsp))) ||
 489                     (! xdr_opaque(xdrs, (caddr_t)&remove.fid,
 490                     sizeof (remove.fid))) ||
 491                     (! xdr_u_longlong_t(xdrs,
 492                     (u_longlong_t *)&remove.fileno)) ||
 493                     (! xdr_int(xdrs, (int *)&remove.uid))) {
 494                         stats_perror(st, SE_CORRUPT,
 495                             gettext("Truncated remove record"));
 496                         goto out;
 497                 }
 498                 remove.type = *type;
 499                 size = sizeof (remove);
 500                 if ((rc = (caddr_t)calloc(1, size)) == NULL) {
 501                         stats_perror(st, SE_NOMEM,
 502                             gettext("Cannot malloc record"));
 503                         goto out;
 504                 }
 505                 memcpy(rc, &remove, size);
 506                 break;
 507 
 508         case CACHEFS_LOG_RMDIR:
 509                 if ((! xdr_int(xdrs, &rmdir.error)) ||
 510                     (! xdr_int(xdrs, (int *)&rmdir.time)) ||
 511                     (! xdr_opaque(xdrs, (caddr_t)&rmdir.vfsp,
 512                     sizeof (rmdir.vfsp))) ||
 513                     (! xdr_opaque(xdrs, (caddr_t)&rmdir.fid,
 514                     sizeof (rmdir.fid))) ||
 515                     (! xdr_u_longlong_t(xdrs, (u_longlong_t *)&rmdir.fileno)) ||
 516                     (! xdr_int(xdrs, (int *)&rmdir.uid))) {
 517                         stats_perror(st, SE_CORRUPT,
 518                             gettext("Truncated rmdir record"));
 519                         goto out;
 520                 }
 521                 rmdir.type = *type;
 522                 size = sizeof (rmdir);
 523                 if ((rc = (caddr_t)calloc(1, size)) == NULL) {
 524                         stats_perror(st, SE_NOMEM,
 525                             gettext("Cannot malloc record"));
 526                         goto out;
 527                 }
 528                 memcpy(rc, &rmdir, size);
 529                 break;
 530 
 531         case CACHEFS_LOG_TRUNCATE:
 532                 if ((! xdr_int(xdrs, &truncate.error)) ||
 533                     (! xdr_int(xdrs, (int *)&truncate.time)) ||
 534                     (! xdr_opaque(xdrs, (caddr_t)&truncate.vfsp,
 535                     sizeof (truncate.vfsp))) ||
 536                     (! xdr_opaque(xdrs, (caddr_t)&truncate.fid,
 537                     sizeof (truncate.fid))) ||
 538                     (! xdr_u_longlong_t(xdrs,
 539                     (u_longlong_t *)&truncate.fileno)) ||
 540                     (! xdr_int(xdrs, (int *)&truncate.uid)) ||
 541                     (! xdr_u_longlong_t(xdrs,
 542                     (u_longlong_t *)&truncate.size))) {
 543                         stats_perror(st, SE_CORRUPT,
 544                             gettext("Truncated truncate record"));
 545                         goto out;
 546                 }
 547                 truncate.type = *type;
 548                 size = sizeof (truncate);
 549                 if ((rc = (caddr_t)calloc(1, size)) == NULL) {
 550                         stats_perror(st, SE_NOMEM,
 551                             gettext("Cannot malloc record"));
 552                         goto out;
 553                 }
 554                 memcpy(rc, &truncate, size);
 555                 break;
 556 
 557         case CACHEFS_LOG_PUTPAGE:
 558                 if ((! xdr_int(xdrs, &putpage.error)) ||
 559                     (! xdr_int(xdrs, (int *)&putpage.time)) ||
 560                     (! xdr_opaque(xdrs, (caddr_t)&putpage.vfsp,
 561                     sizeof (putpage.vfsp))) ||
 562                     (! xdr_opaque(xdrs, (caddr_t)&putpage.fid,
 563                     sizeof (putpage.fid))) ||
 564                     (! xdr_u_longlong_t(xdrs,
 565                     (u_longlong_t *)&putpage.fileno)) ||
 566                     (! xdr_int(xdrs, (int *)&putpage.uid)) ||
 567                     (! xdr_u_longlong_t(xdrs,
 568                     (u_longlong_t *)&putpage.offset)) ||
 569                     (! xdr_u_int(xdrs, &putpage.len))) {
 570                         stats_perror(st, SE_CORRUPT,
 571                             gettext("Truncated putpage record"));
 572                         goto out;
 573                 }
 574                 putpage.type = *type;
 575                 size = sizeof (putpage);
 576                 if ((rc = (caddr_t)calloc(1, size)) == NULL) {
 577                         stats_perror(st, SE_NOMEM,
 578                             gettext("Cannot malloc record"));
 579                         goto out;
 580                 }
 581                 memcpy(rc, &putpage, size);
 582                 break;
 583 
 584         case CACHEFS_LOG_CREATE:
 585                 if ((! xdr_int(xdrs, &create.error)) ||
 586                     (! xdr_int(xdrs, (int *)&create.time)) ||
 587                     (! xdr_opaque(xdrs, (caddr_t)&create.vfsp,
 588                     sizeof (create.vfsp))) ||
 589                     (! xdr_opaque(xdrs, (caddr_t)&create.fid,
 590                     sizeof (create.fid))) ||
 591                     (! xdr_u_longlong_t(xdrs,
 592                     (u_longlong_t *)&create.fileno)) ||
 593                     (! xdr_int(xdrs, (int *)&create.uid))) {
 594                         stats_perror(st, SE_CORRUPT,
 595                             gettext("Truncated create record"));
 596                         goto out;
 597                 }
 598                 create.type = *type;
 599                 size = sizeof (create);
 600                 if ((rc = (struct cachefs_log_create_record *)
 601                     calloc(1, size)) == NULL) {
 602                         stats_perror(st, SE_NOMEM,
 603                             gettext("Cannot malloc record"));
 604                         goto out;
 605                 }
 606                 memcpy(rc, &create, size);
 607                 break;
 608 
 609         case CACHEFS_LOG_MKDIR:
 610                 if ((! xdr_int(xdrs, &mkdir.error)) ||
 611                     (! xdr_int(xdrs, (int *)&mkdir.time)) ||
 612                     (! xdr_opaque(xdrs, (caddr_t)&mkdir.vfsp,
 613                     sizeof (mkdir.vfsp))) ||
 614                     (! xdr_opaque(xdrs, (caddr_t)&mkdir.fid,
 615                     sizeof (mkdir.fid))) ||
 616                     (! xdr_u_longlong_t(xdrs, (u_longlong_t *)&mkdir.fileno)) ||
 617                     (! xdr_int(xdrs, (int *)&mkdir.uid))) {
 618                         stats_perror(st, SE_CORRUPT,
 619                             gettext("Truncated mkdir record"));
 620                         goto out;
 621                 }
 622                 mkdir.type = *type;
 623                 size = sizeof (mkdir);
 624                 if ((rc = (struct cachefs_log_mkdir_record *)
 625                     calloc(1, size)) == NULL) {
 626                         stats_perror(st, SE_NOMEM,
 627                             gettext("Cannot malloc record"));
 628                         goto out;
 629                 }
 630                 memcpy(rc, &mkdir, size);
 631                 break;
 632 
 633         case CACHEFS_LOG_RENAME:
 634                 if ((! xdr_int(xdrs, &rename.error)) ||
 635                     (! xdr_int(xdrs, (int *)&rename.time)) ||
 636                     (! xdr_opaque(xdrs, (caddr_t)&rename.vfsp,
 637                     sizeof (rename.vfsp))) ||
 638                     (! xdr_opaque(xdrs, (caddr_t)&rename.gone,
 639                     sizeof (rename.gone))) ||
 640                     (! xdr_int(xdrs, &rename.removed)) ||
 641                     (! xdr_int(xdrs, (int *)&rename.uid))) {
 642                         stats_perror(st, SE_CORRUPT,
 643                             gettext("Truncated rename record"));
 644                         goto out;
 645                 }
 646                 rename.type = *type;
 647                 size = sizeof (rename);
 648                 if ((rc = (struct cachefs_log_rename_record *)
 649                     calloc(1, size)) == NULL) {
 650                         stats_perror(st, SE_NOMEM,
 651                             gettext("Cannot malloc record"));
 652                         goto out;
 653                 }
 654                 memcpy(rc, &rename, size);
 655                 break;
 656 
 657         case CACHEFS_LOG_SYMLINK:
 658                 if ((! xdr_int(xdrs, &symlink.error)) ||
 659                     (! xdr_int(xdrs, (int *)&symlink.time)) ||
 660                     (! xdr_opaque(xdrs, (caddr_t)&symlink.vfsp,
 661                     sizeof (symlink.vfsp))) ||
 662                     (! xdr_opaque(xdrs, (caddr_t)&symlink.fid,
 663                     sizeof (symlink.fid))) ||
 664                     (! xdr_u_longlong_t(xdrs,
 665                     (u_longlong_t *)&symlink.fileno)) ||
 666                     (! xdr_int(xdrs, (int *)&symlink.uid)) ||
 667                     (! xdr_u_int(xdrs, &symlink.size))) {
 668                         stats_perror(st, SE_CORRUPT,
 669                             gettext("Truncated symlink record"));
 670                         goto out;
 671                 }
 672                 symlink.type = *type;
 673                 size = sizeof (symlink);
 674                 if ((rc = (struct cachefs_log_symlink_record *)
 675                     calloc(1, size)) == NULL) {
 676                         stats_perror(st, SE_NOMEM,
 677                             gettext("Cannot malloc record"));
 678                         goto out;
 679                 }
 680                 memcpy(rc, &symlink, size);
 681                 break;
 682 
 683         case CACHEFS_LOG_POPULATE:
 684                 if ((! xdr_int(xdrs, &populate.error)) ||
 685                     (! xdr_int(xdrs, (int *)&populate.time)) ||
 686                     (! xdr_opaque(xdrs, (caddr_t)&populate.vfsp,
 687                     sizeof (populate.vfsp))) ||
 688                     (! xdr_opaque(xdrs, (caddr_t)&populate.fid,
 689                     sizeof (populate.fid))) ||
 690                     (! xdr_u_longlong_t(xdrs,
 691                     (u_longlong_t *)&populate.fileno)) ||
 692                     (! xdr_u_longlong_t(xdrs, (u_longlong_t *)&populate.off)) ||
 693                     (! xdr_u_int(xdrs, &populate.size))) {
 694                         stats_perror(st, SE_CORRUPT,
 695                             gettext("Truncated populate record"));
 696                         goto out;
 697                 }
 698                 populate.type = *type;
 699                 if ((rc = (struct cachefs_log_populate_record *)
 700                     calloc(1, sizeof (populate))) == NULL) {
 701                         stats_perror(st, SE_NOMEM,
 702                             gettext("Cannot malloc record"));
 703                         goto out;
 704                 }
 705                 memcpy(rc, &populate, sizeof (populate));
 706                 break;
 707 
 708         case CACHEFS_LOG_CSYMLINK:
 709                 if ((! xdr_int(xdrs, &csymlink.error)) ||
 710                     (! xdr_int(xdrs, (int *)&csymlink.time)) ||
 711                     (! xdr_opaque(xdrs, (caddr_t)&csymlink.vfsp,
 712                     sizeof (csymlink.vfsp))) ||
 713                     (! xdr_opaque(xdrs, (caddr_t)&csymlink.fid,
 714                     sizeof (csymlink.fid))) ||
 715                     (! xdr_u_longlong_t(xdrs,
 716                     (u_longlong_t *)&csymlink.fileno)) ||
 717                     (! xdr_int(xdrs, &csymlink.size))) {
 718                         stats_perror(st, SE_CORRUPT,
 719                             gettext("Truncated csymlink record"));
 720                         goto out;
 721                 }
 722                 csymlink.type = *type;
 723                 if ((rc = (struct cachefs_log_csymlink_record *)
 724                     calloc(1, sizeof (csymlink))) == NULL) {
 725                         stats_perror(st, SE_NOMEM,
 726                             gettext("Cannot malloc record"));
 727                         goto out;
 728                 }
 729                 memcpy(rc, &csymlink, sizeof (csymlink));
 730                 break;
 731 
 732         case CACHEFS_LOG_FILLDIR:
 733                 if ((! xdr_int(xdrs, &filldir.error)) ||
 734                     (! xdr_int(xdrs, (int *)&filldir.time)) ||
 735                     (! xdr_opaque(xdrs, (caddr_t)&filldir.vfsp,
 736                     sizeof (filldir.vfsp))) ||
 737                     (! xdr_opaque(xdrs, (caddr_t)&filldir.fid,
 738                     sizeof (filldir.fid))) ||
 739                     (! xdr_u_longlong_t(xdrs,
 740                     (u_longlong_t *)&filldir.fileno)) ||
 741                     (! xdr_int(xdrs, &filldir.size))) {
 742                         stats_perror(st, SE_CORRUPT,
 743                             gettext("Truncated filldir record"));
 744                         goto out;
 745                 }
 746                 filldir.type = *type;
 747                 if ((rc = (struct cachefs_log_filldir_record *)
 748                     calloc(1, sizeof (filldir))) == NULL) {
 749                         stats_perror(st, SE_NOMEM,
 750                             gettext("Cannot malloc record"));
 751                         goto out;
 752                 }
 753                 memcpy(rc, &filldir, sizeof (filldir));
 754                 break;
 755 
 756         case CACHEFS_LOG_MDCREATE:
 757                 if ((! xdr_int(xdrs, &mdcreate.error)) ||
 758                     (! xdr_int(xdrs, (int *)&mdcreate.time)) ||
 759                     (! xdr_opaque(xdrs, (caddr_t)&mdcreate.vfsp,
 760                     sizeof (mdcreate.vfsp))) ||
 761                     (! xdr_opaque(xdrs, (caddr_t)&mdcreate.fid,
 762                     sizeof (mdcreate.fid))) ||
 763                     (! xdr_u_longlong_t(xdrs,
 764                     (u_longlong_t *)&mdcreate.fileno)) ||
 765                     (! xdr_u_int(xdrs, &mdcreate.count))) {
 766                         stats_perror(st, SE_CORRUPT,
 767                             gettext("Truncated mdcreate record"));
 768                         goto out;
 769                 }
 770                 mdcreate.type = *type;
 771                 if ((rc = (struct cachefs_log_mdcreate_record *)
 772                     calloc(1, sizeof (mdcreate))) == NULL) {
 773                         stats_perror(st, SE_NOMEM,
 774                             gettext("Cannot malloc record"));
 775                         goto out;
 776                 }
 777                 memcpy(rc, &mdcreate, sizeof (mdcreate));
 778                 break;
 779 
 780         case CACHEFS_LOG_GPFRONT:
 781                 if ((! xdr_int(xdrs, &gpfront.error)) ||
 782                     (! xdr_int(xdrs, (int *)&gpfront.time)) ||
 783                     (! xdr_opaque(xdrs, (caddr_t)&gpfront.vfsp,
 784                     sizeof (gpfront.vfsp))) ||
 785                     (! xdr_opaque(xdrs, (caddr_t)&gpfront.fid,
 786                     sizeof (gpfront.fid))) ||
 787                     (! xdr_u_longlong_t(xdrs,
 788                     (u_longlong_t *)&gpfront.fileno)) ||
 789                     (! xdr_int(xdrs, (int *)&gpfront.uid)) ||
 790                     (! xdr_u_longlong_t(xdrs, (u_longlong_t *)&gpfront.off)) ||
 791                     (! xdr_u_int(xdrs, &gpfront.len))) {
 792                         stats_perror(st, SE_CORRUPT,
 793                             gettext("Truncated gpfront record"));
 794                         goto out;
 795                 }
 796                 gpfront.type = *type;
 797                 if ((rc = (struct cachefs_log_gpfront_record *)
 798                     calloc(1, sizeof (gpfront))) == NULL) {
 799                         stats_perror(st, SE_NOMEM,
 800                             gettext("Cannot malloc record"));
 801                         goto out;
 802                 }
 803                 memcpy(rc, &gpfront, sizeof (gpfront));
 804                 break;
 805 
 806         case CACHEFS_LOG_RFDIR:
 807                 if ((! xdr_int(xdrs, &rfdir.error)) ||
 808                     (! xdr_int(xdrs, (int *)&rfdir.time)) ||
 809                     (! xdr_opaque(xdrs, (caddr_t)&rfdir.vfsp,
 810                     sizeof (rfdir.vfsp))) ||
 811                     (! xdr_opaque(xdrs, (caddr_t)&rfdir.fid,
 812                     sizeof (rfdir.fid))) ||
 813                     (! xdr_u_longlong_t(xdrs, (u_longlong_t *)&rfdir.fileno)) ||
 814                     (! xdr_int(xdrs, (int *)&rfdir.uid))) {
 815                         stats_perror(st, SE_CORRUPT,
 816                             gettext("Truncated rfdir record"));
 817                         goto out;
 818                 }
 819                 rfdir.type = *type;
 820                 if ((rc = (struct cachefs_log_rfdir_record *)
 821                     calloc(1, sizeof (rfdir))) == NULL) {
 822                         stats_perror(st, SE_NOMEM,
 823                             gettext("Cannot malloc record"));
 824                         goto out;
 825                 }
 826                 memcpy(rc, &rfdir, sizeof (rfdir));
 827                 break;
 828 
 829         case CACHEFS_LOG_UALLOC:
 830                 if ((! xdr_int(xdrs, &ualloc.error)) ||
 831                     (! xdr_int(xdrs, (int *)&ualloc.time)) ||
 832                     (! xdr_opaque(xdrs, (caddr_t)&ualloc.vfsp,
 833                     sizeof (ualloc.vfsp))) ||
 834                     (! xdr_opaque(xdrs, (caddr_t)&ualloc.fid,
 835                     sizeof (ualloc.fid))) ||
 836                     (! xdr_u_longlong_t(xdrs,
 837                     (u_longlong_t *)&ualloc.fileno)) ||
 838                     (! xdr_u_longlong_t(xdrs, (u_longlong_t *)&ualloc.off)) ||
 839                     (! xdr_u_int(xdrs, &ualloc.len))) {
 840                         stats_perror(st, SE_CORRUPT,
 841                             gettext("Truncated ualloc record"));
 842                         goto out;
 843                 }
 844                 ualloc.type = *type;
 845                 if ((rc = (struct cachefs_log_ualloc_record *)
 846                     calloc(1, sizeof (ualloc))) == NULL) {
 847                         stats_perror(st, SE_NOMEM,
 848                             gettext("Cannot malloc record"));
 849                         goto out;
 850                 }
 851                 memcpy(rc, &ualloc, sizeof (ualloc));
 852                 break;
 853 
 854         case CACHEFS_LOG_CALLOC:
 855                 if ((! xdr_int(xdrs, &challoc.error)) ||
 856                     (! xdr_int(xdrs, (int *)&challoc.time)) ||
 857                     (! xdr_opaque(xdrs, (caddr_t)&challoc.vfsp,
 858                     sizeof (challoc.vfsp))) ||
 859                     (! xdr_opaque(xdrs, (caddr_t)&challoc.fid,
 860                     sizeof (challoc.fid))) ||
 861                     (! xdr_u_longlong_t(xdrs,
 862                     (u_longlong_t *)&challoc.fileno)) ||
 863                     (! xdr_u_longlong_t(xdrs, (u_longlong_t *)&challoc.off)) ||
 864                     (! xdr_u_int(xdrs, &challoc.len))) {
 865                         stats_perror(st, SE_CORRUPT,
 866                             gettext("Truncated calloc record"));
 867                         goto out;
 868                 }
 869                 challoc.type = *type;
 870                 if ((rc = (struct cachefs_log_calloc_record *)
 871                     calloc(1, sizeof (challoc))) == NULL) {
 872                         stats_perror(st, SE_NOMEM,
 873                             gettext("Cannot malloc record"));
 874                         goto out;
 875                 }
 876                 memcpy(rc, &challoc, sizeof (challoc));
 877                 break;
 878 
 879         case CACHEFS_LOG_NOCACHE:
 880                 if ((! xdr_int(xdrs, &nocache.error)) ||
 881                     (! xdr_int(xdrs, (int *)&nocache.time)) ||
 882                     (! xdr_opaque(xdrs, (caddr_t)&nocache.vfsp,
 883                     sizeof (nocache.vfsp))) ||
 884                     (! xdr_opaque(xdrs, (caddr_t)&nocache.fid,
 885                     sizeof (nocache.fid))) ||
 886                     (! xdr_u_longlong_t(xdrs,
 887                     (u_longlong_t *)&nocache.fileno))) {
 888                         stats_perror(st, SE_CORRUPT,
 889                             gettext("Truncated nocache record"));
 890                         goto out;
 891                 }
 892                 nocache.type = *type;
 893                 if ((rc = (struct cachefs_log_nocache_record *)
 894                     calloc(1, sizeof (nocache))) == NULL) {
 895                         stats_perror(st, SE_NOMEM,
 896                             gettext("Cannot malloc record"));
 897                         goto out;
 898                 }
 899                 memcpy(rc, &nocache, sizeof (nocache));
 900                 break;
 901 
 902         default:
 903                 stats_perror(st, SE_CORRUPT,
 904                     gettext("Corrupt logfile (position %x)"),
 905                     ftell(st->st_logstream));
 906                 break;
 907         }
 908 
 909 out:
 910         return (rc);
 911 }
 912 
 913 /*
 914  * convert a logfile record (read by stats_log_logfile_read()) to
 915  * ascii.  probably not for end-user consumption, but this should be
 916  * the official way to do it.
 917  */
 918 
 919 char *
 920 stats_log_record_toascii(stats_cookie_t *st, void *recp)
 921 {
 922         int rectype = *((int *)recp);
 923         int recerror = *((int *)recp + 1);
 924         time_t tt = *((time_t *)((int *)recp + 2));
 925         struct tm *tm = localtime(&tt);
 926         char buffy[BUFSIZ], *fidstr, *fidstr2, *fidstr3;
 927 
 928         struct cachefs_log_mount_record *mountp;
 929         struct cachefs_log_umount_record *umountp;
 930         struct cachefs_log_getpage_record *getpagep;
 931         struct cachefs_log_readdir_record *readdirp;
 932         struct cachefs_log_readlink_record *readlinkp;
 933         struct cachefs_log_remove_record *removep;
 934         struct cachefs_log_rmdir_record *rmdirp;
 935         struct cachefs_log_truncate_record *truncatep;
 936         struct cachefs_log_putpage_record *putpagep;
 937         struct cachefs_log_create_record *createp;
 938         struct cachefs_log_mkdir_record *mkdirp;
 939         struct cachefs_log_rename_record *renamep;
 940         struct cachefs_log_symlink_record *symlinkp;
 941         struct cachefs_log_populate_record *populatep;
 942         struct cachefs_log_csymlink_record *csymlinkp;
 943         struct cachefs_log_filldir_record *filldirp;
 944         struct cachefs_log_mdcreate_record *mdcreatep;
 945         struct cachefs_log_gpfront_record *gpfrontp;
 946         struct cachefs_log_rfdir_record *rfdirp;
 947         struct cachefs_log_ualloc_record *uallocp;
 948         struct cachefs_log_calloc_record *callocp;
 949         struct cachefs_log_nocache_record *nocachep;
 950 
 951         assert(stats_good(st));
 952 
 953         (void) sprintf(st->st_asciirec, "%2d/%-2d %2d:%.2d %2d",
 954             tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min,
 955             recerror);
 956 
 957         switch (rectype) {
 958         case CACHEFS_LOG_MOUNT:
 959                 mountp = (struct cachefs_log_mount_record *)recp;
 960                 (void) snprintf(buffy, sizeof (buffy),
 961                     " %-8s %llx %8x %d %d %s (%s)", "Mount", mountp->vfsp,
 962                     mountp->flags, mountp->popsize,
 963                     mountp->fgsize, mountp->path,
 964                     mountp->path + mountp->pathlen + 1);
 965                 (void) strlcat(st->st_asciirec, buffy,
 966                     sizeof (st->st_asciirec));
 967                 break;
 968 
 969         case CACHEFS_LOG_UMOUNT:
 970                 umountp = (struct cachefs_log_umount_record *)recp;
 971                 (void) snprintf(buffy, sizeof (buffy), " %-8s %llx",
 972                     "Umount", umountp->vfsp);
 973                 (void) strlcat(st->st_asciirec, buffy,
 974                     sizeof (st->st_asciirec));
 975                 break;
 976 
 977         case CACHEFS_LOG_GETPAGE:
 978                 getpagep = (struct cachefs_log_getpage_record *)recp;
 979                 (void) snprintf(buffy, sizeof (buffy),
 980                     " %-8s %llx %s %llu %ld %llu %u",
 981                     "Getpage",
 982                     getpagep->vfsp, fidstr = stats_log_fmtfid(&getpagep->fid),
 983                     getpagep->fileno,
 984                     getpagep->uid, getpagep->offset, getpagep->len);
 985                 (void) strlcat(st->st_asciirec, buffy,
 986                     sizeof (st->st_asciirec));
 987                 free(fidstr);
 988                 break;
 989 
 990         case CACHEFS_LOG_READDIR:
 991                 readdirp = (struct cachefs_log_readdir_record *)recp;
 992                 (void) snprintf(buffy, sizeof (buffy),
 993                     " %-8s %llx %s %llu %d %llx %d", "Readdir",
 994                     readdirp->vfsp, fidstr = stats_log_fmtfid(&readdirp->fid),
 995                     readdirp->fileno,
 996                     readdirp->uid, readdirp->offset, readdirp->eof);
 997                 (void) strlcat(st->st_asciirec, buffy,
 998                     sizeof (st->st_asciirec));
 999                 free(fidstr);
1000                 break;
1001 
1002         case CACHEFS_LOG_READLINK:
1003                 readlinkp = (struct cachefs_log_readlink_record *)recp;
1004                 (void) snprintf(buffy, sizeof (buffy),
1005                     " %-8s %llx %s %llu %d %u", "Readlink",
1006                     readlinkp->vfsp,
1007                     fidstr = stats_log_fmtfid(&readlinkp->fid),
1008                     readlinkp->fileno,
1009                     readlinkp->uid, readlinkp->length);
1010                 (void) strlcat(st->st_asciirec, buffy,
1011                     sizeof (st->st_asciirec));
1012                 free(fidstr);
1013                 break;
1014 
1015         case CACHEFS_LOG_REMOVE:
1016                 removep = (struct cachefs_log_remove_record *)recp;
1017                 (void) snprintf(buffy, sizeof (buffy),
1018                     " %-8s %llx %s %llu %d", "Remove",
1019                     removep->vfsp, fidstr = stats_log_fmtfid(&removep->fid),
1020                     removep->fileno,
1021                     removep->uid);
1022                 (void) strlcat(st->st_asciirec, buffy,
1023                     sizeof (st->st_asciirec));
1024                 free(fidstr);
1025                 break;
1026 
1027         case CACHEFS_LOG_RMDIR:
1028                 rmdirp = (struct cachefs_log_rmdir_record *)recp;
1029                 (void) snprintf(buffy, sizeof (buffy),
1030                     " %-8s %llx %s %llu %d", "Rmdir",
1031                     rmdirp->vfsp, fidstr = stats_log_fmtfid(&rmdirp->fid),
1032                     rmdirp->fileno,
1033                     rmdirp->uid);
1034                 (void) strlcat(st->st_asciirec, buffy,
1035                     sizeof (st->st_asciirec));
1036                 free(fidstr);
1037                 break;
1038 
1039         case CACHEFS_LOG_TRUNCATE:
1040                 truncatep = (struct cachefs_log_truncate_record *)recp;
1041                 (void) snprintf(buffy, sizeof (buffy),
1042                     " %-8s %llx %s %llu %d %llu", "Truncate",
1043                     truncatep->vfsp,
1044                     fidstr = stats_log_fmtfid(&truncatep->fid),
1045                     truncatep->fileno,
1046                     truncatep->uid, truncatep->size);
1047                 (void) strlcat(st->st_asciirec, buffy,
1048                     sizeof (st->st_asciirec));
1049                 free(fidstr);
1050                 break;
1051 
1052         case CACHEFS_LOG_PUTPAGE:
1053                 putpagep = (struct cachefs_log_putpage_record *)recp;
1054                 (void) snprintf(buffy, sizeof (buffy),
1055                     " %-8s %llx %s %llu %d %llu %u", "Putpage",
1056                     putpagep->vfsp, fidstr = stats_log_fmtfid(&putpagep->fid),
1057                     putpagep->fileno,
1058                     putpagep->uid, putpagep->offset, putpagep->len);
1059                 (void) strlcat(st->st_asciirec, buffy,
1060                     sizeof (st->st_asciirec));
1061                 free(fidstr);
1062                 break;
1063 
1064         case CACHEFS_LOG_CREATE:
1065                 createp = (struct cachefs_log_create_record *)recp;
1066                 (void) snprintf(buffy, sizeof (buffy),
1067                     " %-8s %llx %s %llu %d", "Create",
1068                     createp->vfsp,
1069                     fidstr = stats_log_fmtfid(&createp->fid),
1070                     createp->fileno,
1071                     createp->uid);
1072                 (void) strlcat(st->st_asciirec, buffy,
1073                     sizeof (st->st_asciirec));
1074                 free(fidstr);
1075                 break;
1076 
1077         case CACHEFS_LOG_MKDIR:
1078                 mkdirp = (struct cachefs_log_mkdir_record *)recp;
1079                 (void) snprintf(buffy, sizeof (buffy),
1080                     " %-8s %llx %s %llu %d", "Mkdir",
1081                     mkdirp->vfsp,
1082                     fidstr = stats_log_fmtfid(&mkdirp->fid),
1083                     mkdirp->fileno,
1084                     mkdirp->uid);
1085                 (void) strlcat(st->st_asciirec, buffy,
1086                     sizeof (st->st_asciirec));
1087                 free(fidstr);
1088                 break;
1089 
1090         case CACHEFS_LOG_RENAME:
1091                 renamep = (struct cachefs_log_rename_record *)recp;
1092                 (void) snprintf(buffy, sizeof (buffy),
1093                     " %-8s %llx %s %llu %d %d", "Rename",
1094                     renamep->vfsp,
1095                     fidstr = stats_log_fmtfid(&renamep->gone),
1096                     renamep->fileno,
1097                     renamep->removed, renamep->uid);
1098                 (void) strlcat(st->st_asciirec, buffy,
1099                     sizeof (st->st_asciirec));
1100                 free(fidstr);
1101                 break;
1102 
1103         case CACHEFS_LOG_SYMLINK:
1104                 symlinkp = (struct cachefs_log_symlink_record *)recp;
1105                 (void) snprintf(buffy, sizeof (buffy),
1106                     " %-8s %llx %s %llu %d %u", "Symlink",
1107                     symlinkp->vfsp,
1108                     fidstr = stats_log_fmtfid(&symlinkp->fid),
1109                     symlinkp->fileno,
1110                     symlinkp->uid, symlinkp->size);
1111                 (void) strlcat(st->st_asciirec, buffy,
1112                     sizeof (st->st_asciirec));
1113                 free(fidstr);
1114                 break;
1115 
1116         case CACHEFS_LOG_POPULATE:
1117                 populatep = (struct cachefs_log_populate_record *)recp;
1118                 (void) snprintf(buffy, sizeof (buffy),
1119                     " %-8s %llx %s %llu %llu %d", "Populate",
1120                     populatep->vfsp,
1121                     fidstr = stats_log_fmtfid(&populatep->fid),
1122                     populatep->fileno,
1123                     populatep->off, populatep->size);
1124                 (void) strlcat(st->st_asciirec, buffy,
1125                     sizeof (st->st_asciirec));
1126                 free(fidstr);
1127                 break;
1128 
1129         case CACHEFS_LOG_CSYMLINK:
1130                 csymlinkp = (struct cachefs_log_csymlink_record *)recp;
1131                 (void) snprintf(buffy, sizeof (buffy),
1132                     " %-8s %llx %s %llu %d", "Csymlink",
1133                     csymlinkp->vfsp,
1134                     fidstr = stats_log_fmtfid(&csymlinkp->fid),
1135                     csymlinkp->fileno,
1136                     csymlinkp->size);
1137                 (void) strlcat(st->st_asciirec, buffy,
1138                     sizeof (st->st_asciirec));
1139                 free(fidstr);
1140                 break;
1141 
1142         case CACHEFS_LOG_FILLDIR:
1143                 filldirp = (struct cachefs_log_filldir_record *)recp;
1144                 (void) snprintf(buffy, sizeof (buffy),
1145                     " %-8s %llx %s %llu %d", "Filldir",
1146                     filldirp->vfsp,
1147                     fidstr = stats_log_fmtfid(&filldirp->fid),
1148                     filldirp->fileno,
1149                     filldirp->size);
1150                 (void) strlcat(st->st_asciirec, buffy,
1151                     sizeof (st->st_asciirec));
1152                 free(fidstr);
1153                 break;
1154 
1155         case CACHEFS_LOG_MDCREATE:
1156                 mdcreatep = (struct cachefs_log_mdcreate_record *)recp;
1157                 (void) snprintf(buffy, sizeof (buffy),
1158                     " %-8s %llx %s %llu %u", "Mdcreate",
1159                     mdcreatep->vfsp,
1160                     fidstr = stats_log_fmtfid(&mdcreatep->fid),
1161                     mdcreatep->fileno, mdcreatep->count);
1162                 (void) strlcat(st->st_asciirec, buffy,
1163                     sizeof (st->st_asciirec));
1164                 free(fidstr);
1165                 break;
1166 
1167         case CACHEFS_LOG_GPFRONT:
1168                 gpfrontp = (struct cachefs_log_gpfront_record *)recp;
1169                 (void) snprintf(buffy, sizeof (buffy),
1170                     " %-8s %llx %s %llu %d %llu %u", "Gpfront",
1171                     gpfrontp->vfsp,
1172                     fidstr = stats_log_fmtfid(&gpfrontp->fid),
1173                     gpfrontp->fileno,
1174                     gpfrontp->uid, gpfrontp->off, gpfrontp->len);
1175                 (void) strlcat(st->st_asciirec, buffy,
1176                     sizeof (st->st_asciirec));
1177                 free(fidstr);
1178                 break;
1179 
1180         case CACHEFS_LOG_RFDIR:
1181                 rfdirp = (struct cachefs_log_rfdir_record *)recp;
1182                 (void) snprintf(buffy, sizeof (buffy),
1183                     " %-8s %llx %s %llu %d", "Rfdir",
1184                     rfdirp->vfsp,
1185                     fidstr = stats_log_fmtfid(&rfdirp->fid),
1186                     rfdirp->fileno,
1187                     rfdirp->uid);
1188                 (void) strlcat(st->st_asciirec, buffy,
1189                     sizeof (st->st_asciirec));
1190                 free(fidstr);
1191                 break;
1192 
1193         case CACHEFS_LOG_UALLOC:
1194                 uallocp = (struct cachefs_log_ualloc_record *)recp;
1195                 (void) snprintf(buffy, sizeof (buffy),
1196                     " %-8s %llx %s %llu %llu %u", "Ualloc",
1197                     uallocp->vfsp,
1198                     fidstr = stats_log_fmtfid(&uallocp->fid),
1199                     uallocp->fileno,
1200                     uallocp->off, uallocp->len);
1201                 (void) strlcat(st->st_asciirec, buffy,
1202                     sizeof (st->st_asciirec));
1203                 free(fidstr);
1204                 break;
1205 
1206         case CACHEFS_LOG_CALLOC:
1207                 callocp = (struct cachefs_log_calloc_record *)recp;
1208                 (void) snprintf(buffy, sizeof (buffy),
1209                     " %-8s %llx %s %llu %llu %u", "Calloc",
1210                     callocp->vfsp,
1211                     fidstr = stats_log_fmtfid(&callocp->fid),
1212                     callocp->fileno, callocp->off, callocp->len);
1213                 (void) strlcat(st->st_asciirec, buffy,
1214                     sizeof (st->st_asciirec));
1215                 free(fidstr);
1216                 break;
1217 
1218         case CACHEFS_LOG_NOCACHE:
1219                 nocachep = (struct cachefs_log_nocache_record *)recp;
1220                 (void) snprintf(buffy, sizeof (buffy),
1221                     " %-8s %llx %s %llu", "Nocache",
1222                     nocachep->vfsp,
1223                     fidstr = stats_log_fmtfid(&nocachep->fid),
1224                     nocachep->fileno);
1225                 (void) strlcat(st->st_asciirec, buffy,
1226                     sizeof (st->st_asciirec));
1227                 free(fidstr);
1228                 break;
1229 
1230         default:
1231                 stats_perror(st, SE_CORRUPT,
1232                     gettext(
1233                     "Attempt to format invalid log type=%d (position %x)"),
1234                     rectype, ftell(st->st_logstream));
1235                 return (NULL);
1236         }
1237 
1238         return (st->st_asciirec);
1239 }
1240 
1241 uint_t
1242 stats_log_get_record_info(stats_cookie_t *sc,
1243     void *recp, caddr_t *vfsp, cfs_fid_t **fidp, ino64_t *filenop,
1244     u_offset_t *offp, u_offset_t *lenp)
1245 {
1246         int type = ((int *)recp)[0];
1247         int error = ((int *)recp)[1];
1248         uint_t rc = 0;
1249 
1250         struct cachefs_log_getpage_record *getpagep;
1251         struct cachefs_log_readdir_record *readdirp;
1252         struct cachefs_log_readlink_record *readlinkp;
1253         struct cachefs_log_remove_record *removep;
1254         struct cachefs_log_rmdir_record *rmdirp;
1255         struct cachefs_log_truncate_record *truncatep;
1256         struct cachefs_log_putpage_record *putpagep;
1257         struct cachefs_log_create_record *createp;
1258         struct cachefs_log_mkdir_record *mkdirp;
1259         struct cachefs_log_rename_record *renamep;
1260         struct cachefs_log_symlink_record *symlinkp;
1261         struct cachefs_log_populate_record *populatep;
1262         struct cachefs_log_csymlink_record *csymlinkp;
1263         struct cachefs_log_filldir_record *filldirp;
1264         struct cachefs_log_mdcreate_record *mdcreatep;
1265         struct cachefs_log_gpfront_record *gpfrontp;
1266         struct cachefs_log_rfdir_record *rfdirp;
1267         struct cachefs_log_ualloc_record *uallocp;
1268         struct cachefs_log_calloc_record *callocp;
1269         struct cachefs_log_nocache_record *nocachep;
1270 
1271         switch (type) {
1272         case CACHEFS_LOG_RFDIR:
1273                 if ((error == EINVAL) || (error == ENOENT))
1274                         error = 0;
1275                 break;
1276         }
1277 
1278         if (error != 0)
1279                 return (0);
1280 
1281         switch (type) {
1282         case CACHEFS_LOG_GETPAGE:
1283                 getpagep = (struct cachefs_log_getpage_record *)recp;
1284                 *fidp = &getpagep->fid;
1285                 *filenop = getpagep->fileno;
1286                 *vfsp = (caddr_t)(uintptr_t)getpagep->vfsp;
1287                 *offp = getpagep->offset;
1288                 *lenp = (u_offset_t)getpagep->len;
1289                 rc = (GRI_ADD | GRI_EXPENSIVE);
1290                 break;
1291 
1292         case CACHEFS_LOG_READDIR:
1293                 readdirp = (struct cachefs_log_readdir_record *)recp;
1294                 *fidp = &readdirp->fid;
1295                 *filenop = readdirp->fileno;
1296                 *vfsp = (caddr_t)(uintptr_t)readdirp->vfsp;
1297                 *offp = readdirp->offset;
1298                 *lenp = (u_offset_t)sc->st_loghead.lh_maxbsize;
1299                 rc = (GRI_ADD | GRI_EXPENSIVE);
1300                 break;
1301 
1302         case CACHEFS_LOG_READLINK:
1303                 readlinkp = (struct cachefs_log_readlink_record *)recp;
1304                 *fidp = &readlinkp->fid;
1305                 *filenop = readlinkp->fileno;
1306                 *vfsp = (caddr_t)(uintptr_t)readlinkp->vfsp;
1307                 *offp = 0LL;
1308                 *lenp = (u_offset_t)((readlinkp->length > C_FSL_SIZE) ?
1309                     readlinkp->length : 0);
1310                 rc = (GRI_ADD | GRI_EXPENSIVE);
1311                 break;
1312 
1313         case CACHEFS_LOG_REMOVE:
1314                 removep = (struct cachefs_log_remove_record *)recp;
1315                 *fidp = &removep->fid;
1316                 *filenop = removep->fileno;
1317                 *vfsp = (caddr_t)(uintptr_t)removep->vfsp;
1318                 *offp = *lenp = 0LL;
1319                 rc = (GRI_TRUNC | GRI_MODIFY);
1320                 break;
1321 
1322         case CACHEFS_LOG_RMDIR:
1323                 rmdirp = (struct cachefs_log_rmdir_record *)recp;
1324                 *fidp = &rmdirp->fid;
1325                 *filenop = rmdirp->fileno;
1326                 *vfsp = (caddr_t)(uintptr_t)rmdirp->vfsp;
1327                 *offp = *lenp = 0LL;
1328                 rc = (GRI_TRUNC | GRI_MODIFY);
1329                 break;
1330 
1331         case CACHEFS_LOG_TRUNCATE:
1332                 truncatep = (struct cachefs_log_truncate_record *)recp;
1333                 *fidp = &truncatep->fid;
1334                 *filenop = truncatep->fileno;
1335                 *vfsp = (caddr_t)(uintptr_t)truncatep->vfsp;
1336                 *offp = 0LL;
1337                 *lenp = truncatep->size;
1338                 rc = (GRI_TRUNC | GRI_MODIFY);
1339                 break;
1340 
1341         case CACHEFS_LOG_PUTPAGE:
1342                 putpagep = (struct cachefs_log_putpage_record *)recp;
1343                 *fidp = &putpagep->fid;
1344                 *filenop = putpagep->fileno;
1345                 *vfsp = (caddr_t)(uintptr_t)putpagep->vfsp;
1346                 *offp = putpagep->offset;
1347                 *lenp = (u_offset_t)putpagep->len;
1348                 rc = (GRI_ADD | GRI_MODIFY);
1349                 break;
1350 
1351         case CACHEFS_LOG_CREATE:
1352                 createp = (struct cachefs_log_create_record *)recp;
1353                 *fidp = &createp->fid;
1354                 *filenop = createp->fileno;
1355                 *vfsp = (caddr_t)(uintptr_t)createp->vfsp;
1356                 *offp = *lenp = 0LL;
1357                 rc = (GRI_ADD | GRI_MODIFY);
1358                 break;
1359 
1360         case CACHEFS_LOG_MKDIR:
1361                 mkdirp = (struct cachefs_log_mkdir_record *)recp;
1362                 *fidp = &mkdirp->fid;
1363                 *filenop = mkdirp->fileno;
1364                 *vfsp = (caddr_t)(uintptr_t)mkdirp->vfsp;
1365                 *offp = *lenp = 0LL;
1366                 rc = (GRI_ADD | GRI_MODIFY);
1367                 break;
1368 
1369         case CACHEFS_LOG_RENAME:
1370                 renamep = (struct cachefs_log_rename_record *)recp;
1371                 *fidp = &renamep->gone;
1372                 *filenop = renamep->fileno;
1373                 *vfsp = (caddr_t)(uintptr_t)renamep->vfsp;
1374                 *offp = *lenp = 0LL;
1375                 rc = GRI_MODIFY;
1376                 if (renamep->removed)
1377                         rc |= GRI_TRUNC;
1378                 break;
1379 
1380         case CACHEFS_LOG_SYMLINK:
1381                 symlinkp = (struct cachefs_log_symlink_record *)recp;
1382                 *fidp = &symlinkp->fid;
1383                 *filenop = symlinkp->fileno;
1384                 *vfsp = (caddr_t)(uintptr_t)symlinkp->vfsp;
1385                 *offp = 0LL;
1386                 *lenp = (u_offset_t)((symlinkp->size > C_FSL_SIZE) ?
1387                     symlinkp->size : 0);
1388                 rc = (GRI_ADD | GRI_MODIFY);
1389                 break;
1390 
1391         case CACHEFS_LOG_POPULATE:
1392                 populatep = (struct cachefs_log_populate_record *)recp;
1393                 *fidp = &populatep->fid;
1394                 *filenop = populatep->fileno;
1395                 *vfsp = (caddr_t)(uintptr_t)populatep->vfsp;
1396                 *offp = populatep->off;
1397                 *lenp = (u_offset_t)populatep->size;
1398                 rc = GRI_ADD;
1399                 break;
1400 
1401         case CACHEFS_LOG_CSYMLINK:
1402                 csymlinkp = (struct cachefs_log_csymlink_record *)recp;
1403                 *fidp = &csymlinkp->fid;
1404                 *filenop = csymlinkp->fileno;
1405                 *vfsp = (caddr_t)(uintptr_t)csymlinkp->vfsp;
1406                 *offp = 0LL;
1407                 *lenp = (u_offset_t)((csymlinkp->size > C_FSL_SIZE) ?
1408                     csymlinkp->size : 0);
1409                 rc = GRI_ADD;
1410                 break;
1411 
1412         case CACHEFS_LOG_FILLDIR:
1413                 filldirp = (struct cachefs_log_filldir_record *)recp;
1414                 *fidp = &filldirp->fid;
1415                 *filenop = filldirp->fileno;
1416                 *vfsp = (caddr_t)(uintptr_t)filldirp->vfsp;
1417                 *offp = 0LL;
1418                 *lenp = (u_offset_t)(filldirp->size);
1419                 rc = GRI_ADD;
1420                 break;
1421 
1422         case CACHEFS_LOG_MDCREATE:
1423                 mdcreatep = (struct cachefs_log_mdcreate_record *)recp;
1424                 *fidp = &mdcreatep->fid;
1425                 *filenop = mdcreatep->fileno;
1426                 *vfsp = (caddr_t)(uintptr_t)mdcreatep->vfsp;
1427                 *lenp = (u_offset_t)mdcreatep->count;
1428                 rc = GRI_METADATA;
1429                 break;
1430 
1431         case CACHEFS_LOG_GPFRONT:
1432                 gpfrontp = (struct cachefs_log_gpfront_record *)recp;
1433                 *fidp = &gpfrontp->fid;
1434                 *filenop = gpfrontp->fileno;
1435                 *vfsp = (caddr_t)(uintptr_t)gpfrontp->vfsp;
1436                 *offp = gpfrontp->off;
1437                 *lenp = (u_offset_t)sc->st_loghead.lh_pagesize;
1438                 rc = (GRI_ADD | GRI_EXPENSIVE);
1439                 break;
1440 
1441         case CACHEFS_LOG_RFDIR:
1442                 rfdirp = (struct cachefs_log_rfdir_record *)recp;
1443                 rfdirp->error = 0;
1444                 *fidp = &rfdirp->fid;
1445                 *filenop = rfdirp->fileno;
1446                 *vfsp = (caddr_t)(uintptr_t)rfdirp->vfsp;
1447                 *offp = 0LL;
1448                 *lenp = (u_offset_t)sc->st_loghead.lh_maxbsize;
1449                 rc = (GRI_ADD | GRI_EXPENSIVE);
1450                 break;
1451 
1452         case CACHEFS_LOG_UALLOC:
1453                 uallocp = (struct cachefs_log_ualloc_record *)recp;
1454                 *fidp = &uallocp->fid;
1455                 *filenop = uallocp->fileno;
1456                 *vfsp = (caddr_t)(uintptr_t)uallocp->vfsp;
1457                 *offp = uallocp->off;
1458                 *lenp = (u_offset_t)uallocp->len;
1459                 rc = (GRI_ADD);
1460                 break;
1461 
1462         case CACHEFS_LOG_CALLOC:
1463                 callocp = (struct cachefs_log_calloc_record *)recp;
1464                 *fidp = &callocp->fid;
1465                 *filenop = callocp->fileno;
1466                 *vfsp = (caddr_t)(uintptr_t)callocp->vfsp;
1467                 *offp = callocp->off;
1468                 *lenp = (u_offset_t)callocp->len;
1469                 rc = (GRI_ADD | GRI_EXPENSIVE);
1470                 break;
1471 
1472         case CACHEFS_LOG_NOCACHE:
1473                 nocachep = (struct cachefs_log_nocache_record *)recp;
1474                 *fidp = &nocachep->fid;
1475                 *filenop = nocachep->fileno;
1476                 *vfsp = (caddr_t)(uintptr_t)nocachep->vfsp;
1477                 *offp = *lenp = 0LL;
1478                 rc = (GRI_TRUNC);
1479                 break;
1480         }
1481 
1482         return (rc);
1483 }
1484 
1485 /*
1486  * ascii formatter for fids.  returns a malloc()ed string -- it's up to
1487  * the caller to free it.
1488  */
1489 
1490 static char *
1491 stats_log_fmtfid(cfs_fid_t *fidp)
1492 {
1493         char buffy[BUFSIZ], *rc;
1494 
1495 (void) strcpy(buffy, "<fid>");
1496 
1497         rc = strdup(buffy);
1498         if (rc == NULL)
1499                 rc = "out of memory";
1500 
1501         return (rc);
1502 }
1503 
1504 void
1505 stats_log_fi_add(stats_cookie_t *st, fid_info *fip, u_offset_t off,
1506 u_offset_t len)
1507 {
1508         int i, j;
1509         u_offset_t iend, jend, tmp;
1510 
1511         assert(stats_good(st));
1512         assert(st->st_flags & ST_DBMOPEN);
1513         assert(st->st_flags & ST_LFOPEN);
1514 
1515         /* shortcut if we had some sort of zero-length thing */
1516 
1517         if (len == 0LL)
1518                 return;
1519 
1520         /* `smear' the offset and length to block boundaries */
1521 
1522         /*
1523          * pre-largefiles: iend = off & ~(st->st_loghead.lh_maxbsize - 1);
1524          * largefiles:  make sure that we ~ all bits in the 64 bit
1525          * version of (st->st_loghead.lh_maxbsize - 1)
1526          */
1527         tmp = (u_offset_t)(st->st_loghead.lh_maxbsize - 1);
1528         iend = off & ~tmp;
1529 
1530         jend = off + len;
1531         jend += (u_offset_t)(st->st_loghead.lh_maxbsize - 1);
1532         /*
1533          * pre-largefiles:  jend &= ~(st->st_loghead.lh_maxbsize - 1);
1534          * largefiles: make sure that we ~ all bits in the 64 bit
1535          * version of (st->st_loghead.lh_maxbsize - 1)
1536          */
1537         jend &= ~tmp;
1538 
1539         off = iend;
1540         len = jend - off;
1541 
1542         /* see if our offset falls within an existing chunk */
1543         for (i = 0; i < fip->fi_ent_n; i++) {
1544                 iend = fip->fi_ent[i].offset + fip->fi_ent[i].len;
1545                 if ((fip->fi_ent[i].offset <= off) && (iend >= off))
1546                         break;
1547         }
1548 
1549         /* update the chunk, or make a new one */
1550         if (i < fip->fi_ent_n) {
1551                 if ((off + len) > iend)
1552                         fip->fi_ent[i].len = off + len - fip->fi_ent[i].offset;
1553         } else if (i < C_MAX_ALLOCINFO_SLOTS) {
1554                 fip->fi_ent_n = i + 1;
1555                 fip->fi_ent[i].offset = off;
1556                 fip->fi_ent[i].len = len;
1557         } else {
1558                 /* cachefs does a nocache, so we'll immitate */
1559 
1560                 /*
1561                  * XXX we're free to grow again.  assume we got
1562                  * inactivated right away -- the worst case!
1563                  */
1564 
1565                 fip->fi_ent_n = 0;
1566                 fip->fi_total = 0LL;
1567         }
1568 
1569         /* quit for the trivial (hopefully the usual) case... */
1570         if (fip->fi_ent_n <= 1) {
1571                 if (fip->fi_ent_n == 0)
1572                         fip->fi_total = 0LL;
1573                 else
1574                         fip->fi_total = fip->fi_ent[0].len;
1575                 return;
1576         }
1577 
1578         /*
1579          * we have to see if we can consolidate any chunks.  the
1580          * chunks aren't guaranteed to be in any kind of order, so we
1581          * do a qsort.  otherwise, the consolidation would be N^2 (but
1582          * we're probably close here).
1583          */
1584 
1585         qsort(fip->fi_ent, fip->fi_ent_n, sizeof (fip->fi_ent[0]),
1586             stats_log_fi_comp);
1587 
1588         /* tag non-essential entries with offset == -1, and consolidate */
1589         for (i = 0; i < fip->fi_ent_n - 1; i++) {
1590                 if ((offset_t)fip->fi_ent[i].offset < 0)
1591                         continue;
1592                 iend = fip->fi_ent[i].offset + fip->fi_ent[i].len;
1593 
1594                 for (j = i + 1; j < fip->fi_ent_n; j++) {
1595                         if (iend < fip->fi_ent[j].offset)
1596                                 break;
1597                         jend = fip->fi_ent[j].offset + fip->fi_ent[j].len;
1598                         if (jend >= iend)
1599                                 fip->fi_ent[i].len =
1600                                     jend - fip->fi_ent[i].offset;
1601                         fip->fi_ent[j].offset = (u_offset_t)-1;
1602                 }
1603         }
1604 
1605         /* get rid of non-essential entries (without preserving order) */
1606         for (i = 0; i < fip->fi_ent_n; i++)
1607                 if ((offset_t)fip->fi_ent[i].offset < 0)
1608                         fip->fi_ent[i--] = fip->fi_ent[--(fip->fi_ent_n)];
1609 
1610         /* add up the new total size */
1611         for (i = fip->fi_total = 0LL; i < fip->fi_ent_n; i++)
1612                 fip->fi_total += fip->fi_ent[i].len;
1613 }
1614 
1615 static int
1616 stats_log_fi_comp(const void *a, const void *b)
1617 {
1618         struct fid_info_allocent *fa = (struct fid_info_allocent *)a;
1619         struct fid_info_allocent *fb = (struct fid_info_allocent *)b;
1620 
1621         if ((offset_t)(fa->offset - fb->offset) > 0)
1622                 return (1);
1623         if ((offset_t)(fa->offset - fb->offset) < 0)
1624                 return (-1);
1625         return (0);
1626 }
1627 
1628 void
1629 stats_log_fi_trunc(stats_cookie_t *st, fid_info *fip, u_offset_t off,
1630 u_offset_t len)
1631 {
1632         fip->fi_ent_n = 1;
1633         fip->fi_ent[0].offset = off;
1634         fip->fi_ent[0].len = len;
1635         fip->fi_total = len;
1636 }
1637 
1638 struct cachefs_log_logfile_header *
1639 stats_log_getheader(stats_cookie_t *st)
1640 {
1641         assert(stats_good(st));
1642         assert(st->st_flags & ST_LFOPEN);
1643 
1644         return (&st->st_loghead);
1645 }
1646 
1647 void
1648 stats_log_compute_wssize(stats_cookie_t *st)
1649 {
1650         void *record;
1651         int type;
1652         struct cachefs_log_mount_record *mountp;
1653         struct cachefs_log_umount_record *umountp;
1654         datum key;
1655         caddr_t vfsp;
1656         mount_info *mi = NULL, *mip;
1657         size_t len1, len2, maxlen;
1658         char *string1, *string2;
1659         uint_t rflags;
1660         fid_info fi, *fip;
1661         cfs_fid_t *fidp;
1662         ino64_t fileno;
1663         u_offset_t off;
1664         u_offset_t len;
1665         struct cachefs_log_logfile_header *lh = &st->st_loghead;
1666         size_t delta;
1667 
1668         assert(stats_good(st));
1669         assert(st->st_flags & ST_LFOPEN);
1670         assert(st->st_flags & ST_DBMOPEN);
1671 
1672         /*
1673          * The maximum size of a mount_info structure is the size of
1674          * the structure less the space already defined for char mi_path[]
1675          * plus the maximum size of mi_path.
1676          *
1677          * Additional space is allocated to mi_path at runtime using
1678          * malloc(). The size needs to be calculated in-situ as ANSI C
1679          * will only allow 'sizeof expression' or 'sizeof (type)'.
1680          */
1681 
1682         mi = malloc(sizeof (*mi) - sizeof (mi->mi_path) + MI_MAX_MI_PATH);
1683         if (mi == NULL) {
1684                 stats_perror(st, SE_NOMEM, gettext("Out of memory"));
1685                 goto out;
1686         }
1687 
1688         st->st_ws_init = st->st_loghead.lh_blocks;
1689 
1690         while (record = stats_log_logfile_read(st, &type)) {
1691                 switch (type) {
1692                 case CACHEFS_LOG_MOUNT:
1693                         mountp = (struct cachefs_log_mount_record *)record;
1694                         if (mountp->error != 0)
1695                                 break;
1696                         for (key = stats_dbm_firstkey(st);
1697                             key.dptr != NULL;
1698                             key = stats_dbm_nextkey(st)) {
1699                                 if (key.dsize != sizeof (vfsp))
1700                                         continue;
1701 
1702                                 memcpy((caddr_t)&vfsp, key.dptr,
1703                                     sizeof (vfsp));
1704                                 mip = stats_dbm_fetch_byvfsp(st, vfsp);
1705                                 if (mip == NULL)
1706                                         continue;
1707 
1708                                 len1 = strlen(mip->mi_path);
1709                                 len2 = strlen(mip->mi_path + len1 + 1);
1710                                 memcpy((caddr_t)mi, mip, sizeof (*mi) +
1711                                     len1 + len2 - CLPAD(mount_info, mi_path));
1712                                 free(mip);
1713 
1714                                 string1 = mi->mi_path + len1 + 1;
1715                                 string2 = mountp->path + mountp->pathlen + 1;
1716                                 if (strcmp(string1, string2) == 0) {
1717                                         stats_dbm_delete_byvfsp(st, vfsp);
1718                                         break;
1719                                 }
1720                         }
1721                         if (key.dptr == NULL) {
1722                                 /* non-idempotent setup stuff */
1723                                 memset(mi, '\0', sizeof (*mi));
1724                                 mi->mi_flags = mountp->flags;
1725                                 mi->mi_filegrp_size = mountp->fgsize;
1726                         }
1727 
1728                         /*
1729                          * idempotent setup stuff
1730                          *
1731                          * Careful string handling around mi_path
1732                          * is required as it contains two NULL
1733                          * terminated strings.
1734                          */
1735 
1736                         mi->mi_mounted = 1;
1737                         maxlen = MI_MAX_MI_PATH - 1;
1738                         len1 = strlcpy(mi->mi_path, mountp->path, maxlen);
1739                         if (len1 >= maxlen) {
1740                                 stats_perror(st, SE_CORRUPT,
1741                                     gettext("Path too long in log file"));
1742                                 break;
1743                         }
1744 
1745                         len1 = strlen(mi->mi_path);
1746                         maxlen = MI_MAX_MI_PATH - (len1 + 1);
1747                         len2 = strlcpy(mi->mi_path + len1 + 1,
1748                             mountp->path + mountp->pathlen + 1, maxlen);
1749                         if (len2 >= maxlen) {
1750                                 stats_perror(st, SE_CORRUPT,
1751                                     gettext("CacheID too long in log file"));
1752                                 break;
1753                         }
1754 
1755                         stats_dbm_store_byvfsp(st,
1756                                         (caddr_t)(uintptr_t)mountp->vfsp, mi);
1757                         break;
1758 
1759                 case CACHEFS_LOG_UMOUNT:
1760                         umountp = (struct cachefs_log_umount_record *)record;
1761                         if (umountp->error != 0)
1762                                 break;
1763                         mip = stats_dbm_fetch_byvfsp(st,
1764                                         (caddr_t)(uintptr_t)umountp->vfsp);
1765                         if (mip == NULL)
1766                                 break;
1767                         mip->mi_mounted = 0;
1768                         stats_dbm_store_byvfsp(st,
1769                                         (caddr_t)(uintptr_t)umountp->vfsp, mip);
1770                         free(mip);
1771                         break;
1772 
1773                 default:
1774                         rflags = stats_log_get_record_info(st, record,
1775                             &vfsp, &fidp, &fileno, &off, &len);
1776                         if (rflags == 0) /* shortcut */
1777                                 break;
1778 
1779                         mip = stats_dbm_fetch_byvfsp(st, vfsp);
1780                         if (mip == NULL) /* hopefully very rare */
1781                                 break;
1782 
1783                         fip = stats_dbm_fetch_byfid(st, fidp);
1784                         if (fip == NULL) {
1785                                 fip = &fi;
1786                                 memset(&fi, '\0', sizeof (fi));
1787                                 fi.fi_vfsp = vfsp;
1788                         }
1789 
1790                         /* account for the creation of the fscache */
1791                         if (! mip->mi_used) {
1792                                 mip->mi_used = 1;
1793 
1794                                 /* account for the .cfs_option file */
1795                                 mip->mi_current += (u_offset_t)lh->lh_maxbsize;
1796                                 st->st_ws_current +=
1797                                     (u_offset_t)lh->lh_maxbsize;
1798                         }
1799 
1800                         /*
1801                          * Add in the size-growth of the attrcache.
1802                          * len will be non-zero only for the record type
1803                          * CACHEFS_LOG_MDCREATE, and len can't be > 2GB because
1804                          * it refers to the number of entries in
1805                          * the attribute cache file.
1806                          */
1807                         assert(len <= UINT_MAX);
1808                         delta = stats_dbm_attrcache_addsize(st, mip, fileno,
1809                             (type == CACHEFS_LOG_MDCREATE) ? (uint_t)len : 0);
1810                         st->st_ws_current += (u_offset_t)delta;
1811                         mip->mi_current += (u_offset_t)delta;
1812 
1813                         /* see if this is an `expensive' logfile */
1814                         if ((! st->st_ws_expensive) && (rflags & GRI_EXPENSIVE))
1815                                 st->st_ws_expensive = 1;
1816 
1817                         /* subtract current frontfile size ... */
1818                         st->st_ws_current -= fip->fi_total;
1819                         mip->mi_current -= fip->fi_total;
1820 
1821                         /* compute new frontfile size */
1822                         if ((mip->mi_flags & CFS_WRITE_AROUND) &&
1823                             (rflags & GRI_MODIFY)) {
1824                                 fip->fi_total = 0LL;
1825                                 fip->fi_ent_n = 0;
1826                         } else if (rflags & GRI_ADD) {
1827                                 stats_log_fi_add(st, fip, off, len);
1828                         } else if (rflags & GRI_TRUNC) {
1829                                 stats_log_fi_trunc(st, fip, off, len);
1830                         }
1831                         if (rflags & GRI_METADATA)
1832                                 fip->fi_flags |= FI_METADATA;
1833 
1834                         /* add back in new frontfile size */
1835                         mip->mi_current += fip->fi_total;
1836                         if (mip->mi_current > mip->mi_high)
1837                                 mip->mi_high = mip->mi_current;
1838                         stats_dbm_store_byvfsp(st, vfsp, mip);
1839                         free(mip);
1840                         st->st_ws_current += fip->fi_total;
1841                         if (st->st_ws_current > st->st_ws_high)
1842                                 st->st_ws_high = st->st_ws_current;
1843 
1844                         stats_dbm_store_byfid(st, fidp, fip);
1845                         if (fip != &fi)
1846                                 free(fip);
1847                         break;
1848                 }
1849 
1850                 free(record);
1851 
1852                 if (stats_inerror(st))
1853                         break;
1854         }
1855 
1856 out:
1857         if (mi != NULL)
1858                 free(mi);
1859         if (! stats_inerror(st))
1860                 st->st_flags |= ST_WSCOMP;
1861 }
1862 
1863 int
1864 stats_log_wssize_init(stats_cookie_t *st)
1865 {
1866         assert(stats_good(st));
1867         assert(st->st_flags & ST_WSCOMP);
1868 
1869         return (st->st_ws_init);
1870 }
1871 
1872 u_offset_t
1873 stats_log_wssize_current(stats_cookie_t *st)
1874 {
1875         assert(stats_good(st));
1876         assert(st->st_flags & ST_WSCOMP);
1877 
1878         return (st->st_ws_current);
1879 }
1880 
1881 u_offset_t
1882 stats_log_wssize_high(stats_cookie_t *st)
1883 {
1884         assert(stats_good(st));
1885         assert(st->st_flags & ST_WSCOMP);
1886 
1887         return (st->st_ws_high);
1888 }
1889 
1890 
1891 int
1892 stats_log_wssize_expensive(stats_cookie_t *st)
1893 {
1894         assert(stats_good(st));
1895         assert(st->st_flags & ST_WSCOMP);
1896 
1897         return (st->st_ws_expensive);
1898 }