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