Skip to main content

ax_fs/
dev.rs

1// Copyright 2025 The Axvisor Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Block device abstraction for disk operations
16
17use alloc::sync::Arc;
18
19use ax_driver::prelude::*;
20use spin::Mutex;
21
22const BLOCK_SIZE: usize = 512;
23
24/// A disk device with a cursor.
25#[derive(Clone)]
26pub struct Disk {
27    block_id: u64,
28    offset: usize,
29    dev: Arc<Mutex<AxBlockDevice>>,
30}
31
32impl Disk {
33    /// Create a new disk.
34    pub fn new(dev: AxBlockDevice) -> Self {
35        assert_eq!(BLOCK_SIZE, dev.block_size());
36        Self {
37            block_id: 0,
38            offset: 0,
39            dev: Arc::new(Mutex::new(dev)),
40        }
41    }
42
43    /// Get the size of the disk.
44    pub fn size(&self) -> u64 {
45        let dev = self.dev.lock();
46        dev.num_blocks() * BLOCK_SIZE as u64
47    }
48
49    /// Get the position of the cursor.
50    pub fn position(&self) -> u64 {
51        self.block_id * BLOCK_SIZE as u64 + self.offset as u64
52    }
53
54    /// Set the position of the cursor.
55    pub fn set_position(&mut self, pos: u64) {
56        self.block_id = pos / BLOCK_SIZE as u64;
57        self.offset = pos as usize % BLOCK_SIZE;
58    }
59
60    /// Read within one block, returns the number of bytes read.
61    pub fn read_one(&mut self, buf: &mut [u8]) -> DevResult<usize> {
62        let read_size = if self.offset == 0 && buf.len() >= BLOCK_SIZE {
63            // whole block
64            let mut dev = self.dev.lock();
65            dev.read_block(self.block_id, &mut buf[0..BLOCK_SIZE])?;
66            self.block_id += 1;
67            BLOCK_SIZE
68        } else {
69            // partial block
70            let mut data = [0u8; BLOCK_SIZE];
71            let start = self.offset;
72            let count = buf.len().min(BLOCK_SIZE - self.offset);
73
74            {
75                let mut dev = self.dev.lock();
76                dev.read_block(self.block_id, &mut data)?;
77            }
78            buf[..count].copy_from_slice(&data[start..start + count]);
79            self.offset += count;
80            if self.offset >= BLOCK_SIZE {
81                self.block_id += 1;
82                self.offset -= BLOCK_SIZE;
83            }
84            count
85        };
86        Ok(read_size)
87    }
88
89    /// Write within one block, returns the number of bytes written.
90    pub fn write_one(&mut self, buf: &[u8]) -> DevResult<usize> {
91        let write_size = if self.offset == 0 && buf.len() >= BLOCK_SIZE {
92            // whole block
93            let mut dev = self.dev.lock();
94            dev.write_block(self.block_id, &buf[0..BLOCK_SIZE])?;
95            self.block_id += 1;
96            BLOCK_SIZE
97        } else {
98            // partial block
99            let mut data = [0u8; BLOCK_SIZE];
100            let start = self.offset;
101            let count = buf.len().min(BLOCK_SIZE - self.offset);
102
103            let mut dev = self.dev.lock();
104            dev.read_block(self.block_id, &mut data)?;
105            data[start..start + count].copy_from_slice(&buf[..count]);
106            dev.write_block(self.block_id, &data)?;
107
108            self.offset += count;
109            if self.offset >= BLOCK_SIZE {
110                self.block_id += 1;
111                self.offset -= BLOCK_SIZE;
112            }
113            count
114        };
115        Ok(write_size)
116    }
117}
118
119/// A partition wrapper that provides access to a specific partition of a disk.
120pub struct Partition {
121    disk: Arc<Mutex<Disk>>,
122    start_lba: u64,
123    end_lba: u64,
124    position: u64,
125}
126
127impl Partition {
128    /// Create a new partition wrapper.
129    pub fn new(disk: Disk, start_lba: u64, end_lba: u64) -> Self {
130        Self {
131            disk: Arc::new(Mutex::new(disk)),
132            start_lba,
133            end_lba,
134            position: 0,
135        }
136    }
137
138    /// Get the size of the partition.
139    pub fn size(&self) -> u64 {
140        (self.end_lba - self.start_lba + 1) * BLOCK_SIZE as u64
141    }
142
143    /// Get the position of the cursor.
144    pub fn position(&self) -> u64 {
145        self.position
146    }
147
148    /// Set the position of the cursor.
149    pub fn set_position(&mut self, pos: u64) {
150        self.position = pos.min(self.size());
151    }
152
153    /// Read within one block, returns the number of bytes read.
154    pub fn read_one(&mut self, buf: &mut [u8]) -> DevResult<usize> {
155        if self.position >= self.size() {
156            return Ok(0);
157        }
158
159        let remaining = self.size() - self.position;
160        let to_read = buf.len().min(remaining as usize);
161        let buf = &mut buf[..to_read];
162
163        // Calculate the absolute position on the disk
164        let abs_pos = self.start_lba * BLOCK_SIZE as u64 + self.position;
165
166        // Set disk position and read
167        let read_len = {
168            let mut disk = self.disk.lock();
169            disk.set_position(abs_pos);
170            disk.read_one(buf)?
171        };
172
173        self.position += read_len as u64;
174        Ok(read_len)
175    }
176
177    /// Write within one block, returns the number of bytes written.
178    pub fn write_one(&mut self, buf: &[u8]) -> DevResult<usize> {
179        if self.position >= self.size() {
180            return Ok(0);
181        }
182
183        let remaining = self.size() - self.position;
184        let to_write = buf.len().min(remaining as usize);
185        let buf = &buf[..to_write];
186
187        // Calculate the absolute position on the disk
188        let abs_pos = self.start_lba * BLOCK_SIZE as u64 + self.position;
189
190        // Set disk position and write
191        let write_len = {
192            let mut disk = self.disk.lock();
193            disk.set_position(abs_pos);
194            disk.write_one(buf)?
195        };
196
197        self.position += write_len as u64;
198        Ok(write_len)
199    }
200}