This is a big overhaul: Change files are new mmap()ed when they are opened, allowing to change most operations to work on byte buffers (instead of file descriptors).
This in turn makes the change deserialization much more testable as we can just feed it arrays of bytes w/o requiring anything on disk.
An error type is introduced and parsing functions should return variants of this error type instead of having the whole program die when a parsing error occurs.
A separate change that unfortunately got worked into this one is the introduction of change_readstr() which can take care of fetching a string length, xmalloc()ing the right amount of space, and copying the string data into it.
SMB3M6ESOIR2LMFII2VFQJSPFIUYXHD7CXYARIGOKPB2QF2ORFLAC
WH7UFRAZRDTII4UZVUFGZBLDUY2P6KIUKRFVJXQZTKP7Y65VPUAAC
ZS3UJEZU4PZEYOXQNZWZGDNXTP5ESNPRAEHPJQ24O3TWZH746IBQC
67HZZ5HS7AA5KA6W4TV24YMO33V5FR6GJ34P3FPTRN547RFNL4DQC
ZTDGWUGPKDVIUA6V5BZ5ATDTGKZEE6ZZIXLGNTUJU2KIV7H7YSVQC
PEUS54XQ5KJQYAVUYBG5MWLEHIOVPMZ3ANVC7HPQP6JUWWPRDW5AC
NZNIG2ULNMS5Z3OWEKRVOPLFA6CWYNF47U7UMXNZFSX256F2ZZVAC
XJ2PEH74CLJUELZBR47QHGUSKXB4Z5T7EKEF6Y4CYY2VBHZXUTDAC
L3HKOF4WYZZLJJY2Q6YJ65WGAB74GQ2A7ICD23M5NQGBP4BXF6DQC
YM2LC6QP2D7TK3JUKOXWUWTQEGMCEGQDGPTOZPWC3BRDFNRKSZWQC
UVEPURUQ333KDWQPCJKOXWVXAZ7JB5B2T4EO5C7PKHEM4STED7GAC
Q7TKZCJP2Z75EICZYKCEZDHKGERSOKZGMTSU3UXETBHTF663T66AC
JVU3TTT5T776LB2UNT5DCISJDWOITRSJPRG3CDTX4NDQEHC5VI3QC
5D2IYPL75HEP6JUEILEADSZRRSV72NECT6UQR3PORNDR35I2P5GQC
2U7P5SFQG3AVALKMPJF4WMZE6PXIXXZYOMZ3RZKILBUJ4UMFXVIAC
Y26WT3ZFN7KSVXOZ26B5Y2OR4M4VQYQLPMAHPC4O5VIT3ENBISXAC
ZKAOPMCHBGP7J7NQB233AQJQDX6TBD7REPLF3KN3EIKZK6TZZMBQC
OBKF6SIIFFHHY7YWKPPX75N23NAVUD5662DKIHXSSIDPKKXQ5ZDQC
VKLGQREYOZDV46F672RFE5XJO3OEOP4EHTCWZYOJY24HVPQX3L6QC
3FT3XTJMUAXQ257T5GB7YWWTI7QBKKVC7WA6DCH5UY26RLQO5PFQC
KDJUAAALIKZDRTVNUZKXYYA5UYMZNYBCI3CCRKS4YONBVRUADPXAC
WGGDK6VO56H2JP26KPSCQEGH5QETOPGWL6JVJVEKG6VK275FX2BAC
YFBKBUKBX3JB7OCB4D3ZKSLWTP6QWZ36HEE5GZROJE55RK33MZCAC
QEFCNNVCEDFUV7O4ASWVI4QUTUFWLHAOTJMDCZ3DFJIUQWTAN3SAC
RIWSVVASWLJQQTSVRHIIUPENOZWOMHQLZMTQVGJUS2ZUGDPSWWIQC
XTKRT6OQYN4LARQRDLI2KBSAJTOFZNT4AMBZ46CPUDZXD7IDYYWQC
WMFNGOYTKIZ7L25V3KMJPSWK23DTN6G3ESP6M55YIH6UTHFKL2XAC
U3JWO63YYQDCJX7CWXCS3N26CHY2TASADTCQIEDSJ7WC25GLZD5QC
7NQTS36DJ6GRXMZDNMORKPWF7VBESQY4DH2XFI7VI2A2JVMFIK5AC
JAGXXSR7DR5FG77VKBF62BW7Q3JMVHCLHF4VB2PSLIZF4RLE553QC
FMYCPGKDAPOUFLQOCXXKZ6TR75HT3TNCG4X3GAJDM4ARWKNWOSWAC
QYRJIOYPM7SAB3OHMALL67K44U5HPBRQ2AFNWUKATPXM6BUYRKBQC
4RYULBDDDIVSJTIOTBBSQAWMSN6ZBZEFJ6UR7P6ECEXEXTGPMUOAC
VXGUQZIVOLPAPINOY3EMFDHLFNBI3H6BNYYENS7P4G2WT4CCWCAQC
LCEKN25G5GFBUWWQAR4REIGPOL6JYNDWLNCDJGZQKOWIDHGIRMZAC
LVX6U4EKYXM6H32X5SZS4UBIM5PW4ND4F4QQOF5EQDX3DUHBYFGAC
YG4DZB3AW3Z3LB5CFBZZ4ORJOLZFN3G4CA2YTAMSUOQX3USVNVEAC
VXQYIOBXHTYEB62XSIAZGRNNIKXMDZLE5ANBGA7E7V3HOBVEMVSAC
YDQLW2ZOAH7PZ7HHVTSFUO5IWE6O7FDNXVNIN7GG4TJ3P2B2BM4AC
WFA5BBRFVUHJ53MOPZ2WDXZ4CEHKA32LDYERSYLVETIZTZDW22GAC
QSQNGA5KZ4BVWE4FUJUMOP6SJRIXAC2PYU6JSRG5KXWLYWZZMAJQC
P5CSMRVSVJVOUGGWY7UVP4B3XXNLZDEQVXNL5XOPT2GGB63HXA4QC
B3XLVPNC4COLLC3FUE34Y7HIKTMF6CJZUASZOU3YM2YGPZKJZP7QC
NETL2N53SORFVLRHT2GK6EYIGK64U6E4QTCES6NGQPBYUPIOVEIAC
N3PUHKQNA2Q5LRKTXHOZVG4LCDRKY5WAOEDSXS34KL27RZCSVHHAC
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#undef NDEBUG
#include <assert.h>
#include "NETL2.h"
#include "../common.h"
#include "../zstdseek.h"
#include "../types.h"
#include "../scaffold.h"
#include "../hash.h"
#include "../vertex.h"
#include "../atom.h"
#include "../hunk.h"
#include "../mbuf.h"
#include "../change.h"
static void
test_changefileoffsets()
{
struct mbuf ch = {
.len = NETL2LEN,
.buf = NETL2,
};
struct offsets off = { 0 };
int err;
err = changefileoffsets(&ch, &off);
assert(err == CHANGEFILE_OK);
assert(off.version == VERSION);
assert(off.hashed_len == 985);
assert(off.unhashed_off = 803);
assert(off.unhashed_len == 104);
assert(off.contents_off == 945);
assert(off.contents_len == 120);
}
static void
test_changefileoffsetsbad()
{
struct mbuf ch = { .len = 55, .buf = NULL };
int err;
struct offsets off = { 0 };
err = changefileoffsets(&ch, &off);
assert(err == CHANGEFILE_TOOSHORT);
}
static void
test_changefileoffsetsversion()
{
struct mbuf ch = {
.len = NETL2LEN,
.buf = NETL2,
};
struct offsets off = { 0 };
int err;
ch.buf[0] = 5;
err = changefileoffsets(&ch, &off);
assert(err == CHANGEFILE_UNSUPPORTEDVERSION);
}
static void
test_decodehashed()
{
struct hashed hashed = { 0 };
struct changeheader *header;
struct author *author;
usize datalen = 747;
usize hashedlen = 985;
u8 buf[hashedlen];
enum error ret;
usize err;
u8 expectedhash[32] = { 0x69, 0x26, 0xbd, 0x37, 0xbb, 0x93, 0xa2, 0x5a,
0xae, 0x27, 0x9e, 0x8c, 0xaf, 0x13, 0x08, 0x32,
0xbd, 0xca, 0x78, 0x9c, 0x84, 0xc4, 0x49, 0x79,
0xa6, 0x83, 0xc3, 0x8a, 0x3d, 0x0e, 0xa9, 0x10 };
err = zstdseek_decompress(buf, hashedlen, NETL2 + 56, datalen);
assert(err > 0);
ret = change_decodehashed(buf, hashedlen, expectedhash, &hashed);
assert(ret == CHANGEFILE_OK);
assert(hashed.version == VERSION);
header = &hashed.header;
assert(strcmp(header->message, "Makefile: add check target") == 0);
assert(header->description == NULL);
assert(strcmp(header->timestamp, "2022-10-31T12:34:27.238139355Z") == 0
);
assert(header->authors.len == 1);
author = &header->authors.map[0];
assert(author->len == 1);
assert(strcmp(author->entries[0].key, "key") == 0);
assert(strcmp(author->entries[0].value,
"AFgzZkD8ARgC21gkkCvxPV5HbL7YDVzgYEhbAxQG7UQY") == 0);
assert(hashed.dependencies.len == 1);
assert(hashed.extraknown.len == 4);
/* assert hunks as expected */
assert(hashed.hunks.len == 2);
hashedfree(&hashed);
}
int
main()
{
test_changefileoffsets();
test_changefileoffsetsbad();
test_changefileoffsetsversion();
test_decodehashed();
}
cases = [
'change',
]
foreach testcase : cases
srcs = files('test_@0@.c'.format(testcase))
exe = executable('test_@0@'.format(testcase), srcs, link_with : lib)
test(testcase, exe, suite : 'unit', timeout : 120)
endforeach
/**
* A fairly simple change that has just two hunks: an edit and a replacement.
*/
unsigned char NETL2[] = {
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x03, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x23, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x03, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x4f, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xb5, 0x2f, 0xfd,
0x00, 0x60, 0x95, 0x06, 0x00, 0x84, 0x0b, 0x06, 0x00, 0x1a, 0x4d, 0x61,
0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x3a, 0x20, 0x61, 0x64, 0x64, 0x20,
0x63, 0x68, 0x65, 0x63, 0x6b, 0x20, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
0x00, 0x1e, 0x32, 0x30, 0x32, 0x32, 0x2d, 0x31, 0x30, 0x2d, 0x33, 0x31,
0x54, 0x31, 0x32, 0x3a, 0x33, 0x34, 0x3a, 0x32, 0x37, 0x2e, 0x32, 0x33,
0x38, 0x31, 0x33, 0x39, 0x33, 0x35, 0x35, 0x5a, 0x01, 0x03, 0x6b, 0x65,
0x79, 0x2c, 0x41, 0x46, 0x67, 0x7a, 0x5a, 0x6b, 0x44, 0x38, 0x41, 0x52,
0x67, 0x43, 0x32, 0x31, 0x67, 0x6b, 0x6b, 0x43, 0x76, 0x78, 0x50, 0x56,
0x35, 0x48, 0x62, 0x4c, 0x37, 0x59, 0x44, 0x56, 0x7a, 0x67, 0x59, 0x45,
0x68, 0x62, 0x41, 0x78, 0x51, 0x47, 0x37, 0x55, 0x51, 0x59, 0xce, 0x1c,
0x40, 0xeb, 0x89, 0x5a, 0xef, 0xf0, 0x24, 0xdc, 0x57, 0x1d, 0x3d, 0x8a,
0x0a, 0xb3, 0x7c, 0x7b, 0x1c, 0x8d, 0x23, 0x74, 0xbe, 0xc1, 0x54, 0x69,
0x68, 0x57, 0xf6, 0x52, 0xcb, 0x16, 0x04, 0x0e, 0xee, 0xba, 0xbd, 0xa2,
0xe0, 0x9c, 0xb5, 0x8b, 0x65, 0xa1, 0x37, 0xcc, 0x7c, 0xe8, 0x54, 0xd8,
0x5f, 0x09, 0x39, 0xa0, 0x25, 0x97, 0x53, 0x78, 0x66, 0xb0, 0x67, 0xe5,
0x49, 0xcb, 0xff, 0x01, 0x00, 0x00, 0x00, 0xd0, 0xb4, 0x42, 0xe8, 0x09,
0x00, 0x79, 0x6a, 0x3c, 0x4a, 0x81, 0x17, 0x97, 0x9c, 0x20, 0xf3, 0x0a,
0xb1, 0x27, 0x95, 0x1d, 0x46, 0x65, 0x5f, 0xc9, 0x77, 0x00, 0x58, 0x28,
0xb5, 0x2f, 0xfd, 0x00, 0x60, 0x85, 0x05, 0x00, 0x04, 0x0a, 0xca, 0x9b,
0x81, 0x3f, 0xc1, 0x1d, 0xf2, 0x94, 0x32, 0x9a, 0x26, 0x91, 0x73, 0x11,
0xbd, 0x54, 0x04, 0x9b, 0x02, 0x87, 0xd7, 0x80, 0xe2, 0xe0, 0x11, 0xef,
0xcd, 0xb8, 0x01, 0x00, 0x00, 0x00, 0xc3, 0x2d, 0x16, 0x4a, 0x1e, 0xad,
0x06, 0x63, 0x03, 0xd2, 0xed, 0x42, 0x93, 0xeb, 0x6f, 0xcc, 0xb9, 0xc0,
0xc8, 0xc5, 0x7b, 0xcf, 0xa0, 0xfa, 0x41, 0x82, 0x73, 0x12, 0xdc, 0x84,
0x32, 0x86, 0x01, 0x00, 0x00, 0x00, 0x62, 0xfb, 0x76, 0x64, 0x94, 0xbb,
0x24, 0x60, 0x76, 0xd2, 0xde, 0x5e, 0xd5, 0x3a, 0x34, 0xeb, 0x46, 0x0a,
0x6a, 0xf8, 0x57, 0xbf, 0xe1, 0xdb, 0xd0, 0x35, 0xcf, 0x00, 0x39, 0x62,
0x77, 0xc8, 0x00, 0x02, 0x06, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0e, 0xee,
0xba, 0xbd, 0xa2, 0xe0, 0x9c, 0xb5, 0x8b, 0x65, 0xa1, 0x37, 0xcc, 0x7c,
0xe8, 0x54, 0xd8, 0x5f, 0x09, 0x39, 0xa0, 0x25, 0x97, 0x53, 0x78, 0x66,
0xb0, 0x67, 0xe5, 0x49, 0xcb, 0xff, 0xa5, 0x02, 0x00, 0x52, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0e, 0xee,
0xba, 0xbd, 0x05, 0x00, 0x06, 0xac, 0xd0, 0x83, 0x77, 0xc0, 0xb1, 0x15,
0x27, 0xa5, 0x0b, 0xcc, 0x28, 0xb5, 0x2f, 0xfd, 0x00, 0x60, 0x35, 0x04,
0x00, 0x64, 0x06, 0xa2, 0xe0, 0x9c, 0xb5, 0x8b, 0x65, 0xa1, 0x37, 0xcc,
0x7c, 0xe8, 0x54, 0xd8, 0x5f, 0x09, 0x39, 0xa0, 0x25, 0x97, 0x53, 0x78,
0x66, 0xb0, 0x67, 0xe5, 0x49, 0xcb, 0xff, 0xca, 0x01, 0x00, 0x08, 0x00,
0x4d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x2f, 0x01, 0x05, 0x55,
0x54, 0x46, 0x2d, 0x38, 0x07, 0x00, 0x00, 0x00, 0x01, 0x81, 0x01, 0x01,
0x00, 0x00, 0x00, 0x0e, 0xee, 0xba, 0xbd, 0xfe, 0x02, 0xce, 0x1c, 0x40,
0xeb, 0x89, 0x5a, 0xef, 0xf0, 0x24, 0xdc, 0x57, 0x1d, 0x3d, 0x8a, 0x0a,
0xb3, 0x7c, 0x7b, 0x1c, 0x8d, 0x23, 0x74, 0xbe, 0xc1, 0x54, 0x69, 0x68,
0x57, 0xf6, 0x52, 0xcb, 0x16, 0x15, 0x06, 0x33, 0x06, 0x0c, 0x00, 0x0a,
0x73, 0x03, 0xb0, 0x32, 0x58, 0x00, 0x1f, 0xaa, 0x06, 0x00, 0xc3, 0x68,
0x60, 0x0c, 0xea, 0x82, 0x5d, 0x46, 0x2c, 0x19, 0xcf, 0xcc, 0x07, 0x0c,
0xfe, 0x02, 0x4e, 0x28, 0xb5, 0x2f, 0xfd, 0x00, 0x60, 0xf5, 0x03, 0x00,
0x24, 0x06, 0x78, 0x66, 0xb0, 0x67, 0xe5, 0x49, 0xcb, 0xff, 0xca, 0x01,
0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0e, 0xee, 0xba, 0xbd, 0xa2, 0xe0,
0x9c, 0xb5, 0x8b, 0x65, 0xa1, 0x37, 0xcc, 0x7c, 0xe8, 0x54, 0xd8, 0x5f,
0x09, 0x39, 0xa0, 0x25, 0x97, 0x53, 0xfe, 0x02, 0x00, 0x01, 0x53, 0x77,
0x08, 0x4d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x37, 0x05, 0x55,
0x54, 0x46, 0x2d, 0x38, 0x01, 0x00, 0x00, 0x00, 0x2b, 0xec, 0x22, 0x87,
0xe4, 0xa3, 0x3f, 0x21, 0x88, 0x9a, 0x3e, 0x30, 0x84, 0xe3, 0xf1, 0xcb,
0x56, 0xc1, 0x0f, 0x17, 0xf3, 0x09, 0x01, 0x7d, 0x56, 0x5d, 0xba, 0x1a,
0xf8, 0x91, 0x99, 0xc3, 0x0a, 0x00, 0xac, 0x2c, 0x90, 0x40, 0x0e, 0x6b,
0x3e, 0x81, 0x11, 0x51, 0x12, 0xcc, 0x65, 0x71, 0x31, 0xee, 0x8c, 0xb8,
0x22, 0xee, 0x42, 0x40, 0x01, 0xbc, 0x5e, 0x2a, 0x4d, 0x18, 0x39, 0x00,
0x00, 0x00, 0xdb, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xf0, 0xe1,
0xe4, 0xb8, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x86, 0xdf,
0x60, 0x45, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x88, 0x6d,
0x55, 0x04, 0x87, 0x00, 0x00, 0x00, 0xd9, 0x00, 0x00, 0x00, 0x42, 0x33,
0x02, 0x89, 0x04, 0x00, 0x00, 0x00, 0x80, 0xb1, 0xea, 0x92, 0x8f, 0x28,
0xb5, 0x2f, 0xfd, 0x00, 0x60, 0x41, 0x03, 0x00, 0x7b, 0x22, 0x73, 0x69,
0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x3a, 0x22, 0x35, 0x46,
0x57, 0x4d, 0x47, 0x79, 0x53, 0x46, 0x33, 0x32, 0x6f, 0x58, 0x6d, 0x73,
0x6a, 0x48, 0x64, 0x61, 0x67, 0x5a, 0x65, 0x6e, 0x4b, 0x57, 0x45, 0x35,
0x67, 0x79, 0x78, 0x50, 0x51, 0x65, 0x52, 0x4d, 0x43, 0x48, 0x41, 0x50,
0x51, 0x41, 0x59, 0x6d, 0x65, 0x46, 0x59, 0x7a, 0x63, 0x64, 0x6b, 0x5a,
0x73, 0x42, 0x6e, 0x31, 0x43, 0x43, 0x4d, 0x52, 0x67, 0x66, 0x72, 0x53,
0x42, 0x51, 0x36, 0x77, 0x61, 0x63, 0x50, 0x51, 0x79, 0x79, 0x67, 0x62,
0x4e, 0x50, 0x31, 0x67, 0x78, 0x7a, 0x4d, 0x52, 0x61, 0x6b, 0x34, 0x45,
0x32, 0x73, 0x22, 0x7d, 0x5e, 0x2a, 0x4d, 0x18, 0x15, 0x00, 0x00, 0x00,
0x71, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x6a, 0xa6, 0x03, 0x07,
0x01, 0x00, 0x00, 0x00, 0x80, 0xb1, 0xea, 0x92, 0x8f, 0x28, 0xb5, 0x2f,
0xfd, 0x00, 0x60, 0xc1, 0x03, 0x00, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x3a,
0x20, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x0a, 0x09, 0x2e, 0x2f, 0x61, 0x6e,
0x69, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x41, 0x47, 0x42,
0x4c, 0x51, 0x4f, 0x51, 0x49, 0x42, 0x36, 0x44, 0x53, 0x44, 0x32, 0x41,
0x50, 0x4f, 0x36, 0x4c, 0x32, 0x50, 0x4c, 0x46, 0x58, 0x46, 0x37, 0x43,
0x52, 0x57, 0x4b, 0x41, 0x44, 0x36, 0x56, 0x41, 0x57, 0x49, 0x41, 0x44,
0x36, 0x46, 0x32, 0x44, 0x54, 0x48, 0x34, 0x45, 0x49, 0x52, 0x59, 0x52,
0x51, 0x43, 0x0a, 0x0a, 0x00, 0x2e, 0x50, 0x48, 0x4f, 0x4e, 0x59, 0x3a,
0x20, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x20, 0x66, 0x6d, 0x74, 0x20, 0x62,
0x75, 0x69, 0x6c, 0x64, 0x20, 0x67, 0x72, 0x61, 0x70, 0x68, 0x20, 0x63,
0x68, 0x65, 0x63, 0x6b, 0x0a, 0x00, 0x5e, 0x2a, 0x4d, 0x18, 0x15, 0x00,
0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x41, 0x5a,
0x84, 0x0b, 0x01, 0x00, 0x00, 0x00, 0x80, 0xb1, 0xea, 0x92, 0x8f
};
unsigned int NETL2LEN = 1103;
'-g3',
'-D_POSIX_C_SOURCE=200809L',
# Extra flags from https://nullprogram.com/blog/2023/04/29/
'-Wdouble-promotion',
'-Wconversion',
#'-fsanitize-undefined-trap-on-error',
#'-fsanitize=address', # can't have this one because of the assembly :-(
'-fno-diagnostics-color',
language: 'c'
'-g3',
'-D_POSIX_C_SOURCE=200809L',
# Extra flags from https://nullprogram.com/blog/2023/04/29/
'-Wdouble-promotion',
'-Wconversion',
#'-fsanitize-undefined-trap-on-error',
#'-fsanitize=address', # can't have this one because of the assembly :-(
'-fno-diagnostics-color',
language: 'c'
#ifndef ANI_CHANGEFILE_H
#define ANI_CHANGEFILE_H
struct mbuf {
size len;
u8 *buf;
};
/**
* Populate a changefile from a file descriptor - returns a negative
* number to indicate some kind of failure.
*/
int mkmbuf(int fd, struct mbuf *);
/**
* Free an mbuf
*/
void freembuf(struct mbuf *b);
#endif
#include <stdint.h>
#include <stddef.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include "common.h"
#include "scaffold.h"
#include "types.h"
#include "mbuf.h"
// https://nullprogram.com/blog/2023/09/27/
// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1428r0.pdf
// mmap()
int
mkmbuf(int fd, struct mbuf *ch)
{
struct stat sb = { 0 };
u8 *addr;
if (fstat(fd, &sb) == -1)
return -1;
addr = mmap(NULL, (size_t)sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (addr == MAP_FAILED)
return -1;
ch->buf = addr;
ch->len = sb.st_size;
return 0;
}
void
freembuf(struct mbuf *b)
{
/* FIXME check return code of munmap() */
munmap(b->buf, (usize)b->len);
}
enum change_err {
VERMISMATCH = -1
enum error {
/* all good */
CHANGEFILE_OK,
/* .change file is too short */
CHANGEFILE_TOOSHORT,
/* .change file length mismatch */
CHANGEFILE_LENGTHMISMATCH,
/* version specified in file is not supported */
CHANGEFILE_UNSUPPORTEDVERSION,
/* hash mismatch */
CHANGEFILE_HASHMISMATCH,
size_t i;
if (len == 0) {
printf("%s = []\n", name);
return;
}
printf("%s = [%u", name, buf[0]);
for (i = 1; i < len; i++)
printf(", %u", buf[i]);
printf("]\n");
}
int
change_decode_offsets(int fd, struct offsets *off)
{
ssize_t r;
uint8_t buf[OFFSETS_SIZE];
if ((r = read(fd, buf, OFFSETS_SIZE)) < OFFSETS_SIZE) {
int err;
if (r >= 0) {
printf("error: not enough was read: %ld\n", r);
err = -2;
} else {
printf("error: not enough was read: %ld\n", r);
err = -1;
}
return err;
}
if (ch->len < OFFSETS_SIZE)
return CHANGEFILE_TOOSHORT;
/**
* Read in an expected amount of bytes (or die failing), and decompress with
* zstd-seekable. Return a malloc()'ed byte buffer that the caller is in charge
* of free()'ing.
*/
static uint8_t *
decompress_segment(int fd, size_t compressed_len, uint64_t expected_len)
static char *
change_strerror(enum error err)
uint8_t *compressed;
uint8_t *buf;
ssize_t compressed_read;
size_t result;
compressed = calloc(sizeof(uint8_t), compressed_len);
if (compressed == NULL) {
perror("malloc compressed");
exit(1);
switch (err) {
case CHANGEFILE_OK:
return "ok";
case CHANGEFILE_TOOSHORT:
return ".change file is too short";
case CHANGEFILE_LENGTHMISMATCH:
return "length in .change file mismatch with actual length";
case CHANGEFILE_UNSUPPORTEDVERSION:
return ".change file version not supported";
case CHANGEFILE_HASHMISMATCH:
return "hash mismatch";
buf = calloc(sizeof(uint8_t), expected_len);
if (buf == NULL) {
perror("malloc buf");
exit(1);
}
/* Read data into compressed */
compressed_read = read(fd, compressed, compressed_len);
if (compressed_read != (ssize_t)compressed_len) {
fprintf(stderr, "error: Expected to read %lu bytes, got %lu\n",
compressed_len, compressed_read);
goto errout;
}
result = zstdseek_decompress(
buf, expected_len, compressed, compressed_len
);
if (result)
goto out;
errout:
free(buf);
buf = NULL;
out:
free(compressed);
return buf;
die("unhandled error variant: %d\n", err);
/**
*
*/
static int
change_decode_hashed(
int fd, size_t comp_hashed_len, size_t hashed_len,
uint8_t *expected_hash, struct hashed *hashed
enum error
change_decodehashed(
u8 *data, usize datalen, u8 *expectedhash, struct hashed *hashed
uint8_t *buf;
uint8_t computed_hash[BLAKE3_LEN];
size_t i;
int err = 0;
uint64_t len;
u8 computedhash[BLAKE3_LEN];
buf = decompress_segment(fd, comp_hashed_len, hashed_len);
if (buf == NULL) {
fprintf(stderr, "error: failed to decompress hashed segment\n");
return 1;
/*Step 1. Compute the hash */
if (expectedhash != NULL) {
blake3_hash(computedhash, data, datalen);
if (blake3_cmp(computedhash, expectedhash))
return CHANGEFILE_HASHMISMATCH;
/* Step 1. Compute the hash of the result buf! */
blake3_hash(computed_hash, buf, hashed_len);
if (expected_hash != NULL) {
err = blake3_cmp(computed_hash, expected_hash);
if (err) {
fprintf(stderr,
"error: change hash mismatch, claimed \n");
for (i = 0; i < BLAKE3_LEN; i++)
fprintf(stderr, "%02x", expected_hash[i]);
fprintf(stderr, ", computed ");
for (i = 0; i < BLAKE3_LEN; i++)
fprintf(stderr, "%02x", expected_hash[i]);
fprintf(stderr, "\n");
}
}
bc.avail = datalen;
bc.buf = data;
len = bincode_getu64(&bc);
hashed->header.message = xmalloc(len + 1);
bincode_getstr(&bc, hashed->header.message, len);
change_readstr(&hashed->header.message, &bc);
len = bincode_getu64(&bc);
len = change_readstr(&hashed->header.timestamp, &bc);
printf("warning: timestamp field has unexpected length %lu (expected 30)\n",
len);
hashed->header.timestamp = xmalloc(len + 1);
bincode_getstr(&bc, hashed->header.timestamp, len);
fprintf(stderr,
"warning: timestamp field has unexpected length %lu\n",
len);
/* Hash of contents */
free(buf);
* Read the contents section of the open change file, decompress it and return
* the raw bytes.
*
* Returns a malloc()'ed byte buffer that the caller is responsible for
* deallocating. Returns NULL if the full contents could not be read, or the
* decompression failed, or some other error occurred.
*/
uint8_t *
change_read_contents(int fd, struct offsets *off)
{
return decompress_segment(
fd, off->total - off->contents_off, off->contents_len
);
}
/**
err = change_decode_offsets(fd, off);
if (err != 0) {
printf("error: failed to decode offsets\n");
cherr = changefileoffsets(&buf, off);
if (cherr != CHANGEFILE_OK) {
printf("error: %s\n", change_strerror(cherr));
err = (int)cherr;
if (off->version != VERSION && off->version != VERSION_NOENC) {
printf("Version %lu is not yet supported, sorry.\n",
off->version);
err = off->version;
scratch = xmalloc(sizeof(u8) * off->hashed_len);
r = zstdseek_decompress(
scratch, off->hashed_len, buf.buf + OFFSETS_SIZE,
off->unhashed_off - OFFSETS_SIZE
);
if (!r) {
err = 1;
free(scratch);
err = change_decode_hashed(
fd, off->unhashed_off - OFFSETS_SIZE, off->hashed_len,
contents_hash, hashed
err = change_decodehashed(
scratch, off->hashed_len, contents_hash, hashed
/**
* Seek to the point in the file where contents begin. Keep in mind,
* we've already read 56 bytes.
*/
if (lseek(fd, (off_t)off->contents_off, SEEK_SET) == -1) {
perror("lseek");
goto out;
c->contents = xmalloc(sizeof(u8) * off->contents_len);
r = zstdseek_decompress(
c->contents, off->contents_len, buf.buf + off->contents_off,
off->total - off->contents_off
);
if (!r) {
err = 1;
free(c->contents);