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}