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 #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 EXACCT_SET_ERR(EXR_CORRUPT_FILE); 598 return (EO_ERROR); 599 } 600 } 601 obj->eo_item.ei_string = buf; 602 /* 603 * Maintain our consistent convention that string lengths 604 * include the terminating NULL character. 605 */ 606 obj->eo_item.ei_size = sz; 607 break; 608 case EXT_UINT8: 609 if (xread(f, &obj->eo_item.ei_uint8, sizeof (uint8_t)) != 610 sizeof (uint8_t)) { 611 EXACCT_SET_ERR(EXR_CORRUPT_FILE); 612 return (EO_ERROR); 613 } 614 obj->eo_item.ei_size = sizeof (uint8_t); 615 break; 616 case EXT_UINT16: 617 if (xread(f, &obj->eo_item.ei_uint16, sizeof (uint16_t)) != 618 sizeof (uint16_t)) { 619 EXACCT_SET_ERR(EXR_CORRUPT_FILE); 620 return (EO_ERROR); 621 } 622 exacct_order16(&obj->eo_item.ei_uint16); 623 obj->eo_item.ei_size = sizeof (uint16_t); 624 break; 625 case EXT_UINT32: 626 if (xread(f, &obj->eo_item.ei_uint32, sizeof (uint32_t)) != 627 sizeof (uint32_t)) { 628 EXACCT_SET_ERR(EXR_CORRUPT_FILE); 629 return (EO_ERROR); 630 } 631 exacct_order32(&obj->eo_item.ei_uint32); 632 obj->eo_item.ei_size = sizeof (uint32_t); 633 break; 634 case EXT_UINT64: 635 if (xread(f, &obj->eo_item.ei_uint64, sizeof (uint64_t)) != 636 sizeof (uint64_t)) { 637 EXACCT_SET_ERR(EXR_CORRUPT_FILE); 638 return (EO_ERROR); 639 } 640 exacct_order64(&obj->eo_item.ei_uint64); 641 obj->eo_item.ei_size = sizeof (uint64_t); 642 break; 643 case EXT_DOUBLE: 644 if (xread(f, &obj->eo_item.ei_double, sizeof (double)) != 645 sizeof (double)) { 646 EXACCT_SET_ERR(EXR_CORRUPT_FILE); 647 return (EO_ERROR); 648 } 649 exacct_order64((uint64_t *)&obj->eo_item.ei_double); 650 obj->eo_item.ei_size = sizeof (double); 651 break; 652 default: 653 /* 654 * We've encountered an unknown type value. Flag the error and 655 * exit. 656 */ 657 EXACCT_SET_ERR(EXR_CORRUPT_FILE); 658 return (EO_ERROR); 659 } 660 661 /* 662 * Advance over current large backskip value, 663 * and position at the start of the next object. 664 */ 665 if (xread(f, &scratch32, sizeof (scratch32)) != sizeof (scratch32)) { 666 EXACCT_SET_ERR(EXR_CORRUPT_FILE); 667 return (EO_ERROR); 668 } 669 if (stack_next_object(f, xread) == -1) { 670 /* exacct_error set above. */ 671 return (EO_ERROR); 672 } 673 674 f->ef_advance = 0; 675 EXACCT_SET_ERR(EXR_OK); 676 return (obj->eo_type); 677 } 678 679 ea_object_type_t 680 ea_get_object(ea_file_t *ef, ea_object_t *obj) 681 { 682 obj->eo_next = NULL; 683 return (xget_object((ea_file_impl_t *)ef, obj, fread_wrapper, 684 fseek_wrapper, fpos_wrapper, EUP_ALLOC)); 685 } 686 687 /* 688 * unpack_group() recursively unpacks record groups from the buffer tucked 689 * within the passed ea_file, and attaches them to grp. 690 */ 691 static int 692 unpack_group(ea_file_impl_t *f, ea_object_t *grp, int flag) 693 { 694 ea_object_t *obj; 695 uint_t nobjs = grp->eo_group.eg_nobjs; 696 int i; 697 698 /* 699 * Set the group's object count to zero, as we will rebuild it via the 700 * individual object attachments. 701 */ 702 grp->eo_group.eg_nobjs = 0; 703 grp->eo_group.eg_objs = NULL; 704 705 for (i = 0; i < nobjs; i++) { 706 if ((obj = ea_alloc(sizeof (ea_object_t))) == NULL) { 707 /* exacct_errno set above. */ 708 return (-1); 709 } 710 obj->eo_next = NULL; 711 if (xget_object(f, obj, bufread_wrapper, bufseek_wrapper, 712 bufpos_wrapper, flag) == -1) { 713 ea_free(obj, sizeof (ea_object_t)); 714 /* exacct_errno set above. */ 715 return (-1); 716 } 717 718 (void) ea_attach_to_group(grp, obj); 719 720 if (obj->eo_type == EO_GROUP && 721 unpack_group(f, obj, flag) == -1) { 722 /* exacct_errno set above. */ 723 return (-1); 724 } 725 } 726 727 if (nobjs != grp->eo_group.eg_nobjs) { 728 EXACCT_SET_ERR(EXR_CORRUPT_FILE); 729 return (-1); 730 } 731 EXACCT_SET_ERR(EXR_OK); 732 return (0); 733 } 734 735 /* 736 * ea_unpack_object() can be considered as a finite series of get operations on 737 * a given buffer, that rebuilds the hierarchy of objects compacted by a pack 738 * operation. Because there is complex state associated with the group depth, 739 * ea_unpack_object() must complete as one operation on a given buffer. 740 */ 741 ea_object_type_t 742 ea_unpack_object(ea_object_t **objp, int flag, void *buf, size_t bufsize) 743 { 744 ea_file_impl_t fake; 745 ea_object_t *obj; 746 ea_object_type_t first_obj_type; 747 748 *objp = NULL; 749 if (buf == NULL) { 750 EXACCT_SET_ERR(EXR_INVALID_BUF); 751 return (EO_ERROR); 752 } 753 754 /* Set up the structures needed for unpacking */ 755 bzero(&fake, sizeof (ea_file_impl_t)); 756 if (stack_check(&fake) == -1) { 757 /* exacct_errno set above. */ 758 return (EO_ERROR); 759 } 760 fake.ef_buf = buf; 761 fake.ef_bufsize = bufsize; 762 763 /* Unpack the first object in the buffer - this should succeed. */ 764 if ((obj = ea_alloc(sizeof (ea_object_t))) == NULL) { 765 stack_free(&fake); 766 /* exacct_errno set above. */ 767 return (EO_ERROR); 768 } 769 obj->eo_next = NULL; 770 if ((first_obj_type = xget_object(&fake, obj, bufread_wrapper, 771 bufseek_wrapper, bufpos_wrapper, flag)) == -1) { 772 stack_free(&fake); 773 ea_free(obj, sizeof (ea_object_t)); 774 /* exacct_errno set above. */ 775 return (EO_ERROR); 776 } 777 778 if (obj->eo_type == EO_GROUP && unpack_group(&fake, obj, flag) == -1) { 779 stack_free(&fake); 780 ea_free_object(obj, flag); 781 /* exacct_errno set above. */ 782 return (EO_ERROR); 783 } 784 *objp = obj; 785 786 /* 787 * There may be other objects in the buffer - if so, chain them onto 788 * the end of the list. We have reached the end of the list when 789 * xget_object() returns -1 with exacct_error set to EXR_EOF. 790 */ 791 for (;;) { 792 if ((obj = ea_alloc(sizeof (ea_object_t))) == NULL) { 793 stack_free(&fake); 794 ea_free_object(*objp, flag); 795 *objp = NULL; 796 /* exacct_errno set above. */ 797 return (EO_ERROR); 798 } 799 obj->eo_next = NULL; 800 if (xget_object(&fake, obj, bufread_wrapper, bufseek_wrapper, 801 bufpos_wrapper, flag) == -1) { 802 stack_free(&fake); 803 ea_free(obj, sizeof (ea_object_t)); 804 if (ea_error() == EXR_EOF) { 805 EXACCT_SET_ERR(EXR_OK); 806 return (first_obj_type); 807 } else { 808 ea_free_object(*objp, flag); 809 *objp = NULL; 810 /* exacct_error set above. */ 811 return (EO_ERROR); 812 } 813 } 814 815 (void) ea_attach_to_object(*objp, obj); 816 817 if (obj->eo_type == EO_GROUP && 818 unpack_group(&fake, obj, flag) == -1) { 819 stack_free(&fake); 820 ea_free(obj, sizeof (ea_object_t)); 821 ea_free_object(*objp, flag); 822 *objp = NULL; 823 /* exacct_errno set above. */ 824 return (EO_ERROR); 825 } 826 } 827 } 828 829 int 830 ea_write_object(ea_file_t *ef, ea_object_t *obj) 831 { 832 ea_size_t sz; 833 void *buf; 834 ea_file_impl_t *f = (ea_file_impl_t *)ef; 835 836 /* 837 * If we weren't opened for writing, this call fails. 838 */ 839 if ((f->ef_oflags & O_RDWR) == 0 && 840 (f->ef_oflags & O_WRONLY) == 0) { 841 EXACCT_SET_ERR(EXR_NOTSUPP); 842 return (-1); 843 } 844 845 /* Pack with a null buffer to get the size. */ 846 sz = ea_pack_object(obj, NULL, 0); 847 if (sz == -1 || (buf = ea_alloc(sz)) == NULL) { 848 /* exacct_error set above. */ 849 return (-1); 850 } 851 if (ea_pack_object(obj, buf, sz) == (size_t)-1) { 852 ea_free(buf, sz); 853 /* exacct_error set above. */ 854 return (-1); 855 } 856 if (fwrite(buf, sizeof (char), sz, f->ef_fp) != sz) { 857 ea_free(buf, sz); 858 EXACCT_SET_ERR(EXR_SYSCALL_FAIL); 859 return (-1); 860 } 861 ea_free(buf, sz); 862 EXACCT_SET_ERR(EXR_OK); 863 return (0); 864 } 865 866 /* 867 * validate_header() must be kept in sync with write_header(), given below, and 868 * exacct_create_header(), in uts/common/os/exacct.c. 869 */ 870 static int 871 validate_header(ea_file_t *ef, const char *creator) 872 { 873 ea_object_t hdr_grp; 874 ea_object_t scratch_obj; 875 int error = EXR_OK; 876 int saw_creator = 0; 877 int saw_version = 0; 878 int saw_type = 0; 879 int saw_hostname = 0; 880 int n; 881 ea_file_impl_t *f = (ea_file_impl_t *)ef; 882 883 bzero(&hdr_grp, sizeof (ea_object_t)); 884 885 if (ea_get_object(ef, &hdr_grp) != EO_GROUP) { 886 error = ea_error(); 887 goto error_case; 888 } 889 890 if (hdr_grp.eo_catalog != 891 (EXT_GROUP | EXC_DEFAULT | EXD_GROUP_HEADER)) { 892 error = EXR_CORRUPT_FILE; 893 goto error_case; 894 } 895 896 for (n = 0; n < hdr_grp.eo_group.eg_nobjs; n++) { 897 bzero(&scratch_obj, sizeof (ea_object_t)); 898 if (ea_get_object(ef, &scratch_obj) == -1) { 899 error = ea_error(); 900 goto error_case; 901 } 902 903 switch (scratch_obj.eo_catalog) { 904 case EXT_UINT32 | EXC_DEFAULT | EXD_VERSION: 905 if (scratch_obj.eo_item.ei_uint32 != EXACCT_VERSION) { 906 error = EXR_UNKN_VERSION; 907 goto error_case; 908 } 909 saw_version++; 910 break; 911 case EXT_STRING | EXC_DEFAULT | EXD_FILETYPE: 912 if (strcmp(scratch_obj.eo_item.ei_string, 913 EXACCT_HDR_STR) != 0) { 914 error = EXR_CORRUPT_FILE; 915 goto error_case; 916 } 917 saw_type++; 918 break; 919 case EXT_STRING | EXC_DEFAULT | EXD_CREATOR: 920 f->ef_creator = 921 ea_strdup(scratch_obj.eo_item.ei_string); 922 if (f->ef_creator == NULL) { 923 error = ea_error(); 924 goto error_case; 925 } 926 saw_creator++; 927 break; 928 /* The hostname is an optional field. */ 929 case EXT_STRING | EXC_DEFAULT | EXD_HOSTNAME: 930 f->ef_hostname = 931 ea_strdup(scratch_obj.eo_item.ei_string); 932 if (f->ef_hostname == NULL) { 933 error = ea_error(); 934 goto error_case; 935 } 936 saw_hostname++; 937 break; 938 default: 939 /* ignore unrecognized header members */ 940 break; 941 } 942 (void) ea_free_item(&scratch_obj, EUP_ALLOC); 943 } 944 945 if (saw_version && saw_type && saw_creator) { 946 if (creator && strcmp(f->ef_creator, creator) != 0) { 947 error = EXR_NO_CREATOR; 948 goto error_case; 949 } 950 EXACCT_SET_ERR(EXR_OK); 951 return (0); 952 } 953 954 error_case: 955 (void) ea_free_item(&scratch_obj, EUP_ALLOC); 956 if (saw_hostname) 957 ea_strfree(f->ef_hostname); 958 if (saw_creator) 959 ea_strfree(f->ef_creator); 960 EXACCT_SET_ERR(error); 961 return (-1); 962 } 963 964 static int 965 write_header(ea_file_t *ef) 966 { 967 ea_object_t hdr_grp; 968 ea_object_t vers_obj; 969 ea_object_t creator_obj; 970 ea_object_t filetype_obj; 971 ea_object_t hostname_obj; 972 uint32_t bskip; 973 const uint32_t version = EXACCT_VERSION; 974 ea_file_impl_t *f = (ea_file_impl_t *)ef; 975 void *buf; 976 size_t bufsize; 977 char hostbuf[SYSINFO_BUFSIZE]; 978 int error = EXR_OK; 979 980 bzero(&hdr_grp, sizeof (ea_object_t)); 981 bzero(&vers_obj, sizeof (ea_object_t)); 982 bzero(&creator_obj, sizeof (ea_object_t)); 983 bzero(&filetype_obj, sizeof (ea_object_t)); 984 bzero(&hostname_obj, sizeof (ea_object_t)); 985 bzero(hostbuf, SYSINFO_BUFSIZE); 986 987 (void) sysinfo(SI_HOSTNAME, hostbuf, SYSINFO_BUFSIZE); 988 989 if (ea_set_item(&vers_obj, EXT_UINT32 | EXC_DEFAULT | EXD_VERSION, 990 (void *)&version, 0) == -1 || 991 ea_set_item(&creator_obj, EXT_STRING | EXC_DEFAULT | EXD_CREATOR, 992 f->ef_creator, strlen(f->ef_creator)) == -1 || 993 ea_set_item(&filetype_obj, EXT_STRING | EXC_DEFAULT | EXD_FILETYPE, 994 EXACCT_HDR_STR, strlen(EXACCT_HDR_STR)) == -1 || 995 ea_set_item(&hostname_obj, EXT_STRING | EXC_DEFAULT | EXD_HOSTNAME, 996 hostbuf, strlen(hostbuf)) == -1) { 997 error = ea_error(); 998 goto cleanup1; 999 } 1000 1001 (void) ea_set_group(&hdr_grp, 1002 EXT_GROUP | EXC_DEFAULT | EXD_GROUP_HEADER); 1003 (void) ea_attach_to_group(&hdr_grp, &vers_obj); 1004 (void) ea_attach_to_group(&hdr_grp, &creator_obj); 1005 (void) ea_attach_to_group(&hdr_grp, &filetype_obj); 1006 (void) ea_attach_to_group(&hdr_grp, &hostname_obj); 1007 1008 /* Get the required size by passing a null buffer. */ 1009 bufsize = ea_pack_object(&hdr_grp, NULL, 0); 1010 if ((buf = ea_alloc(bufsize)) == NULL) { 1011 error = ea_error(); 1012 goto cleanup1; 1013 } 1014 1015 if (ea_pack_object(&hdr_grp, buf, bufsize) == (size_t)-1) { 1016 error = ea_error(); 1017 goto cleanup2; 1018 } 1019 1020 /* 1021 * To prevent reading the header when reading the file backwards, 1022 * set the large backskip of the header group to 0 (last 4 bytes). 1023 */ 1024 bskip = 0; 1025 exacct_order32(&bskip); 1026 bcopy(&bskip, (char *)buf + bufsize - sizeof (bskip), 1027 sizeof (bskip)); 1028 1029 if (fwrite(buf, sizeof (char), bufsize, f->ef_fp) != bufsize || 1030 fflush(f->ef_fp) == EOF) { 1031 error = EXR_SYSCALL_FAIL; 1032 goto cleanup2; 1033 } 1034 1035 cleanup2: 1036 ea_free(buf, bufsize); 1037 cleanup1: 1038 (void) ea_free_item(&vers_obj, EUP_ALLOC); 1039 (void) ea_free_item(&creator_obj, EUP_ALLOC); 1040 (void) ea_free_item(&filetype_obj, EUP_ALLOC); 1041 (void) ea_free_item(&hostname_obj, EUP_ALLOC); 1042 EXACCT_SET_ERR(error); 1043 return (error == EXR_OK ? 0 : -1); 1044 } 1045 1046 const char * 1047 ea_get_creator(ea_file_t *ef) 1048 { 1049 return ((const char *)((ea_file_impl_t *)ef)->ef_creator); 1050 } 1051 1052 const char * 1053 ea_get_hostname(ea_file_t *ef) 1054 { 1055 return ((const char *)((ea_file_impl_t *)ef)->ef_hostname); 1056 } 1057 1058 int 1059 ea_fdopen(ea_file_t *ef, int fd, const char *creator, int aflags, int oflags) 1060 { 1061 ea_file_impl_t *f = (ea_file_impl_t *)ef; 1062 1063 bzero(f, sizeof (*f)); 1064 f->ef_oflags = oflags; 1065 f->ef_fd = fd; 1066 1067 /* Initialize depth stack. */ 1068 if (stack_check(f) == -1) { 1069 /* exacct_error set above. */ 1070 goto error1; 1071 } 1072 1073 /* 1074 * 1. If we are O_CREAT, then we will need to write a header 1075 * after opening name. 1076 */ 1077 if (oflags & O_CREAT) { 1078 if (creator == NULL) { 1079 EXACCT_SET_ERR(EXR_NO_CREATOR); 1080 goto error2; 1081 } 1082 if ((f->ef_creator = ea_strdup(creator)) == NULL) { 1083 /* exacct_error set above. */ 1084 goto error2; 1085 } 1086 if ((f->ef_fp = fdopen(f->ef_fd, "w")) == NULL) { 1087 EXACCT_SET_ERR(EXR_SYSCALL_FAIL); 1088 goto error3; 1089 } 1090 if (write_header(ef) == -1) { 1091 /* exacct_error set above. */ 1092 goto error3; 1093 } 1094 1095 /* 1096 * 2. If we are not O_CREAT, but are RDWR or WRONLY, we need to 1097 * seek to EOF so that appends will succeed. 1098 */ 1099 } else if (oflags & O_RDWR || oflags & O_WRONLY) { 1100 if ((f->ef_fp = fdopen(f->ef_fd, "r+")) == NULL) { 1101 EXACCT_SET_ERR(EXR_SYSCALL_FAIL); 1102 goto error2; 1103 } 1104 1105 if ((aflags & EO_VALIDATE_MSK) == EO_VALID_HDR) { 1106 if (validate_header(ef, creator) < 0) { 1107 /* exacct_error set above. */ 1108 goto error2; 1109 } 1110 } 1111 1112 if (fseeko(f->ef_fp, 0, SEEK_END) == -1) { 1113 EXACCT_SET_ERR(EXR_SYSCALL_FAIL); 1114 goto error2; 1115 } 1116 1117 /* 1118 * 3. This is an undefined manner for opening an exacct file. 1119 */ 1120 } else if (oflags != O_RDONLY) { 1121 EXACCT_SET_ERR(EXR_NOTSUPP); 1122 goto error2; 1123 1124 /* 1125 * 4a. If we are RDONLY, then we are in a position such that 1126 * either a ea_get_object or an ea_next_object will succeed. If 1127 * aflags was set to EO_TAIL, seek to the end of the file. 1128 */ 1129 } else { 1130 if ((f->ef_fp = fdopen(f->ef_fd, "r")) == NULL) { 1131 EXACCT_SET_ERR(EXR_SYSCALL_FAIL); 1132 goto error2; 1133 } 1134 1135 if ((aflags & EO_VALIDATE_MSK) == EO_VALID_HDR) { 1136 if (validate_header(ef, creator) == -1) { 1137 /* exacct_error set above. */ 1138 goto error2; 1139 } 1140 } 1141 1142 /* 1143 * 4b. Handle the "open at end" option, for consumers who want 1144 * to go backwards through the file (i.e. lastcomm). 1145 */ 1146 if ((aflags & EO_POSN_MSK) == EO_TAIL) { 1147 if (fseeko(f->ef_fp, 0, SEEK_END) < 0) { 1148 EXACCT_SET_ERR(EXR_SYSCALL_FAIL); 1149 goto error2; 1150 } 1151 } 1152 } 1153 1154 EXACCT_SET_ERR(EXR_OK); 1155 return (0); 1156 1157 /* Error cleanup code */ 1158 error3: 1159 ea_strfree(f->ef_creator); 1160 error2: 1161 stack_free(f); 1162 error1: 1163 bzero(f, sizeof (*f)); 1164 return (-1); 1165 } 1166 1167 int 1168 ea_open(ea_file_t *ef, const char *name, const char *creator, 1169 int aflags, int oflags, mode_t mode) 1170 { 1171 int fd; 1172 1173 /* 1174 * If overwriting an existing file, make sure to truncate it 1175 * to prevent the file being created corrupt. 1176 */ 1177 if (oflags & O_CREAT) 1178 oflags |= O_TRUNC; 1179 1180 if ((fd = open(name, oflags, mode)) == -1) { 1181 EXACCT_SET_ERR(EXR_SYSCALL_FAIL); 1182 return (-1); 1183 } 1184 1185 if (ea_fdopen(ef, fd, creator, aflags, oflags) == -1) { 1186 (void) close(fd); 1187 return (-1); 1188 } 1189 1190 return (0); 1191 } 1192 1193 /* 1194 * ea_close() performs all appropriate close operations on the open exacct file, 1195 * including releasing any memory allocated while parsing the file. 1196 */ 1197 int 1198 ea_close(ea_file_t *ef) 1199 { 1200 ea_file_impl_t *f = (ea_file_impl_t *)ef; 1201 1202 if (f->ef_creator != NULL) 1203 ea_strfree(f->ef_creator); 1204 if (f->ef_hostname != NULL) 1205 ea_strfree(f->ef_hostname); 1206 1207 ea_free(f->ef_depth, f->ef_mxdeep * sizeof (ea_file_depth_t)); 1208 1209 if (fclose(f->ef_fp)) { 1210 EXACCT_SET_ERR(EXR_SYSCALL_FAIL); 1211 return (-1); 1212 } 1213 1214 EXACCT_SET_ERR(EXR_OK); 1215 return (0); 1216 } 1217 1218 /* 1219 * Empty the input buffer and clear any underlying EOF or error bits set on the 1220 * underlying FILE. This can be used by any library clients who wish to handle 1221 * files that are in motion or who wish to seek the underlying file descriptor. 1222 */ 1223 void 1224 ea_clear(ea_file_t *ef) 1225 { 1226 ea_file_impl_t *f = (ea_file_impl_t *)ef; 1227 1228 (void) fflush(f->ef_fp); 1229 clearerr(f->ef_fp); 1230 } 1231 1232 /* 1233 * Copy an ea_object_t. Note that in the case of a group, just the group 1234 * object will be copied, and not its list of members. To recursively copy 1235 * a group or a list of items use ea_copy_tree(). 1236 */ 1237 ea_object_t * 1238 ea_copy_object(const ea_object_t *src) 1239 { 1240 ea_object_t *dst; 1241 1242 /* Allocate a new object and copy to it. */ 1243 if ((dst = ea_alloc(sizeof (ea_object_t))) == NULL) { 1244 return (NULL); 1245 } 1246 bcopy(src, dst, sizeof (ea_object_t)); 1247 dst->eo_next = NULL; 1248 1249 switch (src->eo_type) { 1250 case EO_GROUP: 1251 dst->eo_group.eg_nobjs = 0; 1252 dst->eo_group.eg_objs = NULL; 1253 break; 1254 case EO_ITEM: 1255 /* Items containing pointers need special treatment. */ 1256 switch (src->eo_catalog & EXT_TYPE_MASK) { 1257 case EXT_STRING: 1258 if (src->eo_item.ei_string != NULL) { 1259 dst->eo_item.ei_string = 1260 ea_strdup(src->eo_item.ei_string); 1261 if (dst->eo_item.ei_string == NULL) { 1262 ea_free_object(dst, EUP_ALLOC); 1263 return (NULL); 1264 } 1265 } 1266 break; 1267 case EXT_RAW: 1268 if (src->eo_item.ei_raw != NULL) { 1269 dst->eo_item.ei_raw = 1270 ea_alloc(src->eo_item.ei_size); 1271 if (dst->eo_item.ei_raw == NULL) { 1272 ea_free_object(dst, EUP_ALLOC); 1273 return (NULL); 1274 } 1275 bcopy(src->eo_item.ei_raw, dst->eo_item.ei_raw, 1276 (size_t)src->eo_item.ei_size); 1277 } 1278 break; 1279 case EXT_EXACCT_OBJECT: 1280 if (src->eo_item.ei_object != NULL) { 1281 dst->eo_item.ei_object = 1282 ea_alloc(src->eo_item.ei_size); 1283 if (dst->eo_item.ei_object == NULL) { 1284 ea_free_object(dst, EUP_ALLOC); 1285 return (NULL); 1286 } 1287 bcopy(src->eo_item.ei_raw, dst->eo_item.ei_raw, 1288 (size_t)src->eo_item.ei_size); 1289 } 1290 break; 1291 default: 1292 /* Other item types require no special handling. */ 1293 break; 1294 } 1295 break; 1296 default: 1297 ea_free_object(dst, EUP_ALLOC); 1298 EXACCT_SET_ERR(EXR_INVALID_OBJ); 1299 return (NULL); 1300 } 1301 EXACCT_SET_ERR(EXR_OK); 1302 return (dst); 1303 } 1304 1305 /* 1306 * Recursively copy a list of ea_object_t. All the elements in the eo_next 1307 * list will be copied, and any group objects will be recursively copied. 1308 */ 1309 ea_object_t * 1310 ea_copy_object_tree(const ea_object_t *src) 1311 { 1312 ea_object_t *ret_obj, *dst, *last; 1313 1314 for (ret_obj = last = NULL; src != NULL; 1315 last = dst, src = src->eo_next) { 1316 1317 /* Allocate a new object and copy to it. */ 1318 if ((dst = ea_copy_object(src)) == NULL) { 1319 ea_free_object(ret_obj, EUP_ALLOC); 1320 return (NULL); 1321 } 1322 1323 /* Groups need the object list copying. */ 1324 if (src->eo_type == EO_GROUP) { 1325 dst->eo_group.eg_objs = 1326 ea_copy_object_tree(src->eo_group.eg_objs); 1327 if (dst->eo_group.eg_objs == NULL) { 1328 ea_free_object(ret_obj, EUP_ALLOC); 1329 return (NULL); 1330 } 1331 dst->eo_group.eg_nobjs = src->eo_group.eg_nobjs; 1332 } 1333 1334 /* Remember the list head the first time round. */ 1335 if (ret_obj == NULL) { 1336 ret_obj = dst; 1337 } 1338 1339 /* Link together if not at the list head. */ 1340 if (last != NULL) { 1341 last->eo_next = dst; 1342 } 1343 } 1344 EXACCT_SET_ERR(EXR_OK); 1345 return (ret_obj); 1346 } 1347 1348 /* 1349 * Read in the specified number of objects, returning the same data 1350 * structure that would have originally been passed to ea_write(). 1351 */ 1352 ea_object_t * 1353 ea_get_object_tree(ea_file_t *ef, uint32_t nobj) 1354 { 1355 ea_object_t *first_obj, *prev_obj, *obj; 1356 1357 first_obj = prev_obj = NULL; 1358 while (nobj--) { 1359 /* Allocate space for the new object. */ 1360 obj = ea_alloc(sizeof (ea_object_t)); 1361 bzero(obj, sizeof (*obj)); 1362 1363 /* Read it in. */ 1364 if (ea_get_object(ef, obj) == -1) { 1365 ea_free(obj, sizeof (ea_object_t)); 1366 if (first_obj != NULL) { 1367 ea_free_object(first_obj, EUP_ALLOC); 1368 } 1369 return (NULL); 1370 } 1371 1372 /* Link it into the list. */ 1373 if (first_obj == NULL) { 1374 first_obj = obj; 1375 } 1376 if (prev_obj != NULL) { 1377 prev_obj->eo_next = obj; 1378 } 1379 prev_obj = obj; 1380 1381 /* Recurse if the object is a group with contents. */ 1382 if (obj->eo_type == EO_GROUP && obj->eo_group.eg_nobjs > 0) { 1383 if ((obj->eo_group.eg_objs = ea_get_object_tree(ef, 1384 obj->eo_group.eg_nobjs)) == NULL) { 1385 /* exacct_error set above. */ 1386 ea_free_object(first_obj, EUP_ALLOC); 1387 return (NULL); 1388 } 1389 } 1390 } 1391 EXACCT_SET_ERR(EXR_OK); 1392 return (first_obj); 1393 } 1394