2 * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer,
10 * without modification.
11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13 * redistribution must be conditioned upon including a substantially
14 * similar Disclaimer requirement for further binary redistribution.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 * THE POSSIBILITY OF SUCH DAMAGES.
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
34 #include <sys/param.h>
35 #include <sys/ctype.h>
36 #include <sys/limits.h>
37 #include <sys/malloc.h>
38 #include <sys/systm.h>
48 #include "bhnd_nvram_private.h"
50 #include "bhnd_nvram_datavar.h"
52 #include "bhnd_nvram_data_tlvreg.h"
55 * CFE TLV NVRAM data class.
57 * The CFE-defined TLV NVRAM format is used on the WGT634U.
60 struct bhnd_nvram_tlv {
61 struct bhnd_nvram_data nv; /**< common instance state */
62 struct bhnd_nvram_io *data; /**< backing buffer */
63 size_t count; /**< variable count */
66 BHND_NVRAM_DATA_CLASS_DEFN(tlv, "WGT634U", BHND_NVRAM_DATA_CAP_DEVPATHS,
67 sizeof(struct bhnd_nvram_tlv))
69 /** Minimal TLV_ENV record header */
70 struct bhnd_nvram_tlv_env_hdr {
75 /** Minimal TLV_ENV record */
76 struct bhnd_nvram_tlv_env {
77 struct bhnd_nvram_tlv_env_hdr hdr;
82 /* Return the length in bytes of an TLV_ENV's envp data */
83 #define NVRAM_TLV_ENVP_DATA_LEN(_env) \
84 (((_env)->hdr.size < sizeof((_env)->flags)) ? 0 : \
85 ((_env)->hdr.size - sizeof((_env)->flags)))
87 /* Maximum supported length of the envp data field, in bytes */
88 #define NVRAM_TLV_ENVP_DATA_MAX_LEN \
89 (UINT8_MAX - sizeof(uint8_t) /* flags */)
92 static int bhnd_nvram_tlv_parse_size(
93 struct bhnd_nvram_io *io,
96 static int bhnd_nvram_tlv_next_record(
97 struct bhnd_nvram_io *io,
98 size_t *next, size_t *offset,
101 static struct bhnd_nvram_tlv_env *bhnd_nvram_tlv_next_env(
102 struct bhnd_nvram_tlv *tlv,
103 size_t *next, void **cookiep);
105 static struct bhnd_nvram_tlv_env *bhnd_nvram_tlv_get_env(
106 struct bhnd_nvram_tlv *tlv,
109 static void *bhnd_nvram_tlv_to_cookie(
110 struct bhnd_nvram_tlv *tlv,
112 static size_t bhnd_nvram_tlv_to_offset(
113 struct bhnd_nvram_tlv *tlv,
117 bhnd_nvram_tlv_probe(struct bhnd_nvram_io *io)
119 struct bhnd_nvram_tlv_env ident;
123 nbytes = bhnd_nvram_io_getsize(io);
125 /* Handle what might be an empty TLV image */
126 if (nbytes < sizeof(ident)) {
129 /* Fetch just the first tag */
130 error = bhnd_nvram_io_read(io, 0x0, &tag, sizeof(tag));
134 /* This *could* be an empty TLV image, but all we're
135 * testing for here is a single 0x0 byte followed by EOF */
136 if (tag == NVRAM_TLV_TYPE_END)
137 return (BHND_NVRAM_DATA_PROBE_MAYBE);
142 /* Otherwise, look at the initial header for a valid TLV ENV tag,
143 * plus one byte of the entry data */
144 error = bhnd_nvram_io_read(io, 0x0, &ident,
145 sizeof(ident) + sizeof(ident.envp[0]));
149 /* First entry should be a variable record (which we statically
150 * assert as being defined to use a single byte size field) */
151 if (ident.hdr.tag != NVRAM_TLV_TYPE_ENV)
154 _Static_assert(NVRAM_TLV_TYPE_ENV & NVRAM_TLV_TF_U8_LEN,
155 "TYPE_ENV is not a U8-sized field");
157 /* The entry must be at least 3 characters ('x=\0') in length */
158 if (ident.hdr.size < 3)
161 /* The first character should be a valid key char (alpha) */
162 if (!bhnd_nv_isalpha(ident.envp[0]))
165 return (BHND_NVRAM_DATA_PROBE_DEFAULT);
169 bhnd_nvram_tlv_getvar_direct(struct bhnd_nvram_io *io, const char *name,
170 void *buf, size_t *len, bhnd_nvram_type type)
172 struct bhnd_nvram_tlv_env env;
173 char data[NVRAM_TLV_ENVP_DATA_MAX_LEN];
175 const char *key, *value;
182 namelen = strlen(name);
184 /* Iterate over the input looking for the requested variable */
186 while (!(error = bhnd_nvram_tlv_next_record(io, &next, &off, &tag))) {
188 case NVRAM_TLV_TYPE_END:
192 case NVRAM_TLV_TYPE_ENV:
193 /* Read the record header */
194 error = bhnd_nvram_io_read(io, off, &env, sizeof(env));
196 BHND_NV_LOG("error reading TLV_ENV record "
197 "header: %d\n", error);
201 /* Read the record data */
202 data_len = NVRAM_TLV_ENVP_DATA_LEN(&env);
203 error = bhnd_nvram_io_read(io, off + sizeof(env), data,
206 BHND_NV_LOG("error reading TLV_ENV record "
207 "data: %d\n", error);
211 /* Parse the key=value string */
212 error = bhnd_nvram_parse_env(data, data_len, '=', &key,
213 &keylen, &value, &vlen);
215 BHND_NV_LOG("error parsing TLV_ENV data: %d\n",
220 /* Match against requested variable name */
221 if (keylen == namelen &&
222 strncmp(key, name, namelen) == 0)
224 return (bhnd_nvram_value_coerce(value, vlen,
225 BHND_NVRAM_TYPE_STRING, buf, len, type));
231 /* Skip unknown tags */
241 bhnd_nvram_tlv_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
242 bhnd_nvram_plist *options, void *outp, size_t *olen)
244 bhnd_nvram_prop *prop;
245 size_t limit, nbytes;
248 /* Determine output byte limit */
256 /* Write all properties */
258 while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
259 struct bhnd_nvram_tlv_env env;
262 size_t name_len, value_len;
265 env.hdr.tag = NVRAM_TLV_TYPE_ENV;
266 env.hdr.size = sizeof(env.flags);
269 /* Fetch name value and add to record length */
270 name = bhnd_nvram_prop_name(prop);
271 name_len = strlen(name) + 1 /* '=' */;
273 if (UINT8_MAX - env.hdr.size < name_len) {
274 BHND_NV_LOG("%s name exceeds maximum TLV record "
276 return (EFTYPE); /* would overflow TLV size */
279 env.hdr.size += name_len;
281 /* Add string value to record length */
282 error = bhnd_nvram_prop_encode(prop, NULL, &value_len,
283 BHND_NVRAM_TYPE_STRING);
285 BHND_NV_LOG("error serializing %s to required type "
287 bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING),
292 if (UINT8_MAX - env.hdr.size < value_len) {
293 BHND_NV_LOG("%s value exceeds maximum TLV record "
295 return (EFTYPE); /* would overflow TLV size */
298 env.hdr.size += value_len;
300 /* Calculate total record size */
301 rec_size = sizeof(env.hdr) + env.hdr.size;
302 if (SIZE_MAX - nbytes < rec_size)
303 return (EFTYPE); /* would overflow size_t */
305 /* Calculate our output pointer */
306 if (nbytes > limit || limit - nbytes < rec_size) {
307 /* buffer is full; cannot write */
310 p = (uint8_t *)outp + nbytes;
313 /* Write to output */
315 memcpy(p, &env, sizeof(env));
318 memcpy(p, name, name_len - 1);
319 p[name_len - 1] = '=';
322 error = bhnd_nvram_prop_encode(prop, p, &value_len,
323 BHND_NVRAM_TYPE_STRING);
325 BHND_NV_LOG("error serializing %s to required "
326 "type %s: %d\n", name,
327 bhnd_nvram_type_name(
328 BHND_NVRAM_TYPE_STRING),
337 /* Write terminating END record */
339 *((uint8_t *)outp + nbytes) = NVRAM_TLV_TYPE_END;
341 if (nbytes == SIZE_MAX)
342 return (EFTYPE); /* would overflow size_t */
345 /* Provide required length */
358 * Initialize @p tlv with the provided NVRAM TLV data mapped by @p src.
360 * @param tlv A newly allocated data instance.
363 bhnd_nvram_tlv_init(struct bhnd_nvram_tlv *tlv, struct bhnd_nvram_io *src)
365 struct bhnd_nvram_tlv_env *env;
370 BHND_NV_ASSERT(tlv->data == NULL, ("tlv data already initialized"));
372 /* Determine the actual size of the TLV source data */
373 if ((error = bhnd_nvram_tlv_parse_size(src, &size)))
376 /* Copy to our own internal buffer */
377 if ((tlv->data = bhnd_nvram_iobuf_copy_range(src, 0x0, size)) == NULL)
380 /* Initialize our backing buffer */
383 while ((env = bhnd_nvram_tlv_next_env(tlv, &next, NULL)) != NULL) {
387 /* TLV_ENV data must not be empty */
388 env_len = NVRAM_TLV_ENVP_DATA_LEN(env);
390 BHND_NV_LOG("cannot parse zero-length TLV_ENV record "
395 /* Parse the key=value string, and then replace the '='
396 * delimiter with '\0' to allow us to provide direct
397 * name pointers from our backing buffer */
398 error = bhnd_nvram_parse_env(env->envp, env_len, '=', NULL,
399 &name_len, NULL, NULL);
401 BHND_NV_LOG("error parsing TLV_ENV data: %d\n", error);
405 /* Replace '=' with '\0' */
406 *(env->envp + name_len) = '\0';
408 /* Add to variable count */
416 bhnd_nvram_tlv_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
419 struct bhnd_nvram_tlv *tlv;
422 /* Allocate and initialize the TLV data instance */
423 tlv = (struct bhnd_nvram_tlv *)nv;
425 /* Parse the TLV input data and initialize our backing
426 * data representation */
427 if ((error = bhnd_nvram_tlv_init(tlv, io))) {
428 bhnd_nvram_tlv_free(nv);
436 bhnd_nvram_tlv_free(struct bhnd_nvram_data *nv)
438 struct bhnd_nvram_tlv *tlv = (struct bhnd_nvram_tlv *)nv;
439 if (tlv->data != NULL)
440 bhnd_nvram_io_free(tlv->data);
444 bhnd_nvram_tlv_count(struct bhnd_nvram_data *nv)
446 struct bhnd_nvram_tlv *tlv = (struct bhnd_nvram_tlv *)nv;
451 static bhnd_nvram_plist *
452 bhnd_nvram_tlv_options(struct bhnd_nvram_data *nv)
458 bhnd_nvram_tlv_caps(struct bhnd_nvram_data *nv)
460 return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS);
464 bhnd_nvram_tlv_next(struct bhnd_nvram_data *nv, void **cookiep)
466 struct bhnd_nvram_tlv *tlv;
467 struct bhnd_nvram_tlv_env *env;
470 tlv = (struct bhnd_nvram_tlv *)nv;
472 /* Find next readable TLV record */
473 if (*cookiep == NULL) {
474 /* Start search at offset 0x0 */
476 env = bhnd_nvram_tlv_next_env(tlv, &io_offset, cookiep);
478 /* Seek past the previous env record */
479 io_offset = bhnd_nvram_tlv_to_offset(tlv, *cookiep);
480 env = bhnd_nvram_tlv_next_env(tlv, &io_offset, NULL);
482 BHND_NV_PANIC("invalid cookiep; record missing");
484 /* Advance to next env record, update the caller's cookiep */
485 env = bhnd_nvram_tlv_next_env(tlv, &io_offset, cookiep);
492 /* Return the NUL terminated name */
497 bhnd_nvram_tlv_find(struct bhnd_nvram_data *nv, const char *name)
499 return (bhnd_nvram_data_generic_find(nv, name));
503 bhnd_nvram_tlv_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
506 if (cookiep1 < cookiep2)
509 if (cookiep1 > cookiep2)
516 bhnd_nvram_tlv_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
517 size_t *len, bhnd_nvram_type type)
519 return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));
523 bhnd_nvram_tlv_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
524 bhnd_nvram_val **value)
526 return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value));
530 bhnd_nvram_tlv_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
531 size_t *len, bhnd_nvram_type *type)
533 struct bhnd_nvram_tlv *tlv;
534 struct bhnd_nvram_tlv_env *env;
538 tlv = (struct bhnd_nvram_tlv *)nv;
540 /* Fetch pointer to the TLV_ENV record */
541 if ((env = bhnd_nvram_tlv_get_env(tlv, cookiep)) == NULL)
542 BHND_NV_PANIC("invalid cookiep: %p", cookiep);
544 /* Parse value pointer and length from key\0value data */
545 error = bhnd_nvram_parse_env(env->envp, NVRAM_TLV_ENVP_DATA_LEN(env),
546 '\0', NULL, NULL, &val, len);
548 BHND_NV_PANIC("unexpected error parsing '%s'", env->envp);
550 /* Type is always CSTR */
551 *type = BHND_NVRAM_TYPE_STRING;
557 bhnd_nvram_tlv_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
559 struct bhnd_nvram_tlv *tlv;
560 const struct bhnd_nvram_tlv_env *env;
562 tlv = (struct bhnd_nvram_tlv *)nv;
564 /* Fetch pointer to the TLV_ENV record */
565 if ((env = bhnd_nvram_tlv_get_env(tlv, cookiep)) == NULL)
566 BHND_NV_PANIC("invalid cookiep: %p", cookiep);
568 /* Return name pointer */
569 return (&env->envp[0]);
573 bhnd_nvram_tlv_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
574 bhnd_nvram_val *value, bhnd_nvram_val **result)
578 bhnd_nvram_type itype;
580 size_t name_len, tlv_nremain;
583 tlv_nremain = NVRAM_TLV_ENVP_DATA_MAX_LEN;
585 /* Name (trimmed of any path prefix) must be valid */
586 if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name)))
589 /* 'name=' must fit within the maximum TLV_ENV record length */
590 name_len = strlen(name) + 1; /* '=' */
591 if (tlv_nremain < name_len) {
592 BHND_NV_LOG("'%s=' exceeds maximum TLV_ENV record length\n",
596 tlv_nremain -= name_len;
598 /* Convert value to a (bcm-formatted) string */
599 error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt,
600 value, BHND_NVRAM_VAL_DYNAMIC);
604 /* The string value must fit within remaining TLV_ENV record length */
605 inp = bhnd_nvram_val_bytes(str, &ilen, &itype);
606 if (tlv_nremain < ilen) {
607 BHND_NV_LOG("'%.*s\\0' exceeds maximum TLV_ENV record length\n",
608 BHND_NV_PRINT_WIDTH(ilen), inp);
610 bhnd_nvram_val_release(str);
613 tlv_nremain -= name_len;
615 /* Success. Transfer result ownership to the caller. */
621 bhnd_nvram_tlv_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
623 /* We permit deletion of any variable */
628 * Iterate over the records starting at @p next, returning the parsed
629 * record's @p tag, @p size, and @p offset.
631 * @param io The I/O context to parse.
632 * @param[in,out] next The next offset to be parsed, or 0x0
633 * to begin parsing. Upon successful
634 * return, will be set to the offset of the
635 * next record (or EOF, if
636 * NVRAM_TLV_TYPE_END was parsed).
637 * @param[out] offset The record's value offset.
638 * @param[out] tag The record's tag.
641 * @retval EINVAL if parsing @p io as TLV fails.
642 * @retval non-zero if reading @p io otherwise fails, a regular unix error
643 * code will be returned.
646 bhnd_nvram_tlv_next_record(struct bhnd_nvram_io *io, size_t *next, size_t
647 *offset, uint8_t *tag)
649 size_t io_offset, io_size;
655 io_size = bhnd_nvram_io_getsize(io);
657 /* Save the record offset */
661 /* Fetch initial tag */
662 error = bhnd_nvram_io_read(io, io_offset, tag, sizeof(*tag));
668 if (*tag == NVRAM_TLV_TYPE_END) {
673 /* Read length field */
674 if (*tag & NVRAM_TLV_TF_U8_LEN) {
675 error = bhnd_nvram_io_read(io, io_offset, &len_hdr,
678 BHND_NV_LOG("error reading TLV record size: %d\n",
683 parsed_len = len_hdr[0];
686 error = bhnd_nvram_io_read(io, io_offset, &len_hdr,
689 BHND_NV_LOG("error reading 16-bit TLV record "
690 "size: %d\n", error);
694 parsed_len = (len_hdr[0] << 8) | len_hdr[1];
698 /* Advance to next record */
699 if (parsed_len > io_size || io_size - parsed_len < io_offset) {
701 BHND_NV_LOG("TLV record length %hu truncated by input "
702 "size of %zu\n", parsed_len, io_size);
706 *next = io_offset + parsed_len;
708 /* Valid record found */
713 * Parse the TLV data in @p io to determine the total size of the TLV
714 * data mapped by @p io (which may be less than the size of @p io).
717 bhnd_nvram_tlv_parse_size(struct bhnd_nvram_io *io, size_t *size)
723 /* We have to perform a minimal parse to determine the actual length */
727 /* Iterate over the input until we hit END tag or the read fails */
729 error = bhnd_nvram_tlv_next_record(io, &next, NULL, &tag);
732 } while (tag != NVRAM_TLV_TYPE_END);
734 /* Offset should now point to EOF */
735 BHND_NV_ASSERT(next <= bhnd_nvram_io_getsize(io),
736 ("parse returned invalid EOF offset"));
743 * Iterate over the records in @p tlv, returning a pointer to the next
744 * NVRAM_TLV_TYPE_ENV record, or NULL if EOF is reached.
746 * @param tlv The TLV instance.
747 * @param[in,out] next The next offset to be parsed, or 0x0
748 * to begin parsing. Upon successful
749 * return, will be set to the offset of the
752 static struct bhnd_nvram_tlv_env *
753 bhnd_nvram_tlv_next_env(struct bhnd_nvram_tlv *tlv, size_t *next,
759 /* Find the next TLV_ENV record, starting at @p next */
764 /* Fetch the next TLV record */
765 error = bhnd_nvram_tlv_next_record(tlv->data, next, &offset,
768 BHND_NV_LOG("unexpected error in next_record(): %d\n",
773 /* Only interested in ENV records */
774 if (tag != NVRAM_TLV_TYPE_ENV)
777 /* Map and return TLV_ENV record pointer */
778 c = bhnd_nvram_tlv_to_cookie(tlv, offset);
780 /* Provide the cookiep value for the returned record */
784 return (bhnd_nvram_tlv_get_env(tlv, c));
785 } while (tag != NVRAM_TLV_TYPE_END);
787 /* No remaining ENV records */
792 * Return a pointer to the TLV_ENV record for @p cookiep, or NULL
795 static struct bhnd_nvram_tlv_env *
796 bhnd_nvram_tlv_get_env(struct bhnd_nvram_tlv *tlv, void *cookiep)
798 struct bhnd_nvram_tlv_env *env;
801 size_t io_offset, io_size;
804 io_size = bhnd_nvram_io_getsize(tlv->data);
805 io_offset = bhnd_nvram_tlv_to_offset(tlv, cookiep);
808 if (io_offset == io_size)
811 /* Fetch non-const pointer to the record entry */
812 error = bhnd_nvram_io_write_ptr(tlv->data, io_offset, &ptr,
813 sizeof(env->hdr), &navail);
815 /* Should never occur with a valid cookiep */
816 BHND_NV_LOG("error mapping record for cookiep: %d\n", error);
820 /* Validate the record pointer */
822 if (env->hdr.tag != NVRAM_TLV_TYPE_ENV) {
823 /* Should never occur with a valid cookiep */
824 BHND_NV_LOG("non-ENV record mapped for %p\n", cookiep);
828 /* Is the required variable name data is mapped? */
829 if (navail < sizeof(struct bhnd_nvram_tlv_env_hdr) + env->hdr.size ||
830 env->hdr.size == sizeof(env->flags))
832 /* Should never occur with a valid cookiep */
833 BHND_NV_LOG("TLV_ENV variable data not mapped for %p\n",
842 * Return a cookiep for the given I/O offset.
845 bhnd_nvram_tlv_to_cookie(struct bhnd_nvram_tlv *tlv, size_t io_offset)
850 BHND_NV_ASSERT(io_offset < bhnd_nvram_io_getsize(tlv->data),
851 ("io_offset %zu out-of-range", io_offset));
852 BHND_NV_ASSERT(io_offset < UINTPTR_MAX,
853 ("io_offset %#zx exceeds UINTPTR_MAX", io_offset));
855 error = bhnd_nvram_io_read_ptr(tlv->data, 0x0, &ptr, io_offset, NULL);
857 BHND_NV_PANIC("error mapping offset %zu: %d", io_offset, error);
859 ptr = (const uint8_t *)ptr + io_offset;
860 return (__DECONST(void *, ptr));
863 /* Convert a cookiep back to an I/O offset */
865 bhnd_nvram_tlv_to_offset(struct bhnd_nvram_tlv *tlv, void *cookiep)
872 BHND_NV_ASSERT(cookiep != NULL, ("null cookiep"));
874 io_size = bhnd_nvram_io_getsize(tlv->data);
876 error = bhnd_nvram_io_read_ptr(tlv->data, 0x0, &ptr, io_size, NULL);
878 BHND_NV_PANIC("error mapping offset %zu: %d", io_size, error);
880 offset = (const uint8_t *)cookiep - (const uint8_t *)ptr;
881 BHND_NV_ASSERT(offset >= 0, ("invalid cookiep"));
882 BHND_NV_ASSERT((uintptr_t)offset < SIZE_MAX, ("cookiep > SIZE_MAX)"));
883 BHND_NV_ASSERT((uintptr_t)offset <= io_size, ("cookiep > io_size)"));
885 return ((size_t)offset);