1.4 Memory ownership
libpeios never hands you an allocation to free(). Instead it uses two ownership patterns — builders for constructing byte buffers and views for reading them — and both keep the memory question simple: you own your buffers, the library borrows or copies, and the two never get confused.
1.4.0.1 Builders — constructing buffers #
Anything you assemble (an ACL, a security descriptor, a token specification) is built with a builder: an opaque, heap-backed object you create, feed, take the bytes from, and free.
Builders have three properties worth knowing up front:
-
They are sticky-error. The incremental
add/setcalls returnvoid— they never fail inline. If one hits a problem (a bad input, an allocation failure), the builder latches the error and every later call is a no-op. You do not have to check each step. Instead you check once, at the end: either call the builder's_error()accessor (it returns the latched errno, or0if all is well), or notice that taking the bytes fails. This lets you write a long, clean sequence ofaddcalls without a conditional after every line. -
You free every builder you create. Each
_new()is paired with a_free(). Builders also have a_reset()that drops the accumulated content and clears the sticky error, so you can reuse one builder across several objects instead of churning allocations. -
Taking the bytes: borrow (and sometimes copy). Every builder has a
_bytes()that hands back a pointer into the builder — zero-copy, no allocation. That pointer is valid only until the next mutating call,_reset(), or_free()on that builder. Use it when you are going to consume the bytes immediately (for instance, pass them straight into a kernel call). The call comes in two shapes, and not every builder offers a copying counterpart:- The security builders (
peios_acl_builder_bytes,peios_sd_builder_bytes) return the pointer —NULLif the sticky error is set — and write the length through an optionallen_outpointer. Each is paired with a_finish()that copies the buffer into a caller-supplied buffer using the two-call protocol above, for when the bytes must outlive the builder. peios_token_builder_bytesandpeios_mp_writer_bytesare shaped the other way round: they return the length as anssize_t(-1witherrnoon a latched error) and write the borrowed pointer through an out-parameter (which may beNULLto get just the length). Neither has a_finish()— copy the borrowed bytes yourself if they need to outlive the builder.
- The security builders (
A typical builder lifecycle:
peios_acl_builder *b = ; /* NULL on OOM */
; /* void — no check */
;
size_t len;
const void *acl = ; /* NULL if errored */
if
/* … use `acl` before the next mutation … */
;
1.4.0.2 Views — reading buffers #
Anything you parse (a security descriptor, an ACL, a SID array from a token) is read through a view: a small, caller-allocated struct that you point at a buffer you already hold.
Views have their own two rules:
-
You allocate the view; it is stack-friendly. A view type such as
peios_sd_viewis an opaque fixed-size struct — you declare one as a local variable and pass its address to the parse call. No heap, no free. The struct's fields are opaque: never read them directly; use the accessor functions. -
A view borrows the buffer it parses — zero-copy. The parse call does not copy the data; the view points into your buffer, and every accessor that yields a SID, a nested ACL, or a blob hands back a pointer into that same buffer. So the buffer must stay alive and unmodified for as long as the view — and anything you derived from it — is in use. Free or mutate the underlying buffer and every pointer the view gave you dangles.
peios_sd_view sd; /* on the stack */
if
const void *owner; size_t owner_len;
if
Views compose: parsing a security descriptor gives you a peios_sd_view, from which you obtain a peios_acl_view for its DACL, from which you obtain each peios_ace_view. Every one of them borrows the same original buffer, so keeping that one buffer alive keeps the whole tree valid.
The symmetry is the thing to remember: builders own heap and must be freed; views own nothing and borrow your buffer. Constructing is builders, reading is views, and neither ever asks you to free something the library allocated.