mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-28 12:26:52 -06:00
Merge #1146: Guard file generation increments against u64 overflow (@Silvertree2010)
# Conflicts: # apps/figcraft-server/src/store_tests.rs
This commit is contained in:
@@ -1243,7 +1243,10 @@ impl Store {
|
||||
Log { generation, file: f, pending: Vec::new(), entries: kept, bytes, dirty: kept > 0 }
|
||||
}
|
||||
// A new snapshot and log of the next generation, so appends start clean.
|
||||
_ => self.rewrite(file, generation + 1, &server).map_err(|e| e.why)?,
|
||||
_ => {
|
||||
let next = generation.checked_add(1).ok_or("the file's generation can't go any higher")?;
|
||||
self.rewrite(file, next, &server).map_err(|e| e.why)?
|
||||
}
|
||||
};
|
||||
self.entry(file)?.log = Some(log);
|
||||
Ok(server)
|
||||
@@ -1319,7 +1322,11 @@ impl Store {
|
||||
fn compact(&mut self, file: &str, server: &Server) {
|
||||
self.compactions += 1;
|
||||
let Some(generation) = self.files.get(file).and_then(|e| e.log.as_ref()).map(|l| l.generation) else { return };
|
||||
match self.rewrite(file, generation + 1, server) {
|
||||
let Some(next) = generation.checked_add(1) else {
|
||||
log::error!("file {file}: compaction failed: the file's generation can't go any higher");
|
||||
return;
|
||||
};
|
||||
match self.rewrite(file, next, server) {
|
||||
Ok(log) => {
|
||||
if let Ok(e) = self.entry(file) {
|
||||
e.log = Some(log);
|
||||
|
||||
@@ -391,3 +391,30 @@ fn names_and_report_reasons_lose_bidi_controls() {
|
||||
store.remove(&id).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_maxed_out_generation_does_not_wrap() {
|
||||
let dir = TempDir::new("maxgen");
|
||||
let c = StoreConfig { compact_after: 3, ..cfg(dir.path()) };
|
||||
let mut store = Store::open(dir.path(), c.clone()).unwrap();
|
||||
let (id, _, _) = publish(&mut store, "Max");
|
||||
let path = dir.path().join(FILES).join(&id);
|
||||
let (_, state) = disk::decode_snapshot(&fs::read(path.join(SNAPSHOT)).unwrap()).unwrap();
|
||||
// Loading: no log goes with the snapshot, so the next generation would overflow.
|
||||
fs::write(path.join(SNAPSHOT), disk::encode_snapshot(u64::MAX, &state).unwrap()).unwrap();
|
||||
fs::remove_file(path.join(LOG)).unwrap();
|
||||
assert!(store.open(&id).is_err());
|
||||
// Compacting: the open log is at the largest generation.
|
||||
fs::write(path.join(SNAPSHOT), disk::encode_snapshot(u64::MAX - 1, &state).unwrap()).unwrap();
|
||||
let _ = fs::remove_file(path.join(LOG));
|
||||
let mut server = store.open(&id).unwrap();
|
||||
let mut a = join(&mut server, Access::Edit);
|
||||
store.changed(&id, &mut server);
|
||||
for i in 0..4 {
|
||||
rename(&mut server, &mut a, &format!("P{i}"));
|
||||
store.changed(&id, &mut server);
|
||||
store.flush();
|
||||
}
|
||||
let snap = disk::check_snapshot(&fs::read(path.join(SNAPSHOT)).unwrap()).unwrap();
|
||||
assert_eq!(snap, u64::MAX);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user