ref: bec6ed73e136c4b8cc960230d9f7439ade29184a
parent: c6f0a3df1217072ca732f0e3550bc07c4a93891b
author: Noam Preil <noam@pixelhero.dev>
date: Mon Jul 28 07:45:24 EDT 2025
disk: fix access to empty buckets
--- a/disk.c
+++ b/disk.c
@@ -77,8 +77,18 @@
return 0;
}
}
- if(s_sect->bucketmagic && U32GET(buf + 2) != s_sect->bucketmagic)
- sysfatal("index is corrupt: invalid bucket magic: sect %ux, buck %ux", s_sect->bucketmagic, U32GET(buf + 2));+ if(s_sect->bucketmagic && U32GET(buf + 2) != s_sect->bucketmagic){+ if(U32GET(buf+2) != 0)
+ sysfatal("index is corrupt: invalid bucket magic: sect %ux, buck %ux", s_sect->bucketmagic, U32GET(buf + 2));+ else{+ cacheunlock(key, bucket & 0xffff);
+ // invoking code should not care about the
+ // distinction between "bucket is empty" and
+ // "bucket has data but not what we want."
+ werrstr("entry not found in bucket");+ return 0;
+ }
+ }
if(!bucketlookup(buf, score, &entry)){cacheunlock(key, bucket & 0xffff);
werrstr("entry not found in bucket");--- a/notebook
+++ b/notebook
@@ -2779,3 +2779,25 @@
Okay, so, we need bucket initialization code, too.
Commit as is and add that, then..
+
+venti/write of empty data works, probably because libventi doesn't actually make a call for that, it just shorts it out. okay, let's try writing a single byte.
+
+With some debug prints:
+
+read 8 bytes from connection
+part of packet is missing, branching elsewhere??
+read 2 bytes from connection
+extracting packet into buffer...
+handling packet...
+computing score for write of len 2
+./7.neoventi: index is corrupt: invalid bucket magic: sect c15cf58e, buck 0
+
+Okay, so, the first read misses some of the packet and we need a second one, that's fine. Then we run into the same issue as on the read side: bucket isn't actually initialized.
+
+What's behavior of venti?
+
+On bucket read, if magic is expected but doesn't match, set entry count to zero, rather than rejecting the bucket?? Okay, then. And then presumably on write itll be set.
+
+Okay, so - we see a read (or a write) that accesses an index bucket. That bucket hasn't been initialized. Thus, we can immediately treat the read as a failure, and the write as dirty.
+
+That works fine for reads, at least, though errors aren't propagated to the client correctly.
--- a/server.c
+++ b/server.c
@@ -164,6 +164,7 @@
// buffer, and read more into the buffer _after_ it.
// Then, repeat.
n = read(conn.fd, buf+sz, 0x10000-sz);
+ print("read %d bytes from connection\n", n);sz += n;
if(sz == 0)
// No data, and none will be coming.
@@ -174,6 +175,7 @@
// As long as there's a complete packet, process it.
u16int len = U16GET(buf + offset);
if(2 + offset + len > sz){+ print("part of packet is missing, branching elsewhere??\n");// missing part of packet!
break;
}
@@ -184,7 +186,9 @@
// anyways? Don't want allocation churn, but we can just move packetbuf
// into VtConn and pass both the source and destination buffer around a lot.
// Not important right now.
+ print("extracting packet into buffer...\n");memcpy(packetbuf, buf+offset, 2+len);
+ print("handling packet...\n");vtconnhandle(conn, packetbuf, len);
offset += 2+len;
}
--
⑨