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 { return Err(());
91 }
92 self.offset = offset as u32;
93
94 Ok(self.offset)
95 }
96 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; let mut pos = 0; 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; let mut pos = 0; while bytes < buf_len {
157 let mut block = LinkedBlock::read(addr);
158 let data = block.data_mut();
159 let data_len = data.len();
160 for i in 0..data_len {
161 if pos == self.offset {
162 if bytes == buf_len {
163 break;
164 }
165 data[i] = buf[bytes];
166 bytes += 1;
167 self.offset += 1;
168 }
169 pos += 1;
170 }
171
172 addr = match block.next() {
173 Some(next_block) => {
174 if bytes < buf_len {
175 next_block.addr()
176 } else {
177 let mut free_block = next_block;
179 loop {
180 BitmapBlock::free(free_block.addr());
181 match free_block.next() { Some(next_block) => free_block = next_block,
183 None => break,
184 }
185 }
186 0
187 }
188 }
189 None => {
190 if bytes < buf_len {
191 match LinkedBlock::alloc() {
192 Some(next_block) => next_block.addr(),
193 None => return Err(()),
194 }
195 } else {
196 0
197 }
198 }
199 };
200
201 block.set_next_addr(addr);
202 block.write();
203 }
204 self.size = self.offset;
205 if let Some(dir) = self.parent.clone() {
206 dir.update_entry(&self.name, self.size);
207 }
208 Ok(bytes)
209 }
210
211 fn close(&mut self) {}
212
213 fn poll(&mut self, event: IO) -> bool {
214 match event {
215 IO::Read => self.offset < self.size,
216 IO::Write => true,
217 }
218 }
219}
220
221#[test_case]
222fn test_file_create() {
223 super::mount_mem();
224 super::format_mem();
225 assert!(File::create("/test").is_some());
226 assert_eq!(File::create("/hello").unwrap().name(), "hello");
227 super::dismount();
228}
229
230#[test_case]
231fn test_file_write() {
232 super::mount_mem();
233 super::format_mem();
234 let mut file = File::create("/test").unwrap();
235 let buf = "Hello, World!".as_bytes();
236 assert_eq!(file.write(&buf), Ok(buf.len()));
237 super::dismount();
238}
239
240#[test_case]
241fn test_file_open() {
242 super::mount_mem();
243 super::format_mem();
244 assert!(File::open("/test").is_none());
245 let mut file = File::create("/test").unwrap();
246 let buf = "Hello, World!".as_bytes();
247 file.write(&buf).unwrap();
248 assert!(File::open("/test").is_some());
249 super::dismount();
250}
251
252#[test_case]
253fn test_file_read() {
254 super::mount_mem();
255 super::format_mem();
256 let mut file = File::create("/test").unwrap();
257 let input = "Hello, World!".as_bytes();
258 file.write(&input).unwrap();
259
260 let mut file = File::open("/test").unwrap();
261 let mut output = [0u8; 13];
262 assert_eq!(file.read(&mut output), Ok(input.len()));
263 assert_eq!(input, output);
264 super::dismount();
265}
266
267#[test_case]
268fn test_file_delete() {
269 super::mount_mem();
270 super::format_mem();
271 assert!(File::open("/test").is_none());
272 assert!(File::create("/test").is_some());
273 assert!(File::open("/test").is_some());
274 assert!(File::delete("/test").is_ok());
275 assert!(File::open("/test").is_none());
276 super::dismount();
277}