1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
//! # Extended fs
//!
//! An OS and architecture independent implementation of some Unix filesystems in Rust.
//!
//! <div class="warning">
//!
//! This crate is provided as is and do not offer any guaranty. It is still in early development so bugs are excepted to
//! occur. If you find one, please report it at <https://codeberg.org/RatCornu/efs/issues>. In all cases, please do **NOT** use this library for important data, and make sure to backup your data before using it.
//!
//! </div>
//!
//! ## Details
//!
//! This crate provides a general interface to deal with some UNIX filesytems, and adds supports for some of them.
//!
//! Currently, the [Second Extended Filesystem](https://en.wikipedia.org/wiki/Ext2) (ext2) and the [Simple File System](https://web.archive.org/web/20170315134201/https://www.d-rift.nl/combuster/vdisk/sfs.html) (sfs) is supported, but you can implement your own filesystem with this interface.
//!
//! This crate does **NOT** provide a virtual filesystem: you can either make one or use another crate on top on this
//! one.
//!
//! **Every** structure, trait and function in this crate is documented and contains source if needed. If you find something unclear, do not hesitate to create an issue at <https://codeberg.org/RatCornu/efs/issues>.
//!
//! This library sticks as much as possible with the POSIX specification, fully available online on <https://pubs.opengroup.org/onlinepubs/9799919799/>.
//!
//! ### File interfaces
//!
//! * As defined in POSIX, a file can either be a [`Regular`](crate::fs::file::Regular), a
//! [`Directory`](crate::fs::file::Directory), a [`SymbolicLink`](crate::fs::file::SymbolicLink), a
//! [`Fifo`](crate::fs::file::Fifo), a [`CharacterDevice`](crate::fs::file::CharacterDevice), a
//! [`BlockDevice`](crate::fs::file::BlockDevice) or a [`Socket`](crate::fs::file::Socket). Traits are available for
//! each one of them, with basic `read` and `write` operations. The `read` and `write` operations are in separated
//! traits ([`FileRead`](crate::fs::file::FileRead) and [`File`](crate::fs::file::File) for example) to be able to
//! define read-only filesystems.
//!
//! * [`File`](crate::fs::file::File) is the base trait of all other file traits. It provides an interface to retrieve
//! and modify general attributes of a POSIX file (basically everything returned by the `stat` command on a UNIX OS).
//!
//! * A [`Regular`](crate::fs::file::Regular) (file) is a basic file containing a sequence of bytes, which can be read
//! into a string (or not, depending on its content).
//!
//! * A [`Directory`](crate::fs::file::Directory) is a node in the tree-like hierarchy of a filesystem. You can
//! retrieve, add and remove entries (which are other files).
//!
//! * A [`SymbolicLink`](crate::fs::file::SymbolicLink) is a file pointing an other file. It can be interpreted as the
//! symbolic link or the pointed file in the [`Filesystem`](crate::fs::Filesystem) trait.
//!
//! * Other file types are defined but cannot be much manipulated as their implementation depends on the virtual file
//! system and on the OS.
//!
//! ### Filesystem interface
//!
//! All the manipulations needed in a filesystem can be made through the file traits. The
//! [`Filesystem`](crate::fs::Filesystem) is here to provide two things : an entry point to the filesystem with the
//! [`root`](crate::fs::FilesystemRead::root) method, and high-level functions to make the file manipulations easier.
//!
//! You can read the documentation in the [`fs`] module for more information on [`Filesystem`](crate::fs::Filesystem)s
//! and on how to implement them.
//!
//! ### Paths
//!
//! As the Rust's native [`Path`](std::path::Path) implementation is in [`std::path`], this crates provides an other
//! [`Path`](crate::path::Path) interface. It is based on [`UnixStr`](crate::path::UnixStr), which are the equivalent of
//! [`OsStr`](std::ffi::OsStr) with a guarantee that: it is never empty nor contains the `<NUL>` character ('\0').
//!
//! ### Devices
//!
//! In this crate, a [`Device`](crate::dev::Device) is a sized structure that can be read, written directly at any
//! point.
//!
//! You can read the documentation in the [`dev`] module for more information on [`Device`](dev::Device)s and on how to
//! implement them.
//!
//! ## Usage
//!
//! ### High-level usage
//!
//! You always need to provide two things to use this crate: a filesystem and a device.
//!
//! For the filesystem, you can use the filesystems provided by this crate or make one by yourself (see the [how to
//! implement a filesystem section](#how-to-implement-a-filesystem)). The usage of a filesystem does not depend on
//! whether you are in a `no_std` environment or not.
//!
//! For the devices, all the objects implementing [`Read`](deku::no_std_io::Read), [`Write`](deku::no_std_io::Write) and
//! [`Seek`](deku::no_std_io::Seek) automatically derive the [`Device`](crate::dev::Device) trait. Then, all the common
//! structures (such as [`File`](std::fs::File), ...) can be directly used. See the part on [how to implement a
//! device](#how-to-implement-a-device) if needed.
//!
//! ```
//! use std::fs::File;
//!
//! use efs::dev::Device;
//!
//! # std::fs::copy(
//! # "./tests/fs/ext2/io_operations.ext2",
//! # "./tests/fs/ext2/example.ext2",
//! # )
//! # .unwrap();
//!
//! let file = File::options().read(true).write(true).open("./tests/fs/ext2/example.ext2").unwrap();
//!
//! // `file` is a `Device`
//!
//! # std::fs::remove_file("./tests/fs/ext2/example.ext2").unwrap();
//! ```
//!
//! ### Concurrency
//!
//! This library do not offer any guaranty for the behaviour of file manipulations when an other program is making
//! `write` operations on the same device at the same time in a general context. If you really need to, each filesystem
//! implementation documentation contains a paragraph describing exactly what structures are cached: updating by hand
//! those structures allow you to handle concurrency correctly.
//!
//! In concrete terms, in particular for OS developers, it's your duty, and more precisely the duty of the [kernel](https://en.wikipedia.org/wiki/Kernel_(operating_system))
//! to handle the case where two programs tries to modify the same data at the same time.
//!
//! ### Example
//!
//! Here is a complete example of what can be do with the interfaces provided.
//!
//! You can find this test file on [efs's codeberg repo](https://codeberg.org/RatCornu/efs).
//!
//! ```
//! use core::str::FromStr;
//!
//! use deku::no_std_io::{Read, Write}; // Same as `no_std_io2::io::{Read, Write}`
//! use efs::fs::FilesystemRead;
//! use efs::fs::ext2::Ext2Fs;
//! use efs::fs::file::*;
//! use efs::fs::permissions::Permissions;
//! use efs::fs::types::{Gid, Uid};
//! use efs::path::{Path, UnixStr};
//!
//! # std::fs::copy(
//! # "./tests/fs/ext2/io_operations.ext2",
//! # "./tests/fs/ext2/example2.ext2",
//! # )
//! # .unwrap();
//!
//! let device_id = 0_u32;
//!
//! // `device` now contains a `Device`
//! let device = std::fs::File::options()
//! .read(true)
//! .write(true)
//! .open("./tests/fs/ext2/example2.ext2")
//! .unwrap();
//!
//! let fs = Ext2Fs::new(device, device_id).unwrap();
//!
//! // `fs` now contains a `FileSystem` with the following structure:
//! // /
//! // ├── bar.txt -> foo.txt
//! // ├── baz.txt
//! // ├── folder
//! // │ ├── ex1.txt
//! // │ └── ex2.txt -> ../foo.txt
//! // ├── foo.txt
//! // └── lost+found
//!
//! /// The root of the filesystem
//! let root = fs.root().unwrap();
//!
//! // We retrieve here `foo.txt` which is a regular file
//! let Some(TypeWithFile::Regular(mut foo_txt)) =
//! root.entry(UnixStr::new("foo.txt").unwrap()).unwrap()
//! else {
//! panic!("foo.txt is a regular file in the root folder");
//! };
//!
//! // We read the content of `foo.txt`.
//! assert_eq!(foo_txt.read_all().unwrap(), b"Hello world!\n");
//!
//! // We retrieve here `folder` which is a directory
//! let Some(TypeWithFile::Directory(mut folder)) =
//! root.entry(UnixStr::new("folder").unwrap()).unwrap()
//! else {
//! panic!("folder is a directory in the root folder");
//! };
//!
//! // In `folder`, we retrieve `ex1.txt` as `/folder/ex1` points to the same
//! // file as `../folder/ex1.txt` when `/folder` is the current directory.
//! //
//! // Here, it is done by the complete path using the `FileSystem` trait.
//! let Ok(TypeWithFile::Regular(mut ex1_txt)) =
//! fs.get_file(&Path::from_str("../folder/ex1.txt").unwrap(), folder.clone(), false)
//! else {
//! panic!("ex1.txt is a regular file at /folder/ex1.txt");
//! };
//!
//! // We read the content of `foo.txt`.
//! ex1_txt.write_all(b"Hello earth!\n").unwrap();
//!
//! // We can also retrieve/create/delete a subentry with the `Directory`
//! // trait.
//! let TypeWithFile::SymbolicLink(mut boo) = folder
//! .add_entry(
//! UnixStr::new("boo.txt").unwrap(),
//! Type::SymbolicLink,
//! Permissions::from_bits_retain(0o777),
//! Uid(0),
//! Gid(0),
//! )
//! .unwrap()
//! else {
//! panic!("Could not create a symbolic link");
//! };
//!
//! // We set the pointed file of the newly created `/folder/boo.txt` to
//! // `../baz.txt`.
//! boo.set_pointed_file("../baz.txt").unwrap();
//!
//! // We ensure now that if we read `/folder/boo.txt` while following the
//! // symbolic links we get the content of `/baz.txt`.
//! let TypeWithFile::Regular(mut baz_txt) =
//! fs.get_file(&Path::from_str("/folder/boo.txt").unwrap(), root, true).unwrap()
//! else {
//! panic!("Could not retrieve baz.txt from boo.txt");
//! };
//! assert_eq!(ex1_txt.read_all().unwrap(), baz_txt.read_all().unwrap());
//!
//! // Here is the state of the filesystem at the end of this example:
//! // /
//! // ├── bar.txt -> foo.txt
//! // ├── baz.txt
//! // ├── folder
//! // │ ├── boo.txt -> ../baz.txt
//! // │ ├── ex1.txt
//! // │ └── ex2.txt -> ../foo.txt
//! // ├── foo.txt
//! // └── lost+found
//!
//! # std::fs::remove_file("./tests/fs/ext2/example2.ext2").unwrap();
//! ```
compile_error!;
extern crate alloc;
extern crate core;
extern crate std;
pub