Skip to main content

kernel/
fs.rs

1use core::fmt::Display;
2use core::mem::{self, MaybeUninit};
3use core::ptr;
4use core::slice;
5
6use crate::buf::{BCACHE, Buf};
7use crate::log::{self, Operation};
8use crate::param::{NINODE, ROOTDEV};
9use crate::proc;
10use crate::sleeplock::{SleepLock, SleepLockGuard};
11use crate::spinlock::SpinLock;
12use crate::sync::OnceLock;
13use crate::vm::VA;
14
15/// File system magic number
16pub const FSMAGIC: u32 = 0x10203040;
17
18/// Root inode number
19pub const ROOTINO: u32 = 1;
20/// Block size
21pub const BSIZE: usize = 1024;
22/// Number of direct block addresses in inode
23pub const NDIRECT: usize = 12;
24/// Number of indirect block addresses in inode
25pub const NINDIRECT: usize = BSIZE / size_of::<u32>();
26/// Max file size (blocks)
27pub const MAXFILE: usize = NDIRECT + NINDIRECT;
28
29/// Inodes per block
30pub const IPB: u32 = (BSIZE / size_of::<DiskInode>()) as u32;
31/// Bitmap bits per block
32pub const BPB: u32 = BSIZE as u32 * 8;
33/// Directory entry name size
34pub const DIRSIZE: usize = 14;
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum FsError {
38    OutOfBlock,
39    OutOfInode,
40    OutOfFile,
41    OutOfRange,
42    OutOfPipe,
43    Read,
44    Write,
45    Create,
46    Link,
47    Resolve,
48    Type,
49    Copy,
50}
51
52impl Display for FsError {
53    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
54        match self {
55            FsError::OutOfBlock => write!(f, "out of block"),
56            FsError::OutOfInode => write!(f, "out of inode"),
57            FsError::OutOfRange => write!(f, "out of range"),
58            FsError::OutOfFile => write!(f, "out of file"),
59            FsError::OutOfPipe => write!(f, "out of pipe"),
60            FsError::Read => write!(f, "read error"),
61            FsError::Write => write!(f, "write error"),
62            FsError::Create => write!(f, "create error"),
63            FsError::Link => write!(f, "link error"),
64            FsError::Resolve => write!(f, "resolve error"),
65            FsError::Type => write!(f, "type error"),
66            FsError::Copy => write!(f, "copy error"),
67        }
68    }
69}
70
71pub static SB: OnceLock<SuperBlock> = OnceLock::new();
72
73/// On-disk superblock (read at boot)
74#[repr(C)]
75#[derive(Debug, Clone)]
76pub struct SuperBlock {
77    /// Must be `FSMAGIC`
78    pub magic: u32,
79    /// Size of file system image (blocks)
80    pub size: u32,
81    /// Number of data blocks
82    pub nblocks: u32,
83    /// Number of inodes
84    pub ninodes: u32,
85    /// Number of log blocks
86    pub nlogs: u32,
87    /// Block number of first log block
88    pub logstart: u32,
89    /// Block number of first inode block
90    pub inodestart: u32,
91    /// Block number of first free map block
92    pub bmapstart: u32,
93}
94
95impl SuperBlock {
96    /// Reads the superblock from disk and initializes the global `SB`.
97    fn initialize(dev: u32) {
98        let buf = BCACHE.read(dev, 1); // superblock is at block 1
99        let sb = unsafe { ptr::read_unaligned(buf.data().as_ptr() as *const SuperBlock) };
100        BCACHE.release(buf);
101
102        assert_eq!(sb.magic, FSMAGIC, "invalid file system");
103
104        SB.initialize(|| Ok::<_, ()>(sb));
105    }
106}
107
108/// Initialize the file system.
109pub fn init(dev: u32) {
110    SuperBlock::initialize(dev);
111    log::init(dev, SB.get().unwrap());
112    Inode::reclaim(dev);
113}
114
115/// A disk block.
116/// This is a wrapper around the block number.
117#[derive(Debug, Clone, Copy, PartialEq, Eq)]
118pub struct Block(pub u32);
119
120impl Block {
121    /// Zeros out the disk block.
122    fn zero(&mut self, dev: u32) {
123        let mut buf = BCACHE.read(dev, self.0);
124        buf.data_mut().fill(0);
125        log::write(&buf);
126        BCACHE.release(buf);
127    }
128
129    /// Allocates a zeroed disk block.
130    pub fn alloc(dev: u32) -> Result<Self, FsError> {
131        let sb = SB.get().unwrap();
132
133        for b in (0..sb.size).step_by(BPB as usize) {
134            let mut buf = BCACHE.read(dev, sb.bmapstart + (b / BPB));
135
136            for bi in 0..BPB {
137                if b + bi >= sb.size {
138                    break;
139                }
140
141                let m = 1u8 << (bi % 8);
142                if buf.data()[bi as usize / 8] & m == 0 {
143                    // block is free, mark it as in use
144                    buf.data_mut()[bi as usize / 8] |= m;
145                    log::write(&buf);
146                    BCACHE.release(buf);
147
148                    let mut block = Self(b + bi);
149                    block.zero(dev);
150
151                    return Ok(block);
152                }
153            }
154
155            BCACHE.release(buf);
156        }
157
158        err!(FsError::OutOfBlock)
159    }
160
161    /// Frees a disk block.
162    pub fn free(self, dev: u32) {
163        let sb = SB.get().unwrap();
164        let mut buf = BCACHE.read(dev, sb.bmapstart + (self.0 / BPB));
165        let bi = self.0 % BPB;
166        let m = 1u8 << (bi % 8);
167
168        if buf.data()[bi as usize / 8] & m == 0 {
169            panic!("bfree: block already free");
170        }
171
172        buf.data_mut()[bi as usize / 8] &= !m;
173        log::write(&buf);
174        BCACHE.release(buf);
175    }
176}
177
178/// Inode types
179#[repr(u16)]
180#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
181pub enum InodeType {
182    #[default]
183    Free = 0,
184    Directory = 1,
185    File = 2,
186    Device = 3,
187}
188
189/// On-disk inode structure
190#[repr(C)]
191#[derive(Debug)]
192pub struct DiskInode {
193    /// File type
194    pub r#type: InodeType,
195    /// Major device number
196    pub major: u16,
197    /// Minor device number
198    pub minor: u16,
199    /// Number of links to inode in file system
200    pub nlink: u16,
201    // Size of file (bytes)
202    pub size: u32,
203    // Data block addresses
204    pub addrs: [u32; NDIRECT + 1],
205}
206
207impl DiskInode {
208    /// Returns a mutable reference to the `DiskInode` with number `inum` in the given buffer.
209    ///
210    /// # Safety
211    /// The caller must ensure that `buf` contains the block that holds the inode with number `inum`.
212    pub unsafe fn from_buf(buf: &mut Buf, inum: u32) -> &'static mut Self {
213        unsafe {
214            &mut *(buf
215                .data_mut()
216                .as_mut_ptr()
217                .add((inum % IPB) as usize * size_of::<DiskInode>())
218                as *mut DiskInode)
219        }
220    }
221}
222
223#[derive(Debug, Clone, Copy, Default)]
224pub struct Stat {
225    pub dev: u32,
226    pub ino: u32,
227    pub r#type: InodeType,
228    pub nlink: u16,
229    pub size: u64,
230}
231
232/// Cached inode data, protected by sleeplock
233#[derive(Debug)]
234pub struct InodeInner {
235    /// Indicates whether inode has been read from disk
236    pub valid: bool,
237    pub r#type: InodeType,
238    pub major: u16,
239    pub minor: u16,
240    pub nlink: u16,
241    pub size: u32,
242    pub addrs: [u32; NDIRECT + 1],
243}
244
245impl InodeInner {
246    #[allow(clippy::new_without_default)]
247    pub const fn new() -> Self {
248        Self {
249            valid: false,
250            r#type: InodeType::Free,
251            major: 0,
252            minor: 0,
253            nlink: 0,
254            size: 0,
255            addrs: [0; NDIRECT + 1],
256        }
257    }
258}
259
260/// Metadata about an inode, protected by SpinLock
261pub struct InodeMeta {
262    /// Device number
263    pub dev: u32,
264    /// Inode number
265    pub inum: u32,
266    /// Reference Count
267    pub r#ref: u32,
268}
269
270impl InodeMeta {
271    #[allow(clippy::new_without_default)]
272    pub const fn new() -> Self {
273        Self {
274            dev: 0,
275            inum: 0,
276            r#ref: 0,
277        }
278    }
279}
280
281/// In-memory inode structure
282/// `id` is the index to the actual data in the inode table.
283/// Also holds device and inode numbers for quick lookup.
284#[derive(Debug, Clone, PartialEq, Eq, Default)]
285pub struct Inode {
286    /// Inode table index
287    pub id: usize,
288    /// Device number
289    pub dev: u32,
290    /// Inode number
291    pub inum: u32,
292}
293
294impl Inode {
295    pub const fn new(id: usize, dev: u32, inum: u32) -> Self {
296        Self { id, dev, inum }
297    }
298}
299
300pub static INODE_TABLE: InodeTable = InodeTable::new();
301
302pub struct InodeTable {
303    meta: SpinLock<[InodeMeta; NINODE]>,
304    inner: [SleepLock<InodeInner>; NINODE],
305}
306
307impl InodeTable {
308    const fn new() -> Self {
309        let meta = {
310            let mut array: [MaybeUninit<InodeMeta>; NINODE] =
311                unsafe { MaybeUninit::uninit().assume_init() };
312
313            let mut i = 0;
314            while i < NINODE {
315                array[i] = MaybeUninit::new(InodeMeta::new());
316                i += 1;
317            }
318
319            SpinLock::new(
320                unsafe {
321                    mem::transmute::<[MaybeUninit<InodeMeta>; NINODE], [InodeMeta; NINODE]>(array)
322                },
323                "itable",
324            )
325        };
326
327        let inner = {
328            let mut array: [MaybeUninit<SleepLock<InodeInner>>; NINODE] =
329                unsafe { MaybeUninit::uninit().assume_init() };
330
331            let mut i = 0;
332            while i < NINODE {
333                array[i] = MaybeUninit::new(SleepLock::new(InodeInner::new(), "inode"));
334                i += 1;
335            }
336
337            unsafe {
338                mem::transmute::<
339                    [MaybeUninit<SleepLock<InodeInner>>; NINODE],
340                    [SleepLock<InodeInner>; NINODE],
341                >(array)
342            }
343        };
344
345        Self { meta, inner }
346    }
347}
348
349impl Inode {
350    /// Allocates an inode on device `dev`.
351    /// Marks it allocated by giving it type `type`.
352    /// Returns an unlocked but allocated and referenced inode or error.
353    pub fn alloc(dev: u32, r#type: InodeType) -> Result<Self, FsError> {
354        let sb = SB.get().unwrap();
355
356        for inum in 1..sb.ninodes {
357            let mut buf = BCACHE.read(dev, sb.inodestart + inum / IPB);
358            let dinode = unsafe { DiskInode::from_buf(&mut buf, inum) };
359
360            if dinode.r#type == InodeType::Free {
361                dinode.r#type = r#type;
362                log::write(&buf);
363                BCACHE.release(buf);
364                return log!(Self::get(dev, inum));
365            }
366
367            BCACHE.release(buf);
368        }
369
370        err!(FsError::OutOfInode);
371    }
372
373    /// Finds the inode with number `inum` on device `dev` and returns the in-memory copy.
374    /// Does not lock the inode and does not read it from disk.
375    pub fn get(dev: u32, inum: u32) -> Result<Self, FsError> {
376        let mut meta = INODE_TABLE.meta.lock();
377
378        let mut empty = None;
379
380        for (id, inode) in meta.iter_mut().enumerate() {
381            if inode.r#ref > 0 && inode.dev == dev && inode.inum == inum {
382                inode.r#ref += 1;
383                return Ok(Self { id, dev, inum });
384            }
385
386            if empty.is_none() && inode.r#ref == 0 {
387                empty = Some(id);
388            }
389        }
390
391        if let Some(id) = empty {
392            let inode = &mut meta[id];
393            inode.dev = dev;
394            inode.inum = inum;
395            inode.r#ref = 1;
396
397            // # Safety: We have exclusive access to this inode since its ref count is 0.
398            let inner = unsafe { INODE_TABLE.inner[id].get_mut_unchecked() };
399            inner.valid = false;
400
401            Ok(Self { id, dev, inum })
402        } else {
403            err!(FsError::OutOfInode)
404        }
405    }
406
407    /// Copies a modified in-memory inode to disk.
408    /// Must be called after every change to an `Inode` field that lives on disk.
409    pub fn update(&self, inner: &SleepLockGuard<'_, InodeInner>) {
410        let sb = SB.get().unwrap();
411
412        let mut buf = BCACHE.read(self.dev, sb.inodestart + (self.inum / IPB));
413        let dinode = unsafe { DiskInode::from_buf(&mut buf, self.inum) };
414
415        dinode.r#type = inner.r#type;
416        dinode.major = inner.major;
417        dinode.minor = inner.minor;
418        dinode.nlink = inner.nlink;
419        dinode.size = inner.size;
420        dinode.addrs.copy_from_slice(&inner.addrs);
421
422        log::write(&buf);
423        BCACHE.release(buf);
424    }
425
426    /// Increments reference count for `inode`.
427    /// Returns `Inode` to enable `inode = idup(inode1)` idiom.
428    pub fn dup(&self) -> Self {
429        let mut meta = INODE_TABLE.meta.lock();
430        meta[self.id].r#ref += 1;
431        self.clone()
432    }
433
434    /// Locks the given `inode`. The lifetime of the lock is static since it comes from the table.
435    /// Reads the inode from disk if necessary.
436    pub fn lock(&self) -> SleepLockGuard<'static, InodeInner> {
437        let sb = SB.get().unwrap();
438
439        let mut inner = INODE_TABLE.inner[self.id].lock();
440
441        if !inner.valid {
442            let mut buf = BCACHE.read(self.dev, sb.inodestart + (self.inum / IPB));
443            let dinode = unsafe { DiskInode::from_buf(&mut buf, self.inum) };
444
445            inner.r#type = dinode.r#type;
446            inner.major = dinode.major;
447            inner.minor = dinode.minor;
448            inner.nlink = dinode.nlink;
449            inner.size = dinode.size;
450            inner.addrs.copy_from_slice(&dinode.addrs);
451
452            BCACHE.release(buf);
453
454            inner.valid = true;
455            assert_ne!(inner.r#type, InodeType::Free, "ilock: no type");
456        }
457
458        inner
459    }
460
461    /// Unlocks the given `inode`.
462    pub fn unlock(&self, guard: SleepLockGuard<'static, InodeInner>) {
463        drop(guard);
464    }
465
466    /// Drops a reference to an in-memory inode.
467    /// If that was the last reference, the inode table entry can be recycled.
468    /// If that was the last reference and the inode has no links to it, free the inode (and its
469    /// content) on disk.
470    /// All calls to `iput()` must be inside a transaction in case it has to free the inode.
471    pub fn put(mut self) {
472        let mut meta = INODE_TABLE.meta.lock();
473
474        if meta[self.id].r#ref == 1 {
475            // We are acquiring sleeplock while spinlock is active (interrupts disabled).
476            // This is normally problematic since sleeplock can sleep and never wake up but,
477            // ref == 1 means no other process can have ip locked, so the inner lock won't block.
478            let mut inner = INODE_TABLE.inner[self.id].lock();
479
480            if inner.valid && inner.nlink == 0 {
481                // inode has no links and no other refereneces: truncate and free
482
483                drop(meta);
484
485                self.trunc(&mut inner);
486                inner.r#type = InodeType::Free;
487                self.update(&inner);
488                inner.valid = false;
489
490                drop(inner);
491
492                // reacquire meta
493                meta = INODE_TABLE.meta.lock();
494            }
495        }
496
497        meta[self.id].r#ref -= 1;
498    }
499
500    /// Common idiom: `unlock()`, then `put()`
501    pub fn unlock_put(self, guard: SleepLockGuard<'static, InodeInner>) {
502        self.unlock(guard);
503        self.put();
504    }
505
506    /// Reclaims orphaned inodes on device `dev`.
507    /// Called at file system initialization.
508    pub fn reclaim(dev: u32) {
509        let sb = SB.get().unwrap();
510
511        for inum in 1..sb.ninodes {
512            let mut buf = BCACHE.read(dev, sb.inodestart + (inum / IPB));
513            let dinode = unsafe { DiskInode::from_buf(&mut buf, inum) };
514
515            let mut inode = None;
516            if dinode.r#type != InodeType::Free && dinode.nlink == 0 {
517                // this is an orphaned inode
518                println!("ireclaim: orphaned inode {}", inum);
519
520                inode.replace(log!(Inode::get(dev, inum)));
521            }
522
523            BCACHE.release(buf);
524
525            if let Some(Ok(inode)) = inode {
526                let _op = Operation::begin();
527                let guard = inode.lock();
528                inode.unlock(guard);
529                inode.put();
530            }
531        }
532    }
533
534    /// Truncates inode (discard contents).
535    pub fn trunc(&mut self, inner: &mut SleepLockGuard<'_, InodeInner>) {
536        for i in 0..NDIRECT {
537            if inner.addrs[i] != 0 {
538                let block = Block(inner.addrs[i]);
539                block.free(self.dev);
540                inner.addrs[i] = 0;
541            }
542        }
543
544        if inner.addrs[NDIRECT] != 0 {
545            let buf = BCACHE.read(self.dev, inner.addrs[NDIRECT]);
546            let array =
547                unsafe { slice::from_raw_parts(buf.data().as_ptr() as *const u32, NINDIRECT) };
548
549            for block in array {
550                if *block != 0 {
551                    let b = Block(*block);
552                    b.free(self.dev);
553                }
554            }
555
556            BCACHE.release(buf);
557            let block = Block(inner.addrs[NDIRECT]);
558            block.free(self.dev);
559            inner.addrs[NDIRECT] = 0;
560        }
561
562        inner.size = 0;
563        self.update(inner);
564    }
565
566    /// Returns the disk block address of the nth block in `inode`.
567    /// If there is no such block, allocates one.
568    pub fn map(
569        &self,
570        inner: &mut SleepLockGuard<'_, InodeInner>,
571        block_no: u32,
572    ) -> Result<u32, FsError> {
573        let mut block_no = block_no as usize;
574
575        if block_no < NDIRECT {
576            let addr = &mut inner.addrs[block_no];
577
578            if *addr == 0 {
579                let block = try_log!(Block::alloc(self.dev));
580                *addr = block.0;
581            }
582
583            return Ok(*addr);
584        }
585
586        block_no -= NDIRECT;
587
588        if block_no < NINDIRECT {
589            // load indiret block, allocating if necessary
590            let in_block_no = &mut inner.addrs[NDIRECT];
591
592            if *in_block_no == 0 {
593                let block = try_log!(Block::alloc(self.dev));
594                *in_block_no = block.0;
595            }
596
597            let mut buf = BCACHE.read(self.dev, *in_block_no);
598            let in_block = unsafe {
599                slice::from_raw_parts_mut(buf.data_mut().as_mut_ptr() as *mut u32, NINDIRECT)
600            };
601
602            let addr = &mut in_block[block_no];
603            if *addr == 0 {
604                let block = try_log!(Block::alloc(self.dev));
605
606                *addr = block.0;
607                log::write(&buf);
608            }
609
610            BCACHE.release(buf);
611
612            return Ok(*addr);
613        }
614
615        Err(FsError::OutOfRange)
616    }
617
618    pub fn stat(&self, inner: &SleepLockGuard<'_, InodeInner>) -> Stat {
619        Stat {
620            dev: self.dev,
621            r#type: inner.r#type,
622            nlink: inner.nlink,
623            size: inner.size as u64,
624            ino: self.inum,
625        }
626    }
627
628    /// Reads data from inode.
629    /// `dst_user` indicates whether `dst` is a user space address.
630    pub fn read(
631        &self,
632        inner: &mut SleepLockGuard<'_, InodeInner>,
633        offset: u32,
634        dst: &mut [u8],
635        dst_user: bool,
636    ) -> Result<u32, FsError> {
637        let mut dst = dst;
638        let mut n = dst.len() as u32;
639        let mut offset = offset;
640
641        if offset > inner.size || offset.checked_add(n).is_none() {
642            err!(FsError::Read);
643        }
644
645        if offset + n > inner.size {
646            n = inner.size - offset;
647        }
648
649        let mut total = 0;
650
651        while total < n {
652            if let Ok(addr) = log!(self.map(inner, offset / BSIZE as u32)) {
653                let buf = BCACHE.read(self.dev, addr);
654
655                let m = (n - total).min(BSIZE as u32 - offset % BSIZE as u32);
656
657                let src = &buf.data()[(offset as usize % BSIZE)..][..m as usize];
658
659                if dst_user {
660                    let dst_va = VA::from(dst.as_mut_ptr() as usize);
661                    if log!(proc::copy_to_user(src, dst_va)).is_err() {
662                        BCACHE.release(buf);
663                        err!(FsError::Read);
664                    }
665                } else {
666                    unsafe { ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), src.len()) }
667                }
668
669                BCACHE.release(buf);
670
671                total += m;
672                offset += m;
673                dst = &mut dst[m as usize..];
674            } else {
675                break;
676            }
677        }
678
679        Ok(total)
680    }
681
682    /// Writes data to inode.
683    /// `src_user` indicates whether `src` is a user space address.
684    pub fn write(
685        &self,
686        inner: &mut SleepLockGuard<'_, InodeInner>,
687        offset: u32,
688        src: &[u8],
689        src_user: bool,
690    ) -> Result<u32, FsError> {
691        let mut src = src;
692        let n = src.len() as u32;
693        let mut offset = offset;
694
695        if offset > inner.size || offset.checked_add(n).is_none() {
696            err!(FsError::Write);
697        }
698
699        if offset + n > (MAXFILE * BSIZE) as u32 {
700            err!(FsError::Write);
701        }
702
703        let mut total = 0;
704
705        while total < n {
706            if let Ok(addr) = log!(self.map(inner, offset / BSIZE as u32)) {
707                let mut buf = BCACHE.read(self.dev, addr);
708                let m = (n - total).min(BSIZE as u32 - (offset % BSIZE as u32));
709
710                let dst = &mut buf.data_mut()[(offset as usize % BSIZE)..][..m as usize];
711
712                if src_user {
713                    let src_va = VA::from(src.as_ptr() as usize);
714                    if log!(proc::copy_from_user(src_va, dst)).is_err() {
715                        BCACHE.release(buf);
716                        break;
717                    }
718                } else {
719                    unsafe { ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), src.len()) }
720                }
721
722                log::write(&buf);
723                BCACHE.release(buf);
724
725                total += m;
726                offset += m;
727                src = &src[m as usize..];
728            } else {
729                break;
730            }
731        }
732
733        if offset > inner.size {
734            inner.size = offset;
735        }
736
737        // write the inode back to disk even if the size didn't change because the loop above might
738        // have called `map()` and added a new block to `addrs[]`.
739        self.update(inner);
740
741        Ok(total)
742    }
743
744    pub fn create(
745        path: &Path,
746        r#type: InodeType,
747        major: u16,
748        minor: u16,
749    ) -> Result<(Self, SleepLockGuard<'static, InodeInner>), FsError> {
750        let (parent, name) = try_log!(path.resolve_parent());
751
752        let mut parent_inner = parent.lock();
753
754        // check if the file already exists
755        if let Ok(Some((_, inode))) = log!(Directory::lookup(&parent, &mut parent_inner, name)) {
756            parent.unlock_put(parent_inner);
757
758            let inode_inner = inode.lock();
759
760            // check type matches
761            if r#type == InodeType::File
762                && (inode_inner.r#type == InodeType::File
763                    || inode_inner.r#type == InodeType::Device)
764            {
765                return Ok((inode, inode_inner));
766            }
767
768            // type mismatch
769            inode.unlock_put(inode_inner);
770            err!(FsError::Create);
771        }
772
773        let inode = match log!(Self::alloc(parent.dev, r#type)) {
774            Ok(i) => i,
775            Err(e) => {
776                parent.unlock_put(parent_inner);
777                return Err(e);
778            }
779        };
780
781        let mut inode_inner = inode.lock();
782        inode_inner.major = major;
783        inode_inner.minor = minor;
784        inode_inner.nlink = 1;
785        inode.update(&inode_inner);
786
787        // create `.` and `..` entries if it is a directory
788        // no inode.nlink += 1 for `.` to avoid cyclic ref count
789        if r#type == InodeType::Directory
790            && (log!(Directory::link(
791                &inode,
792                &mut inode_inner,
793                ".",
794                inode.inum as u16
795            ))
796            .is_err()
797                || log!(Directory::link(
798                    &inode,
799                    &mut inode_inner,
800                    "..",
801                    parent.inum as u16
802                ))
803                .is_err())
804        {
805            // fail
806            inode_inner.nlink = 0;
807            inode.update(&inode_inner);
808            inode.unlock_put(inode_inner);
809            parent.unlock_put(parent_inner);
810            err!(FsError::Create);
811        }
812
813        if log!(Directory::link(
814            &parent,
815            &mut parent_inner,
816            name,
817            inode.inum as u16
818        ))
819        .is_err()
820        {
821            // fail
822            inode_inner.nlink = 0;
823            inode.update(&inode_inner);
824            inode.unlock_put(inode_inner);
825            parent.unlock_put(parent_inner);
826            err!(FsError::Create);
827        }
828
829        if r#type == InodeType::Directory {
830            // success is now guarenteed
831            parent_inner.nlink += 1;
832            parent.update(&parent_inner);
833        }
834
835        parent.unlock_put(parent_inner);
836
837        Ok((inode, inode_inner))
838    }
839}
840
841#[repr(C)]
842#[derive(Debug, Clone, Default)]
843pub struct Directory {
844    pub inum: u16,
845    pub name: [u8; DIRSIZE],
846}
847
848impl Directory {
849    pub const SIZE: usize = size_of::<Self>();
850
851    pub const fn new_empty() -> Self {
852        Self {
853            inum: 0,
854            name: [0; DIRSIZE],
855        }
856    }
857
858    fn from_bytes(bytes: &[u8; Self::SIZE]) -> Self {
859        unsafe { ptr::read_unaligned(bytes.as_ptr() as *const Self) }
860    }
861
862    pub fn as_bytes(&self) -> &[u8] {
863        unsafe { slice::from_raw_parts(self as *const Self as *const u8, Self::SIZE) }
864    }
865
866    fn from_inode(
867        inode: &Inode,
868        inner: &mut SleepLockGuard<'_, InodeInner>,
869        offset: u32,
870    ) -> Result<Self, FsError> {
871        let mut buf = [0; Self::SIZE];
872        let read = try_log!(inode.read(inner, offset, &mut buf, false));
873        assert_eq!(read as usize, Self::SIZE, "dir read from inode");
874        Ok(Self::from_bytes(&buf))
875    }
876
877    fn is_name_equal(&self, name: &str) -> bool {
878        let end = self.name.iter().position(|&c| c == 0).unwrap_or(DIRSIZE);
879        &self.name[..end] == name.as_bytes()
880    }
881
882    fn set_name(&mut self, name: &str) {
883        self.name.fill(0);
884        let bytes = name.as_bytes();
885        let len = bytes.len().min(DIRSIZE);
886        self.name[..len].copy_from_slice(&bytes[..len]);
887    }
888
889    /// Checks whether the directory is empty (only contains `.` and `..`).
890    pub fn is_empty(inode: &Inode, inner: &mut SleepLockGuard<'_, InodeInner>) -> bool {
891        for offset in ((2 * Self::SIZE as u32)..inner.size).step_by(Self::SIZE) {
892            let dir = log!(Self::from_inode(inode, inner, offset)).expect("dir is_empty");
893            if dir.inum != 0 {
894                return false;
895            }
896        }
897
898        true
899    }
900
901    /// Looks up for a directory entry in a directory.
902    /// If found, returns byte offset and Inode.
903    pub fn lookup(
904        inode: &Inode,
905        inner: &mut SleepLockGuard<'_, InodeInner>,
906        name: &str,
907    ) -> Result<Option<(u32, Inode)>, FsError> {
908        assert_eq!(inner.r#type, InodeType::Directory, "dirlookup not DIR");
909
910        for offset in (0..inner.size).step_by(Self::SIZE) {
911            let dir = try_log!(Self::from_inode(inode, inner, offset));
912
913            if dir.inum == 0 {
914                continue;
915            }
916
917            if dir.is_name_equal(name) {
918                // entry matches path element
919                let dir_inode = try_log!(Inode::get(inode.dev, dir.inum as u32));
920                return Ok(Some((offset, dir_inode)));
921            }
922        }
923
924        Ok(None)
925    }
926
927    /// Writes a new directory entry (name, inum) into the directory Inode.
928    pub fn link(
929        inode: &Inode,
930        inner: &mut SleepLockGuard<'_, InodeInner>,
931        name: &str,
932        inum: u16,
933    ) -> Result<(), FsError> {
934        // check the name is not present
935        if let Ok(Some((_, dir))) = log!(Self::lookup(inode, inner, name)) {
936            dir.put();
937            err!(FsError::Link);
938        }
939
940        // look for an empty directory
941        let mut dir = Self::new_empty();
942        let mut offset = 0;
943
944        while offset < inner.size {
945            dir = try_log!(Self::from_inode(inode, inner, offset));
946
947            // if we find an empty slot break and use it.
948            // otherwise, the dir will be appended to the end
949            if dir.inum == 0 {
950                break;
951            } else {
952                offset += Self::SIZE as u32;
953            }
954        }
955
956        dir.set_name(name);
957        dir.inum = inum;
958
959        let write = try_log!(inode.write(inner, offset, dir.as_bytes(), false));
960        if write as usize != Self::SIZE {
961            err!(FsError::Link);
962        }
963
964        Ok(())
965    }
966}
967
968#[derive(Debug, Clone)]
969pub struct Path<'a>(&'a str);
970
971impl<'a> Path<'a> {
972    pub const fn new(name: &'a str) -> Path<'a> {
973        Self(name)
974    }
975
976    pub fn as_str(&self) -> &'a str {
977        self.0
978    }
979
980    fn is_empty(&self) -> bool {
981        self.0.is_empty()
982    }
983
984    fn is_absolute(&self) -> bool {
985        self.0.starts_with('/')
986    }
987
988    /// Returns (next path component, rest).
989    /// The returned path has no leading slashes, so the caller can check to see if the component
990    /// is the last one (Path == '\0').
991    /// If no component to remove, returns None.
992    pub fn next_component(&self) -> Option<(&'a str, Path<'a>)> {
993        let s = self.0.trim_start_matches('/');
994
995        if s.is_empty() {
996            return None;
997        }
998
999        match s.find('/') {
1000            Some(i) => {
1001                let rest = s[i..].trim_start_matches('/');
1002                Some((&s[..i], Path(rest)))
1003            }
1004            None => Some((s, Path(""))),
1005        }
1006    }
1007
1008    fn resolve_inner(&self, parent: bool) -> Result<(Inode, &'a str), FsError> {
1009        let mut inode = if self.is_absolute() {
1010            try_log!(Inode::get(ROOTDEV, ROOTINO))
1011        } else {
1012            proc::current_proc().data().cwd.dup()
1013        };
1014
1015        let mut name = "";
1016        let mut path = self.clone();
1017
1018        // walk the path, one component at at time
1019        while let Some((component, rest)) = path.next_component() {
1020            let mut inner = inode.lock();
1021
1022            if inner.r#type != InodeType::Directory {
1023                inode.unlock_put(inner);
1024                err!(FsError::Resolve);
1025            }
1026
1027            // stop one level early
1028            if parent && rest.is_empty() {
1029                inode.unlock(inner);
1030                return Ok((inode, component));
1031            }
1032
1033            // get the next inode
1034            match log!(Directory::lookup(&inode, &mut inner, component)) {
1035                Ok(Some((_, next))) => {
1036                    inode.unlock_put(inner);
1037                    inode = next;
1038                }
1039                Ok(None) => {
1040                    inode.unlock_put(inner);
1041                    err!(FsError::Resolve);
1042                }
1043                Err(e) => {
1044                    inode.unlock_put(inner);
1045                    return Err(e);
1046                }
1047            }
1048
1049            name = component;
1050            path = rest;
1051        }
1052
1053        // we returned early to put the last inode
1054        if parent {
1055            inode.put();
1056            err!(FsError::Resolve);
1057        }
1058
1059        Ok((inode, name))
1060    }
1061
1062    /// Resolves the full path to an inode.
1063    pub fn resolve(&self) -> Result<Inode, FsError> {
1064        log!(self.resolve_inner(false).map(|(inode, _)| inode))
1065    }
1066
1067    /// Resolves to the parent directory, returning (parent, final_name).
1068    pub fn resolve_parent(&self) -> Result<(Inode, &'a str), FsError> {
1069        log!(self.resolve_inner(true))
1070    }
1071}