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