moros/sys/fs/
file.rs

1use super::bitmap_block::BitmapBlock;
2use super::block::LinkedBlock;
3use super::dir::Dir;
4use super::dir_entry::DirEntry;
5use super::{dirname, filename, realpath, FileIO, IO};
6
7use alloc::boxed::Box;
8use alloc::string::{String, ToString};
9use alloc::vec;
10
11pub enum SeekFrom {
12    Start(u32),
13    Current(i32),
14    End(i32),
15}
16
17#[derive(Debug, Clone)]
18pub struct File {
19    parent: Option<Box<Dir>>,
20    name: String,
21    addr: u32,
22    size: u32,
23    offset: u32,
24}
25
26impl From<DirEntry> for File {
27    fn from(entry: DirEntry) -> Self {
28        Self {
29            parent: Some(Box::new(entry.dir())),
30            name: entry.name(),
31            addr: entry.addr(),
32            size: entry.size(),
33            offset: 0,
34        }
35    }
36}
37
38impl File {
39    pub fn new() -> Self {
40        Self {
41            parent: None,
42            name: String::new(),
43            addr: 0,
44            size: 0,
45            offset: 0,
46        }
47    }
48
49    pub fn create(pathname: &str) -> Option<Self> {
50        let pathname = realpath(pathname);
51        let dirname = dirname(&pathname);
52        let filename = filename(&pathname);
53        if let Some(mut dir) = Dir::open(dirname) {
54            if let Some(dir_entry) = dir.create_file(filename) {
55                return Some(dir_entry.into());
56            }
57        }
58        None
59    }
60
61    pub fn open(pathname: &str) -> Option<Self> {
62        let pathname = realpath(pathname);
63        let dirname = dirname(&pathname);
64        let filename = filename(&pathname);
65        if let Some(dir) = Dir::open(dirname) {
66            if let Some(dir_entry) = dir.find(filename) {
67                if dir_entry.is_file() {
68                    return Some(dir_entry.into());
69                }
70            }
71        }
72        None
73    }
74
75    pub fn name(&self) -> String {
76        self.name.clone()
77    }
78
79    pub fn size(&self) -> usize {
80        self.size as usize
81    }
82
83    pub fn seek(&mut self, pos: SeekFrom) -> Result<u32, ()> {
84        let offset = match pos {
85            SeekFrom::Start(i)   => i as i32,
86            SeekFrom::Current(i) => i + self.offset as i32,
87            SeekFrom::End(i)     => i + self.size as i32,
88        };
89        if offset < 0 || offset > self.size as i32 { // TODO: offset > size?
90            return Err(());
91        }
92        self.offset = offset as u32;
93
94        Ok(self.offset)
95    }
96    // TODO: Add `read_to_end(&self, buf: &mut Vec<u8>) -> Result<u32>`
97
98    // TODO: `return Result<String>`
99    pub fn read_to_string(&mut self) -> String {
100        let mut buf = vec![0; self.size()];
101        if let Ok(bytes) = self.read(&mut buf) {
102            buf.resize(bytes, 0);
103        }
104        String::from_utf8_lossy(&buf).to_string()
105    }
106
107    pub fn addr(&self) -> u32 {
108        self.addr
109    }
110
111    pub fn delete(pathname: &str) -> Result<(), ()> {
112        let pathname = realpath(pathname);
113        let dirname = dirname(&pathname);
114        let filename = filename(&pathname);
115        if let Some(mut dir) = Dir::open(dirname) {
116            dir.delete_entry(filename)
117        } else {
118            Err(())
119        }
120    }
121}
122
123impl FileIO for File {
124    fn read(&mut self, buf: &mut [u8]) -> Result<usize, ()> {
125        let buf_len = buf.len();
126        let mut addr = self.addr;
127        let mut bytes = 0; // Number of bytes read
128        let mut pos = 0; // Position in the file
129        loop {
130            let block = LinkedBlock::read(addr);
131            let data = block.data();
132            let data_len = data.len();
133            for i in 0..data_len {
134                if pos == self.offset {
135                    if bytes == buf_len || pos as usize == self.size() {
136                        return Ok(bytes);
137                    }
138                    buf[bytes] = data[i];
139                    bytes += 1;
140                    self.offset += 1;
141                }
142                pos += 1;
143            }
144            match block.next() {
145                Some(next_block) => addr = next_block.addr(),
146                None => return Ok(bytes),
147            }
148        }
149    }
150
151    fn write(&mut self, buf: &[u8]) -> Result<usize, ()> {
152        let buf_len = buf.len();
153        let mut addr = self.addr;
154        let mut bytes = 0; // Number of bytes written
155        let mut pos = 0; // Position in the file
156
157        // Optimization: when appending, skip to the last block
158        if self.offset == self.size && self.size > 0 {
159            let mut block = LinkedBlock::read(addr);
160            while let Some(next_block) = block.next() {
161                addr = next_block.addr();
162                block = LinkedBlock::read(addr);
163            }
164            // If last block is full, allocate a new one
165            let block_data_len = block.len() as u32;
166            if self.size % block_data_len == 0 {
167                match LinkedBlock::alloc() {
168                    Some(new_block) => {
169                        let mut last_block = LinkedBlock::read(addr);
170                        last_block.set_next_addr(new_block.addr());
171                        last_block.write();
172                        addr = new_block.addr();
173                        pos = self.size;
174                    }
175                    None => return Err(()),
176                }
177            } else {
178                pos = self.size - (self.size % block_data_len);
179            }
180        }
181
182        while bytes < buf_len {
183            let mut block = LinkedBlock::read(addr);
184            let data = block.data_mut();
185            let data_len = data.len();
186            for i in 0..data_len {
187                if pos == self.offset {
188                    if bytes == buf_len {
189                        break;
190                    }
191                    data[i] = buf[bytes];
192                    bytes += 1;
193                    self.offset += 1;
194                }
195                pos += 1;
196            }
197
198            addr = match block.next() {
199                Some(next_block) => {
200                    if bytes < buf_len {
201                        next_block.addr()
202                    } else {
203                        // Free next block(s)
204                        let mut free_block = next_block;
205                        loop {
206                            BitmapBlock::free(free_block.addr());
207                            match free_block.next() { // FIXME: read after free?
208                                Some(next_block) => free_block = next_block,
209                                None => break,
210                            }
211                        }
212                        0
213                    }
214                }
215                None => {
216                    if bytes < buf_len {
217                        match LinkedBlock::alloc() {
218                            Some(next_block) => next_block.addr(),
219                            None => return Err(()),
220                        }
221                    } else {
222                        0
223                    }
224                }
225            };
226
227            block.set_next_addr(addr);
228            block.write();
229        }
230        self.size = self.offset;
231        if let Some(dir) = self.parent.clone() {
232            dir.update_entry(&self.name, self.size);
233        }
234        Ok(bytes)
235    }
236
237    fn close(&mut self) {}
238
239    fn poll(&mut self, event: IO) -> bool {
240        match event {
241            IO::Read => self.offset < self.size,
242            IO::Write => true,
243        }
244    }
245}
246
247#[test_case]
248fn test_file_create() {
249    super::mount_mem();
250    super::format_mem();
251    assert!(File::create("/test").is_some());
252    assert_eq!(File::create("/hello").unwrap().name(), "hello");
253    super::dismount();
254}
255
256#[test_case]
257fn test_file_write() {
258    super::mount_mem();
259    super::format_mem();
260    let mut file = File::create("/test").unwrap();
261    let buf = "Hello, World!".as_bytes();
262    assert_eq!(file.write(&buf), Ok(buf.len()));
263    super::dismount();
264}
265
266#[test_case]
267fn test_file_open() {
268    super::mount_mem();
269    super::format_mem();
270    assert!(File::open("/test").is_none());
271    let mut file = File::create("/test").unwrap();
272    let buf = "Hello, World!".as_bytes();
273    file.write(&buf).unwrap();
274    assert!(File::open("/test").is_some());
275    super::dismount();
276}
277
278#[test_case]
279fn test_file_read() {
280    super::mount_mem();
281    super::format_mem();
282    let mut file = File::create("/test").unwrap();
283    let input = "Hello, World!".as_bytes();
284    file.write(&input).unwrap();
285
286    let mut file = File::open("/test").unwrap();
287    let mut output = [0u8; 13];
288    assert_eq!(file.read(&mut output), Ok(input.len()));
289    assert_eq!(input, output);
290    super::dismount();
291}
292
293#[test_case]
294fn test_file_delete() {
295    super::mount_mem();
296    super::format_mem();
297    assert!(File::open("/test").is_none());
298    assert!(File::create("/test").is_some());
299    assert!(File::open("/test").is_some());
300    assert!(File::delete("/test").is_ok());
301    assert!(File::open("/test").is_none());
302    super::dismount();
303}