blob: 72a3cb985d493b814fc162e06ec4c5cf548181f5 [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
2// SPDX-License-Identifier: Apache-2.0
3
Lorenz Brun1e0e3a42023-06-28 16:40:18 +02004package blockdev
5
6import (
7 "errors"
8 "fmt"
9 "io"
10 "math/bits"
11)
12
13// Memory is a memory-backed implementation of BlockDev. It is optimal
14// for testing and temporary use, as it is fast and platform-independent.
15type Memory struct {
16 blockSize int64
17 blockCount int64
18 data []byte
19}
20
21// NewMemory returns a new memory-backed block device with the given geometry.
22func NewMemory(blockSize, blockCount int64) (*Memory, error) {
23 if blockSize <= 0 {
24 return nil, errors.New("block size cannot be zero or negative")
25 }
26 if bits.OnesCount64(uint64(blockSize)) > 1 {
27 return nil, fmt.Errorf("block size must be a power of two (got %d)", blockSize)
28 }
29 if blockCount < 0 {
30 return nil, errors.New("block count cannot be negative")
31 }
32 return &Memory{
33 blockSize: blockSize,
34 blockCount: blockCount,
35 data: make([]byte, blockSize*blockCount),
36 }, nil
37}
38
39// MustNewMemory works exactly like NewMemory, but panics when NewMemory would
40// return an error. Intended for use in tests.
41func MustNewMemory(blockSize, blockCount int64) *Memory {
42 m, err := NewMemory(blockSize, blockCount)
43 if err != nil {
44 panic(err)
45 }
46 return m
47}
48
Jan Schära6da1712024-08-21 15:12:11 +020049func (m *Memory) ReadAt(p []byte, off int64) (n int, err error) {
50 if off < 0 {
51 return 0, errors.New("blockdev.Memory.ReadAt: negative offset")
52 }
53 if off > int64(len(m.data)) {
Lorenz Brun1e0e3a42023-06-28 16:40:18 +020054 return 0, io.EOF
55 }
56 // TODO: Alignment checks?
Jan Schära6da1712024-08-21 15:12:11 +020057 n = copy(p, m.data[off:])
Lorenz Brun1e0e3a42023-06-28 16:40:18 +020058 if n < len(p) {
Jan Schära6da1712024-08-21 15:12:11 +020059 err = io.EOF
Lorenz Brun1e0e3a42023-06-28 16:40:18 +020060 }
Jan Schära6da1712024-08-21 15:12:11 +020061 return
Lorenz Brun1e0e3a42023-06-28 16:40:18 +020062}
63
Jan Schära6da1712024-08-21 15:12:11 +020064func (m *Memory) WriteAt(p []byte, off int64) (n int, err error) {
65 if off < 0 || off > int64(len(m.data)) {
66 return 0, ErrOutOfBounds
Lorenz Brun1e0e3a42023-06-28 16:40:18 +020067 }
68 // TODO: Alignment checks?
Jan Schära6da1712024-08-21 15:12:11 +020069 n = copy(m.data[off:], p)
Lorenz Brun1e0e3a42023-06-28 16:40:18 +020070 if n < len(p) {
Jan Schära6da1712024-08-21 15:12:11 +020071 err = ErrOutOfBounds
Lorenz Brun1e0e3a42023-06-28 16:40:18 +020072 }
Jan Schära6da1712024-08-21 15:12:11 +020073 return
Lorenz Brun1e0e3a42023-06-28 16:40:18 +020074}
75
76func (m *Memory) BlockCount() int64 {
77 return m.blockCount
78}
79
Jan Schära6da1712024-08-21 15:12:11 +020080func (m *Memory) BlockSize() int64 {
81 return m.blockSize
82}
83
Lorenz Brun1e0e3a42023-06-28 16:40:18 +020084func (m *Memory) OptimalBlockSize() int64 {
85 return m.blockSize
86}
87
Lorenz Brun1e0e3a42023-06-28 16:40:18 +020088func (m *Memory) Discard(startByte, endByte int64) error {
Jan Schära6da1712024-08-21 15:12:11 +020089 if err := validAlignedRange(m, startByte, endByte); err != nil {
Lorenz Brun1e0e3a42023-06-28 16:40:18 +020090 return err
91 }
92 for i := startByte; i < endByte; i++ {
93 // Intentionally don't set to zero as Discard doesn't guarantee
94 // any specific contents. Call Zero if you need this.
95 m.data[i] = 0xaa
96 }
97 return nil
98}
99
100func (m *Memory) Zero(startByte, endByte int64) error {
Jan Schära6da1712024-08-21 15:12:11 +0200101 if err := validAlignedRange(m, startByte, endByte); err != nil {
Lorenz Brun1e0e3a42023-06-28 16:40:18 +0200102 return err
103 }
104 for i := startByte; i < endByte; i++ {
105 m.data[i] = 0x00
106 }
107 return nil
108}
Jan Schära6da1712024-08-21 15:12:11 +0200109
110func (m *Memory) Sync() error {
111 return nil
112}