/* * If the stack is NULL, create and initialise it. * If is is not NULL, check it still has space - if not, double its size. */ static int stack_check(ea_file_impl_t *f) { if (f->ef_depth == NULL) { if ((f->ef_depth = ea_alloc(DEFAULT_ENTRIES * sizeof (ea_file_depth_t))) == NULL) { /* exacct_errno set above. */ return (-1); } bzero(f->ef_depth, DEFAULT_ENTRIES * sizeof (ea_file_depth_t)); f->ef_mxdeep = DEFAULT_ENTRIES; f->ef_ndeep = -1; } else if (f->ef_ndeep + 1 >= f->ef_mxdeep) { ea_file_depth_t *newstack; if ((newstack = ea_alloc(f->ef_mxdeep * 2 * sizeof (ea_file_depth_t))) == NULL) { /* exacct_errno set above. */ return (-1); } bcopy(f->ef_depth, newstack, f->ef_mxdeep * sizeof (ea_file_depth_t)); bzero(newstack + f->ef_mxdeep, f->ef_mxdeep * sizeof (ea_file_depth_t)); ea_free(f->ef_depth, f->ef_mxdeep * sizeof (ea_file_depth_t)); f->ef_mxdeep *= 2; f->ef_depth = newstack; } return (0); }
/* * Free a stack. */ static void stack_free(ea_file_impl_t *f) { if (f->ef_depth != NULL) { ea_free(f->ef_depth, f->ef_mxdeep * sizeof (ea_file_depth_t)); f->ef_depth = NULL; } f->ef_mxdeep = 0; f->ef_ndeep = -1; }
int ea_write_object(ea_file_t *ef, ea_object_t *obj) { ea_size_t sz; void *buf; ea_file_impl_t *f = (ea_file_impl_t *)ef; /* * If we weren't opened for writing, this call fails. */ if ((f->ef_oflags & O_RDWR) == 0 && (f->ef_oflags & O_WRONLY) == 0) { EXACCT_SET_ERR(EXR_NOTSUPP); return (-1); } /* Pack with a null buffer to get the size. */ sz = ea_pack_object(obj, NULL, 0); if (sz == -1 || (buf = ea_alloc(sz)) == NULL) { /* exacct_error set above. */ return (-1); } if (ea_pack_object(obj, buf, sz) == (size_t)-1) { ea_free(buf, sz); /* exacct_error set above. */ return (-1); } if (fwrite(buf, sizeof (char), sz, f->ef_fp) != sz) { ea_free(buf, sz); EXACCT_SET_ERR(EXR_SYSCALL_FAIL); return (-1); } ea_free(buf, sz); EXACCT_SET_ERR(EXR_OK); return (0); }
/* * unpack_group() recursively unpacks record groups from the buffer tucked * within the passed ea_file, and attaches them to grp. */ static int unpack_group(ea_file_impl_t *f, ea_object_t *grp, int flag) { ea_object_t *obj; uint_t nobjs = grp->eo_group.eg_nobjs; int i; /* * Set the group's object count to zero, as we will rebuild it via the * individual object attachments. */ grp->eo_group.eg_nobjs = 0; grp->eo_group.eg_objs = NULL; for (i = 0; i < nobjs; i++) { if ((obj = ea_alloc(sizeof (ea_object_t))) == NULL) { /* exacct_errno set above. */ return (-1); } obj->eo_next = NULL; if (xget_object(f, obj, bufread_wrapper, bufseek_wrapper, bufpos_wrapper, flag) == -1) { ea_free(obj, sizeof (ea_object_t)); /* exacct_errno set above. */ return (-1); } (void) ea_attach_to_group(grp, obj); if (obj->eo_type == EO_GROUP && unpack_group(f, obj, flag) == -1) { /* exacct_errno set above. */ return (-1); } } if (nobjs != grp->eo_group.eg_nobjs) { EXACCT_SET_ERR(EXR_CORRUPT_FILE); return (-1); } EXACCT_SET_ERR(EXR_OK); return (0); }
/* * Read in the specified number of objects, returning the same data * structure that would have originally been passed to ea_write(). */ ea_object_t * ea_get_object_tree(ea_file_t *ef, uint32_t nobj) { ea_object_t *first_obj, *prev_obj, *obj; first_obj = prev_obj = NULL; while (nobj--) { /* Allocate space for the new object. */ obj = ea_alloc(sizeof (ea_object_t)); bzero(obj, sizeof (*obj)); /* Read it in. */ if (ea_get_object(ef, obj) == -1) { ea_free(obj, sizeof (ea_object_t)); if (first_obj != NULL) { ea_free_object(first_obj, EUP_ALLOC); } return (NULL); } /* Link it into the list. */ if (first_obj == NULL) { first_obj = obj; } if (prev_obj != NULL) { prev_obj->eo_next = obj; } prev_obj = obj; /* Recurse if the object is a group with contents. */ if (obj->eo_type == EO_GROUP && obj->eo_group.eg_nobjs > 0) { if ((obj->eo_group.eg_objs = ea_get_object_tree(ef, obj->eo_group.eg_nobjs)) == NULL) { /* exacct_error set above. */ ea_free_object(first_obj, EUP_ALLOC); return (NULL); } } } EXACCT_SET_ERR(EXR_OK); return (first_obj); }
/* * ea_close() performs all appropriate close operations on the open exacct file, * including releasing any memory allocated while parsing the file. */ int ea_close(ea_file_t *ef) { ea_file_impl_t *f = (ea_file_impl_t *)ef; if (f->ef_creator != NULL) ea_strfree(f->ef_creator); if (f->ef_hostname != NULL) ea_strfree(f->ef_hostname); ea_free(f->ef_depth, f->ef_mxdeep * sizeof (ea_file_depth_t)); if (fclose(f->ef_fp)) { EXACCT_SET_ERR(EXR_SYSCALL_FAIL); return (-1); } EXACCT_SET_ERR(EXR_OK); return (0); }
static int write_header(ea_file_t *ef) { ea_object_t hdr_grp; ea_object_t vers_obj; ea_object_t creator_obj; ea_object_t filetype_obj; ea_object_t hostname_obj; uint32_t bskip; const uint32_t version = EXACCT_VERSION; ea_file_impl_t *f = (ea_file_impl_t *)ef; void *buf; size_t bufsize; char hostbuf[SYSINFO_BUFSIZE]; int error = EXR_OK; bzero(&hdr_grp, sizeof (ea_object_t)); bzero(&vers_obj, sizeof (ea_object_t)); bzero(&creator_obj, sizeof (ea_object_t)); bzero(&filetype_obj, sizeof (ea_object_t)); bzero(&hostname_obj, sizeof (ea_object_t)); bzero(hostbuf, SYSINFO_BUFSIZE); (void) sysinfo(SI_HOSTNAME, hostbuf, SYSINFO_BUFSIZE); if (ea_set_item(&vers_obj, EXT_UINT32 | EXC_DEFAULT | EXD_VERSION, (void *)&version, 0) == -1 || ea_set_item(&creator_obj, EXT_STRING | EXC_DEFAULT | EXD_CREATOR, f->ef_creator, strlen(f->ef_creator)) == -1 || ea_set_item(&filetype_obj, EXT_STRING | EXC_DEFAULT | EXD_FILETYPE, EXACCT_HDR_STR, strlen(EXACCT_HDR_STR)) == -1 || ea_set_item(&hostname_obj, EXT_STRING | EXC_DEFAULT | EXD_HOSTNAME, hostbuf, strlen(hostbuf)) == -1) { error = ea_error(); goto cleanup1; } (void) ea_set_group(&hdr_grp, EXT_GROUP | EXC_DEFAULT | EXD_GROUP_HEADER); (void) ea_attach_to_group(&hdr_grp, &vers_obj); (void) ea_attach_to_group(&hdr_grp, &creator_obj); (void) ea_attach_to_group(&hdr_grp, &filetype_obj); (void) ea_attach_to_group(&hdr_grp, &hostname_obj); /* Get the required size by passing a null buffer. */ bufsize = ea_pack_object(&hdr_grp, NULL, 0); if ((buf = ea_alloc(bufsize)) == NULL) { error = ea_error(); goto cleanup1; } if (ea_pack_object(&hdr_grp, buf, bufsize) == (size_t)-1) { error = ea_error(); goto cleanup2; } /* * To prevent reading the header when reading the file backwards, * set the large backskip of the header group to 0 (last 4 bytes). */ bskip = 0; exacct_order32(&bskip); bcopy(&bskip, (char *)buf + bufsize - sizeof (bskip), sizeof (bskip)); if (fwrite(buf, sizeof (char), bufsize, f->ef_fp) != bufsize || fflush(f->ef_fp) == EOF) { error = EXR_SYSCALL_FAIL; goto cleanup2; } cleanup2: ea_free(buf, bufsize); cleanup1: (void) ea_free_item(&vers_obj, EUP_ALLOC); (void) ea_free_item(&creator_obj, EUP_ALLOC); (void) ea_free_item(&filetype_obj, EUP_ALLOC); (void) ea_free_item(&hostname_obj, EUP_ALLOC); EXACCT_SET_ERR(error); return (error == EXR_OK ? 0 : -1); }
/* * ea_unpack_object() can be considered as a finite series of get operations on * a given buffer, that rebuilds the hierarchy of objects compacted by a pack * operation. Because there is complex state associated with the group depth, * ea_unpack_object() must complete as one operation on a given buffer. */ ea_object_type_t ea_unpack_object(ea_object_t **objp, int flag, void *buf, size_t bufsize) { ea_file_impl_t fake; ea_object_t *obj; ea_object_type_t first_obj_type; *objp = NULL; if (buf == NULL) { EXACCT_SET_ERR(EXR_INVALID_BUF); return (EO_ERROR); } /* Set up the structures needed for unpacking */ bzero(&fake, sizeof (ea_file_impl_t)); if (stack_check(&fake) == -1) { /* exacct_errno set above. */ return (EO_ERROR); } fake.ef_buf = buf; fake.ef_bufsize = bufsize; /* Unpack the first object in the buffer - this should succeed. */ if ((obj = ea_alloc(sizeof (ea_object_t))) == NULL) { stack_free(&fake); /* exacct_errno set above. */ return (EO_ERROR); } obj->eo_next = NULL; if ((first_obj_type = xget_object(&fake, obj, bufread_wrapper, bufseek_wrapper, bufpos_wrapper, flag)) == -1) { stack_free(&fake); ea_free(obj, sizeof (ea_object_t)); /* exacct_errno set above. */ return (EO_ERROR); } if (obj->eo_type == EO_GROUP && unpack_group(&fake, obj, flag) == -1) { stack_free(&fake); ea_free_object(obj, flag); /* exacct_errno set above. */ return (EO_ERROR); } *objp = obj; /* * There may be other objects in the buffer - if so, chain them onto * the end of the list. We have reached the end of the list when * xget_object() returns -1 with exacct_error set to EXR_EOF. */ for (;;) { if ((obj = ea_alloc(sizeof (ea_object_t))) == NULL) { stack_free(&fake); ea_free_object(*objp, flag); *objp = NULL; /* exacct_errno set above. */ return (EO_ERROR); } obj->eo_next = NULL; if (xget_object(&fake, obj, bufread_wrapper, bufseek_wrapper, bufpos_wrapper, flag) == -1) { stack_free(&fake); ea_free(obj, sizeof (ea_object_t)); if (ea_error() == EXR_EOF) { EXACCT_SET_ERR(EXR_OK); return (first_obj_type); } else { ea_free_object(*objp, flag); *objp = NULL; /* exacct_error set above. */ return (EO_ERROR); } } (void) ea_attach_to_object(*objp, obj); if (obj->eo_type == EO_GROUP && unpack_group(&fake, obj, flag) == -1) { stack_free(&fake); ea_free(obj, sizeof (ea_object_t)); ea_free_object(*objp, flag); *objp = NULL; /* exacct_errno set above. */ return (EO_ERROR); } } }
/* * xget_object() contains the logic for extracting an individual object from a * packed buffer, which it consumes using xread() and xseek() operations * provided by the caller. flags may be set to either EUP_ALLOC, in which case * new memory is allocated for the variable length items unpacked, or * EUP_NOALLOC, in which case item data pointer indicate locations within the * buffer, using the provided xpos() function. EUP_NOALLOC is generally not * useful for callers representing interaction with actual file streams, and * should not be specified thereby. */ 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) { ea_size_t sz; uint32_t gp_backskip, scratch32; void *buf; size_t r; /* Read the catalog tag. */ if ((r = xread(f, &obj->eo_catalog, sizeof (ea_catalog_t))) == 0) { EXACCT_SET_ERR(EXR_EOF); return (EO_ERROR); } else if (r != sizeof (ea_catalog_t)) { EXACCT_SET_ERR(EXR_CORRUPT_FILE); return (EO_ERROR); } exacct_order32(&obj->eo_catalog); /* * If this is a record group, we treat it separately: only record * groups cause us to allocate new depth frames. */ if ((obj->eo_catalog & EXT_TYPE_MASK) == EXT_GROUP) { obj->eo_type = EO_GROUP; /* Read size field, and number of objects. */ if (xread(f, &sz, sizeof (ea_size_t)) != sizeof (ea_size_t)) { EXACCT_SET_ERR(EXR_CORRUPT_FILE); return (EO_ERROR); } exacct_order64(&sz); if (xread(f, &obj->eo_group.eg_nobjs, sizeof (uint32_t)) != sizeof (uint32_t)) { EXACCT_SET_ERR(EXR_CORRUPT_FILE); return (EO_ERROR); } exacct_order32(&obj->eo_group.eg_nobjs); /* Now read the group's small backskip. */ if (xread(f, &gp_backskip, sizeof (uint32_t)) != sizeof (uint32_t)) { EXACCT_SET_ERR(EXR_CORRUPT_FILE); return (EO_ERROR); } /* Push a new depth stack frame. */ if (stack_new_group(f, obj->eo_group.eg_nobjs) != 0) { /* exacct_error set above */ return (EO_ERROR); } /* * If the group has no items, we now need to position to the * end of the group, because there will be no subsequent calls * to process the group, it being empty. */ if (obj->eo_group.eg_nobjs == 0) { if (stack_next_object(f, xread) == -1) { /* exacct_error set above. */ return (EO_ERROR); } } f->ef_advance = 0; EXACCT_SET_ERR(EXR_OK); return (obj->eo_type); } /* * Otherwise we are reading an item. */ obj->eo_type = EO_ITEM; switch (obj->eo_catalog & EXT_TYPE_MASK) { case EXT_STRING: case EXT_EXACCT_OBJECT: case EXT_RAW: if (xread(f, &sz, sizeof (ea_size_t)) != sizeof (ea_size_t)) { EXACCT_SET_ERR(EXR_CORRUPT_FILE); return (EO_ERROR); } exacct_order64(&sz); /* * Subtract backskip value from size. */ sz -= sizeof (uint32_t); if ((flags & EUP_ALLOC_MASK) == EUP_NOALLOC) { buf = xpos(f); if (xseek(f, sz) == -1) { EXACCT_SET_ERR(EXR_CORRUPT_FILE); return (EO_ERROR); } } else { if ((buf = ea_alloc(sz)) == NULL) /* exacct_error set above. */ return (EO_ERROR); if (xread(f, buf, sz) != sz) { ea_free(buf, sz); EXACCT_SET_ERR(EXR_CORRUPT_FILE); return (EO_ERROR); } } obj->eo_item.ei_string = buf; /* * Maintain our consistent convention that string lengths * include the terminating NULL character. */ obj->eo_item.ei_size = sz; break; case EXT_UINT8: if (xread(f, &obj->eo_item.ei_uint8, sizeof (uint8_t)) != sizeof (uint8_t)) { EXACCT_SET_ERR(EXR_CORRUPT_FILE); return (EO_ERROR); } obj->eo_item.ei_size = sizeof (uint8_t); break; case EXT_UINT16: if (xread(f, &obj->eo_item.ei_uint16, sizeof (uint16_t)) != sizeof (uint16_t)) { EXACCT_SET_ERR(EXR_CORRUPT_FILE); return (EO_ERROR); } exacct_order16(&obj->eo_item.ei_uint16); obj->eo_item.ei_size = sizeof (uint16_t); break; case EXT_UINT32: if (xread(f, &obj->eo_item.ei_uint32, sizeof (uint32_t)) != sizeof (uint32_t)) { EXACCT_SET_ERR(EXR_CORRUPT_FILE); return (EO_ERROR); } exacct_order32(&obj->eo_item.ei_uint32); obj->eo_item.ei_size = sizeof (uint32_t); break; case EXT_UINT64: if (xread(f, &obj->eo_item.ei_uint64, sizeof (uint64_t)) != sizeof (uint64_t)) { EXACCT_SET_ERR(EXR_CORRUPT_FILE); return (EO_ERROR); } exacct_order64(&obj->eo_item.ei_uint64); obj->eo_item.ei_size = sizeof (uint64_t); break; case EXT_DOUBLE: if (xread(f, &obj->eo_item.ei_double, sizeof (double)) != sizeof (double)) { EXACCT_SET_ERR(EXR_CORRUPT_FILE); return (EO_ERROR); } exacct_order64((uint64_t *)&obj->eo_item.ei_double); obj->eo_item.ei_size = sizeof (double); break; default: /* * We've encountered an unknown type value. Flag the error and * exit. */ EXACCT_SET_ERR(EXR_CORRUPT_FILE); return (EO_ERROR); } /* * Advance over current large backskip value, * and position at the start of the next object. */ if (xread(f, &scratch32, sizeof (scratch32)) != sizeof (scratch32)) { EXACCT_SET_ERR(EXR_CORRUPT_FILE); return (EO_ERROR); } if (stack_next_object(f, xread) == -1) { /* exacct_error set above. */ return (EO_ERROR); } f->ef_advance = 0; EXACCT_SET_ERR(EXR_OK); return (obj->eo_type); }
/* * Convert the perl form of an ::Object into the corresponding exacct form. * This is used prior to writing an ::Object to a file, or passing it to * putacct. This is only required for embedded items and groups - for normal * items it is a no-op. */ ea_object_t * deflate_xs_ea_object(SV *sv) { xs_ea_object_t *xs_obj; ea_object_t *ea_obj; /* Get the source xs_ea_object_t. */ PERL_ASSERT(sv != NULL); sv = SvRV(sv); PERL_ASSERT(sv != NULL); xs_obj = INT2PTR(xs_ea_object_t *, SvIV(sv)); PERL_ASSERT(xs_obj != NULL); ea_obj = xs_obj->ea_obj; PERL_ASSERT(ea_obj != NULL); /* Break any list this object is a part of. */ ea_obj->eo_next = NULL; /* Deal with Items containing embedded Objects. */ if (IS_EMBED_ITEM(xs_obj)) { xs_ea_object_t *child_xs_obj; SV *perl_obj; size_t bufsz; /* Get the underlying perl object an deflate that in turn. */ perl_obj = xs_obj->perl_obj; PERL_ASSERT(perl_obj != NULL); deflate_xs_ea_object(perl_obj); perl_obj = SvRV(perl_obj); PERL_ASSERT(perl_obj != NULL); child_xs_obj = INT2PTR(xs_ea_object_t *, SvIV(perl_obj)); PERL_ASSERT(child_xs_obj->ea_obj != NULL); /* Free any existing object contents. */ if (ea_obj->eo_item.ei_object != NULL) { ea_free(ea_obj->eo_item.ei_object, ea_obj->eo_item.ei_size); ea_obj->eo_item.ei_object = NULL; ea_obj->eo_item.ei_size = 0; } /* Pack the object. */ while (1) { /* Use the last buffer size as a best guess. */ if (last_bufsz != 0) { ea_obj->eo_item.ei_object = ea_alloc(last_bufsz); PERL_ASSERT(ea_obj->eo_item.ei_object != NULL); } else { ea_obj->eo_item.ei_object = NULL; } /* * Pack the object. If the buffer is too small, * we will go around again with the correct size. * If unsucessful, we will bail. */ if ((bufsz = ea_pack_object(child_xs_obj->ea_obj, ea_obj->eo_item.ei_object, last_bufsz)) == -1) { ea_free(ea_obj->eo_item.ei_object, last_bufsz); ea_obj->eo_item.ei_object = NULL; return (NULL); } else if (bufsz > last_bufsz) { ea_free(ea_obj->eo_item.ei_object, last_bufsz); last_bufsz = bufsz; continue; } else { ea_obj->eo_item.ei_size = bufsz; break; } } /* Deal with Groups. */ } else if (IS_GROUP(xs_obj)) {