From 84046f75826125a4c025dafa2b26587626e19dfc Mon Sep 17 00:00:00 2001
From: Eemeli <eemeli.o.lehtonen@utu.fi>
Date: Mon, 22 May 2023 15:35:53 +0300
Subject: [PATCH] WIP: vfs devices lazy init

---
 src/arch/x86_64/cpu/ints.rs   |   3 +-
 src/driver/acpi/apic.rs       |  15 ++--
 src/driver/acpi/hpet.rs       |  82 ++++++++++++++++----
 src/driver/acpi/mod.rs        |   8 +-
 src/driver/rtc.rs             |  49 ++----------
 src/main.rs                   |  16 +---
 src/scheduler/kshell/shell.rs |   5 +-
 src/scheduler/mod.rs          |   1 +
 src/scheduler/timer.rs        |  21 +++++
 src/util/atomic_map.rs        |   4 +
 src/util/mod.rs               |   1 +
 src/util/slice_read.rs        |  47 +++++++++++
 src/vfs/devices.rs            |  11 +++
 src/vfs/mod.rs                | 142 ++++++++++++++++++++++++++++++++--
 14 files changed, 315 insertions(+), 90 deletions(-)
 create mode 100644 src/scheduler/timer.rs
 create mode 100644 src/util/slice_read.rs
 create mode 100644 src/vfs/devices.rs

diff --git a/src/arch/x86_64/cpu/ints.rs b/src/arch/x86_64/cpu/ints.rs
index baf95e1..04dd61f 100644
--- a/src/arch/x86_64/cpu/ints.rs
+++ b/src/arch/x86_64/cpu/ints.rs
@@ -160,7 +160,6 @@ pub extern "x86-interrupt" fn keyboard(_: InterruptStackFrame) {
 }
 
 pub extern "x86-interrupt" fn rtc_tick(_: InterruptStackFrame) {
-    provide_tick();
     RTC.int_ack();
     eoi_irq(Irq::PicRtc as _);
 }
@@ -171,7 +170,7 @@ pub extern "x86-interrupt" fn apic_timer(_: InterruptStackFrame) {
 }
 
 pub extern "x86-interrupt" fn apic_spurious(_: InterruptStackFrame) {
-    provide_tick();
+    // spurdo spärde keskeytys
     eoi();
 }
 
