mirror of https://github.com/vinc/moros.git
Add time to dir entry (#215)
* Add creation time to dir entry * Update and publish fuse script * Change boot offset in doc * Change creation time into last modified time * Add sort parameter to list command * Add error for missing sort key * Add shortcut params for sort
This commit is contained in:
parent
1575b40f9c
commit
bbcfc66e18
|
@ -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)
|
||||
+------------+
|
||||
|
@ -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 last modified 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 | time |n| name buffer |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+
|
||||
|
||||
k = kind of entry
|
||||
n = length of name buffer
|
||||
|
|
|
@ -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)
|
|
@ -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
|
|
@ -1,6 +1,7 @@
|
|||
use crate::sys;
|
||||
use core::fmt;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Style {
|
||||
foreground: Option<usize>,
|
||||
background: Option<usize>,
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
@ -192,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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -564,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;
|
||||
}
|
||||
|
@ -624,15 +649,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 +694,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() {
|
||||
|
|
|
@ -1,31 +1,70 @@
|
|||
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();
|
||||
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 {
|
||||
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,
|
||||
// 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 mut max_size = 0;
|
||||
for file in &files {
|
||||
max_size = core::cmp::max(max_size, file.size());
|
||||
}
|
||||
let width = max_size.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!("{: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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue