From 21225c218b5bd7275e74d1c090540272fce1be07 Mon Sep 17 00:00:00 2001 From: Andrew Gunnerson Date: Thu, 2 Mar 2023 15:58:06 -0500 Subject: [PATCH] Produce sparse output files This significantly reduces the size when patching dummy OTAs. This is a naive implementation that just seeks when the entire write() buffer is all zeroes, but that seems to be more than sufficient. The output files have fewer blocks allocated than the (dummy) input files. Signed-off-by: Andrew Gunnerson --- avbroot/ota.py | 6 +++++- avbroot/util.py | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/avbroot/ota.py b/avbroot/ota.py index 824d4ea..37bf965 100644 --- a/avbroot/ota.py +++ b/avbroot/ota.py @@ -659,7 +659,11 @@ class _TeeFileDescriptor: self.capture.write(data) else: for stream in self.streams: - stream.write(data) + # Naive hole punching to create sparse files + if stream is self.backing and util.is_zero(data): + stream.seek(len(data), os.SEEK_CUR) + else: + stream.write(data) return len(data) diff --git a/avbroot/util.py b/avbroot/util.py index e52a70f..19cd5c7 100644 --- a/avbroot/util.py +++ b/avbroot/util.py @@ -3,6 +3,9 @@ import os import tempfile +_ZERO_BLOCK = memoryview(b'\0' * 16384) + + @contextlib.contextmanager def open_output_file(path): ''' @@ -145,3 +148,21 @@ def read_exact(f, size: int) -> bytes: return bytes(data) else: return data + + +def is_zero(data): + ''' + Check if all bytes in the bytes-like object are null bytes. + ''' + + view = memoryview(data) + + while view: + n = min(len(view), len(_ZERO_BLOCK)) + + if view[:n] != _ZERO_BLOCK[:n]: + return False + + view = view[n:] + + return True