Thanks to visit codestin.com
Credit goes to docs.rs

Crate efs

Crate efs 

Source
Expand description

§Extended fs

An OS and architecture independent implementation of some Unix filesystems in Rust.

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.

§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 (ext2) and the Simple File System (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, a Directory, a SymbolicLink, a Fifo, a CharacterDevice, a BlockDevice or a 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 and File for example) to be able to define read-only filesystems.

  • 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 (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 is a node in the tree-like hierarchy of a filesystem. You can retrieve, add and remove entries (which are other files).

  • A SymbolicLink is a file pointing an other file. It can be interpreted as the symbolic link or the pointed file in the 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 is here to provide two things : an entry point to the filesystem with the 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 Filesystems and on how to implement them.

§Paths

As the Rust’s native Path implementation is in std::path, this crates provides an other Path interface. It is based on UnixStr, which are the equivalent of OsStr with a guarantee that: it is never empty nor contains the <NUL> character (‘\0’).

§Devices

In this crate, a 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 Devices 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). 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, Write and Seek automatically derive the Device trait. Then, all the common structures (such as File, …) can be directly used. See the part on how to implement a device if needed.

use std::fs::File;

use efs::dev::Device;


let file = File::options().read(true).write(true).open("./tests/fs/ext2/example.ext2").unwrap();

// `file` is a `Device`

§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 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.

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};


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

Modules§

arch
Architecture related functions, such as integer conversions.
celled
Interface to use celled objects.
dev
Everything related to the devices.
error
Interface for efs possible errors.
fs
General interface for filesystems.
path
Path manipulation for UNIX-like filesystems.