shithub: neoventi

Download patch

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;
 		}
--