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 */
53static kstat_t *stats_log_kstat_read(stats_cookie_t *);
54static char *stats_log_fmtfid(cfs_fid_t *);
55static bool_t stats_xdr_loghead(XDR *, struct cachefs_log_logfile_header *);
56static int stats_log_fi_comp(const void *a, const void *b);
57
58int
59stats_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
139out:
140	if ((error != 0) && (path[0] != '\0') && (exists != 0))
141		(void) unlink(path);
142
143	return (error);
144}
145
146int
147stats_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
173out:
174	return (error);
175}
176
177char *
178stats_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
193out:
194	return (rc);
195}
196
197static kstat_t *
198stats_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
223out:
224	return (rc);
225}
226
227int
228stats_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
276out:
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
290static bool_t
291stats_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
305void *
306stats_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
909out:
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
919char *
920stats_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
1241uint_t
1242stats_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
1490static char *
1491stats_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
1504void
1505stats_log_fi_add(stats_cookie_t *st, fid_info *fip, u_offset_t off,
1506u_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
1615static int
1616stats_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
1628void
1629stats_log_fi_trunc(stats_cookie_t *st, fid_info *fip, u_offset_t off,
1630u_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
1638struct cachefs_log_logfile_header *
1639stats_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
1647void
1648stats_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
1856out:
1857	if (mi != NULL)
1858		free(mi);
1859	if (! stats_inerror(st))
1860		st->st_flags |= ST_WSCOMP;
1861}
1862
1863int
1864stats_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
1872u_offset_t
1873stats_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
1881u_offset_t
1882stats_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
1891int
1892stats_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}
1899