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