summary refs log tree commit diff
path: root/ripple/fossil
diff options
context:
space:
mode:
authoredef <edef@unfathomable.blue>2022-04-28 20:27:51 +0000
committeredef <edef@unfathomable.blue>2022-04-28 20:29:27 +0000
commit92a503ba90adf9a319b25306414e4012a1214260 (patch)
tree0cee9173a1e0eb2aa64c05b01e7c8b4cc1e4907b /ripple/fossil
parenta9ca2be90b9a927d16d5ae64c09cf664cc73536d (diff)
downloadunf-legacy-92a503ba90adf9a319b25306414e4012a1214260.tar.zst
ripple/fossil/mount: don't serve short reads
FUSE doesn't actually respect the usual read() contract, so this
resulted in us serving truncated files.

Change-Id: I8bdb0bd7f03162fb78774f3f84daeefc5ba5e3b1
Diffstat (limited to 'ripple/fossil')
-rw-r--r--ripple/fossil/src/bin/mount.rs23
1 files changed, 21 insertions, 2 deletions
diff --git a/ripple/fossil/src/bin/mount.rs b/ripple/fossil/src/bin/mount.rs
index 3335c1f..2e6945b 100644
--- a/ripple/fossil/src/bin/mount.rs
+++ b/ripple/fossil/src/bin/mount.rs
@@ -215,8 +215,27 @@ impl fuser::Filesystem for Filesystem {
 					offset as u64
 				);
 
-				let mut buffer = vec![0; size];
-				let n = contents.read(&mut buffer).unwrap();
+				// NOTE: FUSE read() doesn't actually work like you expect.
+				// If you return a short read, it *will* simply treat that
+				// as the file being short. `contents` is an `io::Read`,
+				// which adheres to the usual contract, so we partially
+				// reimplement `read_exact` here.
+				let mut buffer = vec![0u8; size];
+				let n = {
+					let mut buffer = &mut buffer[..];
+					let mut total = 0;
+					loop {
+						match contents.read(buffer).unwrap() {
+							0 => break,
+							n => {
+								buffer = &mut buffer[n..];
+								total += n;
+							}
+						}
+					}
+					total
+				};
+
 				reply.data(&buffer[..n]);
 			}
 			_ => reply.error(EINVAL),