/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ │ Copyright 2020 Justine Alexandra Roberts Tunney │ │ │ │ This program is free software; you can redistribute it and/or modify │ │ it under the terms of the GNU General Public License as published by │ │ the Free Software Foundation; version 2 of the License. │ │ │ │ This program is distributed in the hope that it will be useful, but │ │ WITHOUT ANY WARRANTY; without even the implied warranty of │ │ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ │ General Public License for more details. │ │ │ │ You should have received a copy of the GNU General Public License │ │ along with this program; if not, write to the Free Software │ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/struct/iovec.h" #include "libc/log/check.h" #include "libc/macros.h" #include "libc/nexgen32e/bsr.h" #include "libc/runtime/gc.h" #include "libc/sock/sock.h" #include "libc/str/str.h" #include "libc/sysv/consts/o.h" #include "libc/x/x.h" #include "tool/build/lib/persist.h" static bool IsWithin(size_t sz1, void *vp1, size_t sz2, void *vp2) { char *p1 = vp1, *p2 = vp2; return p1 >= p2 && p1 + sz1 <= p2 + sz2; } static bool IsOverlapping(void *vx1, void *vy1, void *vx2, void *vy2) { char *x1 = vx1, *y1 = vy1, *x2 = vx2, *y2 = vy2; return (x1 >= x2 && x1 <= y2) || (y1 >= x2 && y1 <= y2); } static bool IsOverlappingIov(struct iovec *a, struct iovec *b) { char *ap = a->iov_base, *bp = b->iov_base; return IsOverlapping(ap, ap + a->iov_len, bp, bp + b->iov_len); } /** * Writes struct w/ dynamic arrays to mappable file, e.g. * * PersistObject(path, 64, &(struct ObjectParam){ * sizeof(*o), o, &o->magic, &o->abi, * &(struct ObjectArrayParam){ * {&o->a1.i, sizeof(o->a1.p[0]), &o->a1.p}, * {&o->a2.i, sizeof(o->a2.p[0]), &o->a2.p}, * {0}, * }}); * * @param obj->magic needs to be unique for struct * @param obj->abi monotonically tracks breaking changes * @param obj->arrays needs sentinel with item size of zero * @note non-recursive i.e. array elements can't have pointers * @see MapObject() */ void PersistObject(const char *path, size_t align, const struct ObjectParam *obj) { struct iovec *iov; int i, n, fd, iovlen; const char *tmp, *pad; long len, size, bytes, filesize; unsigned char *hdr, *p1, *p2, **pp; intptr_t arrayptroffset, arraydataoffset; filesize = 0; DCHECK_GE(align, 1); CHECK_GT(*obj->magic, 0); CHECK_GT(*obj->abi, 0); CHECK(IsWithin(sizeof(*obj->magic), obj->magic, obj->size, obj->p)); CHECK(IsWithin(sizeof(*obj->abi), obj->abi, obj->size, obj->p)); for (n = i = 0; obj->arrays[i].size; ++i) ++n; iovlen = (n + 1) * 2; pad = gc(xcalloc(align, 1)); hdr = gc(xmalloc(obj->size)); iov = gc(xcalloc(iovlen, sizeof(*iov))); tmp = gc(xasprintf("%s.%d.%s", path, getpid(), "tmp")); bytes = obj->size; iov[0].iov_base = memcpy(hdr, obj->p, obj->size); iov[0].iov_len = bytes; iov[1].iov_base = pad; iov[1].iov_len = ROUNDUP(bytes, align) - bytes; filesize += ROUNDUP(bytes, align); for (i = 0; i < n; ++i) { pp = obj->arrays[i].pp; len = obj->arrays[i].len; size = obj->arrays[i].size; if (!*pp || !len) continue; p1 = obj->p; p2 = obj->arrays[i].pp; arrayptroffset = p2 - p1; arraydataoffset = filesize; CHECK((!len || bsrl(len) + bsrl(size) < 31), "path=%s i=%d len=%,lu size=%,lu", path, i, len, size); CHECK(IsWithin(sizeof(void *), pp, obj->size, obj->p)); CHECK(!IsOverlapping(pp, pp + sizeof(void *), obj->magic, obj->magic + sizeof(*obj->magic))); CHECK(!IsOverlapping(pp, pp + sizeof(void *), obj->abi, obj->abi + sizeof(*obj->abi))); memcpy(hdr + arrayptroffset, &arraydataoffset, sizeof(intptr_t)); CHECK_LT(filesize + arraydataoffset, 0x7ffff000); bytes = len * size; iov[(i + 1) * 2 + 0].iov_base = *pp; iov[(i + 1) * 2 + 0].iov_len = bytes; iov[(i + 1) * 2 + 1].iov_base = pad; iov[(i + 1) * 2 + 1].iov_len = ROUNDUP(bytes, align) - bytes; filesize += ROUNDUP(bytes, align); CHECK(!IsOverlappingIov(&iov[(i + 0) * 2], &iov[(i + 1) * 2]), "iov[%d]={%#p,%#x}, iov[%d]={%#p,%#x} path=%s", (i + 0) * 2, iov[(i + 0) * 2].iov_base, iov[(i + 0) * 2].iov_len, (i + 1) * 2, iov[(i + 1) * 2].iov_base, iov[(i + 1) * 2].iov_len, path); } CHECK_NE(-1, (fd = open(tmp, O_CREAT | O_WRONLY | O_EXCL, 0644)), "%s", tmp); CHECK_EQ(filesize, writev(fd, iov, iovlen)); CHECK_NE(-1, close(fd)); CHECK_NE(-1, rename(tmp, path), "%s", path); }