xref: /illumos-gate/usr/src/common/exacct/exacct_core.c (revision 7a6d80f1660abd4755c68cbd094d4a914681d26e)
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 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <sys/types.h>
28 #include <sys/exacct.h>
29 #include <sys/exacct_catalog.h>
30 #include <sys/exacct_impl.h>
31 
32 #ifndef	_KERNEL
33 #include <limits.h>
34 #include <errno.h>
35 #include <poll.h>
36 #include <stdlib.h>
37 #include <strings.h>
38 #else
39 #include <sys/systm.h>
40 #endif
41 
42 /*
43  * extended accounting file core routines
44  *
45  *   Routines shared by libexacct and the kernel for the definition,
46  *   construction and packing of extended accounting (exacct) records.
47  *
48  * Locking
49  *   All routines in this file use ea_alloc(), which is a malloc() wrapper
50  *   in userland and a kmem_alloc(..., KM_SLEEP) wrapper in the kernel.
51  *   Accordingly, all routines require a context suitable for KM_SLEEP
52  *   allocations.
53  */
54 
55 #define	DEFAULT_ENTRIES 4
56 
57 /*
58  * ea_alloc() and ea_free() provide a wrapper for the common
59  * exacct code offering access to either the kmem allocator, or to libc's
60  * malloc.
61  */
62 void *
63 ea_alloc(size_t size)
64 {
65 #ifndef _KERNEL
66 	void *p;
67 
68 	while ((p = malloc(size)) == NULL && errno == EAGAIN)
69 		(void) poll(NULL, 0, 10 * MILLISEC);
70 	if (p == NULL) {
71 		EXACCT_SET_ERR(EXR_SYSCALL_FAIL);
72 	} else {
73 		EXACCT_SET_ERR(EXR_OK);
74 	}
75 	return (p);
76 #else
77 	return (kmem_alloc(size, KM_SLEEP));
78 #endif
79 }
80 
81 #ifndef _KERNEL
82 /*ARGSUSED*/
83 #endif
84 void
85 ea_free(void *ptr, size_t size)
86 {
87 #ifndef _KERNEL
88 	free(ptr);
89 #else
90 	kmem_free(ptr, size);
91 #endif
92 }
93 
94 /*
95  * ea_strdup() returns a pointer that, if non-NULL, must be freed using
96  * ea_strfree() once its useful life ends.
97  */
98 char *
99 ea_strdup(const char *ptr)
100 {
101 	/* Sets exacct_errno. */
102 	char *p = ea_alloc(strlen(ptr) + 1);
103 	if (p != NULL) {
104 		bcopy(ptr, p, strlen(ptr) + 1);
105 	}
106 	return (p);
107 }
108 
109 /*
110  * ea_strfree() frees a string allocated with ea_strdup().
111  */
112 void
113 ea_strfree(char *ptr)
114 {
115 #ifndef _KERNEL
116 	free(ptr);
117 #else
118 	kmem_free(ptr, strlen(ptr) + 1);
119 #endif
120 }
121 
122 /*
123  * ea_cond_memcpy_at_offset() provides a simple conditional memcpy() that allows
124  * us to write a pack routine that returns a valid buffer size, copying only in
125  * the case that a non-NULL buffer is provided.
126  */
127 static void
128 ea_cond_memcpy_at_offset(void *dst, size_t offset, size_t dstsize, void *src,
129     size_t size)
130 {
131 	char *cdst = dst;
132 	char *csrc = src;
133 
134 	if (dst == NULL || src == NULL || size == 0 || offset + size > dstsize)
135 		return;
136 
137 	bcopy(csrc, cdst + offset, size);
138 }
139 
140 /*
141  * exacct_order{16,32,64}() are byte-swapping routines that place the native
142  * data indicated by the input pointer in big-endian order.  Each exacct_order
143  * function is its own inverse.
144  */
145 #ifndef _LITTLE_ENDIAN
146 /*ARGSUSED*/
147 #endif /* _LITTLE_ENDIAN */
148 void
149 exacct_order16(uint16_t *in)
150 {
151 #ifdef _LITTLE_ENDIAN
152 	uint8_t s;
153 	union {
154 		uint16_t agg;
155 		uint8_t arr[2];
156 	} t;
157 
158 	t.agg = *in;
159 
160 	s = t.arr[0];
161 	t.arr[0] = t.arr[1];
162 	t.arr[1] = s;
163 
164 	*in = t.agg;
165 #endif /* _LITTLE_ENDIAN */
166 }
167 
168 #ifndef _LITTLE_ENDIAN
169 /*ARGSUSED*/
170 #endif /* _LITTLE_ENDIAN */
171 void
172 exacct_order32(uint32_t *in)
173 {
174 #ifdef _LITTLE_ENDIAN
175 	uint16_t s;
176 	union {
177 		uint32_t agg;
178 		uint16_t arr[2];
179 	} t;
180 
181 	t.agg = *in;
182 	exacct_order16(&t.arr[0]);
183 	exacct_order16(&t.arr[1]);
184 
185 	s = t.arr[0];
186 	t.arr[0] = t.arr[1];
187 	t.arr[1] = s;
188 
189 	*in = t.agg;
190 #endif /* _LITTLE_ENDIAN */
191 }
192 
193 #ifndef _LITTLE_ENDIAN
194 /*ARGSUSED*/
195 #endif /* _LITTLE_ENDIAN */
196 void
197 exacct_order64(uint64_t *in)
198 {
199 #ifdef _LITTLE_ENDIAN
200 	uint32_t s;
201 	union {
202 		uint64_t agg;
203 		uint32_t arr[2];
204 	} t;
205 
206 	t.agg = *in;
207 	exacct_order32(&t.arr[0]);
208 	exacct_order32(&t.arr[1]);
209 
210 	s = t.arr[0];
211 	t.arr[0] = t.arr[1];
212 	t.arr[1] = s;
213 
214 	*in = t.agg;
215 #endif /* _LITTLE_ENDIAN */
216 }
217 
218 int
219 ea_match_object_catalog(ea_object_t *obj, ea_catalog_t catmask)
220 {
221 	ea_catalog_t catval = obj->eo_catalog;
222 
223 #define	EM_MATCH(v, m, M)	((m & M) == 0 || (v & M) == (m & M))
224 	return (EM_MATCH(catval, catmask, EXT_TYPE_MASK) &&
225 	    EM_MATCH(catval, catmask, EXC_CATALOG_MASK) &&
226 	    EM_MATCH(catval, catmask, EXD_DATA_MASK));
227 #undef EM_MATCH
228 }
229 
230 int
231 ea_set_item(ea_object_t *obj, ea_catalog_t tag,
232     const void *value, size_t valsize)
233 {
234 	ea_item_t *item = &obj->eo_item;
235 
236 	if ((tag & EXT_TYPE_MASK) == EXT_GROUP) {
237 		EXACCT_SET_ERR(EXR_INVALID_OBJ);
238 		return (-1);
239 	}
240 
241 	bzero(obj, sizeof (ea_object_t));
242 	obj->eo_type = EO_ITEM;
243 	obj->eo_catalog = tag;
244 
245 	switch (obj->eo_catalog & EXT_TYPE_MASK) {
246 	case EXT_UINT8:
247 		item->ei_u.ei_u_uint8 = *(uint8_t *)value;
248 		item->ei_size = sizeof (uint8_t);
249 		break;
250 	case EXT_UINT16:
251 		item->ei_u.ei_u_uint16 = *(uint16_t *)value;
252 		item->ei_size = sizeof (uint16_t);
253 		break;
254 	case EXT_UINT32:
255 		item->ei_u.ei_u_uint32 = *(uint32_t *)value;
256 		item->ei_size = sizeof (uint32_t);
257 		break;
258 	case EXT_UINT64:
259 		item->ei_u.ei_u_uint64 = *(uint64_t *)value;
260 		item->ei_size = sizeof (uint64_t);
261 		break;
262 	case EXT_DOUBLE:
263 		item->ei_u.ei_u_double = *(double *)value;
264 		item->ei_size = sizeof (double);
265 		break;
266 	case EXT_STRING:
267 		if ((item->ei_string = ea_strdup((char *)value)) == NULL) {
268 			/* exacct_errno set above. */
269 			return (-1);
270 		}
271 		item->ei_size = strlen(item->ei_string) + 1;
272 		break;
273 	case EXT_EXACCT_OBJECT:
274 		if ((item->ei_object = ea_alloc(valsize)) == NULL) {
275 			/* exacct_errno set above. */
276 			return (-1);
277 		}
278 		bcopy(value, item->ei_object, valsize);
279 		item->ei_size = valsize;
280 		break;
281 	case EXT_RAW:
282 		if ((item->ei_raw = ea_alloc(valsize)) == NULL) {
283 			/* exacct_errno set above. */
284 			return (-1);
285 		}
286 		bcopy(value, item->ei_raw, valsize);
287 		item->ei_size = valsize;
288 		break;
289 	default:
290 		EXACCT_SET_ERR(EXR_INVALID_OBJ);
291 		return (-1);
292 	}
293 
294 	EXACCT_SET_ERR(EXR_OK);
295 	return (0);
296 }
297 
298 int
299 ea_set_group(ea_object_t *obj, ea_catalog_t tag)
300 {
301 	if ((tag & EXT_TYPE_MASK) != EXT_GROUP) {
302 		EXACCT_SET_ERR(EXR_INVALID_OBJ);
303 		return (-1);
304 	}
305 
306 	bzero(obj, sizeof (ea_object_t));
307 
308 	obj->eo_type = EO_GROUP;
309 	obj->eo_catalog = tag;
310 	obj->eo_u.eo_u_group.eg_nobjs = 0;
311 	obj->eo_u.eo_u_group.eg_objs = NULL;
312 
313 	EXACCT_SET_ERR(EXR_OK);
314 	return (0);
315 }
316 
317 void
318 ea_free_object(ea_object_t *obj, int flag)
319 {
320 	ea_object_t *next = obj;
321 	ea_object_t *save;
322 
323 	while (next != NULL) {
324 		if (next->eo_type == EO_GROUP) {
325 			ea_free_object(next->eo_group.eg_objs, flag);
326 		} else if (next->eo_type == EO_ITEM) {
327 			switch (next->eo_catalog & EXT_TYPE_MASK) {
328 			case EXT_STRING:
329 				if (flag == EUP_ALLOC)
330 					ea_strfree(next->eo_item.ei_string);
331 				break;
332 			case EXT_RAW:
333 			case EXT_EXACCT_OBJECT:
334 				if (flag == EUP_ALLOC)
335 					ea_free(next->eo_item.ei_raw,
336 					    next->eo_item.ei_size);
337 				break;
338 			default:
339 				/* No action required for other types. */
340 				break;
341 			}
342 		}
343 		/* No action required for EO_NONE. */
344 
345 		save = next;
346 		next = next->eo_next;
347 #ifdef _KERNEL
348 		kmem_cache_free(exacct_object_cache, save);
349 #else
350 		ea_free(save, sizeof (ea_object_t));
351 #endif /* _KERNEL */
352 	}
353 }
354 
355 int
356 ea_free_item(ea_object_t *obj, int flag)
357 {
358 	if (obj->eo_type != EO_ITEM) {
359 		EXACCT_SET_ERR(EXR_INVALID_OBJ);
360 		return (-1);
361 	}
362 
363 	switch (obj->eo_catalog & EXT_TYPE_MASK) {
364 	case EXT_STRING:
365 		if (flag == EUP_ALLOC)
366 			ea_strfree(obj->eo_item.ei_string);
367 		break;
368 	case EXT_RAW:
369 	case EXT_EXACCT_OBJECT:
370 		if (flag == EUP_ALLOC)
371 			ea_free(obj->eo_item.ei_raw, obj->eo_item.ei_size);
372 		break;
373 	default:
374 		/* No action required for other types. */
375 		break;
376 	}
377 
378 	obj->eo_catalog = 0;
379 	obj->eo_type = EO_NONE;
380 	EXACCT_SET_ERR(EXR_OK);
381 	return (0);
382 }
383 
384 static void
385 ea_attach_object(ea_object_t **objp, ea_object_t *obj)
386 {
387 	ea_object_t *tp;
388 
389 	tp = *objp;
390 	*objp = obj;
391 	obj->eo_next = tp;
392 }
393 
394 int
395 ea_attach_to_object(ea_object_t *root, ea_object_t *obj)
396 {
397 	if (obj->eo_type == EO_GROUP || obj->eo_type == EO_ITEM) {
398 		ea_attach_object(&root->eo_next, obj);
399 		EXACCT_SET_ERR(EXR_OK);
400 		return (0);
401 	} else {
402 		EXACCT_SET_ERR(EXR_INVALID_OBJ);
403 		return (-1);
404 	}
405 }
406 
407 /*
408  * ea_attach_to_group() takes a group object and an additional exacct object and
409  * attaches the latter to the object list of the former.  The attached exacct
410  * object can be the head of a chain of objects.  If group isn't actually an
411  * object of type EO_GROUP, do nothing, such that we don't destroy its contents.
412  */
413 int
414 ea_attach_to_group(ea_object_t *group, ea_object_t *obj)
415 {
416 	uint_t n = 0;
417 	ea_object_t *next;
418 	ea_object_t **nextp;
419 
420 	if (group->eo_type != EO_GROUP) {
421 		EXACCT_SET_ERR(EXR_INVALID_OBJ);
422 		return (-1);
423 	}
424 
425 	for (next = obj; next != NULL; next = next->eo_next)
426 		n++;
427 
428 	group->eo_group.eg_nobjs += n;
429 
430 	for (nextp = &group->eo_group.eg_objs; *nextp != NULL;
431 	    nextp = &(*nextp)->eo_next)
432 		continue;
433 
434 	ea_attach_object(nextp, obj);
435 	EXACCT_SET_ERR(EXR_OK);
436 	return (0);
437 }
438 
439 /*
440  * ea_pack_object takes the given exacct object series beginning with obj and
441  * places it in buf.  Since ea_pack_object needs to be runnable in kernel
442  * context, we construct it to use its own stack of state.  Specifically, we
443  * store the locations of the sizes of open records (records whose construction
444  * is in progress).  curr_frame is used to indicate the current frame.  Just
445  * prior to decrementing curr_frame, we must ensure that the correct size for
446  * that frame is placed in the given offset.
447  */
448 struct es_frame {
449 	ea_object_t	*esf_obj;
450 	ea_size_t	esf_size;
451 	ea_size_t	esf_bksize;
452 	ea_size_t	esf_offset;
453 };
454 
455 static void
456 incr_parent_frames(struct es_frame *base, int n, size_t amt)
457 {
458 	int i;
459 
460 	for (i = 0; i <= n; i++) {
461 		base[i].esf_size += amt;
462 		base[i].esf_bksize += amt;
463 	}
464 }
465 
466 size_t
467 ea_pack_object(ea_object_t *obj, void *buf, size_t bufsize)
468 {
469 	struct es_frame *estack;
470 	uint_t neframes;
471 	ea_object_t *curr_obj = obj;
472 	int curr_frame = 0;
473 	size_t curr_pos = 0;
474 	ea_size_t placeholder = 0;
475 	int end_of_group = 0;
476 	uint32_t gp_backskip = sizeof (ea_catalog_t) + sizeof (ea_size_t) +
477 	    sizeof (uint32_t) + sizeof (uint32_t);
478 	uint32_t lge_backskip;
479 
480 	exacct_order32(&gp_backskip);
481 	estack = ea_alloc(sizeof (struct es_frame) * DEFAULT_ENTRIES);
482 	if (estack == NULL) {
483 		/* exacct_errno set above. */
484 		return ((size_t)-1);
485 	}
486 	bzero(estack, sizeof (struct es_frame) * DEFAULT_ENTRIES);
487 	neframes = DEFAULT_ENTRIES;
488 
489 	/*
490 	 * 1.  Start with the current object.
491 	 */
492 	for (;;) {
493 		void *src;
494 		size_t size;
495 
496 		/*
497 		 * 1a.  If at the bottom of the stack, we are done.
498 		 * If at the end of a group, place the correct size at the head
499 		 * of the chain, the correct backskip amount in the next
500 		 * position in the buffer, and retreat to the previous frame.
501 		 */
502 		if (end_of_group) {
503 			if (--curr_frame < 0) {
504 				break;
505 			}
506 
507 			exacct_order64(&estack[curr_frame].esf_size);
508 			ea_cond_memcpy_at_offset(buf,
509 			    estack[curr_frame].esf_offset, bufsize,
510 			    &estack[curr_frame].esf_size, sizeof (ea_size_t));
511 			exacct_order64(&estack[curr_frame].esf_size);
512 
513 			/*
514 			 * Note that the large backskip is only 32 bits, whereas
515 			 * an object can be up to 2^64 bytes long.  If an object
516 			 * is greater than 2^32 bytes long set the large
517 			 * backskip to 0.  This will  prevent the file being
518 			 * read backwards by causing EOF to be returned when the
519 			 * big object is encountered, but reading forwards will
520 			 * still be OK as it ignores the large backskip field.
521 			 */
522 			estack[curr_frame].esf_bksize += sizeof (uint32_t);
523 
524 			lge_backskip =
525 			    estack[curr_frame].esf_bksize > UINT_MAX
526 			    ? 0 : (uint32_t)estack[curr_frame].esf_bksize;
527 			exacct_order32(&lge_backskip);
528 			ea_cond_memcpy_at_offset(buf, curr_pos, bufsize,
529 			    &lge_backskip, sizeof (lge_backskip));
530 
531 			curr_pos += sizeof (uint32_t);
532 			incr_parent_frames(estack, curr_frame,
533 			    sizeof (uint32_t));
534 
535 			if ((curr_obj = estack[curr_frame].esf_obj) != NULL) {
536 				end_of_group = 0;
537 				estack[curr_frame].esf_obj = NULL;
538 				estack[curr_frame].esf_size = 0;
539 				estack[curr_frame].esf_bksize = 0;
540 			} else {
541 				continue;
542 			}
543 		}
544 
545 		/*
546 		 * 2.  Write the catalog tag.
547 		 */
548 		exacct_order32(&curr_obj->eo_catalog);
549 		ea_cond_memcpy_at_offset(buf, curr_pos, bufsize,
550 		    &curr_obj->eo_catalog, sizeof (ea_catalog_t));
551 		exacct_order32(&curr_obj->eo_catalog);
552 
553 		incr_parent_frames(estack, curr_frame, sizeof (ea_catalog_t));
554 		estack[curr_frame].esf_size -= sizeof (ea_catalog_t);
555 		curr_pos += sizeof (ea_catalog_t);
556 		estack[curr_frame].esf_offset = curr_pos;
557 
558 		/*
559 		 * 2a. If this type is of variable size, reserve space for the
560 		 * size field.
561 		 */
562 		switch (curr_obj->eo_catalog & EXT_TYPE_MASK) {
563 		case EXT_GROUP:
564 		case EXT_STRING:
565 		case EXT_EXACCT_OBJECT:
566 		case EXT_RAW:
567 			exacct_order64(&placeholder);
568 			ea_cond_memcpy_at_offset(buf, curr_pos, bufsize,
569 			    &placeholder, sizeof (ea_size_t));
570 			exacct_order64(&placeholder);
571 
572 			incr_parent_frames(estack, curr_frame,
573 			    sizeof (ea_size_t));
574 			estack[curr_frame].esf_size -= sizeof (ea_size_t);
575 			curr_pos += sizeof (ea_size_t);
576 			break;
577 		default:
578 			break;
579 		}
580 
581 		if (curr_obj->eo_type == EO_GROUP) {
582 			/*
583 			 * 3A.  If it's a group put its next pointer, size, and
584 			 * size position on the stack, add 1 to the stack,
585 			 * set the current object to eg_objs, and goto 1.
586 			 */
587 			estack[curr_frame].esf_obj = curr_obj->eo_next;
588 
589 			/*
590 			 * 3Aa. Insert the number of objects in the group.
591 			 */
592 			exacct_order32(&curr_obj->eo_group.eg_nobjs);
593 			ea_cond_memcpy_at_offset(buf, curr_pos, bufsize,
594 			    &curr_obj->eo_group.eg_nobjs,
595 			    sizeof (uint32_t));
596 			exacct_order32(&curr_obj->eo_group.eg_nobjs);
597 
598 			incr_parent_frames(estack, curr_frame,
599 			    sizeof (uint32_t));
600 			curr_pos += sizeof (uint32_t);
601 
602 			/*
603 			 * 3Ab. Insert a backskip of the appropriate size.
604 			 */
605 			ea_cond_memcpy_at_offset(buf, curr_pos, bufsize,
606 			    &gp_backskip, sizeof (uint32_t));
607 
608 			incr_parent_frames(estack, curr_frame,
609 			    sizeof (uint32_t));
610 			curr_pos += sizeof (uint32_t);
611 
612 			curr_frame++;
613 
614 			if (curr_frame >= neframes) {
615 				/*
616 				 * Expand the eframe stack to handle the
617 				 * requested depth.
618 				 */
619 				uint_t new_neframes = 2 * neframes;
620 				struct es_frame *new_estack =
621 				    ea_alloc(new_neframes *
622 				    sizeof (struct es_frame));
623 				if (new_estack == NULL) {
624 					ea_free(estack, neframes *
625 					    sizeof (struct es_frame));
626 					/* exacct_errno set above. */
627 					return ((size_t)-1);
628 				}
629 
630 				bzero(new_estack, new_neframes *
631 				    sizeof (struct es_frame));
632 				bcopy(estack, new_estack, neframes *
633 				    sizeof (struct es_frame));
634 
635 				ea_free(estack, neframes *
636 				    sizeof (struct es_frame));
637 				estack = new_estack;
638 				neframes = new_neframes;
639 			} else {
640 				bzero(&estack[curr_frame],
641 				    sizeof (struct es_frame));
642 			}
643 
644 			estack[curr_frame].esf_offset = curr_pos;
645 			if ((curr_obj = curr_obj->eo_group.eg_objs) == NULL) {
646 				end_of_group = 1;
647 			}
648 
649 			continue;
650 		}
651 
652 		/*
653 		 * 3B. Otherwise we're considering an item: add its ei_size to
654 		 * all sizes on the stack, and copy its size into position.
655 		 */
656 		switch (curr_obj->eo_catalog & EXT_TYPE_MASK) {
657 		case EXT_UINT8:
658 			src = &curr_obj->eo_item.ei_uint8;
659 			size = sizeof (uint8_t);
660 			break;
661 		case EXT_UINT16:
662 			src = &curr_obj->eo_item.ei_uint16;
663 			size = sizeof (uint16_t);
664 			exacct_order16(src);
665 			break;
666 		case EXT_UINT32:
667 			src = &curr_obj->eo_item.ei_uint32;
668 			size = sizeof (uint32_t);
669 			exacct_order32(src);
670 			break;
671 		case EXT_UINT64:
672 			src = &curr_obj->eo_item.ei_uint64;
673 			size = sizeof (uint64_t);
674 			exacct_order64(src);
675 			break;
676 		case EXT_DOUBLE:
677 			src = &curr_obj->eo_item.ei_double;
678 			size = sizeof (double);
679 			exacct_order64((uint64_t *)src);
680 			break;
681 		case EXT_STRING:
682 			src = curr_obj->eo_item.ei_string;
683 			size = curr_obj->eo_item.ei_size;
684 			break;
685 		case EXT_EXACCT_OBJECT:
686 			src = curr_obj->eo_item.ei_object;
687 			size = curr_obj->eo_item.ei_size;
688 			break;
689 		case EXT_RAW:
690 			src = curr_obj->eo_item.ei_raw;
691 			size = curr_obj->eo_item.ei_size;
692 			break;
693 		case EXT_NONE:
694 		default:
695 			src = NULL;
696 			size = 0;
697 			break;
698 		}
699 
700 		ea_cond_memcpy_at_offset(buf, curr_pos, bufsize, src, size);
701 		incr_parent_frames(estack, curr_frame, size);
702 		curr_pos += size;
703 
704 		/*
705 		 * 4. Write the large backskip amount into the buffer.
706 		 * See above for note about why this may be set to 0.
707 		 */
708 		incr_parent_frames(estack, curr_frame, sizeof (uint32_t));
709 
710 		lge_backskip = estack[curr_frame].esf_bksize > UINT_MAX
711 		    ? 0 : (uint32_t)estack[curr_frame].esf_bksize;
712 		exacct_order32(&lge_backskip);
713 		ea_cond_memcpy_at_offset(buf, curr_pos, bufsize,
714 		    &lge_backskip, sizeof (lge_backskip));
715 
716 		curr_pos += sizeof (uint32_t);
717 
718 		switch (curr_obj->eo_catalog & EXT_TYPE_MASK) {
719 		case EXT_RAW:
720 		case EXT_STRING:
721 		case EXT_EXACCT_OBJECT:
722 			exacct_order64(&estack[curr_frame].esf_size);
723 			ea_cond_memcpy_at_offset(buf,
724 			    estack[curr_frame].esf_offset, bufsize,
725 			    &estack[curr_frame].esf_size, sizeof (ea_size_t));
726 			exacct_order64(&estack[curr_frame].esf_size);
727 			break;
728 		case EXT_UINT16:
729 			exacct_order16(src);
730 			break;
731 		case EXT_UINT32:
732 			exacct_order32(src);
733 			break;
734 		case EXT_UINT64:
735 			exacct_order64(src);
736 			break;
737 		case EXT_DOUBLE:
738 			exacct_order64((uint64_t *)src);
739 			break;
740 		default:
741 			break;
742 		}
743 
744 		/*
745 		 * 5.  If ei_next is NULL, we are at the end of a group.a  If
746 		 * not, move on to the next item on the list.
747 		 */
748 		if (curr_obj->eo_next == NULL) {
749 			end_of_group = 1;
750 		} else {
751 			curr_obj = curr_obj->eo_next;
752 			estack[curr_frame].esf_obj = NULL;
753 			estack[curr_frame].esf_size = 0;
754 			estack[curr_frame].esf_bksize = 0;
755 		}
756 	}
757 
758 	ea_free(estack, neframes * sizeof (struct es_frame));
759 	EXACCT_SET_ERR(EXR_OK);
760 	return (curr_pos);
761 }
762