stable_fs/runtime/
file.rs

1use crate::{
2    error::Error,
3    runtime::types::{FdFlags, FdStat, Whence},
4    storage::{
5        Storage,
6        types::{FileSize, FileType, Node},
7    },
8};
9
10#[derive(Clone, Debug)]
11pub struct File {
12    pub node: Node,
13    pub cursor: FileSize,
14    pub stat: FdStat,
15}
16
17impl File {
18    // Create new file entry.
19    pub fn new(node: Node, stat: FdStat, storage: &dyn Storage) -> Result<Self, Error> {
20        let metadata = storage.get_metadata(node)?;
21        let file_type = metadata.file_type;
22        match file_type {
23            FileType::RegularFile => {}
24            FileType::Directory => {
25                unreachable!("Unexpected file type, expected a regular file.");
26            }
27            FileType::SymbolicLink => unimplemented!("Symbolic links are not implemented yet"),
28        };
29        let cursor = if stat.flags.contains(FdFlags::APPEND) {
30            metadata.size
31        } else {
32            0
33        };
34        Ok(Self { node, cursor, stat })
35    }
36
37    // Seek a position in a file for reading or writing.
38    pub fn seek(
39        &mut self,
40        delta: i64,
41        whence: Whence,
42        storage: &dyn Storage,
43    ) -> Result<FileSize, Error> {
44        let size = storage.get_metadata(self.node)?.size;
45        let position = match whence {
46            Whence::SET => {
47                if delta < 0 {
48                    return Err(Error::InvalidArgument);
49                }
50                delta as FileSize
51            }
52            Whence::CUR => {
53                let back = if delta < 0 {
54                    (-delta).try_into().map_err(|_| Error::InvalidArgument)?
55                } else {
56                    0
57                };
58                let fwd = if delta >= 0 { delta as FileSize } else { 0 };
59
60                if self.cursor < back {
61                    // seeking before zero position
62                    return Err(Error::InvalidArgument);
63                }
64                self.cursor + fwd - back
65            }
66            Whence::END => {
67                let back: FileSize = (-delta).try_into().map_err(|_| Error::InvalidSeek)?;
68                if back > size {
69                    return Err(Error::InvalidSeek);
70                }
71                size - back
72            }
73        };
74        self.cursor = position;
75        Ok(self.cursor)
76    }
77
78    // Get the file's current cursor position.
79    pub fn tell(&self) -> FileSize {
80        self.cursor
81    }
82
83    // Read file at the cursor position, the cursor position will be updated after reading.
84    pub fn read_with_cursor(
85        &mut self,
86        buf: &mut [u8],
87        storage: &mut dyn Storage,
88    ) -> Result<FileSize, Error> {
89        let read_size = self.read_with_offset(self.cursor, buf, storage)?;
90        self.cursor += read_size;
91        Ok(read_size)
92    }
93
94    // Read file from a given offset, the cursor position will NOT be updated after reading.
95    pub fn read_with_offset(
96        &self,
97        offset: FileSize,
98        buf: &mut [u8],
99        storage: &mut dyn Storage,
100    ) -> Result<FileSize, Error> {
101        if buf.is_empty() {
102            return Ok(0 as FileSize);
103        }
104
105        let read_size = storage.read(self.node, offset, buf)?;
106
107        Ok(read_size as FileSize)
108    }
109
110    // Write file to a given offset, the cursor position will NOT be updated after reading.
111    pub fn write_with_offset(
112        &self,
113        offset: FileSize,
114        buf: &[u8],
115        storage: &mut dyn Storage,
116    ) -> Result<FileSize, Error> {
117        storage.write(self.node, offset, buf)
118    }
119
120    // Truncate file to 0 size.
121    pub fn truncate(&self, storage: &mut dyn Storage) -> Result<(), Error> {
122        let mut metadata = storage.get_metadata(self.node)?;
123        metadata.size = 0;
124        storage.put_metadata(self.node, &metadata)?;
125        Ok(())
126    }
127}
128
129#[cfg(test)]
130mod tests {
131    use crate::{
132        fs::OpenFlags,
133        test_utils::{test_fs_setups, test_stable_fs_v2},
134    };
135
136    use super::*;
137
138    #[test]
139    fn seek_and_tell() {
140        let mut fs = test_stable_fs_v2();
141        let fd = fs
142            .create_open_file(fs.root_fd(), "test", FdStat::default(), 0)
143            .unwrap();
144
145        let mut file = fs.get_test_file(fd);
146        let storage = fs.get_test_storage();
147
148        file.write_with_offset(0, &[0; 1000], storage).unwrap();
149
150        assert_eq!(file.tell(), 0);
151        let pos = file.seek(10, Whence::CUR, storage).unwrap();
152        assert_eq!(pos, 10);
153        assert_eq!(file.tell(), 10);
154
155        let pos = file.seek(-9, Whence::CUR, storage).unwrap();
156        assert_eq!(pos, 1);
157        assert_eq!(file.tell(), 1);
158
159        let err = file.seek(-2, Whence::CUR, storage).unwrap_err();
160        assert_eq!(err, Error::InvalidArgument);
161        assert_eq!(file.tell(), 1);
162
163        let pos = file.seek(0, Whence::END, storage).unwrap();
164        assert_eq!(pos, 1000);
165        assert_eq!(file.tell(), 1000);
166
167        let pos = file.seek(500, Whence::SET, storage).unwrap();
168        assert_eq!(pos, 500);
169        assert_eq!(file.tell(), 500);
170
171        let err = file.seek(-1, Whence::SET, storage).unwrap_err();
172        assert_eq!(err, Error::InvalidArgument);
173        assert_eq!(file.tell(), 500);
174
175        let pos = file.seek(1001, Whence::SET, storage).unwrap();
176        assert_eq!(pos, 1001);
177        assert_eq!(file.tell(), 1001);
178    }
179
180    #[test]
181    fn read_and_write_offset() {
182        let mut fs = test_stable_fs_v2();
183        let fd = fs
184            .create_open_file(fs.root_fd(), "test", FdStat::default(), 0)
185            .unwrap();
186
187        let mut file = fs.get_test_file(fd);
188        let storage = fs.get_test_storage();
189
190        for i in 0..1000 {
191            let buf = [(i % 256) as u8; 16];
192            file.write_with_offset(i * 16, &buf, storage).unwrap();
193        }
194
195        file.seek(-1000 * 16, Whence::END, storage).unwrap();
196        for i in 0..1000 {
197            let mut buf = [0; 16];
198            file.read_with_offset(i * 16, &mut buf, storage).unwrap();
199            let expected = [(i % 256) as u8; 16];
200            assert_eq!(buf, expected);
201        }
202    }
203
204    #[test]
205    fn read_and_write_small_and_big_buffer() {
206        let mut fs = test_stable_fs_v2();
207        let fd = fs
208            .create_open_file(fs.root_fd(), "test", FdStat::default(), 0)
209            .unwrap();
210
211        let file = fs.get_test_file(fd);
212        let storage = fs.get_test_storage();
213
214        for i in 0..1000 {
215            let buf = [(i % 256) as u8; 10];
216            file.write_with_offset(i * 16, &buf, storage).unwrap();
217        }
218
219        for i in 0..1000 {
220            let mut buf1 = [0; 13];
221            let mut buf2 = [0; 5000];
222            let mut buf3 = [0; 15000];
223
224            let r1 = file.read_with_offset(i * 17, &mut buf1, storage).unwrap() as usize;
225            let r2 = file.read_with_offset(i * 17, &mut buf2, storage).unwrap() as usize;
226            let _r3 = file.read_with_offset(i * 17, &mut buf3, storage).unwrap() as usize;
227
228            assert_eq!(buf1[..r1], buf2[..r1]);
229            assert_eq!(buf2[..r2], buf3[..r2]);
230        }
231    }
232
233    #[test]
234    fn read_and_write_offset_chunk() {
235        for mut fs in [test_stable_fs_v2()] {
236            //test_fs_setups("test") {
237            let fd = fs
238                .open(
239                    fs.root_fd(),
240                    "test",
241                    FdStat::default(),
242                    OpenFlags::CREATE,
243                    0,
244                )
245                .unwrap();
246
247            let mut file = fs.get_test_file(fd);
248            let storage = fs.get_test_storage();
249
250            for i in 0..1000 {
251                let buf = [(i % 256) as u8; 16];
252                file.write_with_offset(i * 16, &buf, storage).unwrap();
253            }
254
255            file.seek(-1000 * 16, Whence::END, storage).unwrap();
256
257            for i in 0..1000 {
258                let mut buf = [0; 16];
259                file.read_with_offset(i * 16, &mut buf, storage).unwrap();
260
261                let expected = [(i % 256) as u8; 16];
262                assert_eq!(buf, expected);
263            }
264        }
265    }
266
267    #[test]
268    fn read_and_write_offset_vs_range() {
269        for mut fs in test_fs_setups("test") {
270            let fd = fs
271                .open(
272                    fs.root_fd(),
273                    "test",
274                    FdStat::default(),
275                    OpenFlags::CREATE,
276                    0,
277                )
278                .unwrap();
279
280            let file = fs.get_test_file(fd);
281            let storage = fs.get_test_storage();
282
283            for i in 0..1000 {
284                let buf = [(i % 256) as u8; 16];
285                file.write_with_offset(i * 16, &buf, storage).unwrap();
286            }
287
288            for i in 0..1000 {
289                let mut buf1 = [0; 13];
290                let len1 = file.read_with_offset(i * 16, &mut buf1, storage).unwrap();
291
292                let mut buf2 = [0; 13];
293                let len2 = file.read_with_offset(i * 16, &mut buf2, storage).unwrap();
294
295                assert_eq!(buf1, buf2);
296                assert_eq!(len1, len2);
297            }
298
299            for i in 0..2050 {
300                let mut buf1 = [0; 5003];
301                let len1 = file.read_with_offset(i * 13, &mut buf1, storage).unwrap();
302
303                let mut buf2 = [0; 5003];
304                let len2 = file.read_with_offset(i * 13, &mut buf2, storage).unwrap();
305
306                assert_eq!(buf1, buf2);
307                assert_eq!(len1, len2);
308            }
309        }
310    }
311}