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