xref: /illumos-gate/usr/src/lib/libexacct/common/exacct_ops.c (revision 1a220b56b93ff1dc80855691548503117af4cc10)
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 2006 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 #include <sys/systeminfo.h>
30 
31 #include <exacct.h>
32 #include <exacct_impl.h>
33 #include <sys/exacct_impl.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 #include <strings.h>
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <errno.h>
40 #include <thread.h>
41 
42 #define	EXACCT_HDR_STR	"exacct"
43 #define	EXACCT_HDR_LEN	7
44 
45 #define	DEFAULT_ENTRIES	4
46 #define	SYSINFO_BUFSIZE	256
47 
48 static mutex_t		keylock;
49 static thread_key_t	errkey;
50 static int		keyonce = 0;
51 static int		exacct_errval = 0;
52 
53 /*
54  * extended accounting file access routines
55  *
56  *   exacct_ops.c implements the library-specific routines of libexacct:  the
57  *   operations associated with file access and record traversal.  (The
58  *   complementary routines which permit hierarchy building and record packing
59  *   are provided in exacct_core.c, which is used by both libexacct and the
60  *   kernel.) At its heart are the unpack, get, and next routines, which
61  *   navigate the packed records produced by ea_pack_object.
62  */
63 
64 /*
65  * Group stack manipulation code.  As groups can be nested, we need a mechanism
66  * for saving and restoring the current position within the outer groups.  This
67  * state stack is stored within the ea_file_impl_t structure, in the ef_depth,
68  * ef_ndeep and ef_mxdeep members.  On error all these functions set
69  * exacct_error and return -1.
70  */
71 
72 /*
73  * If the stack is NULL, create and initialise it.
74  * If is is not NULL, check it still has space - if not, double its size.
75  */
76 static int stack_check(ea_file_impl_t *f)
77 {
78 	if (f->ef_depth == NULL) {
79 		if ((f->ef_depth =
80 		    ea_alloc(DEFAULT_ENTRIES * sizeof (ea_file_depth_t)))
81 		    == NULL) {
82 			/* exacct_errno set above. */
83 			return (-1);
84 		}
85 		bzero(f->ef_depth, DEFAULT_ENTRIES * sizeof (ea_file_depth_t));
86 		f->ef_mxdeep = DEFAULT_ENTRIES;
87 		f->ef_ndeep = -1;
88 	} else if (f->ef_ndeep + 1 >= f->ef_mxdeep) {
89 		ea_file_depth_t *newstack;
90 
91 		if ((newstack =
92 		    ea_alloc(f->ef_mxdeep * 2 * sizeof (ea_file_depth_t)))
93 		    == NULL) {
94 			/* exacct_errno set above. */
95 			return (-1);
96 		}
97 		bcopy(f->ef_depth, newstack,
98 		    f->ef_mxdeep * sizeof (ea_file_depth_t));
99 		bzero(newstack + f->ef_mxdeep,
100 		    f->ef_mxdeep * sizeof (ea_file_depth_t));
101 		ea_free(f->ef_depth, f->ef_mxdeep * sizeof (ea_file_depth_t));
102 		f->ef_mxdeep *= 2;
103 		f->ef_depth = newstack;
104 	}
105 	return (0);
106 }
107 
108 /*
109  * Free a stack.
110  */
111 static void stack_free(ea_file_impl_t *f)
112 {
113 	if (f->ef_depth != NULL) {
114 		ea_free(f->ef_depth, f->ef_mxdeep * sizeof (ea_file_depth_t));
115 		f->ef_depth = NULL;
116 	}
117 	f->ef_mxdeep = 0;
118 	f->ef_ndeep = -1;
119 }
120 
121 /*
122  * Add a new group onto the stack, pushing down one frame.  nobj is the number
123  * of items in the group.  We have to read this many objects before popping
124  * back up to an enclosing group - see next_object() and previous_object()
125  * below.
126  */
127 static int stack_new_group(ea_file_impl_t *f, int nobjs)
128 {
129 	if (stack_check(f) != 0) {
130 		stack_free(f);
131 		/* exacct_errno set above. */
132 		return (-1);
133 	}
134 	f->ef_ndeep++;
135 	f->ef_depth[f->ef_ndeep].efd_obj = 0;
136 	f->ef_depth[f->ef_ndeep].efd_nobjs = nobjs;
137 	return (0);
138 }
139 
140 /*
141  * Step forwards along the objects within the current group.  If we are still
142  * within a group, return 1.  If we have reached the end of the current group,
143  * unwind the stack back up to the nearest enclosing group that still has
144  * unprocessed objects and return 0.  On EOF or error, set exacct_error
145  * accordingly and return -1.  xread() is required so that this function can
146  * work either on files or memory buffers.
147  */
148 static int
149 stack_next_object(
150     ea_file_impl_t *f,
151     size_t (*xread)(ea_file_impl_t *, void *, size_t))
152 {
153 	uint32_t scratch32;
154 
155 	/*
156 	 * If the stack is empty we are not in a group, so there will be no
157 	 * stack manipulation to do and no large backskips to step over.
158 	 */
159 	if (f->ef_ndeep < 0) {
160 		return (0);
161 	}
162 
163 	/*
164 	 * Otherwise we must be in a group.  If there are objects left in the
165 	 * group, move onto the next one in the group and return.
166 	 */
167 	if (++f->ef_depth[f->ef_ndeep].efd_obj <
168 	    f->ef_depth[f->ef_ndeep].efd_nobjs) {
169 		return (1);
170 
171 	/*
172 	 * If we are at the end of a group we need to move backwards up the
173 	 * stack, consuming the large backskips as we go, until we find a group
174 	 * that still contains unprocessed items, or until we have unwound back
175 	 * off the bottom of the stack (i.e. out of all the groups).
176 	 */
177 	} else {
178 		while (f->ef_ndeep >= 0 &&
179 		    ++f->ef_depth[f->ef_ndeep].efd_obj >=
180 		    f->ef_depth[f->ef_ndeep].efd_nobjs) {
181 			/* Read the large backskip. */
182 			f->ef_ndeep--;
183 			if (xread(f, &scratch32, sizeof (scratch32)) !=
184 			    sizeof (scratch32)) {
185 				EXACCT_SET_ERR(EXR_CORRUPT_FILE);
186 				return (-1);
187 			}
188 		}
189 		return (0);
190 	}
191 }
192 
193 /*
194  * Step backwards along the objects within the current group.  If we are still
195  * within a group, return 1.  If we have reached the end of the current group,
196  * unwind the stack back up to the enclosing group and return 0.
197  */
198 static int stack_previous_object(ea_file_impl_t *f)
199 {
200 	/*
201 	 * If the stack is empty we are not in a group, so there will be no
202 	 * stack manipulation to do.
203 	 */
204 	if (f->ef_ndeep < 0) {
205 		return (0);
206 	}
207 
208 	/*
209 	 * Otherwise we must be in a group.  If there are objects left in the
210 	 * group, move onto the previous one in the group and return.
211 	 */
212 	if (--f->ef_depth[f->ef_ndeep].efd_obj >= 0) {
213 		return (1);
214 
215 	/* Otherwise, step one level back up the group stack. */
216 	} else {
217 		f->ef_ndeep--;
218 		return (0);
219 	}
220 }
221 
222 /*
223  * read/seek/pos virtualisation wrappers.  Because objects can come either from
224  * a file or memory, the read/seek/pos functions need to be wrapped to allow
225  * them to be used on either a file handle or a memory buffer.
226  */
227 
228 static size_t
229 fread_wrapper(ea_file_impl_t *f, void *buf, size_t sz)
230 {
231 	size_t retval;
232 
233 	retval = fread(buf, 1, sz, f->ef_fp);
234 	if (retval == 0 && ferror(f->ef_fp)) {
235 		retval = (size_t)-1;
236 	}
237 	return (retval);
238 }
239 
240 static size_t
241 bufread_wrapper(ea_file_impl_t *f, void *buf, size_t sz)
242 {
243 	if (f->ef_bufsize == 0 && sz != 0)
244 		return ((size_t)0);
245 
246 	if (f->ef_bufsize < sz)
247 		sz = f->ef_bufsize;
248 
249 	bcopy(f->ef_buf, buf, sz);
250 	f->ef_buf += sz;
251 	f->ef_bufsize -= sz;
252 
253 	return (sz);
254 }
255 
256 static off_t
257 fseek_wrapper(ea_file_impl_t *f, off_t adv)
258 {
259 	return (fseeko(f->ef_fp, adv, SEEK_CUR));
260 }
261 
262 static off_t
263 bufseek_wrapper(ea_file_impl_t *f, off_t adv)
264 {
265 	if (f->ef_bufsize == 0 && adv != 0)
266 		return (-1);
267 
268 	if (f->ef_bufsize < adv)
269 		adv = f->ef_bufsize;
270 
271 	f->ef_buf += adv;
272 	f->ef_bufsize -= adv;
273 
274 	return (0);
275 }
276 
277 /*ARGSUSED*/
278 static void *
279 fpos_wrapper(ea_file_impl_t *f)
280 {
281 	return (NULL);
282 }
283 
284 static void *
285 bufpos_wrapper(ea_file_impl_t *f)
286 {
287 	return (f->ef_buf);
288 }
289 
290 /*
291  * Public API
292  */
293 
294 void
295 exacct_seterr(int errval)
296 {
297 	if (thr_main()) {
298 		exacct_errval = errval;
299 		return;
300 	}
301 	if (keyonce == 0) {
302 		(void) mutex_lock(&keylock);
303 		if (keyonce == 0) {
304 			(void) thr_keycreate(&errkey, 0);
305 			keyonce++;
306 		}
307 		(void) mutex_unlock(&keylock);
308 	}
309 	(void) thr_setspecific(errkey, (void *)(intptr_t)errval);
310 }
311 
312 int
313 ea_error(void)
314 {
315 	intptr_t errvalp;
316 
317 	if (thr_main())
318 		return (exacct_errval);
319 	if (keyonce == 0)
320 		return (EXR_OK);
321 
322 	(void) thr_getspecific(errkey, (void**)&errvalp);
323 	return ((int)errvalp);
324 }
325 
326 /*
327  * ea_next_object(), ea_previous_object(), and ea_get_object() are written such
328  * that the file cursor is always located on an object boundary.
329  */
330 ea_object_type_t
331 ea_next_object(ea_file_t *ef, ea_object_t *obj)
332 {
333 	ea_file_impl_t *f = (ea_file_impl_t *)ef;
334 	ea_size_t len;
335 	off_t backup;
336 	size_t ret;
337 
338 	/*
339 	 * If ef_advance is zero, then we are executing after a get or previous
340 	 * operation and do not move to the next or previous object.  Otherwise,
341 	 * advance to the next available item.  Note that ef_advance does NOT
342 	 * include the large backskip at the end of a object, this being dealt
343 	 * with by the depth stack handling in stack_next_object.
344 	 */
345 	if (f->ef_advance != 0) {
346 		if (fseeko(f->ef_fp, (off_t)f->ef_advance, SEEK_CUR) == -1) {
347 			EXACCT_SET_ERR(EXR_SYSCALL_FAIL);
348 			return (EO_ERROR);
349 		}
350 		if (stack_next_object(f, fread_wrapper) == -1) {
351 			/* exacct_error set above. */
352 			return (EO_ERROR);
353 		}
354 	}
355 	f->ef_advance = 0;
356 
357 	/* Read the catalog tag */
358 	ret = fread(&obj->eo_catalog, 1, sizeof (ea_catalog_t), f->ef_fp);
359 	if (ret == 0) {
360 		EXACCT_SET_ERR(EXR_EOF);
361 		return (EO_ERROR);
362 	} else if (ret < sizeof (ea_catalog_t)) {
363 		EXACCT_SET_ERR(EXR_CORRUPT_FILE);
364 		return (EO_ERROR);
365 	}
366 	exacct_order32(&obj->eo_catalog);
367 
368 	backup = sizeof (ea_catalog_t);
369 	obj->eo_type = EO_ITEM;
370 
371 	/* Figure out the offset to just before the large backskip. */
372 	switch (obj->eo_catalog & EXT_TYPE_MASK) {
373 	case EXT_GROUP:
374 		obj->eo_type = EO_GROUP;
375 		f->ef_advance = sizeof (uint32_t);
376 	/* FALLTHROUGH */
377 	case EXT_STRING:
378 	case EXT_EXACCT_OBJECT:
379 	case EXT_RAW:
380 		if (fread(&len, 1, sizeof (ea_size_t), f->ef_fp)
381 		    < sizeof (ea_size_t)) {
382 			obj->eo_type = EO_NONE;
383 			EXACCT_SET_ERR(EXR_CORRUPT_FILE);
384 			return (EO_ERROR);
385 		}
386 		exacct_order64(&len);
387 		/* Note: len already includes the size of the backskip. */
388 		f->ef_advance += sizeof (ea_catalog_t) +
389 		    sizeof (ea_size_t) + len;
390 		backup += sizeof (ea_size_t);
391 		break;
392 	case EXT_UINT8:
393 		f->ef_advance = sizeof (ea_catalog_t) + sizeof (uint8_t) +
394 		    sizeof (uint32_t);
395 		break;
396 	case EXT_UINT16:
397 		f->ef_advance = sizeof (ea_catalog_t) + sizeof (uint16_t) +
398 		    sizeof (uint32_t);
399 		break;
400 	case EXT_UINT32:
401 		f->ef_advance = sizeof (ea_catalog_t) + sizeof (uint32_t) +
402 		    sizeof (uint32_t);
403 		break;
404 	case EXT_UINT64:
405 		f->ef_advance = sizeof (ea_catalog_t) + sizeof (uint64_t) +
406 		    sizeof (uint32_t);
407 		break;
408 	case EXT_DOUBLE:
409 		f->ef_advance = sizeof (ea_catalog_t) + sizeof (double) +
410 		    sizeof (uint32_t);
411 		break;
412 	default:
413 		obj->eo_type = EO_NONE;
414 		EXACCT_SET_ERR(EXR_CORRUPT_FILE);
415 		return (EO_ERROR);
416 	}
417 
418 	/* Reposition to the start of this object. */
419 	if (fseeko(f->ef_fp, -backup, SEEK_CUR) == -1) {
420 		obj->eo_type = EO_NONE;
421 		f->ef_advance = 0;
422 		EXACCT_SET_ERR(EXR_SYSCALL_FAIL);
423 		return (EO_ERROR);
424 	}
425 
426 	EXACCT_SET_ERR(EXR_OK);
427 	return (obj->eo_type);
428 }
429 
430 ea_object_type_t
431 ea_previous_object(ea_file_t *ef, ea_object_t *obj)
432 {
433 	ea_file_impl_t *f = (ea_file_impl_t *)ef;
434 	uint32_t bkskip;
435 	int r;
436 
437 	if (fseeko(f->ef_fp, -((off_t)sizeof (uint32_t)), SEEK_CUR) == -1) {
438 		if (errno == EINVAL) {
439 			EXACCT_SET_ERR(EXR_EOF);
440 		} else {
441 			EXACCT_SET_ERR(EXR_SYSCALL_FAIL);
442 		}
443 		return (EO_ERROR);
444 	}
445 
446 	if ((r = fread(&bkskip, 1, sizeof (uint32_t), f->ef_fp)) !=
447 	    sizeof (uint32_t)) {
448 		if (r == 0) {
449 			EXACCT_SET_ERR(EXR_EOF);
450 		} else {
451 			EXACCT_SET_ERR(EXR_SYSCALL_FAIL);
452 		}
453 		return (EO_ERROR);
454 	}
455 	exacct_order32(&bkskip);
456 
457 	/*
458 	 * A backskip of 0 means that the current record can't be skipped over.
459 	 * This will be true for the header record, and for records longer than
460 	 * 2^32.
461 	 */
462 	if (bkskip == 0) {
463 		EXACCT_SET_ERR(EXR_EOF);
464 		return (EO_ERROR);
465 	}
466 	(void) stack_previous_object(f);
467 
468 	if (fseeko(f->ef_fp, -((off_t)bkskip), SEEK_CUR) == -1) {
469 		if (errno == EINVAL) {
470 			/*
471 			 * If we attempted to seek past BOF, then the file was
472 			 * corrupt, as we can only trust the backskip we read.
473 			 */
474 			EXACCT_SET_ERR(EXR_CORRUPT_FILE);
475 		} else {
476 			EXACCT_SET_ERR(EXR_SYSCALL_FAIL);
477 		}
478 		return (EO_ERROR);
479 	}
480 
481 	f->ef_advance = 0;
482 	return (ea_next_object(ef, obj));
483 }
484 
485 /*
486  * xget_object() contains the logic for extracting an individual object from a
487  * packed buffer, which it consumes using xread() and xseek() operations
488  * provided by the caller.  flags may be set to either EUP_ALLOC, in which case
489  * new memory is allocated for the variable length items unpacked, or
490  * EUP_NOALLOC, in which case item data pointer indicate locations within the
491  * buffer, using the provided xpos() function.  EUP_NOALLOC is generally not
492  * useful for callers representing interaction with actual file streams, and
493  * should not be specified thereby.
494  */
495 static ea_object_type_t
496 xget_object(
497     ea_file_impl_t *f,
498     ea_object_t *obj,
499     size_t (*xread)(ea_file_impl_t *, void *, size_t),
500     off_t (*xseek)(ea_file_impl_t *, off_t),
501     void *(*xpos)(ea_file_impl_t *),
502     int flags)
503 {
504 	ea_size_t sz;
505 	uint32_t gp_backskip, scratch32;
506 	void *buf;
507 	size_t r;
508 
509 	/* Read the catalog tag. */
510 	if ((r = xread(f, &obj->eo_catalog, sizeof (ea_catalog_t))) == 0) {
511 		EXACCT_SET_ERR(EXR_EOF);
512 		return (EO_ERROR);
513 	} else if (r != sizeof (ea_catalog_t)) {
514 		EXACCT_SET_ERR(EXR_CORRUPT_FILE);
515 		return (EO_ERROR);
516 	}
517 	exacct_order32(&obj->eo_catalog);
518 
519 	/*
520 	 * If this is a record group, we treat it separately:  only record
521 	 * groups cause us to allocate new depth frames.
522 	 */
523 	if ((obj->eo_catalog & EXT_TYPE_MASK) == EXT_GROUP) {
524 		obj->eo_type = EO_GROUP;
525 
526 		/* Read size field, and number of objects. */
527 		if (xread(f, &sz, sizeof (ea_size_t)) != sizeof (ea_size_t)) {
528 			EXACCT_SET_ERR(EXR_CORRUPT_FILE);
529 			return (EO_ERROR);
530 		}
531 		exacct_order64(&sz);
532 		if (xread(f, &obj->eo_group.eg_nobjs, sizeof (uint32_t)) !=
533 		    sizeof (uint32_t)) {
534 			EXACCT_SET_ERR(EXR_CORRUPT_FILE);
535 			return (EO_ERROR);
536 		}
537 		exacct_order32(&obj->eo_group.eg_nobjs);
538 
539 		/* Now read the group's small backskip. */
540 		if (xread(f, &gp_backskip, sizeof (uint32_t)) !=
541 		    sizeof (uint32_t)) {
542 			EXACCT_SET_ERR(EXR_CORRUPT_FILE);
543 			return (EO_ERROR);
544 		}
545 
546 		/* Push a new depth stack frame. */
547 		if (stack_new_group(f, obj->eo_group.eg_nobjs) != 0) {
548 			/* exacct_error set above */
549 			return (EO_ERROR);
550 		}
551 
552 		/*
553 		 * If the group has no items, we now need to position to the
554 		 * end of the group, because there will be no subsequent calls
555 		 * to process the group, it being empty.
556 		 */
557 		if (obj->eo_group.eg_nobjs == 0) {
558 			if (stack_next_object(f, xread) == -1) {
559 				/* exacct_error set above. */
560 				return (EO_ERROR);
561 			}
562 		}
563 
564 		f->ef_advance = 0;
565 		EXACCT_SET_ERR(EXR_OK);
566 		return (obj->eo_type);
567 	}
568 
569 	/*
570 	 * Otherwise we are reading an item.
571 	 */
572 	obj->eo_type = EO_ITEM;
573 	switch (obj->eo_catalog & EXT_TYPE_MASK) {
574 	case EXT_STRING:
575 	case EXT_EXACCT_OBJECT:
576 	case EXT_RAW:
577 		if (xread(f, &sz, sizeof (ea_size_t)) != sizeof (ea_size_t)) {
578 			EXACCT_SET_ERR(EXR_CORRUPT_FILE);
579 			return (EO_ERROR);
580 		}
581 		exacct_order64(&sz);
582 		/*
583 		 * Subtract backskip value from size.
584 		 */
585 		sz -= sizeof (uint32_t);
586 		if ((flags & EUP_ALLOC_MASK) == EUP_NOALLOC) {
587 			buf = xpos(f);
588 			if (xseek(f, sz) == -1) {
589 				EXACCT_SET_ERR(EXR_CORRUPT_FILE);
590 				return (EO_ERROR);
591 			}
592 		} else {
593 			if ((buf = ea_alloc(sz)) == NULL)
594 				/* exacct_error set above. */
595 				return (EO_ERROR);
596 			if (xread(f, buf, sz) != sz) {
597 				ea_free(buf, sz);
598 				EXACCT_SET_ERR(EXR_CORRUPT_FILE);
599 				return (EO_ERROR);
600 			}
601 		}
602 		obj->eo_item.ei_string = buf;
603 		/*
604 		 * Maintain our consistent convention that string lengths
605 		 * include the terminating NULL character.
606 		 */
607 		obj->eo_item.ei_size = sz;
608 		break;
609 	case EXT_UINT8:
610 		if (xread(f, &obj->eo_item.ei_uint8, sizeof (uint8_t)) !=
611 		    sizeof (uint8_t)) {
612 			EXACCT_SET_ERR(EXR_CORRUPT_FILE);
613 			return (EO_ERROR);
614 		}
615 		obj->eo_item.ei_size = sizeof (uint8_t);
616 		break;
617 	case EXT_UINT16:
618 		if (xread(f, &obj->eo_item.ei_uint16, sizeof (uint16_t)) !=
619 		    sizeof (uint16_t)) {
620 			EXACCT_SET_ERR(EXR_CORRUPT_FILE);
621 			return (EO_ERROR);
622 		}
623 		exacct_order16(&obj->eo_item.ei_uint16);
624 		obj->eo_item.ei_size = sizeof (uint16_t);
625 		break;
626 	case EXT_UINT32:
627 		if (xread(f, &obj->eo_item.ei_uint32, sizeof (uint32_t)) !=
628 		    sizeof (uint32_t)) {
629 			EXACCT_SET_ERR(EXR_CORRUPT_FILE);
630 			return (EO_ERROR);
631 		}
632 		exacct_order32(&obj->eo_item.ei_uint32);
633 		obj->eo_item.ei_size = sizeof (uint32_t);
634 		break;
635 	case EXT_UINT64:
636 		if (xread(f, &obj->eo_item.ei_uint64, sizeof (uint64_t)) !=
637 		    sizeof (uint64_t)) {
638 			EXACCT_SET_ERR(EXR_CORRUPT_FILE);
639 			return (EO_ERROR);
640 		}
641 		exacct_order64(&obj->eo_item.ei_uint64);
642 		obj->eo_item.ei_size = sizeof (uint64_t);
643 		break;
644 	case EXT_DOUBLE:
645 		if (xread(f, &obj->eo_item.ei_double, sizeof (double)) !=
646 		    sizeof (double)) {
647 			EXACCT_SET_ERR(EXR_CORRUPT_FILE);
648 			return (EO_ERROR);
649 		}
650 		exacct_order64((uint64_t *)&obj->eo_item.ei_double);
651 		obj->eo_item.ei_size = sizeof (double);
652 		break;
653 	default:
654 		/*
655 		 * We've encountered an unknown type value.  Flag the error and
656 		 * exit.
657 		 */
658 		EXACCT_SET_ERR(EXR_CORRUPT_FILE);
659 		return (EO_ERROR);
660 	}
661 
662 	/*
663 	 * Advance over current large backskip value,
664 	 * and position at the start of the next object.
665 	 */
666 	if (xread(f, &scratch32, sizeof (scratch32)) != sizeof (scratch32)) {
667 		EXACCT_SET_ERR(EXR_CORRUPT_FILE);
668 		return (EO_ERROR);
669 	}
670 	if (stack_next_object(f, xread) == -1) {
671 		/* exacct_error set above. */
672 		return (EO_ERROR);
673 	}
674 
675 	f->ef_advance = 0;
676 	EXACCT_SET_ERR(EXR_OK);
677 	return (obj->eo_type);
678 }
679 
680 ea_object_type_t
681 ea_get_object(ea_file_t *ef, ea_object_t *obj)
682 {
683 	obj->eo_next = NULL;
684 	return (xget_object((ea_file_impl_t *)ef, obj, fread_wrapper,
685 		    fseek_wrapper, fpos_wrapper, EUP_ALLOC));
686 }
687 
688 /*
689  * unpack_group() recursively unpacks record groups from the buffer tucked
690  * within the passed ea_file, and attaches them to grp.
691  */
692 static int
693 unpack_group(ea_file_impl_t *f, ea_object_t *grp, int flag)
694 {
695 	ea_object_t *obj;
696 	uint_t nobjs = grp->eo_group.eg_nobjs;
697 	int i;
698 
699 	/*
700 	 * Set the group's object count to zero, as we will rebuild it via the
701 	 * individual object attachments.
702 	 */
703 	grp->eo_group.eg_nobjs = 0;
704 	grp->eo_group.eg_objs = NULL;
705 
706 	for (i = 0; i < nobjs; i++) {
707 		if ((obj = ea_alloc(sizeof (ea_object_t))) == NULL) {
708 			/* exacct_errno set above. */
709 			return (-1);
710 		}
711 		obj->eo_next = NULL;
712 		if (xget_object(f, obj, bufread_wrapper, bufseek_wrapper,
713 			    bufpos_wrapper, flag) == -1) {
714 			ea_free(obj, sizeof (ea_object_t));
715 			/* exacct_errno set above. */
716 			return (-1);
717 		}
718 
719 		(void) ea_attach_to_group(grp, obj);
720 
721 		if (obj->eo_type == EO_GROUP &&
722 		    unpack_group(f, obj, flag) == -1) {
723 			/* exacct_errno set above. */
724 			return (-1);
725 		}
726 	}
727 
728 	if (nobjs != grp->eo_group.eg_nobjs) {
729 		EXACCT_SET_ERR(EXR_CORRUPT_FILE);
730 		return (-1);
731 	}
732 	EXACCT_SET_ERR(EXR_OK);
733 	return (0);
734 }
735 
736 /*
737  * ea_unpack_object() can be considered as a finite series of get operations on
738  * a given buffer, that rebuilds the hierarchy of objects compacted by a pack
739  * operation.  Because there is complex state associated with the group depth,
740  * ea_unpack_object() must complete as one operation on a given buffer.
741  */
742 ea_object_type_t
743 ea_unpack_object(ea_object_t **objp, int flag, void *buf, size_t bufsize)
744 {
745 	ea_file_impl_t fake;
746 	ea_object_t *obj;
747 	ea_object_type_t first_obj_type;
748 
749 	*objp = NULL;
750 	if (buf == NULL) {
751 		EXACCT_SET_ERR(EXR_INVALID_BUF);
752 		return (EO_ERROR);
753 	}
754 
755 	/* Set up the structures needed for unpacking */
756 	bzero(&fake, sizeof (ea_file_impl_t));
757 	if (stack_check(&fake) == -1) {
758 		/* exacct_errno set above. */
759 		return (EO_ERROR);
760 	}
761 	fake.ef_buf = buf;
762 	fake.ef_bufsize = bufsize;
763 
764 	/* Unpack the first object in the buffer - this should succeed. */
765 	if ((obj = ea_alloc(sizeof (ea_object_t))) == NULL) {
766 		stack_free(&fake);
767 		/* exacct_errno set above. */
768 		return (EO_ERROR);
769 	}
770 	obj->eo_next = NULL;
771 	if ((first_obj_type = xget_object(&fake, obj, bufread_wrapper,
772 	    bufseek_wrapper, bufpos_wrapper, flag)) == -1) {
773 		stack_free(&fake);
774 		ea_free(obj, sizeof (ea_object_t));
775 		/* exacct_errno set above. */
776 		return (EO_ERROR);
777 	}
778 
779 	if (obj->eo_type == EO_GROUP && unpack_group(&fake, obj, flag) == -1) {
780 		stack_free(&fake);
781 		ea_free_object(obj, flag);
782 		/* exacct_errno set above. */
783 		return (EO_ERROR);
784 	}
785 	*objp = obj;
786 
787 	/*
788 	 * There may be other objects in the buffer - if so, chain them onto
789 	 * the end of the list.  We have reached the end of the list when
790 	 * xget_object() returns -1 with exacct_error set to EXR_EOF.
791 	 */
792 	for (;;) {
793 		if ((obj = ea_alloc(sizeof (ea_object_t))) == NULL) {
794 			stack_free(&fake);
795 			ea_free_object(*objp, flag);
796 			*objp = NULL;
797 			/* exacct_errno set above. */
798 			return (EO_ERROR);
799 		}
800 		obj->eo_next = NULL;
801 		if (xget_object(&fake, obj, bufread_wrapper, bufseek_wrapper,
802 			    bufpos_wrapper, flag) == -1) {
803 			stack_free(&fake);
804 			ea_free(obj, sizeof (ea_object_t));
805 			if (ea_error() == EXR_EOF) {
806 				EXACCT_SET_ERR(EXR_OK);
807 				return (first_obj_type);
808 			} else {
809 				ea_free_object(*objp, flag);
810 				*objp = NULL;
811 				/* exacct_error set above. */
812 				return (EO_ERROR);
813 			}
814 		}
815 
816 		(void) ea_attach_to_object(*objp, obj);
817 
818 		if (obj->eo_type == EO_GROUP &&
819 		    unpack_group(&fake, obj, flag) == -1) {
820 			stack_free(&fake);
821 			ea_free(obj, sizeof (ea_object_t));
822 			ea_free_object(*objp, flag);
823 			*objp = NULL;
824 			/* exacct_errno set above. */
825 			return (EO_ERROR);
826 		}
827 	}
828 }
829 
830 int
831 ea_write_object(ea_file_t *ef, ea_object_t *obj)
832 {
833 	ea_size_t sz;
834 	void *buf;
835 	ea_file_impl_t *f = (ea_file_impl_t *)ef;
836 
837 	/*
838 	 * If we weren't opened for writing, this call fails.
839 	 */
840 	if ((f->ef_oflags & O_RDWR) == 0 &&
841 	    (f->ef_oflags & O_WRONLY) == 0) {
842 		EXACCT_SET_ERR(EXR_NOTSUPP);
843 		return (-1);
844 	}
845 
846 	/* Pack with a null buffer to get the size. */
847 	sz = ea_pack_object(obj, NULL, 0);
848 	if (sz == -1 || (buf = ea_alloc(sz)) == NULL) {
849 		/* exacct_error set above. */
850 		return (-1);
851 	}
852 	if (ea_pack_object(obj, buf, sz) == (size_t)-1) {
853 		ea_free(buf, sz);
854 		/* exacct_error set above. */
855 		return (-1);
856 	}
857 	if (fwrite(buf, sizeof (char), sz, f->ef_fp) != sz) {
858 		ea_free(buf, sz);
859 		EXACCT_SET_ERR(EXR_SYSCALL_FAIL);
860 		return (-1);
861 	}
862 	ea_free(buf, sz);
863 	EXACCT_SET_ERR(EXR_OK);
864 	return (0);
865 }
866 
867 /*
868  * validate_header() must be kept in sync with write_header(), given below, and
869  * exacct_create_header(), in uts/common/os/exacct.c.
870  */
871 static int
872 validate_header(ea_file_t *ef, const char *creator)
873 {
874 	ea_object_t hdr_grp;
875 	ea_object_t scratch_obj;
876 	int error = EXR_OK;
877 	int saw_creator = 0;
878 	int saw_version = 0;
879 	int saw_type = 0;
880 	int saw_hostname = 0;
881 	int n;
882 	ea_file_impl_t *f = (ea_file_impl_t *)ef;
883 
884 	bzero(&hdr_grp, sizeof (ea_object_t));
885 
886 	if (ea_get_object(ef, &hdr_grp) != EO_GROUP) {
887 		error = ea_error();
888 		goto error_case;
889 	}
890 
891 	if (hdr_grp.eo_catalog !=
892 	    (EXT_GROUP | EXC_DEFAULT | EXD_GROUP_HEADER)) {
893 		error = EXR_CORRUPT_FILE;
894 		goto error_case;
895 	}
896 
897 	for (n = 0; n < hdr_grp.eo_group.eg_nobjs; n++) {
898 		bzero(&scratch_obj, sizeof (ea_object_t));
899 		if (ea_get_object(ef, &scratch_obj) == -1) {
900 			error = ea_error();
901 			goto error_case;
902 		}
903 
904 		switch (scratch_obj.eo_catalog) {
905 		case EXT_UINT32 | EXC_DEFAULT | EXD_VERSION:
906 			if (scratch_obj.eo_item.ei_uint32 != EXACCT_VERSION) {
907 				error = EXR_UNKN_VERSION;
908 				goto error_case;
909 			}
910 			saw_version++;
911 			break;
912 		case EXT_STRING | EXC_DEFAULT | EXD_FILETYPE:
913 			if (strcmp(scratch_obj.eo_item.ei_string,
914 			    EXACCT_HDR_STR) != 0) {
915 				error = EXR_CORRUPT_FILE;
916 				goto error_case;
917 			}
918 			saw_type++;
919 			break;
920 		case EXT_STRING | EXC_DEFAULT | EXD_CREATOR:
921 			f->ef_creator =
922 			    ea_strdup(scratch_obj.eo_item.ei_string);
923 			if (f->ef_creator == NULL) {
924 				error = ea_error();
925 				goto error_case;
926 			}
927 			saw_creator++;
928 			break;
929 		/* The hostname is an optional field. */
930 		case EXT_STRING | EXC_DEFAULT | EXD_HOSTNAME:
931 			f->ef_hostname =
932 			    ea_strdup(scratch_obj.eo_item.ei_string);
933 			if (f->ef_hostname == NULL) {
934 				error = ea_error();
935 				goto error_case;
936 			}
937 			saw_hostname++;
938 			break;
939 		default:
940 			/* ignore unrecognized header members */
941 			break;
942 		}
943 		(void) ea_free_item(&scratch_obj, EUP_ALLOC);
944 	}
945 
946 	if (saw_version && saw_type && saw_creator) {
947 		if (creator && strcmp(f->ef_creator, creator) != 0) {
948 			error = EXR_NO_CREATOR;
949 			goto error_case;
950 		}
951 		EXACCT_SET_ERR(EXR_OK);
952 		return (0);
953 	}
954 
955 error_case:
956 	(void) ea_free_item(&scratch_obj, EUP_ALLOC);
957 	if (saw_hostname)
958 		ea_strfree(f->ef_hostname);
959 	if (saw_creator)
960 		ea_strfree(f->ef_creator);
961 	EXACCT_SET_ERR(error);
962 	return (-1);
963 }
964 
965 static int
966 write_header(ea_file_t *ef)
967 {
968 	ea_object_t hdr_grp;
969 	ea_object_t vers_obj;
970 	ea_object_t creator_obj;
971 	ea_object_t filetype_obj;
972 	ea_object_t hostname_obj;
973 	uint32_t bskip;
974 	const uint32_t version = EXACCT_VERSION;
975 	ea_file_impl_t *f = (ea_file_impl_t *)ef;
976 	void *buf;
977 	size_t bufsize;
978 	char hostbuf[SYSINFO_BUFSIZE];
979 	int error = EXR_OK;
980 
981 	bzero(&hdr_grp, sizeof (ea_object_t));
982 	bzero(&vers_obj, sizeof (ea_object_t));
983 	bzero(&creator_obj, sizeof (ea_object_t));
984 	bzero(&filetype_obj, sizeof (ea_object_t));
985 	bzero(&hostname_obj, sizeof (ea_object_t));
986 	bzero(hostbuf, SYSINFO_BUFSIZE);
987 
988 	(void) sysinfo(SI_HOSTNAME, hostbuf, SYSINFO_BUFSIZE);
989 
990 	if (ea_set_item(&vers_obj, EXT_UINT32 | EXC_DEFAULT | EXD_VERSION,
991 		    (void *)&version, 0) == -1 ||
992 	    ea_set_item(&creator_obj, EXT_STRING | EXC_DEFAULT | EXD_CREATOR,
993 		    f->ef_creator, strlen(f->ef_creator)) == -1 ||
994 	    ea_set_item(&filetype_obj, EXT_STRING | EXC_DEFAULT | EXD_FILETYPE,
995 		    EXACCT_HDR_STR, strlen(EXACCT_HDR_STR)) == -1 ||
996 	    ea_set_item(&hostname_obj, EXT_STRING | EXC_DEFAULT | EXD_HOSTNAME,
997 		    hostbuf, strlen(hostbuf)) == -1) {
998 		error = ea_error();
999 		goto cleanup1;
1000 	}
1001 
1002 	(void) ea_set_group(&hdr_grp,
1003 	    EXT_GROUP | EXC_DEFAULT | EXD_GROUP_HEADER);
1004 	(void) ea_attach_to_group(&hdr_grp, &vers_obj);
1005 	(void) ea_attach_to_group(&hdr_grp, &creator_obj);
1006 	(void) ea_attach_to_group(&hdr_grp, &filetype_obj);
1007 	(void) ea_attach_to_group(&hdr_grp, &hostname_obj);
1008 
1009 	/* Get the required size by passing a null buffer. */
1010 	bufsize = ea_pack_object(&hdr_grp, NULL, 0);
1011 	if ((buf = ea_alloc(bufsize)) == NULL) {
1012 		error = ea_error();
1013 		goto cleanup1;
1014 	}
1015 
1016 	if (ea_pack_object(&hdr_grp, buf, bufsize) == (size_t)-1) {
1017 		error = ea_error();
1018 		goto cleanup2;
1019 	}
1020 
1021 	/*
1022 	 * To prevent reading the header when reading the file backwards,
1023 	 * set the large backskip of the header group to 0 (last 4 bytes).
1024 	 */
1025 	bskip = 0;
1026 	exacct_order32(&bskip);
1027 	bcopy(&bskip, (char *)buf + bufsize - sizeof (bskip),
1028 	    sizeof (bskip));
1029 
1030 	if (fwrite(buf, sizeof (char), bufsize, f->ef_fp) != bufsize ||
1031 	    fflush(f->ef_fp) == EOF) {
1032 		error = EXR_SYSCALL_FAIL;
1033 		goto cleanup2;
1034 	}
1035 
1036 cleanup2:
1037 	ea_free(buf, bufsize);
1038 cleanup1:
1039 	(void) ea_free_item(&vers_obj, EUP_ALLOC);
1040 	(void) ea_free_item(&creator_obj, EUP_ALLOC);
1041 	(void) ea_free_item(&filetype_obj, EUP_ALLOC);
1042 	(void) ea_free_item(&hostname_obj, EUP_ALLOC);
1043 	EXACCT_SET_ERR(error);
1044 	return (error == EXR_OK ? 0 : -1);
1045 }
1046 
1047 const char *
1048 ea_get_creator(ea_file_t *ef)
1049 {
1050 	return ((const char *)((ea_file_impl_t *)ef)->ef_creator);
1051 }
1052 
1053 const char *
1054 ea_get_hostname(ea_file_t *ef)
1055 {
1056 	return ((const char *)((ea_file_impl_t *)ef)->ef_hostname);
1057 }
1058 
1059 int
1060 ea_fdopen(ea_file_t *ef, int fd, const char *creator, int aflags, int oflags)
1061 {
1062 	ea_file_impl_t *f = (ea_file_impl_t *)ef;
1063 
1064 	bzero(f, sizeof (*f));
1065 	f->ef_oflags = oflags;
1066 	f->ef_fd = fd;
1067 
1068 	/* Initialize depth stack. */
1069 	if (stack_check(f) == -1) {
1070 		/* exacct_error set above. */
1071 		goto error1;
1072 	}
1073 
1074 	/*
1075 	 * 1.  If we are O_CREAT, then we will need to write a header
1076 	 * after opening name.
1077 	 */
1078 	if (oflags & O_CREAT) {
1079 		if (creator == NULL) {
1080 			EXACCT_SET_ERR(EXR_NO_CREATOR);
1081 			goto error2;
1082 		}
1083 		if ((f->ef_creator = ea_strdup(creator)) == NULL) {
1084 			/* exacct_error set above. */
1085 			goto error2;
1086 		}
1087 		if ((f->ef_fp = fdopen(f->ef_fd, "w")) == NULL) {
1088 			EXACCT_SET_ERR(EXR_SYSCALL_FAIL);
1089 			goto error3;
1090 		}
1091 		if (write_header(ef) == -1) {
1092 			/* exacct_error set above. */
1093 			goto error3;
1094 		}
1095 
1096 	/*
1097 	 * 2.  If we are not O_CREAT, but are RDWR or WRONLY, we need to
1098 	 * seek to EOF so that appends will succeed.
1099 	 */
1100 	} else if (oflags & O_RDWR || oflags & O_WRONLY) {
1101 		if ((f->ef_fp = fdopen(f->ef_fd, "r+")) == NULL) {
1102 			EXACCT_SET_ERR(EXR_SYSCALL_FAIL);
1103 			goto error2;
1104 		}
1105 
1106 		if ((aflags & EO_VALIDATE_MSK) == EO_VALID_HDR) {
1107 			if (validate_header(ef, creator) < 0) {
1108 				/* exacct_error set above. */
1109 				goto error2;
1110 			}
1111 		}
1112 
1113 		if (fseeko(f->ef_fp, 0, SEEK_END) == -1) {
1114 			EXACCT_SET_ERR(EXR_SYSCALL_FAIL);
1115 			goto error2;
1116 		}
1117 
1118 	/*
1119 	 * 3. This is an undefined manner for opening an exacct file.
1120 	 */
1121 	} else if (oflags != O_RDONLY) {
1122 		EXACCT_SET_ERR(EXR_NOTSUPP);
1123 		goto error2;
1124 
1125 	/*
1126 	 * 4a.  If we are RDONLY, then we are in a position such that
1127 	 * either a ea_get_object or an ea_next_object will succeed.  If
1128 	 * aflags was set to EO_TAIL, seek to the end of the file.
1129 	 */
1130 	} else {
1131 		if ((f->ef_fp = fdopen(f->ef_fd, "r")) == NULL) {
1132 			EXACCT_SET_ERR(EXR_SYSCALL_FAIL);
1133 			goto error2;
1134 		}
1135 
1136 		if ((aflags & EO_VALIDATE_MSK) == EO_VALID_HDR) {
1137 			if (validate_header(ef, creator) == -1) {
1138 				/* exacct_error set above. */
1139 				goto error2;
1140 			}
1141 		}
1142 
1143 		/*
1144 		 * 4b.  Handle the "open at end" option, for consumers who want
1145 		 * to go backwards through the file (i.e. lastcomm).
1146 		 */
1147 		if ((aflags & EO_POSN_MSK) == EO_TAIL) {
1148 			if (fseeko(f->ef_fp, 0, SEEK_END) < 0) {
1149 				EXACCT_SET_ERR(EXR_SYSCALL_FAIL);
1150 				goto error2;
1151 			}
1152 		}
1153 	}
1154 
1155 	EXACCT_SET_ERR(EXR_OK);
1156 	return (0);
1157 
1158 	/* Error cleanup code */
1159 error3:
1160 	ea_strfree(f->ef_creator);
1161 error2:
1162 	stack_free(f);
1163 error1:
1164 	bzero(f, sizeof (*f));
1165 	return (-1);
1166 }
1167 
1168 int
1169 ea_open(ea_file_t *ef, const char *name, const char *creator,
1170     int aflags, int oflags, mode_t mode)
1171 {
1172 	int fd;
1173 
1174 	/*
1175 	 * If overwriting an existing file, make sure to truncate it
1176 	 * to prevent the file being created corrupt.
1177 	 */
1178 	if (oflags & O_CREAT)
1179 		oflags |= O_TRUNC;
1180 
1181 	if ((fd = open(name, oflags, mode)) == -1) {
1182 		EXACCT_SET_ERR(EXR_SYSCALL_FAIL);
1183 		return (-1);
1184 	}
1185 
1186 	if (ea_fdopen(ef, fd, creator, aflags, oflags) == -1) {
1187 		(void) close(fd);
1188 		return (-1);
1189 	}
1190 
1191 	return (0);
1192 }
1193 
1194 /*
1195  * ea_close() performs all appropriate close operations on the open exacct file,
1196  * including releasing any memory allocated while parsing the file.
1197  */
1198 int
1199 ea_close(ea_file_t *ef)
1200 {
1201 	ea_file_impl_t *f = (ea_file_impl_t *)ef;
1202 
1203 	if (f->ef_creator != NULL)
1204 		ea_strfree(f->ef_creator);
1205 	if (f->ef_hostname != NULL)
1206 		ea_strfree(f->ef_hostname);
1207 
1208 	ea_free(f->ef_depth, f->ef_mxdeep * sizeof (ea_file_depth_t));
1209 
1210 	if (fclose(f->ef_fp)) {
1211 		EXACCT_SET_ERR(EXR_SYSCALL_FAIL);
1212 		return (-1);
1213 	}
1214 
1215 	EXACCT_SET_ERR(EXR_OK);
1216 	return (0);
1217 }
1218 
1219 /*
1220  * Empty the input buffer and clear any underlying EOF or error bits set on the
1221  * underlying FILE.  This can be used by any library clients who wish to handle
1222  * files that are in motion or who wish to seek the underlying file descriptor.
1223  */
1224 void
1225 ea_clear(ea_file_t *ef)
1226 {
1227 	ea_file_impl_t *f = (ea_file_impl_t *)ef;
1228 
1229 	(void) fflush(f->ef_fp);
1230 	clearerr(f->ef_fp);
1231 }
1232 
1233 /*
1234  * Copy an ea_object_t.  Note that in the case of a group, just the group
1235  * object will be copied, and not its list of members.  To recursively copy
1236  * a group or a list of items use ea_copy_tree().
1237  */
1238 ea_object_t *
1239 ea_copy_object(const ea_object_t *src)
1240 {
1241 	ea_object_t *dst;
1242 
1243 	/* Allocate a new object and copy to it. */
1244 	if ((dst = ea_alloc(sizeof (ea_object_t))) == NULL) {
1245 		return (NULL);
1246 	}
1247 	bcopy(src, dst, sizeof (ea_object_t));
1248 	dst->eo_next = NULL;
1249 
1250 	switch (src->eo_type) {
1251 	case EO_GROUP:
1252 		dst->eo_group.eg_nobjs = 0;
1253 		dst->eo_group.eg_objs = NULL;
1254 		break;
1255 	case EO_ITEM:
1256 		/* Items containing pointers need special treatment. */
1257 		switch (src->eo_catalog & EXT_TYPE_MASK) {
1258 		case EXT_STRING:
1259 			if (src->eo_item.ei_string != NULL) {
1260 				dst->eo_item.ei_string =
1261 				    ea_strdup(src->eo_item.ei_string);
1262 				if (dst->eo_item.ei_string == NULL) {
1263 					ea_free_object(dst, EUP_ALLOC);
1264 					return (NULL);
1265 				}
1266 			}
1267 			break;
1268 		case EXT_RAW:
1269 			if (src->eo_item.ei_raw != NULL) {
1270 				dst->eo_item.ei_raw =
1271 				    ea_alloc(src->eo_item.ei_size);
1272 				if (dst->eo_item.ei_raw == NULL) {
1273 					ea_free_object(dst, EUP_ALLOC);
1274 					return (NULL);
1275 				}
1276 				bcopy(src->eo_item.ei_raw, dst->eo_item.ei_raw,
1277 				    (size_t)src->eo_item.ei_size);
1278 			}
1279 			break;
1280 		case EXT_EXACCT_OBJECT:
1281 			if (src->eo_item.ei_object != NULL) {
1282 				dst->eo_item.ei_object =
1283 				    ea_alloc(src->eo_item.ei_size);
1284 				if (dst->eo_item.ei_object == NULL) {
1285 					ea_free_object(dst, EUP_ALLOC);
1286 					return (NULL);
1287 				}
1288 				bcopy(src->eo_item.ei_raw, dst->eo_item.ei_raw,
1289 				    (size_t)src->eo_item.ei_size);
1290 			}
1291 			break;
1292 		default:
1293 			/* Other item types require no special handling. */
1294 			break;
1295 		}
1296 		break;
1297 	default:
1298 		ea_free_object(dst, EUP_ALLOC);
1299 		EXACCT_SET_ERR(EXR_INVALID_OBJ);
1300 		return (NULL);
1301 	}
1302 	EXACCT_SET_ERR(EXR_OK);
1303 	return (dst);
1304 }
1305 
1306 /*
1307  * Recursively copy a list of ea_object_t.  All the elements in the eo_next
1308  * list will be copied, and any group objects will be recursively copied.
1309  */
1310 ea_object_t *
1311 ea_copy_object_tree(const ea_object_t *src)
1312 {
1313 	ea_object_t *ret_obj, *dst, *last;
1314 
1315 	for (ret_obj = last = NULL; src != NULL;
1316 	    last = dst, src = src->eo_next) {
1317 
1318 		/* Allocate a new object and copy to it. */
1319 		if ((dst = ea_copy_object(src)) == NULL) {
1320 			ea_free_object(ret_obj, EUP_ALLOC);
1321 			return (NULL);
1322 		}
1323 
1324 		/* Groups need the object list copying. */
1325 		if (src->eo_type == EO_GROUP) {
1326 			dst->eo_group.eg_objs =
1327 			    ea_copy_object_tree(src->eo_group.eg_objs);
1328 			if (dst->eo_group.eg_objs == NULL) {
1329 				ea_free_object(ret_obj, EUP_ALLOC);
1330 				return (NULL);
1331 			}
1332 			dst->eo_group.eg_nobjs = src->eo_group.eg_nobjs;
1333 		}
1334 
1335 		/* Remember the list head the first time round. */
1336 		if (ret_obj == NULL) {
1337 			ret_obj = dst;
1338 		}
1339 
1340 		/* Link together if not at the list head. */
1341 		if (last != NULL) {
1342 			last->eo_next = dst;
1343 		}
1344 	}
1345 	EXACCT_SET_ERR(EXR_OK);
1346 	return (ret_obj);
1347 }
1348 
1349 /*
1350  * Read in the specified number of objects, returning the same data
1351  * structure that would have originally been passed to ea_write().
1352  */
1353 ea_object_t *
1354 ea_get_object_tree(ea_file_t *ef, uint32_t nobj)
1355 {
1356 	ea_object_t *first_obj, *prev_obj, *obj;
1357 
1358 	first_obj = prev_obj = NULL;
1359 	while (nobj--) {
1360 		/* Allocate space for the new object. */
1361 		obj = ea_alloc(sizeof (ea_object_t));
1362 		bzero(obj, sizeof (*obj));
1363 
1364 		/* Read it in. */
1365 		if (ea_get_object(ef, obj) == -1) {
1366 			ea_free(obj, sizeof (ea_object_t));
1367 			if (first_obj != NULL) {
1368 				ea_free_object(first_obj, EUP_ALLOC);
1369 			}
1370 			return (NULL);
1371 		}
1372 
1373 		/* Link it into the list. */
1374 		if (first_obj == NULL) {
1375 			first_obj = obj;
1376 		}
1377 		if (prev_obj != NULL) {
1378 			prev_obj->eo_next = obj;
1379 		}
1380 		prev_obj = obj;
1381 
1382 		/* Recurse if the object is a group with contents. */
1383 		if (obj->eo_type == EO_GROUP && obj->eo_group.eg_nobjs > 0) {
1384 			if ((obj->eo_group.eg_objs = ea_get_object_tree(ef,
1385 			    obj->eo_group.eg_nobjs)) == NULL) {
1386 				/* exacct_error set above. */
1387 				ea_free_object(first_obj, EUP_ALLOC);
1388 				return (NULL);
1389 			}
1390 		}
1391 	}
1392 	EXACCT_SET_ERR(EXR_OK);
1393 	return (first_obj);
1394 }
1395