From c6d15424d02d6141ac821c16fcacd94668d013c0 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 26 Jul 2021 10:11:08 +0200 Subject: [PATCH 1/7] Add creation time to dir entry --- doc/filesystem.md | 15 ++++----- src/api/console.rs | 1 + src/sys/fs.rs | 77 +++++++++++++++++++++++++++++++--------------- src/usr/list.rs | 17 +++++++++- 4 files changed, 77 insertions(+), 33 deletions(-) diff --git a/doc/filesystem.md b/doc/filesystem.md index dd6d828e8..b9cd6a2d3 100644 --- a/doc/filesystem.md +++ b/doc/filesystem.md @@ -103,16 +103,17 @@ Structure: A directory entry represents a file or a directory contained inside a directory. Each entry use a variable number of bytes that must fit inside the data of one block. Those bytes represent the kind of entry (file or dir), the -address of the first block, the filesize (max 4GB), the length of the filename, -and the filename (max 255 chars) of the entry. +address of the first block, the filesize (max 4GB), the creation time in +seconds since Unix Epoch, the length of the filename, and the filename (max +255 chars) of the entry. Structure: - 0 1 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 m - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+ - |k| addr | size |n| name buffer | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+ + 0 1 2 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 m + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+ + |k| addr | size | creation time |n| name buffer | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+ k = kind of entry n = length of name buffer diff --git a/src/api/console.rs b/src/api/console.rs index 3227f1c72..20bfeb9da 100644 --- a/src/api/console.rs +++ b/src/api/console.rs @@ -1,6 +1,7 @@ use crate::sys; use core::fmt; +#[derive(Clone, Copy)] pub struct Style { foreground: Option, background: Option, diff --git a/src/sys/fs.rs b/src/sys/fs.rs index eec22e1b4..23d81e48f 100644 --- a/src/sys/fs.rs +++ b/src/sys/fs.rs @@ -59,6 +59,7 @@ pub struct File { name: String, addr: u32, size: u32, + time: u64, dir: Dir, // TODO: Replace with `parent: Some(Dir)` and also add it to `Dir` offset: u32, } @@ -371,13 +372,14 @@ pub struct DirEntry { kind: FileType, addr: u32, size: u32, + time: u64, name: String, } impl DirEntry { - pub fn new(dir: Dir, kind: FileType, addr: u32, size: u32, name: &str) -> Self { + pub fn new(dir: Dir, kind: FileType, addr: u32, size: u32, time: u64, name: &str) -> Self { let name = String::from(name); - Self { dir, kind, addr, size, name } + Self { dir, kind, addr, size, time, name } } pub fn is_dir(&self) -> bool { @@ -392,6 +394,10 @@ impl DirEntry { self.size } + pub fn time(&self) -> u64 { + self.time + } + pub fn name(&self) -> String { self.name.clone() } @@ -407,13 +413,14 @@ impl DirEntry { name: self.name.clone(), addr: self.addr, size: self.size, + time: self.time, dir: self.dir, offset: 0, } } pub fn len(&self) -> usize { - 1 + 4 + 4 + 1 + self.name.len() + 1 + 4 + 4 + 8 + 1 + self.name.len() } } @@ -509,28 +516,37 @@ impl Dir { let entry_kind = kind; let entry_size = 0; + let entry_time = sys::clock::realtime() as u64; let entry_addr = new_block.addr(); let entry_name = name.as_bytes(); let n = entry_name.len(); let i = read_dir.data_offset; let data = read_dir.block.data_mut(); - data[i + 0] = entry_kind as u8; - data[i + 1] = entry_addr.get_bits(24..32) as u8; - data[i + 2] = entry_addr.get_bits(16..24) as u8; - data[i + 3] = entry_addr.get_bits(8..16) as u8; - data[i + 4] = entry_addr.get_bits(0..8) as u8; - data[i + 5] = entry_size.get_bits(24..32) as u8; - data[i + 6] = entry_size.get_bits(16..24) as u8; - data[i + 7] = entry_size.get_bits(8..16) as u8; - data[i + 8] = entry_size.get_bits(0..8) as u8; - data[i + 9] = n as u8; + data[i + 0] = entry_kind as u8; + data[i + 1] = entry_addr.get_bits(24..32) as u8; + data[i + 2] = entry_addr.get_bits(16..24) as u8; + data[i + 3] = entry_addr.get_bits(8..16) as u8; + data[i + 4] = entry_addr.get_bits(0..8) as u8; + data[i + 5] = entry_size.get_bits(24..32) as u8; + data[i + 6] = entry_size.get_bits(16..24) as u8; + data[i + 7] = entry_size.get_bits(8..16) as u8; + data[i + 8] = entry_size.get_bits(0..8) as u8; + data[i + 9] = entry_time.get_bits(56..64) as u8; + data[i + 10] = entry_time.get_bits(48..56) as u8; + data[i + 11] = entry_time.get_bits(40..48) as u8; + data[i + 12] = entry_time.get_bits(32..40) as u8; + data[i + 13] = entry_time.get_bits(24..32) as u8; + data[i + 14] = entry_time.get_bits(16..24) as u8; + data[i + 15] = entry_time.get_bits(8..16) as u8; + data[i + 16] = entry_time.get_bits(0..8) as u8; + data[i + 17] = n as u8; for j in 0..n { - data[i + 10 + j] = entry_name[j]; + data[i + 18 + j] = entry_name[j]; } read_dir.block.write(); - Some(DirEntry::new(*self, kind, entry_addr, entry_size, name)) + Some(DirEntry::new(*self, kind, entry_addr, entry_size, entry_time, name)) } // Deleting an entry is done by setting the entry address to 0 @@ -624,15 +640,26 @@ impl Iterator for ReadDir { 1 => FileType::File, _ => break, }; - let entry_addr = (data[i + 1] as u32) << 24 - | (data[i + 2] as u32) << 16 - | (data[i + 3] as u32) << 8 - | (data[i + 4] as u32); - let entry_size = (data[i + 5] as u32) << 24 - | (data[i + 6] as u32) << 16 - | (data[i + 7] as u32) << 8 - | (data[i + 8] as u32); - i += 9; + + let entry_addr = (data[i + 1] as u32) << 24 + | (data[i + 2] as u32) << 16 + | (data[i + 3] as u32) << 8 + | (data[i + 4] as u32); + + let entry_size = (data[i + 5] as u32) << 24 + | (data[i + 6] as u32) << 16 + | (data[i + 7] as u32) << 8 + | (data[i + 8] as u32); + + let entry_time = (data[i + 9] as u64) << 56 + | (data[i + 10] as u64) << 48 + | (data[i + 11] as u64) << 40 + | (data[i + 12] as u64) << 32 + | (data[i + 13] as u64) << 24 + | (data[i + 14] as u64) << 16 + | (data[i + 15] as u64) << 8 + | (data[i + 16] as u64); + i += 17; let mut n = data[i]; if n == 0 || n as usize >= data.len() - i { @@ -658,7 +685,7 @@ impl Iterator for ReadDir { continue; } - return Some(DirEntry::new(self.dir, entry_kind, entry_addr, entry_size, &entry_name)); + return Some(DirEntry::new(self.dir, entry_kind, entry_addr, entry_size, entry_time, &entry_name)); } match self.block.next() { diff --git a/src/usr/list.rs b/src/usr/list.rs index 7be3ade2b..a4cf43bff 100644 --- a/src/usr/list.rs +++ b/src/usr/list.rs @@ -1,5 +1,8 @@ use crate::{sys, usr}; +use crate::api::console::Style; +use alloc::string::ToString; use alloc::vec::Vec; +use time::OffsetDateTime; pub fn main(args: &[&str]) -> usr::shell::ExitCode { let current_dir = sys::process::dir(); @@ -20,8 +23,20 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { files.sort_by_key(|f| f.name()); + //let w = files.map(|f| f.size()).to_string().len(); + let mut w = 0; + for file in &files { + w = core::cmp::max(w, file.size()); + } + let w = w.to_string().len(); + + let csi_color = Style::color("Blue"); + let csi_reset = Style::reset(); + for file in files { - println!("{}", file.name()); + let date = OffsetDateTime::from_unix_timestamp(file.time() as i64); + let color = if file.is_dir() { csi_color } else { csi_reset }; + println!("{:w$} {} {}{}{}", file.size(), date.format("%F %H:%M:%S"), color, file.name(), csi_reset, w = w); } usr::shell::ExitCode::CommandSuccessful } else { From 261584ed2dda3edba8625eba9a3dee27b28cd923 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 26 Jul 2021 10:27:58 +0200 Subject: [PATCH 2/7] Update and publish fuse script --- run/moros-fuse.py | 110 ++++++++++++++++++++++++++++++++++++++++++++++ run/moros-fuse.sh | 9 ++++ 2 files changed, 119 insertions(+) create mode 100644 run/moros-fuse.py create mode 100644 run/moros-fuse.sh diff --git a/run/moros-fuse.py b/run/moros-fuse.py new file mode 100644 index 000000000..6ad1c23f0 --- /dev/null +++ b/run/moros-fuse.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python + +from errno import ENOENT +from fuse import FUSE, FuseOSError, Operations, LoggingMixIn +from stat import S_IFDIR, S_IFREG + +class MorosFuse(Operations): + chmod = None + chown = None + create = None + mkdir = None + readlink = None + rename = None + rmdir = None + symlink = None + truncate = None + unlink = None + utimens = None + write = None + + def __init__(self, path): + self.image = open(path, "rb") + self.image_offset = 4096 + self.block_size = 512 + addr = self.image_offset * self.block_size + self.image.seek(addr) + block = self.image.read(self.block_size) + + def destroy(self, path): + self.image.close() + return + + def getattr(self, path, fh=None): + (kind, addr, size, time, name) = self.__scan(path) + if addr == 0: + raise FuseOSError(ENOENT) + mode = S_IFDIR | 0o755 if kind == 0 else S_IFREG | 0o644 + return { "st_atime": 0, "st_mtime": time, "st_uid": 0, "st_gid": 0, "st_mode": mode, "st_size": size } + + def read(self, path, size, offset, fh): + (kind, next_block_addr, size, time, name) = self.__scan(path) + res = b"" + while next_block_addr != 0: + self.image.seek(next_block_addr) + next_block_addr = int.from_bytes(self.image.read(4), "big") * self.block_size + if offset < self.block_size - 4: + buf = self.image.read(min(self.block_size - 4, size)) + res = b"".join([res, buf[offset:]]) + offset = 0 + else: + offset -= self.block_size - 4 + size -= self.block_size - 4 + return res + + def readdir(self, path, fh): + files = [".", ".."] + (_, next_block_addr, _, _, _) = self.__scan(path) + while next_block_addr != 0: + self.image.seek(next_block_addr) + next_block_addr = int.from_bytes(self.image.read(4), "big") + offset = 4 + while offset < self.block_size: + kind = int.from_bytes(self.image.read(1), "big") + addr = int.from_bytes(self.image.read(4), "big") * self.block_size + if addr == 0: + break + size = int.from_bytes(self.image.read(4), "big") + time = int.from_bytes(self.image.read(8), "big") + n = int.from_bytes(self.image.read(1), "big") + name = self.image.read(n).decode("utf-8") + offset += 1 + 4 + 4 + 8 + 1 + n + files.append(name) + return files + + def __scan(self, path): + dirs = path[1:].split("/") + d = dirs.pop(0) + next_block_addr = (self.image_offset + 2 + self.block_size) * self.block_size + if d == "": + return (0, next_block_addr, 0, 0, d) + while next_block_addr != 0: + self.image.seek(next_block_addr) + next_block_addr = int.from_bytes(self.image.read(4), "big") + offset = 4 + while offset < self.block_size: + kind = int.from_bytes(self.image.read(1), "big") + addr = int.from_bytes(self.image.read(4), "big") * self.block_size + if addr == 0: + break + size = int.from_bytes(self.image.read(4), "big") + time = int.from_bytes(self.image.read(8), "big") + n = int.from_bytes(self.image.read(1), "big") + name = self.image.read(n).decode("utf-8") + offset += 1 + 4 + 4 + 1 + n + if name == d: + if len(dirs) == 0: + return (kind, addr, size, time, name) + else: + next_block_addr = addr + d = dirs.pop(0) + break + return (0, 0, 0, 0, "") + +if __name__ == '__main__': + import argparse + parser = argparse.ArgumentParser() + parser.add_argument('image') + parser.add_argument('mount') + args = parser.parse_args() + fuse = FUSE(MorosFuse(args.image), args.mount, ro=True, foreground=True, allow_other=True) diff --git a/run/moros-fuse.sh b/run/moros-fuse.sh new file mode 100644 index 000000000..fbb7e4eb1 --- /dev/null +++ b/run/moros-fuse.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +img="disk.img" +path="/tmp/moros" + +# pip install fusepy +mkdir -p $path +echo "Mounting $img in $path" +python run/moros-fuse.py $img $path From bd95287c84e556e45c868800a32f3a8fc47b8ea6 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 26 Jul 2021 10:51:20 +0200 Subject: [PATCH 3/7] Change boot offset in doc --- doc/filesystem.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/filesystem.md b/doc/filesystem.md index b9cd6a2d3..86ff7f5da 100644 --- a/doc/filesystem.md +++ b/doc/filesystem.md @@ -5,7 +5,7 @@ A hard drive is separated in blocks of 512 bytes, grouped into 4 areas: +------------+ - | Boot | (2048 blocks) + | Boot | (4096 blocks) +------------+ | Superblock | (2 blocks) +------------+ From 07eec5aadb865168a164e28e9f285002e13880e5 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 26 Jul 2021 20:37:46 +0200 Subject: [PATCH 4/7] Change creation time into last modified time --- doc/filesystem.md | 4 ++-- src/sys/fs.rs | 21 +++++++++++++++------ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/doc/filesystem.md b/doc/filesystem.md index 86ff7f5da..6914b3117 100644 --- a/doc/filesystem.md +++ b/doc/filesystem.md @@ -103,7 +103,7 @@ Structure: A directory entry represents a file or a directory contained inside a directory. Each entry use a variable number of bytes that must fit inside the data of one block. Those bytes represent the kind of entry (file or dir), the -address of the first block, the filesize (max 4GB), the creation time in +address of the first block, the filesize (max 4GB), the last modified time in seconds since Unix Epoch, the length of the filename, and the filename (max 255 chars) of the entry. @@ -112,7 +112,7 @@ Structure: 0 1 2 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 m +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+ - |k| addr | size | creation time |n| name buffer | + |k| addr | size | time |n| name buffer | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+ k = kind of entry diff --git a/src/sys/fs.rs b/src/sys/fs.rs index 23d81e48f..56192f65a 100644 --- a/src/sys/fs.rs +++ b/src/sys/fs.rs @@ -193,7 +193,7 @@ impl File { block.write(); } self.size = self.offset; - self.dir.update_entry_size(&self.name, self.size); + self.dir.update_entry(&self.name, self.size); Ok(bytes) } @@ -580,16 +580,25 @@ impl Dir { Err(()) } - fn update_entry_size(&mut self, name: &str, size: u32) { + fn update_entry(&mut self, name: &str, size: u32) { let mut read_dir = self.read(); for entry in &mut read_dir { if entry.name == name { + let time = sys::clock::realtime() as u64; let data = read_dir.block.data_mut(); let i = read_dir.data_offset - entry.len(); - data[i + 5] = size.get_bits(24..32) as u8; - data[i + 6] = size.get_bits(16..24) as u8; - data[i + 7] = size.get_bits(8..16) as u8; - data[i + 8] = size.get_bits(0..8) as u8; + data[i + 5] = size.get_bits(24..32) as u8; + data[i + 6] = size.get_bits(16..24) as u8; + data[i + 7] = size.get_bits(8..16) as u8; + data[i + 8] = size.get_bits(0..8) as u8; + data[i + 9] = time.get_bits(56..64) as u8; + data[i + 10] = time.get_bits(48..56) as u8; + data[i + 11] = time.get_bits(40..48) as u8; + data[i + 12] = time.get_bits(32..40) as u8; + data[i + 13] = time.get_bits(24..32) as u8; + data[i + 14] = time.get_bits(16..24) as u8; + data[i + 15] = time.get_bits(8..16) as u8; + data[i + 16] = time.get_bits(0..8) as u8; read_dir.block.write(); break; } From c6e922bc227aac31f0cad22a8fcca6de6a53dc9b Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 26 Jul 2021 21:35:44 +0200 Subject: [PATCH 5/7] Add sort parameter to list command --- src/usr/list.rs | 47 +++++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/src/usr/list.rs b/src/usr/list.rs index a4cf43bff..329ba93f6 100644 --- a/src/usr/list.rs +++ b/src/usr/list.rs @@ -5,30 +5,45 @@ use alloc::vec::Vec; use time::OffsetDateTime; pub fn main(args: &[&str]) -> usr::shell::ExitCode { - let current_dir = sys::process::dir(); - let mut pathname = if args.len() == 2 && !args[1].is_empty() { - args[1] - } else { - ¤t_dir - }; + let mut path: &str = &sys::process::dir(); + let mut sort = "name"; + + let mut i = 1; + let n = args.len(); + while i < n { + if args[i] == "--sort" && i + 1 < n { + sort = args[i + 1]; + i += 2; + } else { + path = args[i]; + i += 1; + } + } // The commands `list /usr/alice/` and `list /usr/alice` are equivalent, // but `list /` should not be modified. - if pathname.len() > 1 { - pathname = pathname.trim_end_matches('/'); + if path.len() > 1 { + path = path.trim_end_matches('/'); } - if let Some(dir) = sys::fs::Dir::open(pathname) { + if let Some(dir) = sys::fs::Dir::open(path) { let mut files: Vec<_> = dir.read().collect(); - files.sort_by_key(|f| f.name()); + match sort { + "name" => files.sort_by_key(|f| f.name()), + "size" => files.sort_by_key(|f| f.size()), + "time" => files.sort_by_key(|f| f.time()), + _ => { + println!("Invalid sort key '{}'", sort); + return usr::shell::ExitCode::CommandError; + } + } - //let w = files.map(|f| f.size()).to_string().len(); - let mut w = 0; + let mut max_size = 0; for file in &files { - w = core::cmp::max(w, file.size()); + max_size = core::cmp::max(max_size, file.size()); } - let w = w.to_string().len(); + let width = max_size.to_string().len(); let csi_color = Style::color("Blue"); let csi_reset = Style::reset(); @@ -36,11 +51,11 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { for file in files { let date = OffsetDateTime::from_unix_timestamp(file.time() as i64); let color = if file.is_dir() { csi_color } else { csi_reset }; - println!("{:w$} {} {}{}{}", file.size(), date.format("%F %H:%M:%S"), color, file.name(), csi_reset, w = w); + println!("{:width$} {} {}{}{}", file.size(), date.format("%F %H:%M:%S"), color, file.name(), csi_reset, width = width); } usr::shell::ExitCode::CommandSuccessful } else { - println!("Dir not found '{}'", pathname); + println!("Dir not found '{}'", path); usr::shell::ExitCode::CommandError } } From bb437744e9d7c6f0c134d81a7192a29eb90d744d Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 26 Jul 2021 21:39:55 +0200 Subject: [PATCH 6/7] Add error for missing sort key --- src/usr/list.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/usr/list.rs b/src/usr/list.rs index 329ba93f6..297303db6 100644 --- a/src/usr/list.rs +++ b/src/usr/list.rs @@ -11,9 +11,14 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { let mut i = 1; let n = args.len(); while i < n { - if args[i] == "--sort" && i + 1 < n { - sort = args[i + 1]; - i += 2; + if args[i] == "--sort" { + if i + 1 < n { + sort = args[i + 1]; + i += 2; + } else { + println!("Missing sort key"); + return usr::shell::ExitCode::CommandError; + } } else { path = args[i]; i += 1; From d485b25eac7d1f7e43058923273e94b7be2029f5 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 26 Jul 2021 21:45:37 +0200 Subject: [PATCH 7/7] Add shortcut params for sort --- src/usr/list.rs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/usr/list.rs b/src/usr/list.rs index 297303db6..38eadcc55 100644 --- a/src/usr/list.rs +++ b/src/usr/list.rs @@ -11,18 +11,22 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { let mut i = 1; let n = args.len(); while i < n { - if args[i] == "--sort" { - if i + 1 < n { - sort = args[i + 1]; - i += 2; - } else { - println!("Missing sort key"); - return usr::shell::ExitCode::CommandError; - } - } else { - path = args[i]; - i += 1; + match args[i] { + "--sort" => { + if i + 1 < n { + sort = args[i + 1]; + i += 1; + } else { + println!("Missing sort key"); + return usr::shell::ExitCode::CommandError; + } + }, + "-t" => sort = "time", + "-s" => sort = "size", + "-n" => sort = "name", + _ => path = args[i], } + i += 1; } // The commands `list /usr/alice/` and `list /usr/alice` are equivalent,