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};
183use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};
184use core::{iter::Step, num::NonZeroU32};
185
186/// A global file system abstraction.
187///
188/// The `FileSystem` struct provides an interface for interacting with the file
189/// system, including operations such as opening and creating files.
190///
191/// # Example
192/// ```
193/// let fs = file_system();
194/// if let Some(file) = fs.open("example.txt") {
195/// println!("Opened file: {}", file.name());
196/// }
197/// ```
198pub struct FileSystem {
199 _p: (),
200}
201
202static mut FS: Option<Box<dyn traits::FileSystem>> = None;
203
204impl FileSystem {
205 /// Retrieves the root directory of the filesystem.
206 ///
207 /// # Returns
208 /// - `Directory`: A reference to the root directory.
209 pub fn root() -> Directory {
210 unsafe { FS.as_ref() }
211 .and_then(|fs| fs.root())
212 .expect("Filesystem is not available.")
213 }
214
215 /// Register the global file system.
216 pub fn register(fs: impl traits::FileSystem + 'static) {
217 unsafe {
218 FS = Some(Box::new(fs));
219 }
220 }
221}
222
223/// A handle to a regular file.
224///
225/// This struct provides a reference-counted handle to a file that supports
226/// reading and writing operations at the kernel level.
227#[derive(Clone)]
228pub struct RegularFile(pub Arc<dyn traits::RegularFile>);
229
230impl RegularFile {
231 /// Inode number of the file.
232 pub fn ino(&self) -> InodeNumber {
233 self.0.ino()
234 }
235
236 /// Creates a new [`RegularFile`] handle from a given implementation of
237 /// [`traits::RegularFile`].
238 ///
239 /// This function takes an instance of any type that implements the
240 /// [`traits::RegularFile`] trait, wraps it in a reference-counted
241 /// [`Arc`], and returns a [`RegularFile`] handle.
242 ///
243 /// # Parameters
244 /// - `r`: An instance of a type that implements [`traits::RegularFile`].
245 ///
246 /// # Returns
247 /// A [`RegularFile`] handle that enables reference-counted access to the
248 /// underlying file.
249 pub fn new(r: impl traits::RegularFile + 'static) -> Self {
250 Self(Arc::new(r))
251 }
252
253 /// Returns the size of the file in bytes.
254 #[inline]
255 pub fn size(&self) -> usize {
256 self.0.size()
257 }
258
259 /// Reads data from the file into the provided buffer.
260 ///
261 /// # Parameters
262 /// - `fba`: The `FileBlockNumber` which to read.
263 /// - `buf`: A mutable slice where the file content will be stored.
264 ///
265 /// # Returns
266 /// - `Ok(usize)`: The number of bytes read.
267 /// - `Err(Error)`: An error if the read operation fails.
268 #[inline]
269 pub fn read(&self, mut position: usize, buf: &mut [u8]) -> Result<usize, KernelError> {
270 let mut bounce_buffer = alloc::boxed::Box::new([0; 4096]);
271 let max_read = self
272 .size()
273 .min(position + buf.len())
274 .saturating_sub(position);
275 let mut read_bytes = 0;
276 let first_segment = position & 0xfff;
277 if first_segment != 0 {
278 self.0.read(
279 FileBlockNumber::from_offset(position & !0xfff),
280 &mut bounce_buffer,
281 )?;
282 read_bytes += (0x1000 - first_segment).min(max_read);
283 buf[..read_bytes]
284 .copy_from_slice(&bounce_buffer[first_segment..first_segment + read_bytes]);
285 position += read_bytes;
286 }
287
288 for i in (read_bytes..max_read).step_by(0x1000) {
289 self.0
290 .read(FileBlockNumber::from_offset(position), &mut bounce_buffer)?;
291 let remainder = (max_read - i).min(0x1000);
292 buf[i..i + remainder].copy_from_slice(&bounce_buffer[..remainder]);
293 position += remainder;
294 read_bytes += remainder;
295 }
296 Ok(read_bytes)
297 }
298
299 /// Writes data from the buffer into the file.
300 ///
301 /// If the write position is beyond the current file size, file will be
302 /// extended to minimum size required to reflect the update.
303 ///
304 /// # Parameters
305 /// - `fba`: The `FileBlockNumber` which to write.
306 /// - `buf`: An slice containing the data to write.
307 ///
308 /// # Returns
309 /// - `Ok(usize)`: The number of bytes written.
310 /// - `Err(Error)`: An error if the write operation fails.
311 #[inline]
312 pub fn write(&self, mut position: usize, buf: &[u8]) -> Result<usize, KernelError> {
313 let mut bounce_buffer = alloc::boxed::Box::new([0; 4096]);
314 let mut write_bytes = 0;
315 let first_segment = position & 0xfff;
316 if first_segment != 0 {
317 let r = self.0.read(
318 FileBlockNumber::from_offset(position & !0xfff),
319 &mut bounce_buffer,
320 );
321 if matches!(
322 r,
323 Err(KernelError::IOError) | Err(KernelError::FilesystemCorrupted(_))
324 ) {
325 return r.map(|_| 0);
326 }
327 write_bytes += (0x1000 - first_segment).min(buf.len());
328 bounce_buffer[first_segment..first_segment + write_bytes]
329 .copy_from_slice(&buf[..write_bytes]);
330 self.0.write(
331 FileBlockNumber::from_offset(position & !0xfff),
332 &bounce_buffer,
333 position + write_bytes,
334 )?;
335 position += write_bytes;
336 }
337 for i in (write_bytes..buf.len()).step_by(0x1000) {
338 if buf.len() - i < 0x1000 {
339 break;
340 }
341 self.0.write(
342 FileBlockNumber::from_offset(position),
343 buf[i..i + 0x1000].as_array().unwrap(),
344 position + 0x1000,
345 )?;
346 position += 0x1000;
347 write_bytes += 0x1000;
348 }
349 if write_bytes != buf.len() {
350 let r = self
351 .0
352 .read(FileBlockNumber::from_offset(position), &mut bounce_buffer);
353 if matches!(
354 r,
355 Err(KernelError::IOError) | Err(KernelError::FilesystemCorrupted(_))
356 ) {
357 return r.map(|_| 0);
358 }
359 let remainder = buf.len() - write_bytes;
360 assert!(remainder < 0x1000);
361 bounce_buffer[..remainder].copy_from_slice(&buf[write_bytes..]);
362 self.0.write(
363 FileBlockNumber::from_offset(position),
364 &bounce_buffer,
365 position + remainder,
366 )?;
367 write_bytes += remainder;
368 }
369 Ok(write_bytes)
370 }
371
372 /// Maps a file block into memory.
373 ///
374 /// This method retrieves the contents of the file at the specified file
375 /// block number (`fba`) and returns it as a [`Page`] of the file
376 /// block.
377 ///
378 /// The memory-mapped page reflects the current contents of the file at
379 /// the requested block, and it can be used for reading or
380 /// modifying file data at page granularity.
381 ///
382 /// # Parameters
383 /// - `fba`: The file block number to map into memory. This is a logical
384 /// offset into the file, measured in fixed-size blocks (not bytes).
385 ///
386 /// # Returns
387 /// - `Ok(Page)`: A reference-counted, in-memory page containing the file
388 /// block's data.
389 /// - `Err(KernelError)`: If the file block cannot be found or loaded (e.g.,
390 /// out-of-bounds access).
391 #[inline]
392 pub fn mmap(&self, fba: FileBlockNumber) -> Result<Page, KernelError> {
393 self.0.mmap(fba)
394 }
395
396 /// Write back the file to disk.
397 pub fn writeback(&self) -> Result<(), KernelError> {
398 self.0.writeback()
399 }
400}
401
402/// A handle to a directory.
403///
404/// This struct represents a reference-counted directory that supports
405/// file entry management, including opening and removing entries.
406#[derive(Clone)]
407pub struct Directory(pub Arc<dyn traits::Directory>);
408
409impl Directory {
410 /// Inode number of the directory.
411 pub fn ino(&self) -> InodeNumber {
412 self.0.ino()
413 }
414
415 /// Returns the size of the file in bytes.
416 #[inline]
417 pub fn size(&self) -> usize {
418 self.0.size()
419 }
420
421 /// Link count of the directory.
422 pub fn link_count(&self) -> usize {
423 self.0.link_count()
424 }
425
426 /// Creates a new [`Directory`] handle from a given implementation of
427 /// [`traits::Directory`].
428 ///
429 /// This function takes an instance of any type that implements the
430 /// [`traits::Directory`] trait, wraps it in a reference-counted
431 /// [`Arc`], and returns a [`Directory`] handle.
432 ///
433 /// # Parameters
434 /// - `r`: An instance of a type that implements [`traits::Directory`].
435 ///
436 /// # Returns
437 /// A [`Directory`] handle that enables reference-counted access to the
438 /// underlying file.
439 pub fn new(r: impl traits::Directory + 'static) -> Self {
440 Self(Arc::new(r))
441 }
442
443 /// Opens a path from the directory.
444 ///
445 /// # Parameters
446 /// - `path`: The path to the entry.
447 ///
448 /// # Returns
449 /// - `Ok(File)`: The type of the file (e.g., regular file, directory).
450 /// - `Err(Error)`: An error if the entry cannot be found or accessed.
451 #[inline]
452 pub fn open(&self, mut path: &str) -> Result<File, KernelError> {
453 let mut ret = File::Directory(if path.starts_with("/") {
454 path = &path[1..];
455 FileSystem::root()
456 } else {
457 self.clone()
458 });
459
460 for part in path.split("/").filter(|&s| !s.is_empty()) {
461 match ret {
462 File::Directory(d) => ret = d.0.open_entry(part)?,
463 File::RegularFile(_) => return Err(KernelError::NotDirectory),
464 }
465 }
466 Ok(ret)
467 }
468
469 /// Create an entry in the directory.
470 ///
471 /// # Parameters
472 /// - `path`: The path to the entry.
473 /// - `is_dir`: Indicate whether the entry is directory or not.
474 ///
475 /// # Returns
476 /// - `Ok(())`: If the entry was successfully added.
477 /// - `Err(Error)`: An error if the add fails.
478 #[inline]
479 pub fn create(&self, mut path: &str, is_dir: bool) -> Result<File, KernelError> {
480 let mut dstdir = if path.starts_with("/") {
481 path = &path[1..];
482 FileSystem::root()
483 } else {
484 self.clone()
485 };
486
487 let mut list: Vec<&str> = path.split("/").filter(|&s| !s.is_empty()).collect();
488 let entry = list.pop().ok_or(KernelError::InvalidArgument)?;
489
490 for part in list {
491 dstdir = dstdir
492 .0
493 .open_entry(part)?
494 .into_directory()
495 .ok_or(KernelError::NoSuchEntry)?;
496 }
497
498 dstdir.0.create_entry(entry, is_dir)
499 }
500
501 /// Unlink an entry in the directory.
502 ///
503 /// # Parameters
504 /// - `path`: The path to the entry.
505 ///
506 /// # Returns
507 /// - `Ok(())`: If the entry was successfully added.
508 /// - `Err(Error)`: An error if the add fails.
509 #[inline]
510 pub fn unlink(&self, mut path: &str) -> Result<(), KernelError> {
511 let mut dstdir = if path.starts_with("/") {
512 path = &path[1..];
513 FileSystem::root()
514 } else {
515 self.clone()
516 };
517
518 let mut list: Vec<&str> = path.split("/").filter(|&s| !s.is_empty()).collect();
519 let entry = list.pop().ok_or(KernelError::InvalidArgument)?;
520
521 for part in list {
522 dstdir = dstdir
523 .0
524 .open_entry(part)?
525 .into_directory()
526 .ok_or(KernelError::NoSuchEntry)?;
527 }
528
529 dstdir.0.unlink_entry(entry)
530 }
531
532 /// Reads the contents of the directory.
533 ///
534 /// This function lists all the entries within the directory.
535 ///
536 /// # Returns
537 /// - `Ok(())`: If the directory was successfully read.
538 /// - `Err(Error)`: An error if the read operation fails.
539 #[inline]
540 pub fn read_dir(&self) -> Result<Vec<(InodeNumber, String)>, KernelError> {
541 self.0.read_dir()
542 }
543
544 /// Returns [`AtomicBool`] which contains whether directory is removed.
545 ///
546 /// This is important because directory operations against the removed
547 /// directory will result in undesirable behavior (e.g. unreachable file).
548 ///
549 /// # Returns
550 /// - `Ok(())`: If the directory was successfully read.
551 /// - `Err(Error)`: An error if the operation fails.
552 #[inline]
553 pub fn removed(&self) -> Result<&AtomicBool, KernelError> {
554 self.0.removed()
555 }
556}
557
558/// Represents a file system entry, which can be either a file or a directory.
559///
560/// This enum allows distinguishing between regular files and directories within
561/// the filesystem. It provides flexibility for handling different file system
562/// objects in a unified manner.
563#[derive(Clone)]
564pub enum File {
565 /// A regular file.
566 ///
567 /// This variant represents a standard file in the filesystem, which can be
568 /// read from or written to.
569 RegularFile(RegularFile),
570
571 /// A directory.
572 ///
573 /// This variant represents a directory in the filesystem, which can contain
574 /// other files or directories.
575 Directory(Directory),
576}
577
578impl File {
579 /// Converts the [`File`] into a [`RegularFile`], if it is one.
580 ///
581 /// # Returns
582 ///
583 /// - `Some(RegularFile)` if `self` is a [`RegularFile`].
584 /// - `None` if `self` is not a `RegularFile`.
585 ///
586 /// This function allows extracting the [`RegularFile`] from [`File`]
587 /// safely.
588 pub fn into_regular_file(self) -> Option<RegularFile> {
589 if let File::RegularFile(r) = self {
590 Some(r)
591 } else {
592 None
593 }
594 }
595
596 /// Converts the `File` into a `Directory`, if it is one.
597 ///
598 /// # Returns
599 ///
600 /// - `Some(Directory)` if `self` is a `Directory`.
601 /// - `None` if `self` is not a `Directory`.
602 ///
603 /// This function allows extracting the `Directory` from `File` safely.
604 ///
605 /// # Example
606 ///
607 /// ```
608 /// let dir = File::Directory(directory);
609 /// assert!(dir.into_directory().is_some());
610 ///
611 /// let file = File::RegularFile(regular_file);
612 /// assert!(file.into_directory().is_none());
613 /// ```
614 pub fn into_directory(self) -> Option<Directory> {
615 if let File::Directory(d) = self {
616 Some(d)
617 } else {
618 None
619 }
620 }
621
622 /// Get [`InodeNumber`] of this [`File`] regardless of its inner type.
623 pub fn ino(&self) -> InodeNumber {
624 match self {
625 File::RegularFile(r) => r.ino(),
626 File::Directory(d) => d.ino(),
627 }
628 }
629
630 /// Get size of this [`File`] regardless of its inner type.
631 pub fn size(&self) -> u64 {
632 match self {
633 File::RegularFile(r) => r.size() as u64,
634 File::Directory(d) => d.size() as u64,
635 }
636 }
637}
638
639/// Sector, an access granuality for the disk.
640#[repr(transparent)]
641#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
642pub struct Sector(pub usize);
643
644impl Sector {
645 /// Get offset that represented by the sector.
646 #[inline]
647 pub fn into_offset(self) -> usize {
648 self.0 * 512
649 }
650
651 /// Cast into usize.
652 #[inline]
653 pub fn into_usize(self) -> usize {
654 self.0
655 }
656}
657
658impl core::ops::Add<usize> for Sector {
659 type Output = Self;
660
661 fn add(self, rhs: usize) -> Self {
662 Self(self.0 + rhs)
663 }
664}
665
666/// Represents a unique identifier for an inode in the filesystem.
667///
668/// An inode number uniquely identifies a file or directory within a filesystem.
669/// It is typically used to reference file metadata rather than file names.
670#[repr(transparent)]
671#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug)]
672pub struct InodeNumber(NonZeroU32);
673
674impl InodeNumber {
675 /// Creates a [`InodeNumber`] if the given value is not zero.
676 pub const fn new(n: u32) -> Option<Self> {
677 if let Some(v) = NonZeroU32::new(n) {
678 Some(Self(v))
679 } else {
680 None
681 }
682 }
683
684 /// Returns the contained value as a u32.
685 #[inline]
686 pub fn into_u32(&self) -> u32 {
687 self.0.get()
688 }
689}
690
691/// Represents a file block number within a file.
692///
693/// This number refers to the position of a block within a specific file.
694/// Each block contains 4096 bytes of contents.
695/// It helps in translating file-relative offsets to actual storage locations.
696#[repr(transparent)]
697#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug)]
698pub struct FileBlockNumber(pub usize);
699
700impl FileBlockNumber {
701 /// Computes the file block number from a byte offset within a file.
702 ///
703 /// In KeOS, each file is divided into file blocks of `0x1000` bytes (4
704 /// KiB). This function calculates the file block index corresponding to a
705 /// given byte offset.
706 ///
707 /// # Parameters
708 /// - `offset`: The byte offset within the file.
709 ///
710 /// # Returns
711 /// - The [`FileBlockNumber`] that corresponds to the given offset.
712 pub const fn from_offset(offset: usize) -> Self {
713 Self(offset / 0x1000)
714 }
715}
716
717impl Step for FileBlockNumber {
718 fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
719 if start.0 <= end.0 {
720 let steps = end.0 - start.0;
721 (steps, Some(steps))
722 } else {
723 (0, None)
724 }
725 }
726
727 fn forward_checked(start: Self, count: usize) -> Option<Self> {
728 start.0.checked_add(count).map(Self)
729 }
730
731 fn backward_checked(start: Self, count: usize) -> Option<Self> {
732 start.0.checked_sub(count).map(Self)
733 }
734}
735
736impl core::ops::Add<usize> for FileBlockNumber {
737 type Output = Self;
738
739 fn add(self, rhs: usize) -> Self {
740 Self(self.0 + rhs)
741 }
742}
743
744// The type for disk hooking.
745#[doc(hidden)]
746pub type Hook =
747 Arc<dyn Fn(Sector, &[u8; 512], bool) -> Result<(), KernelError> + Send + Sync + 'static>;
748
749/// The disk, a device that has byte sink.
750///
751/// It gets slot number as its field.
752pub struct Disk {
753 index: usize,
754 is_ro: bool,
755 hook: Option<Hook>,
756}
757
758impl Disk {
759 /// Create a new FsDisk from the index.
760 pub fn new(index: usize) -> Self {
761 Self {
762 index,
763 is_ro: false,
764 hook: None,
765 }
766 }
767
768 /// Make the disk read-only.
769 pub fn ro(self) -> Self {
770 Self {
771 index: self.index,
772 is_ro: true,
773 hook: self.hook,
774 }
775 }
776
777 /// Add a hook for the disk.
778 pub fn hook(self, hook: Hook) -> Self {
779 Self {
780 index: self.index,
781 is_ro: self.is_ro,
782 hook: Some(hook),
783 }
784 }
785
786 /// Read 512 bytes from disk starting from sector.
787 pub fn read(&self, sector: Sector, buf: &mut [u8; 512]) -> Result<(), KernelError> {
788 let dev = abyss::dev::get_bdev(self.index).ok_or(KernelError::IOError)?;
789 if let Some(hook) = self.hook.as_ref() {
790 hook(sector, buf, false)?;
791 }
792 dev.read_bios(&mut Some((512 * sector.into_usize(), buf.as_mut())).into_iter())
793 .map_err(|_| KernelError::IOError)
794 }
795
796 /// Write 512 bytes to disk starting from sector.
797 pub fn write(&self, sector: Sector, buf: &[u8; 512]) -> Result<(), KernelError> {
798 let dev = abyss::dev::get_bdev(self.index).ok_or(KernelError::IOError)?;
799 if self.is_ro {
800 Err(KernelError::NotSupportedOperation)
801 } else {
802 if let Some(hook) = self.hook.as_ref() {
803 hook(sector, buf, true)?;
804 }
805 dev.write_bios(&mut Some((512 * sector.into_usize(), buf.as_ref())).into_iter())
806 .map_err(|_| KernelError::IOError)
807 }
808 }
809}