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
15pub const FSMAGIC: u32 = 0x10203040;
17
18pub const ROOTINO: u32 = 1;
20pub const BSIZE: usize = 1024;
22pub const NDIRECT: usize = 12;
24pub const NINDIRECT: usize = BSIZE / size_of::<u32>();
26pub const MAXFILE: usize = NDIRECT + NINDIRECT;
28
29pub const IPB: u32 = (BSIZE / size_of::<DiskInode>()) as u32;
31pub const BPB: u32 = BSIZE as u32 * 8;
33pub 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#[repr(C)]
75#[derive(Debug, Clone)]
76pub struct SuperBlock {
77 pub magic: u32,
79 pub size: u32,
81 pub nblocks: u32,
83 pub ninodes: u32,
85 pub nlogs: u32,
87 pub logstart: u32,
89 pub inodestart: u32,
91 pub bmapstart: u32,
93}
94
95impl SuperBlock {
96 fn initialize(dev: u32) {
98 let buf = BCACHE.read(dev, 1); 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
108pub fn init(dev: u32) {
110 SuperBlock::initialize(dev);
111 log::init(dev, SB.get().unwrap());
112 Inode::reclaim(dev);
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq)]
118pub struct Block(pub u32);
119
120impl Block {
121 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 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 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 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#[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#[repr(C)]
191#[derive(Debug)]
192pub struct DiskInode {
193 pub r#type: InodeType,
195 pub major: u16,
197 pub minor: u16,
199 pub nlink: u16,
201 pub size: u32,
203 pub addrs: [u32; NDIRECT + 1],
205}
206
207impl DiskInode {
208 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#[derive(Debug)]
234pub struct InodeInner {
235 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
260pub struct InodeMeta {
262 pub dev: u32,
264 pub inum: u32,
266 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#[derive(Debug, Clone, PartialEq, Eq, Default)]
285pub struct Inode {
286 pub id: usize,
288 pub dev: u32,
290 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 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 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 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 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 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 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 pub fn unlock(&self, guard: SleepLockGuard<'static, InodeInner>) {
463 drop(guard);
464 }
465
466 pub fn put(mut self) {
472 let mut meta = INODE_TABLE.meta.lock();
473
474 if meta[self.id].r#ref == 1 {
475 let mut inner = INODE_TABLE.inner[self.id].lock();
479
480 if inner.valid && inner.nlink == 0 {
481 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 meta = INODE_TABLE.meta.lock();
494 }
495 }
496
497 meta[self.id].r#ref -= 1;
498 }
499
500 pub fn unlock_put(self, guard: SleepLockGuard<'static, InodeInner>) {
502 self.unlock(guard);
503 self.put();
504 }
505
506 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 pub fn link(
929 inode: &Inode,
930 inner: &mut SleepLockGuard<'_, InodeInner>,
931 name: &str,
932 inum: u16,
933 ) -> Result<(), FsError> {
934 if let Ok(Some((_, dir))) = log!(Self::lookup(inode, inner, name)) {
936 dir.put();
937 err!(FsError::Link);
938 }
939
940 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 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 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 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 if parent && rest.is_empty() {
1029 inode.unlock(inner);
1030 return Ok((inode, component));
1031 }
1032
1033 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 if parent {
1055 inode.put();
1056 err!(FsError::Resolve);
1057 }
1058
1059 Ok((inode, name))
1060 }
1061
1062 pub fn resolve(&self) -> Result<Inode, FsError> {
1064 log!(self.resolve_inner(false).map(|(inode, _)| inode))
1065 }
1066
1067 pub fn resolve_parent(&self) -> Result<(Inode, &'a str), FsError> {
1069 log!(self.resolve_inner(true))
1070 }
1071}