keos/fs.rs
1//! Filesystem abstraction.
2
3/// Defines traits for file system operations.
4pub mod traits {
5 use alloc::{string::String, vec::Vec};
6
7 use super::{File, FileBlockNumber, InodeNumber};
8 use crate::{KernelError, mm::Page, sync::atomic::AtomicBool};
9
10 /// Trait representing a filesystem.
11 ///
12 /// This trait provides access to the root directory of the filesystem,
13 /// allowing operations on files and directories.
14 pub trait FileSystem
15 where
16 Self: Sync + Send,
17 {
18 /// Retrieves the root directory of the filesystem.
19 ///
20 /// # Returns
21 /// - `Some(Directory)`: A reference to the root directory if available.
22 /// - `None`: If the root directory is inaccessible or the filesystem is
23 /// uninitialized.
24 fn root(&self) -> Option<super::Directory>;
25 }
26
27 /// Trait representing a regular file in the filesystem.
28 ///
29 /// A regular file contains user data and supports basic read and write
30 /// operations.
31 pub trait RegularFile
32 where
33 Self: Send + Sync,
34 {
35 /// Returns the inode number of the file.
36 fn ino(&self) -> InodeNumber;
37
38 /// Returns the size of the file in bytes.
39 fn size(&self) -> usize;
40
41 /// Reads data from the file into the provided buffer.
42 ///
43 /// # Parameters
44 /// - `fba`: The `FileBlockNumber` which to read.
45 /// - `buf`: A mutable array where the file content will be stored.
46 ///
47 /// # Returns
48 /// - `Ok(true)`: If the read success.
49 /// - `Ok(false)`: If the read success.
50 /// - `Err(Error)`: An error occured while the read operation.
51 fn read(&self, fba: FileBlockNumber, buf: &mut [u8; 4096]) -> Result<bool, KernelError>;
52
53 /// Writes a 4096-byte page of data into the specified file block.
54 ///
55 /// This method writes the contents of `buf` to the file block indicated
56 /// by `fba`. If the target block lies beyond the current end of
57 /// the file, the file may be extended up to `new_size` bytes to
58 /// accommodate the write.
59 ///
60 /// However, if the target block lies beyond the current file size
61 /// **and** `min_size` is insufficient to reach it, the write
62 /// will fail.
63 ///
64 /// # Parameters
65 /// - `fba`: The `FileBlockNumber` indicating the block to write to.
66 /// - `buf`: A buffer containing exactly 4096 bytes of data to write.
67 /// - `min_size`: The desired minimum file size (in bytes) after the
68 /// write. If this value is less than or equal to the current file
69 /// size, no growth occurs.
70 ///
71 /// # Returns
72 /// - `Ok(())` if the write is successful.
73 /// - `Err(KernelError)` if the operation fails (e.g., out-of-bounds
74 /// write, I/O error).
75 fn write(
76 &self,
77 fba: FileBlockNumber,
78 buf: &[u8; 4096],
79 min_size: usize,
80 ) -> Result<(), KernelError>;
81
82 /// Maps a file block into memory.
83 ///
84 /// This method retrieves the contents of the file at the specified file
85 /// block number (`fba`) and returns it as a [`Page`] containing the
86 /// contents of the file block.
87 ///
88 /// The memory-mapped page reflects the current contents of the file at
89 /// the requested block, and it can be used for reading or
90 /// modifying file data at page granularity.
91 ///
92 /// # Parameters
93 /// - `fba`: The file block number to map into memory. This is a logical
94 /// offset into the file, measured in fixed-size blocks (not bytes).
95 ///
96 /// # Returns
97 /// - `Ok(Page)`: A reference-counted, in-memory page containing the
98 /// file block's data.
99 /// - `Err(KernelError)`: If the file block cannot be found or loaded
100 /// (e.g., out-of-bounds access).
101 fn mmap(&self, fba: FileBlockNumber) -> Result<Page, KernelError> {
102 let mut page = Page::new();
103 self.read(fba, page.inner_mut().as_mut_array().unwrap())?;
104 Ok(page)
105 }
106
107 /// Write back the file to disk.
108 fn writeback(&self) -> Result<(), KernelError>;
109 }
110
111 /// Trait representing a directory in the filesystem.
112 ///
113 /// A directory contains entries that reference other files or directories.
114 pub trait Directory
115 where
116 Self: Send + Sync,
117 {
118 /// Returns the inode number of the directory.
119 fn ino(&self) -> InodeNumber;
120
121 /// Returns the size of the file in bytes.
122 fn size(&self) -> usize;
123
124 /// Returns the link count of the directory.
125 fn link_count(&self) -> usize;
126
127 /// Opens an entry by name.
128 ///
129 /// # Parameters
130 /// - `entry`: The name of the entry to open.
131 ///
132 /// # Returns
133 /// - `Ok(File)`: The enumerate of the file (e.g., regular file,
134 /// directory).
135 /// - `Err(Error)`: An error if the entry cannot be found or accessed.
136 fn open_entry(&self, entry: &str) -> Result<File, KernelError>;
137
138 /// Create an entry by name.
139 ///
140 /// # Parameters
141 /// - `entry`: The name of the entry to add.
142 /// - `is_dir`: Indicate whether the entry is directory or not.
143 ///
144 /// # Returns
145 /// - `Ok(())`: If the entry was successfully added.
146 /// - `Err(Error)`: An error if the add fails.
147 fn create_entry(&self, entry: &str, is_dir: bool) -> Result<File, KernelError>;
148
149 /// Unlinks a directory entry by name.
150 ///
151 /// # Parameters
152 /// - `entry`: The name of the entry to remove.
153 ///
154 /// # Returns
155 /// - `Ok(())`: If the entry was successfully removed.
156 /// - `Err(Error)`: An error if the removal fails.
157 fn unlink_entry(&self, entry: &str) -> Result<(), KernelError>;
158
159 /// Reads the contents of the directory.
160 ///
161 /// This function lists all the entries within the directory.
162 ///
163 /// # Returns
164 /// - `Ok(())`: If the directory was successfully read.
165 /// - `Err(Error)`: An error if the read operation fails.
166 fn read_dir(&self) -> Result<Vec<(InodeNumber, String)>, KernelError>;
167
168 /// Returns a reference of [`AtomicBool`] which contains whether
169 /// directory is removed.
170 ///
171 /// This is important because directory operations against the removed
172 /// directory will result in undesirable behavior (e.g. unreachable
173 /// file).
174 ///
175 /// # Returns
176 /// - `Ok(())`: If the directory was successfully read.
177 /// - `Err(Error)`: An error if the operation fails.
178 fn removed(&self) -> Result<&AtomicBool, KernelError>;
179 }
180}
181
182use crate::{KernelError, mm::Page, sync::atomic::AtomicBool};
183pub use abyss::dev::{BlockOps, Sector};
184use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};
185use core::{iter::Step, num::NonZeroU32};
186
187/// A global file system abstraction.
188///
189/// The `FileSystem` struct provides an interface for interacting with the file
190/// system, including operations such as opening and creating files.
191///
192/// # Example
193/// ```
194/// let fs = file_system();
195/// if let Some(file) = fs.open("example.txt") {
196/// println!("Opened file: {}", file.name());
197/// }
198/// ```
199pub struct FileSystem {
200 _p: (),
201}
202
203static mut FS: Option<Box<dyn traits::FileSystem>> = None;
204
205impl FileSystem {
206 /// Retrieves the root directory of the filesystem.
207 ///
208 /// # Returns
209 /// - `Directory`: A reference to the root directory.
210 pub fn root() -> Directory {
211 unsafe { FS.as_ref() }
212 .and_then(|fs| fs.root())
213 .expect("Filesystem is not available.")
214 }
215
216 /// Register the global file system.
217 pub fn register(fs: impl traits::FileSystem + 'static) {
218 unsafe {
219 FS = Some(Box::new(fs));
220 }
221 }
222}
223
224/// A handle to a regular file.
225///
226/// This struct provides a reference-counted handle to a file that supports
227/// reading and writing operations at the kernel level.
228#[derive(Clone)]
229pub struct RegularFile(pub Arc<dyn traits::RegularFile>);
230
231impl RegularFile {
232 /// Inode number of the file.
233 pub fn ino(&self) -> InodeNumber {
234 self.0.ino()
235 }
236
237 /// Creates a new [`RegularFile`] handle from a given implementation of
238 /// [`traits::RegularFile`].
239 ///
240 /// This function takes an instance of any type that implements the
241 /// [`traits::RegularFile`] trait, wraps it in a reference-counted
242 /// [`Arc`], and returns a [`RegularFile`] handle.
243 ///
244 /// # Parameters
245 /// - `r`: An instance of a type that implements [`traits::RegularFile`].
246 ///
247 /// # Returns
248 /// A [`RegularFile`] handle that enables reference-counted access to the
249 /// underlying file.
250 pub fn new(r: impl traits::RegularFile + 'static) -> Self {
251 Self(Arc::new(r))
252 }
253
254 /// Returns the size of the file in bytes.
255 #[inline]
256 pub fn size(&self) -> usize {
257 self.0.size()
258 }
259
260 /// Reads data from the file into the provided buffer.
261 ///
262 /// # Parameters
263 /// - `fba`: The `FileBlockNumber` which to read.
264 /// - `buf`: A mutable slice where the file content will be stored.
265 ///
266 /// # Returns
267 /// - `Ok(usize)`: The number of bytes read.
268 /// - `Err(Error)`: An error if the read operation fails.
269 #[inline]
270 pub fn read(&self, mut position: usize, buf: &mut [u8]) -> Result<usize, KernelError> {
271 let mut bounce_buffer = alloc::boxed::Box::new([0; 4096]);
272 let max_read = self
273 .size()
274 .min(position + buf.len())
275 .saturating_sub(position);
276 let mut read_bytes = 0;
277 let first_segment = position & 0xfff;
278 if first_segment != 0 {
279 self.0.read(
280 FileBlockNumber::from_offset(position & !0xfff),
281 &mut bounce_buffer,
282 )?;
283 read_bytes += (0x1000 - first_segment).min(max_read);
284 buf[..read_bytes]
285 .copy_from_slice(&bounce_buffer[first_segment..first_segment + read_bytes]);
286 position += read_bytes;
287 }
288
289 for i in (read_bytes..max_read).step_by(0x1000) {
290 self.0
291 .read(FileBlockNumber::from_offset(position), &mut bounce_buffer)?;
292 let remainder = (max_read - i).min(0x1000);
293 buf[i..i + remainder].copy_from_slice(&bounce_buffer[..remainder]);
294 position += remainder;
295 read_bytes += remainder;
296 }
297 Ok(read_bytes)
298 }
299
300 /// Writes data from the buffer into the file.
301 ///
302 /// If the write position is beyond the current file size, file will be
303 /// extended to minimum size required to reflect the update.
304 ///
305 /// # Parameters
306 /// - `fba`: The `FileBlockNumber` which to write.
307 /// - `buf`: An slice containing the data to write.
308 ///
309 /// # Returns
310 /// - `Ok(usize)`: The number of bytes written.
311 /// - `Err(Error)`: An error if the write operation fails.
312 #[inline]
313 pub fn write(&self, mut position: usize, buf: &[u8]) -> Result<usize, KernelError> {
314 let mut bounce_buffer = alloc::boxed::Box::new([0; 4096]);
315 let mut write_bytes = 0;
316 let first_segment = position & 0xfff;
317 if first_segment != 0 {
318 let r = self.0.read(
319 FileBlockNumber::from_offset(position & !0xfff),
320 &mut bounce_buffer,
321 );
322 if matches!(
323 r,
324 Err(KernelError::IOError) | Err(KernelError::FilesystemCorrupted(_))
325 ) {
326 return r.map(|_| 0);
327 }
328 write_bytes += (0x1000 - first_segment).min(buf.len());
329 bounce_buffer[first_segment..first_segment + write_bytes]
330 .copy_from_slice(&buf[..write_bytes]);
331 self.0.write(
332 FileBlockNumber::from_offset(position & !0xfff),
333 &bounce_buffer,
334 position + write_bytes,
335 )?;
336 position += write_bytes;
337 }
338 for i in (write_bytes..buf.len()).step_by(0x1000) {
339 if buf.len() - i < 0x1000 {
340 break;
341 }
342 self.0.write(
343 FileBlockNumber::from_offset(position),
344 buf[i..i + 0x1000].as_array().unwrap(),
345 position + 0x1000,
346 )?;
347 position += 0x1000;
348 write_bytes += 0x1000;
349 }
350 if write_bytes != buf.len() {
351 let r = self
352 .0
353 .read(FileBlockNumber::from_offset(position), &mut bounce_buffer);
354 if matches!(
355 r,
356 Err(KernelError::IOError) | Err(KernelError::FilesystemCorrupted(_))
357 ) {
358 return r.map(|_| 0);
359 }
360 let remainder = buf.len() - write_bytes;
361 assert!(remainder < 0x1000);
362 bounce_buffer[..remainder].copy_from_slice(&buf[write_bytes..]);
363 self.0.write(
364 FileBlockNumber::from_offset(position),
365 &bounce_buffer,
366 position + remainder,
367 )?;
368 write_bytes += remainder;
369 }
370 Ok(write_bytes)
371 }
372
373 /// Maps a file block into memory.
374 ///
375 /// This method retrieves the contents of the file at the specified file
376 /// block number (`fba`) and returns it as a [`Page`] of the file
377 /// block.
378 ///
379 /// The memory-mapped page reflects the current contents of the file at
380 /// the requested block, and it can be used for reading or
381 /// modifying file data at page granularity.
382 ///
383 /// # Parameters
384 /// - `fba`: The file block number to map into memory. This is a logical
385 /// offset into the file, measured in fixed-size blocks (not bytes).
386 ///
387 /// # Returns
388 /// - `Ok(Page)`: A reference-counted, in-memory page containing the file
389 /// block's data.
390 /// - `Err(KernelError)`: If the file block cannot be found or loaded (e.g.,
391 /// out-of-bounds access).
392 #[inline]
393 pub fn mmap(&self, fba: FileBlockNumber) -> Result<Page, KernelError> {
394 self.0.mmap(fba)
395 }
396
397 /// Write back the file to disk.
398 pub fn writeback(&self) -> Result<(), KernelError> {
399 self.0.writeback()
400 }
401}
402
403/// A handle to a directory.
404///
405/// This struct represents a reference-counted directory that supports
406/// file entry management, including opening and removing entries.
407#[derive(Clone)]
408pub struct Directory(pub Arc<dyn traits::Directory>);
409
410impl Directory {
411 /// Inode number of the directory.
412 pub fn ino(&self) -> InodeNumber {
413 self.0.ino()
414 }
415
416 /// Returns the size of the file in bytes.
417 #[inline]
418 pub fn size(&self) -> usize {
419 self.0.size()
420 }
421
422 /// Link count of the directory.
423 pub fn link_count(&self) -> usize {
424 self.0.link_count()
425 }
426
427 /// Creates a new [`Directory`] handle from a given implementation of
428 /// [`traits::Directory`].
429 ///
430 /// This function takes an instance of any type that implements the
431 /// [`traits::Directory`] trait, wraps it in a reference-counted
432 /// [`Arc`], and returns a [`Directory`] handle.
433 ///
434 /// # Parameters
435 /// - `r`: An instance of a type that implements [`traits::Directory`].
436 ///
437 /// # Returns
438 /// A [`Directory`] handle that enables reference-counted access to the
439 /// underlying file.
440 pub fn new(r: impl traits::Directory + 'static) -> Self {
441 Self(Arc::new(r))
442 }
443
444 /// Opens a path from the directory.
445 ///
446 /// # Parameters
447 /// - `path`: The path to the entry.
448 ///
449 /// # Returns
450 /// - `Ok(File)`: The type of the file (e.g., regular file, directory).
451 /// - `Err(Error)`: An error if the entry cannot be found or accessed.
452 #[inline]
453 pub fn open(&self, mut path: &str) -> Result<File, KernelError> {
454 let mut ret = File::Directory(if path.starts_with("/") {
455 path = &path[1..];
456 FileSystem::root()
457 } else {
458 self.clone()
459 });
460
461 for part in path.split("/").filter(|&s| !s.is_empty()) {
462 match ret {
463 File::Directory(d) => ret = d.0.open_entry(part)?,
464 File::RegularFile(_) => return Err(KernelError::NotDirectory),
465 }
466 }
467 Ok(ret)
468 }
469
470 /// Create an entry in the directory.
471 ///
472 /// # Parameters
473 /// - `path`: The path to the entry.
474 /// - `is_dir`: Indicate whether the entry is directory or not.
475 ///
476 /// # Returns
477 /// - `Ok(())`: If the entry was successfully added.
478 /// - `Err(Error)`: An error if the add fails.
479 #[inline]
480 pub fn create(&self, mut path: &str, is_dir: bool) -> Result<File, KernelError> {
481 let mut dstdir = if path.starts_with("/") {
482 path = &path[1..];
483 FileSystem::root()
484 } else {
485 self.clone()
486 };
487
488 let mut list: Vec<&str> = path.split("/").filter(|&s| !s.is_empty()).collect();
489 let entry = list.pop().ok_or(KernelError::InvalidArgument)?;
490
491 for part in list {
492 dstdir = dstdir
493 .0
494 .open_entry(part)?
495 .into_directory()
496 .ok_or(KernelError::NoSuchEntry)?;
497 }
498
499 dstdir.0.create_entry(entry, is_dir)
500 }
501
502 /// Unlink an entry in the directory.
503 ///
504 /// # Parameters
505 /// - `path`: The path to the entry.
506 ///
507 /// # Returns
508 /// - `Ok(())`: If the entry was successfully unlinked.
509 /// - `Err(Error)`: An error if the unlink operation fails.
510 #[inline]
511 pub fn unlink(&self, mut path: &str) -> Result<(), KernelError> {
512 let mut dstdir = if path.starts_with("/") {
513 path = &path[1..];
514 FileSystem::root()
515 } else {
516 self.clone()
517 };
518
519 let mut list: Vec<&str> = path.split("/").filter(|&s| !s.is_empty()).collect();
520 let entry = list.pop().ok_or(KernelError::InvalidArgument)?;
521
522 for part in list {
523 dstdir = dstdir
524 .0
525 .open_entry(part)?
526 .into_directory()
527 .ok_or(KernelError::NoSuchEntry)?;
528 }
529
530 dstdir.0.unlink_entry(entry)
531 }
532
533 /// Reads the contents of the directory.
534 ///
535 /// This function lists all the entries within the directory.
536 ///
537 /// # Returns
538 /// - `Ok(())`: If the directory was successfully read.
539 /// - `Err(Error)`: An error if the read operation fails.
540 #[inline]
541 pub fn read_dir(&self) -> Result<Vec<(InodeNumber, String)>, KernelError> {
542 self.0.read_dir()
543 }
544
545 /// Returns [`AtomicBool`] which contains whether directory is removed.
546 ///
547 /// This is important because directory operations against the removed
548 /// directory will result in undesirable behavior (e.g. unreachable file).
549 ///
550 /// # Returns
551 /// - `Ok(Removed)`: If the directory exists in current filesystem.
552 /// - `Err(Error)`: An error if the operation fails.
553 #[inline]
554 pub fn removed(&self) -> Result<&AtomicBool, KernelError> {
555 self.0.removed()
556 }
557}
558
559/// Represents a file system entry, which can be either a file or a directory.
560///
561/// This enum allows distinguishing between regular files and directories within
562/// the filesystem. It provides flexibility for handling different file system
563/// objects in a unified manner.
564#[derive(Clone)]
565pub enum File {
566 /// A regular file.
567 ///
568 /// This variant represents a standard file in the filesystem, which can be
569 /// read from or written to.
570 RegularFile(RegularFile),
571
572 /// A directory.
573 ///
574 /// This variant represents a directory in the filesystem, which can contain
575 /// other files or directories.
576 Directory(Directory),
577}
578
579impl File {
580 /// Converts the [`File`] into a [`RegularFile`], if it is one.
581 ///
582 /// # Returns
583 ///
584 /// - `Some(RegularFile)` if `self` is a [`RegularFile`].
585 /// - `None` if `self` is not a `RegularFile`.
586 ///
587 /// This function allows extracting the [`RegularFile`] from [`File`]
588 /// safely.
589 pub fn into_regular_file(self) -> Option<RegularFile> {
590 if let File::RegularFile(r) = self {
591 Some(r)
592 } else {
593 None
594 }
595 }
596
597 /// Converts the `File` into a `Directory`, if it is one.
598 ///
599 /// # Returns
600 ///
601 /// - `Some(Directory)` if `self` is a `Directory`.
602 /// - `None` if `self` is not a `Directory`.
603 ///
604 /// This function allows extracting the `Directory` from `File` safely.
605 ///
606 /// # Example
607 ///
608 /// ```
609 /// let dir = File::Directory(directory);
610 /// assert!(dir.into_directory().is_some());
611 ///
612 /// let file = File::RegularFile(regular_file);
613 /// assert!(file.into_directory().is_none());
614 /// ```
615 pub fn into_directory(self) -> Option<Directory> {
616 if let File::Directory(d) = self {
617 Some(d)
618 } else {
619 None
620 }
621 }
622
623 /// Get [`InodeNumber`] of this [`File`] regardless of its inner type.
624 pub fn ino(&self) -> InodeNumber {
625 match self {
626 File::RegularFile(r) => r.ino(),
627 File::Directory(d) => d.ino(),
628 }
629 }
630
631 /// Get size of this [`File`] regardless of its inner type.
632 pub fn size(&self) -> u64 {
633 match self {
634 File::RegularFile(r) => r.size() as u64,
635 File::Directory(d) => d.size() as u64,
636 }
637 }
638}
639
640/// Represents a unique identifier for an inode in the filesystem.
641///
642/// An inode number uniquely identifies a file or directory within a filesystem.
643/// It is typically used to reference file metadata rather than file names.
644#[repr(transparent)]
645#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug)]
646pub struct InodeNumber(NonZeroU32);
647
648impl InodeNumber {
649 /// Creates a [`InodeNumber`] if the given value is not zero.
650 pub const fn new(n: u32) -> Option<Self> {
651 if let Some(v) = NonZeroU32::new(n) {
652 Some(Self(v))
653 } else {
654 None
655 }
656 }
657
658 /// Returns the contained value as a u32.
659 #[inline]
660 pub fn into_u32(&self) -> u32 {
661 self.0.get()
662 }
663}
664
665/// Represents a file block number within a file.
666///
667/// This number refers to the position of a block within a specific file.
668/// Each block contains 4096 bytes of contents.
669/// It helps in translating file-relative offsets to actual storage locations.
670#[repr(transparent)]
671#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug)]
672pub struct FileBlockNumber(pub usize);
673
674impl FileBlockNumber {
675 /// Computes the file block number from a byte offset within a file.
676 ///
677 /// In KeOS, each file is divided into file blocks of `0x1000` bytes (4
678 /// KiB). This function calculates the file block index corresponding to a
679 /// given byte offset.
680 ///
681 /// # Parameters
682 /// - `offset`: The byte offset within the file.
683 ///
684 /// # Returns
685 /// - The [`FileBlockNumber`] that corresponds to the given offset.
686 pub const fn from_offset(offset: usize) -> Self {
687 Self(offset / 0x1000)
688 }
689}
690
691impl Step for FileBlockNumber {
692 fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
693 if start.0 <= end.0 {
694 let steps = end.0 - start.0;
695 (steps, Some(steps))
696 } else {
697 (0, None)
698 }
699 }
700
701 fn forward_checked(start: Self, count: usize) -> Option<Self> {
702 start.0.checked_add(count).map(Self)
703 }
704
705 fn backward_checked(start: Self, count: usize) -> Option<Self> {
706 start.0.checked_sub(count).map(Self)
707 }
708}
709
710impl core::ops::Add<usize> for FileBlockNumber {
711 type Output = Self;
712
713 fn add(self, rhs: usize) -> Self {
714 Self(self.0 + rhs)
715 }
716}
717
718// The type for disk hooking.
719#[doc(hidden)]
720pub type Hook =
721 Arc<dyn Fn(Sector, &[u8; 512], bool) -> Result<(), KernelError> + Send + Sync + 'static>;
722
723/// The disk, a device that has byte sink.
724///
725/// It gets slot number as its field.
726pub struct Disk {
727 index: usize,
728 is_ro: bool,
729 hook: Option<Hook>,
730}
731
732impl Disk {
733 /// Create a new FsDisk from the index.
734 pub fn new(index: usize) -> Self {
735 Self {
736 index,
737 is_ro: false,
738 hook: None,
739 }
740 }
741
742 /// Make the disk read-only.
743 pub fn ro(self) -> Self {
744 Self {
745 index: self.index,
746 is_ro: true,
747 hook: self.hook,
748 }
749 }
750
751 /// Add a hook for the disk.
752 pub fn hook(self, hook: Hook) -> Self {
753 Self {
754 index: self.index,
755 is_ro: self.is_ro,
756 hook: Some(hook),
757 }
758 }
759
760 /// Read 512 bytes from disk starting from sector.
761 pub fn read(&self, sector: Sector, buf: &mut [u8; 512]) -> Result<(), KernelError> {
762 let dev = abyss::dev::get_bdev(self.index).ok_or(KernelError::IOError)?;
763 if let Some(hook) = self.hook.as_ref() {
764 hook(sector, buf, false)?;
765 }
766 if dev.read(sector, buf) {
767 Ok(())
768 } else {
769 Err(KernelError::IOError)
770 }
771 }
772
773 /// Write 512 bytes to disk starting from sector.
774 pub fn write(&self, sector: Sector, buf: &[u8; 512]) -> Result<(), KernelError> {
775 let dev = abyss::dev::get_bdev(self.index).ok_or(KernelError::IOError)?;
776 if self.is_ro {
777 Err(KernelError::NotSupportedOperation)
778 } else {
779 if let Some(hook) = self.hook.as_ref() {
780 hook(sector, buf, true)?;
781 }
782 if dev.write(sector, buf) {
783 Ok(())
784 } else {
785 Err(KernelError::IOError)
786 }
787 }
788 }
789}