diff --git a/src/driver/acpi/apic.rs b/src/driver/acpi/apic.rs
index 0bb393a..461e340 100644
--- a/src/driver/acpi/apic.rs
+++ b/src/driver/acpi/apic.rs
@@ -14,21 +14,25 @@ use crate::{
 
 // enable APIC for this processor
 pub fn enable() {
-    crate::debug!("Initializing {:?}", ApicId::current());
     write_msr(
         IA32_APIC_BASE,
         read_msr(IA32_APIC_BASE) | IA32_APIC_XAPIC_ENABLE,
     );
 
     let regs = unsafe { &mut *(MADT.local_apic_addr as *mut ApicRegs) };
-    LAPICS.insert(ApicId::current(), RwLock::new(Lapic { regs }));
-    let mut lapic = LAPICS.get(&ApicId::current()).unwrap().write();
+    let apic_id = ApicId(regs.lapic_id.read());
+
+    crate::debug!("Initializing {apic_id:?}");
+    LAPICS.insert(apic_id, RwLock::new(Lapic { regs }));
+    let mut lapic = LAPICS.get(&apic_id).unwrap().write();
 
     reset(lapic.regs);
     init_lvt_timer(lapic.regs);
-    crate::debug!("Done Initializing {:?}", ApicId::current());
+    crate::debug!("Done Initializing {apic_id:?}");
     // trace!("APIC regs: {:#?}", lapic.regs);
 
+    // write_msr(IA32_TSC_AUX, apic_id.inner() as _);
+
     INT_CONTROLLER.store(IntController::Apic);
 }
 
@@ -56,7 +60,7 @@ impl ApicId {
     pub fn current() -> Self {
         let regs = unsafe { &*(MADT.local_apic_addr as *const ApicRegs) };
         Self(regs.lapic_id.read())
-        // Self(read_msr(...) as u32)
+        /* Self(read_msr(IA32_TSC_AUX) as u32) */
     }
 
     pub fn lapic(&self) -> RwLockReadGuard<'static, Lapic> {
@@ -101,6 +105,7 @@ impl Lapic {
 static LAPICS: AtomicMap<ApicId, RwLock<Lapic>> = AtomicMap::new();
 
 const IA32_APIC_BASE: u32 = 0x1B;
+const IA32_TSC_AUX: u32 = 0xC0000103; // lapic id storage - same as in Theseus
 
 const IA32_APIC_XAPIC_ENABLE: u64 = 1 << 11;
 const _IA32_APIC_X2APIC_ENABLE: u64 = 1 << 10;
diff --git a/src/driver/acpi/hpet.rs b/src/driver/acpi/hpet.rs
index d3aae34..5dbf8a0 100644
--- a/src/driver/acpi/hpet.rs
+++ b/src/driver/acpi/hpet.rs
@@ -6,15 +6,18 @@ use core::ptr::{read_volatile, write_volatile};
 
 use bit_field::BitField;
 use chrono::Duration;
-use spin::Lazy;
+use spin::{Lazy, Mutex};
 
-use crate::{debug, trace};
+use crate::{
+    debug, trace,
+    vfs::{FileDevice, IoResult},
+};
 
 use super::{rsdt::RSDT, SdtError};
 
 //
 
-pub static HPET: Lazy<Hpet> = Lazy::new(Hpet::init);
+pub static HPET: Lazy<Mutex<Hpet>> = Lazy::new(|| Mutex::new(Hpet::init()));
 
 //
 
@@ -55,12 +58,8 @@ pub enum HpetError {
 //
 
 impl Hpet {
-    pub fn get() -> &'static Self {
-        &HPET
-    }
-
     pub fn init() -> Self {
-        Self::try_init().expect("MADT should be valid")
+        Self::try_init().expect("HPET should be valid")
     }
 
     pub fn try_init() -> Result<Self, HpetError> {
@@ -116,7 +115,7 @@ impl Hpet {
     }
 
     pub fn main_counter_value(&mut self) -> CounterValue {
-        self.read_reg(0x030)
+        self.read_reg(0x0F0)
     }
 
     pub fn set_main_counter_value(&mut self, val: CounterValue) {
@@ -125,6 +124,40 @@ impl Hpet {
 
     //
 
+    pub fn femtos(&mut self) -> u128 {
+        self.period as u128 * self.main_counter_value() as u128
+    }
+
+    pub fn picos(&mut self) -> u128 {
+        self.femtos() / 1_000
+    }
+
+    pub fn nanos(&mut self) -> u128 {
+        self.picos() / 1_000
+    }
+
+    pub fn micros(&mut self) -> u128 {
+        self.nanos() / 1_000
+    }
+
+    pub fn millis(&mut self) -> u128 {
+        self.micros() / 1_000
+    }
+
+    pub fn seconds(&mut self) -> u128 {
+        self.millis() / 1_000
+    }
+
+    pub fn minutes(&mut self) -> u128 {
+        self.millis() / 60
+    }
+
+    pub fn now_bytes(&mut self) -> [u8; 16] {
+        self.femtos().to_le_bytes()
+    }
+
+    //
+
     fn read_reg(&mut self, reg: u64) -> u64 {
         unsafe { read_volatile((self.addr + reg) as *const u64) }
     }
@@ -143,11 +176,10 @@ impl Hpet {
         config.set_enable_cnf(1);
         self.set_config(config);
 
+        debug!("HPET caps: {:#x?}", self.caps());
+        debug!("HPET config: {:#x?}", self.config());
+        debug!("HPET int status: {:#x?}", self.interrupt_status());
         debug!("HPET freq: {}", Self::freq(self.period));
-
-        /* loop {
-            println!("main counter: {}", self.main_counter_value());
-        } */
     }
 
     #[allow(unused)]
@@ -191,6 +223,28 @@ impl From<SdtError> for HpetError {
 
 //
 
+pub struct HpetDevice;
+
+//
+
+impl FileDevice for HpetDevice {
+    fn len(&self) -> usize {
+        core::mem::size_of::<i64>()
+    }
+
+    fn read(&self, offset: usize, buf: &mut [u8]) -> IoResult<usize> {
+        let bytes = &HPET.lock().now_bytes()[..];
+        bytes.read(offset, buf)
+    }
+
+    fn write(&mut self, offset: usize, buf: &[u8]) -> IoResult<usize> {
+        let mut bytes = &HPET.lock().now_bytes()[..];
+        bytes.write(offset, buf)
+    }
+}
+
+//
+
 macro_rules! bitfield {
     ($name:ident = $t:ty { $($field:ident : $range:expr),* $(,)? }) => {
         ::paste::paste! {
@@ -211,7 +265,7 @@ macro_rules! bitfield {
 
             impl ::core::fmt::Debug for $name {
                 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-                    f.debug_struct("GeneralCaps")
+                    f.debug_struct(stringify!($name))
                         $(
                             .field(stringify!($field), &self.$field())
                          )*
diff --git a/src/driver/acpi/mod.rs b/src/driver/acpi/mod.rs
index 07b52a8..296db8d 100644
--- a/src/driver/acpi/mod.rs
+++ b/src/driver/acpi/mod.rs
@@ -243,25 +243,25 @@ pub struct Reserved<T = u32> {
 
 // TODO: should be <T: Copy> but it breaks rust-analyzer
 impl<T> ReadOnly<T> {
-    fn read(&self) -> T {
+    pub fn read(&self) -> T {
         unsafe { ptr::read_volatile(&self.val as _) }
     }
 }
 
 // TODO: should be <T: Copy> but it breaks rust-analyzer
 impl<T> ReadWrite<T> {
-    fn read(&self) -> T {
+    pub fn read(&self) -> T {
         unsafe { ptr::read_volatile(&self.val as _) }
     }
 
-    fn write(&mut self, val: T) {
+    pub fn write(&mut self, val: T) {
         unsafe { ptr::write_volatile(&mut self.val as _, val) }
     }
 }
 
 // TODO: should be <T: Copy> but it breaks rust-analyzer
 impl<T> WriteOnly<T> {
-    fn write(&mut self, val: T) {
+    pub fn write(&mut self, val: T) {
         unsafe { ptr::write_volatile(&mut self.val as _, val) }
     }
 }
diff --git a/src/driver/rtc.rs b/src/driver/rtc.rs
index 1daae31..5bd0d56 100644
--- a/src/driver/rtc.rs
+++ b/src/driver/rtc.rs
@@ -2,7 +2,6 @@ use crate::{
     debug, error,
     vfs::{self, FileDevice},
 };
-use alloc::sync::Arc;
 use chrono::{DateTime, TimeZone, Utc};
 use core::{
     mem,
@@ -37,10 +36,6 @@ impl Rtc {
         }
     }
 
-    pub fn install_device() {
-        _ = vfs::create_device("/dev/rtc", true, Arc::new(Mutex::new(RtcDevice)) as _);
-    }
-
     pub fn enable_ints(&self) {
         without_interrupts(|| {
             let mut ports = self.ports.lock();
@@ -108,49 +103,21 @@ impl Rtc {
     }
 }
 
-struct RtcDevice;
+pub struct RtcDevice;
 
 impl FileDevice for RtcDevice {
-    fn len(&mut self) -> usize {
+    fn len(&self) -> usize {
         mem::size_of::<i64>()
     }
 
-    fn read(&mut self, offset: usize, buf: &mut [u8]) -> vfs::IoResult<usize> {
-        let bytes = RTC.now_bytes();
-
-        let len = self
-            .len()
-            .checked_sub(offset)
-            .ok_or(vfs::IoError::UnexpectedEOF)?
-            .min(buf.len());
-
-        buf[..len].copy_from_slice(
-            bytes
-                .get(offset..offset + len)
-                .ok_or(vfs::IoError::UnexpectedEOF)?,
-        );
-
-        Ok(len)
-    }
-
-    fn read_exact(&mut self, offset: usize, buf: &mut [u8]) -> vfs::IoResult<()> {
-        let bytes = RTC.now_bytes();
-
-        buf.copy_from_slice(
-            bytes
-                .get(offset..offset + buf.len())
-                .ok_or(vfs::IoError::UnexpectedEOF)?,
-        );
-
-        Ok(())
-    }
-
-    fn write(&mut self, _: usize, _: &mut [u8]) -> vfs::IoResult<usize> {
-        Err(vfs::IoError::PermissionDenied)
+    fn read(&self, offset: usize, buf: &mut [u8]) -> vfs::IoResult<usize> {
+        let bytes = &RTC.now_bytes()[..];
+        bytes.read(offset, buf)
     }
 
-    fn write_exact(&mut self, _: usize, _: &mut [u8]) -> vfs::IoResult<()> {
-        Err(vfs::IoError::PermissionDenied)
+    fn write(&mut self, offset: usize, buf: &[u8]) -> vfs::IoResult<usize> {
+        let mut bytes = &RTC.now_bytes()[..];
+        bytes.write(offset, buf)
     }
 }
 
diff --git a/src/main.rs b/src/main.rs
index 2673477..fd91494 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -26,10 +26,7 @@ use core::sync::atomic::{AtomicUsize, Ordering};
 use crate::{
     arch::cpu::idt::Irq,
     driver::{
-        acpi::{
-            apic::ApicId,
-            ioapic::{IoApic, IO_APICS},
-        },
+        acpi::{apic::ApicId, ioapic::IoApic},
         rtc,
     },
     scheduler::kshell::kshell,
@@ -102,17 +99,6 @@ fn kernel_main() -> ! {
     #[cfg(test)]
     test_main();
 
-    if let Some(time) = rtc::RTC.now() {
-        debug!("RTC time: {time:?}");
-    }
-
-    rtc::RTC.enable_ints();
-    rtc::Rtc::install_device();
-    debug!(
-        "ints enabled?: {}",
-        x86_64::instructions::interrupts::are_enabled()
-    );
-
     // main task(s)
     scheduler::spawn(kshell());
 
diff --git a/src/scheduler/kshell/shell.rs b/src/scheduler/kshell/shell.rs
index dfd37d6..34f8360 100644
--- a/src/scheduler/kshell/shell.rs
+++ b/src/scheduler/kshell/shell.rs
@@ -1,4 +1,5 @@
 use crate::{
+    driver::acpi::hpet::HPET,
     mem::pmm::PageFrameAllocator,
     util::fmt::NumberPostfix,
     vfs::{
@@ -67,7 +68,9 @@ impl<'fbo> Shell<'fbo> {
         self.term.flush();
     }
 
-    pub fn tick(&mut self) {}
+    pub fn tick(&mut self) {
+        // crate::debug!("tick : {}", HPET.lock().main_counter_value());
+    }
 
     fn prompt(&mut self) {
         _ = write!(self.term, "\n[kshell {}]# ", self.current_dir.as_str());
diff --git a/src/scheduler/mod.rs b/src/scheduler/mod.rs
index 9d8f69d..4abe4e5 100644
--- a/src/scheduler/mod.rs
+++ b/src/scheduler/mod.rs
@@ -3,6 +3,7 @@ pub mod keyboard;
 pub mod kshell;
 pub mod task;
 pub mod tick;
+pub mod timer;
 
 //
 
diff --git a/src/scheduler/timer.rs b/src/scheduler/timer.rs
new file mode 100644
index 0000000..3707dba
--- /dev/null
+++ b/src/scheduler/timer.rs
@@ -0,0 +1,21 @@
+use alloc::collections::BinaryHeap;
+use spin::Mutex;
+
+use crate::{driver::acpi::apic::ApicId, util::atomic_map::AtomicMap};
+
+//
+
+/* pub static DEADLINES: BinaryHeap<TimerEntry>; */
+
+//
+
+/* pub struct TimerEntry {
+    // deadline:
+} */
+
+//
+
+pub fn test() {
+    let lapic = ApicId::current().lapic_mut();
+    lapic.regs().timer_current.read();
+}
diff --git a/src/util/atomic_map.rs b/src/util/atomic_map.rs
index e57528e..7d91daf 100644
--- a/src/util/atomic_map.rs
+++ b/src/util/atomic_map.rs
@@ -65,6 +65,10 @@ impl<K, V> AtomicMap<K, V> {
         self.len.load(Ordering::Acquire)
     }
 
+    pub fn is_empty(&self) -> bool {
+        self.len() == 0
+    }
+
     pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
         let mut cur = &self.head;
         core::iter::from_fn(move || {
diff --git a/src/util/mod.rs b/src/util/mod.rs
index b13c840..a536c11 100644
--- a/src/util/mod.rs
+++ b/src/util/mod.rs
@@ -1,4 +1,5 @@
 pub mod atomic_map;
 pub mod bitmap;
 pub mod fmt;
+pub mod slice_read;
 pub mod stack_str;
diff --git a/src/util/slice_read.rs b/src/util/slice_read.rs
new file mode 100644
index 0000000..64bfaef
--- /dev/null
+++ b/src/util/slice_read.rs
@@ -0,0 +1,47 @@
+use crate::vfs::{FileDevice, IoError, IoResult};
+
+//
+
+impl FileDevice for &'_ [u8] {
+    fn len(&self) -> usize {
+        self.len()
+    }
+
+    fn read(&self, offset: usize, buf: &mut [u8]) -> IoResult<usize> {
+        let len = self
+            .len()
+            .checked_sub(offset)
+            .ok_or(IoError::UnexpectedEOF)?
+            .min(buf.len());
+
+        buf[..len].copy_from_slice(&self[offset..offset + len]);
+
+        Ok(len)
+    }
+
+    fn write(&mut self, offset: usize, buf: &[u8]) -> IoResult<usize> {
+        Err(IoError::PermissionDenied)
+    }
+}
+
+impl FileDevice for &'_ mut [u8] {
+    fn len(&self) -> usize {
+        self.len()
+    }
+
+    fn read(&self, offset: usize, buf: &mut [u8]) -> IoResult<usize> {
+        self.as_ref().read(offset, buf)
+    }
+
+    fn write(&mut self, offset: usize, buf: &[u8]) -> IoResult<usize> {
+        let len = self
+            .len()
+            .checked_sub(offset)
+            .ok_or(IoError::UnexpectedEOF)?
+            .min(buf.len());
+
+        self[offset..offset + len].copy_from_slice(&buf[..len]);
+
+        Ok(len)
+    }
+}
diff --git a/src/vfs/devices.rs b/src/vfs/devices.rs
new file mode 100644
index 0000000..7ea9798
--- /dev/null
+++ b/src/vfs/devices.rs
@@ -0,0 +1,11 @@
+use crate::{
+    driver::{acpi::hpet::HpetDevice, rtc::RtcDevice},
+    vfs,
+};
+
+//
+
+pub fn install() {
+    vfs::install_dev("/dev/rtc", RtcDevice);
+    vfs::install_dev("/dev/hpet", HpetDevice);
+}
diff --git a/src/vfs/mod.rs b/src/vfs/mod.rs
index 1531956..55b6a0c 100644
--- a/src/vfs/mod.rs
+++ b/src/vfs/mod.rs
@@ -13,11 +13,17 @@ use self::path::Path;
 
 //
 
+pub mod devices;
 pub mod path;
 
 //
 
-pub static ROOT: Lazy<Root> = Lazy::new(|| Directory::from(""));
+static _ROOT_NODE: Lazy<Root> = Lazy::new(|| Directory::from(""));
+pub static ROOT: Lazy<Root> = Lazy::new(|| {
+    debug!("Initializing VFS");
+    devices::install();
+    _ROOT_NODE.clone()
+});
 
 //
 
@@ -120,19 +126,51 @@ pub struct Directory {
 }
 
 pub trait FileDevice {
-    fn len(&mut self) -> usize;
+    fn len(&self) -> usize;
 
-    fn is_empty(&mut self) -> bool {
+    fn is_empty(&self) -> bool {
         self.len() == 0
     }
 
-    fn read(&mut self, offset: usize, buf: &mut [u8]) -> IoResult<usize>;
-
-    fn read_exact(&mut self, offset: usize, buf: &mut [u8]) -> IoResult<()>;
+    fn read(&self, offset: usize, buf: &mut [u8]) -> IoResult<usize>;
+
+    fn read_exact(&self, mut offset: usize, mut buf: &mut [u8]) -> IoResult<()> {
+        while !buf.is_empty() {
+            match self.read(offset, buf) {
+                Ok(0) => break,
+                Ok(n) => {
+                    offset += n;
+                    let tmp = buf;
+                    buf = &mut tmp[n..];
+                }
+                Err(IoError::Interrupted) => {}
+                Err(err) => return Err(err),
+            }
+        }
 
-    fn write(&mut self, offset: usize, bytes: &mut [u8]) -> IoResult<usize>;
+        if !buf.is_empty() {
+            Err(IoError::UnexpectedEOF)
+        } else {
+            Ok(())
+        }
+    }
 
-    fn write_exact(&mut self, offset: usize, bytes: &mut [u8]) -> IoResult<()>;
+    fn write(&mut self, offset: usize, buf: &[u8]) -> IoResult<usize>;
+
+    fn write_exact(&mut self, mut offset: usize, mut buf: &[u8]) -> IoResult<()> {
+        while !buf.is_empty() {
+            match self.write(offset, buf) {
+                Ok(0) => return Err(IoError::WriteZero),
+                Ok(n) => {
+                    offset += n;
+                    buf = &buf[n..];
+                }
+                Err(IoError::Interrupted) => {}
+                Err(err) => return Err(err),
+            }
+        }
+        Ok(())
+    }
 }
 
 pub trait DirectoryDevice {
@@ -165,6 +203,12 @@ pub enum IoError {
 
     #[snafu(display("unexpected end of file"))]
     UnexpectedEOF,
+
+    #[snafu(display("interrupted"))]
+    Interrupted,
+
+    #[snafu(display("wrote nothing"))]
+    WriteZero,
 }
 
 pub type IoResult<T> = Result<T, IoError>;
@@ -215,12 +259,94 @@ impl IoError {
             IoError::FilesystemError => "filesystem error",
             IoError::PermissionDenied => "permission denied",
             IoError::UnexpectedEOF => "unexpected eof",
+            IoError::Interrupted => "interrupted",
+            IoError::WriteZero => "wrote nothing",
         }
     }
 }
 
 //
 
+fn get_node_with(mut node: Node, path: impl AsRef<Path>, make_dirs: bool) -> IoResult<Node> {
+    for part in path.as_ref().iter() {
+        match node {
+            Node::File(_) => return Err(IoError::NotADirectory),
+            Node::Directory(_dir) => {
+                let mut dir = _dir.lock();
+                // TODO: only Node::Directory should be cloned
+
+                node = if let Ok(node) = dir.get_node(part) {
+                    node
+                } else if make_dirs {
+                    let node = Node::Directory(Directory::from(part));
+                    dir.create_node(part, node.clone())?;
+                    node
+                } else {
+                    return Err(IoError::NotFound);
+                };
+            }
+        }
+    }
+
+    Ok(node)
+}
+
+fn get_dir_with(node: Node, path: impl AsRef<Path>, make_dirs: bool) -> IoResult<DirRef> {
+    let node = get_node_with(node, path, make_dirs)?;
+    match node {
+        Node::File(_) => Err(IoError::NotADirectory),
+        Node::Directory(dir) => Ok(dir),
+    }
+}
+
+fn get_file_with(
+    node: Node,
+    path: impl AsRef<Path>,
+    make_dirs: bool,
+    _create: bool,
+) -> IoResult<FileRef> {
+    let node = get_node_with(node, path, make_dirs)?;
+    match node {
+        Node::File(file) => Ok(file),
+        Node::Directory(_) => Err(IoError::IsADirectory),
+    }
+}
+
+fn create_device_with(
+    node: Node,
+    path: impl AsRef<Path>,
+    make_dirs: bool,
+    dev: FileRef,
+) -> IoResult<()> {
+    create_node_with(node, path, make_dirs, Node::File(dev))
+}
+
+fn install_dev_with(
+    node: Node,
+    path: impl AsRef<Path>,
+    dev: impl FileDevice + Send + Sync + 'static,
+) {
+    let path = path.as_ref();
+    debug!("installing VFS device at {path:?}");
+    if let Err(err) = create_device_with(node, path, true, Arc::new(Mutex::new(dev)) as _) {
+        error!("failed to install VFS device at {path:?} : {err:?}");
+    }
+}
+
+fn create_node_with(
+    node: Node,
+    path: impl AsRef<Path>,
+    make_dirs: bool,
+    node: Node,
+) -> IoResult<()> {
+    let (parent_dir, file_name) = path.as_ref().split().ok_or(IoError::NotFound)?;
+    let parent_dir = get_dir(parent_dir, make_dirs)?;
+
+    let mut parent_dir = parent_dir.lock();
+    parent_dir.create_node_with(node, file_name, node)?;
+
+    Ok(())
+}
 fn create_node(path: impl AsRef<Path>, make_dirs: bool, node: Node) -> IoResult<()> {
     let (parent_dir, file_name) = path.as_ref().split().ok_or(IoError::NotFound)?;
     let parent_dir = get_dir(parent_dir, make_dirs)?;
-- 
GitLab