xref: /titanic_44/usr/src/cmd/fs.d/cachefs/common/stats_log.c (revision aad98a6d8e89f8f5a62a1793da807d4bc5e5b159)
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 }
1899