From d08d4b2455e2897266351717c5110e6503866520 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Tue, 16 Dec 2025 13:54:18 +0100 Subject: [PATCH] Add tinyfs.SliceBlockDev that will "partition" a block dev This is useful to create a fixed-size BlockDevice from machine.Flash, which can vary when the firmware changes (and might require a format each time it crosses a page boundary). Tested with littlefs, it seems to be working fine. --- slice.go | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 slice.go diff --git a/slice.go b/slice.go new file mode 100644 index 0000000..90ab0b1 --- /dev/null +++ b/slice.go @@ -0,0 +1,75 @@ +package tinyfs + +import ( + "errors" + "math/bits" +) + +var ( + ErrInvalidBlockSize = errors.New("tinyfs: write/erase block size too big or not aligned") + ErrDeviceTooSmall = errors.New("tinyfs: requested device size is smaller than underlying device") +) + +type blockDevOffset struct { + dev BlockDevice + offset int64 +} + +// Create a new virtual BlockDevice that is the upper part of the given full +// BlockDevice, with the given size. This can be useful for example to use only +// a fixed size machine.Flash object that won't change on firmware changes. +func SliceBlockDev(full BlockDevice, size int64) (BlockDevice, error) { + if bits.OnesCount16(uint16(full.WriteBlockSize())) != 1 || bits.OnesCount16(uint16(full.EraseBlockSize())) != 1 { + // Check that the write/erase block size is a power of two. + // This is normally the case, and makes the following calculations + // easier. + return nil, ErrInvalidBlockSize + } + + // Check whether the requested size can be used (is a multiple of the block + // size). + blockSize := max(full.EraseBlockSize(), full.WriteBlockSize()) + if size/blockSize*blockSize != size { + return nil, ErrInvalidBlockSize + } + + fullSize := full.Size() + if fullSize < size { + // Not big enough to provide the expected device size. + return nil, ErrDeviceTooSmall + } + if fullSize == size { + // Shortcut: the requested size is exactly big enough. + return full, nil + } + offset := fullSize - size + return &blockDevOffset{ + dev: full, + offset: offset, + }, nil +} + +func (b *blockDevOffset) ReadAt(buf []byte, offset int64) (n int, err error) { + return b.dev.ReadAt(buf, offset+b.offset) +} + +func (b *blockDevOffset) WriteAt(buf []byte, offset int64) (n int, err error) { + return b.dev.WriteAt(buf, offset+b.offset) +} + +func (b *blockDevOffset) Size() int64 { + return b.dev.Size() - b.offset +} + +func (b *blockDevOffset) EraseBlockSize() int64 { + return b.dev.EraseBlockSize() +} + +func (b *blockDevOffset) WriteBlockSize() int64 { + return b.dev.WriteBlockSize() +} + +func (b *blockDevOffset) EraseBlocks(start, len int64) error { + blockOffset := b.offset / b.EraseBlockSize() + return b.dev.EraseBlocks(start+blockOffset, len) +}