esp-rs / rust

Rust for the xtensa architecture. Built in targets for the ESP32 and ESP8266
https://www.rust-lang.org
Other
743 stars 39 forks source link

Unable to list files in a SPIFFS partition #117

Closed bugadani closed 2 years ago

bugadani commented 2 years ago

Given is the following test code, built on a CMake setup (with some details omitted):

# Cargo.toml

...

[dependencies]
c_str_macro = "1"
// main.rs
use std::os::raw::c_char;
use std::ffi::CStr;
use c_str_macro::c_str;

#[repr(C)]
pub struct esp_vfs_spiffs_conf_t {
    pub base_path: *const c_char,
    pub partition_label: *const c_char,
    pub max_files: usize,
    pub format_if_mount_failed: bool,
}

extern "C" {
    pub fn esp_vfs_spiffs_register(conf: *const esp_vfs_spiffs_conf_t) -> i32;
}

fn init_partition(path: &CStr, label: &CStr, max_files: usize) {
    let storage_conf = esp_vfs_spiffs_conf_t {
        base_path: path.as_ptr(),
        partition_label: label.as_ptr(),
        max_files,
        format_if_mount_failed: true,
    };

    unsafe { esp_vfs_spiffs_register(&storage_conf) };
}

fn test() {
    let path = std::path::Path::new("/storage/foobar");

    if let Err(err) = std::fs::File::create(path) {
        println!("Failed to create file: {err}");
        return;
    }

    match std::fs::read_dir("/storage") {
        Ok(dir) => {
            for entry in dir {
                println!("{entry:?}");
            }
        }
        Err(err) => println!("Failed to read root: {err}"),
    }

    if let Err(err) = std::fs::remove_file(path) {
        println!("Failed to remove file: {err}");
    }
}

#[no_mangle]
pub extern "C" fn app_main() {
    init_partition(c_str!("/storage"), c_str!("storage"), 3);

    test();
}

I expect the following output:

Ok(DirEntry("/storage/foobar"))

Instead, the output I get is the following:

Ok(DirEntry("/storage/"))
Err(Os { code: 5, kind: Uncategorized, message: "I/O error" })

The OS error is unrelated (SPIFFS seems to indicate the end of folder iteration by returning a value that gets turned into EIO), but note that the path in the DirEntry has an empty filename.

This issue is present on:

rustc verion esp-idf 4.4.1
Rustc 1.60.0.0 x
Rustc 1.59.0.1 x
Rustc 1.59.0.0 not affected
MabezDev commented 2 years ago

Hey @bugadani, thanks for your patience! I think I've tracked this down to another mismatch of structs on the C side and rust side.

The files are created, and when you step into the ReadDir Iterator, if you look carefully the entries are being populated; with one problem. A zeroed field not included in the C side happens to overlap with the start of the name field, meaning the name on the Rust side starts with a null pointer... hence why only the root directory is printed!

I need to track down whether this is an issue with rust-lang/libc or the newlib component of esp-idf.

MabezDev commented 2 years ago

This is an esp-idf issue, specifically this definition.

d_ino should be type ino_t, but its defined as an int. sizeof(int) == 4, sizeof(ino_t) == 2 on the esp-idf platform, hence this failure. I'll submit a fix asap :).

MabezDev commented 2 years ago

The final part Err(Os { code: 5, kind: Uncategorized, message: "I/O error" }) was yet another issue; this time with spiffs. The spiffs module can emit SPIFFS_VIS_END, even though this is not a publically documented error code. According to the developer it is not an error, and should not be emitted.

I've now submitted the two patches internally. Hopefully, they'll be merged and backported soon :).

bugadani commented 2 years ago

Awesome, thanks :)

MabezDev commented 2 years ago

Thanks for the report @bugadani. This is now fixed & backported to V4.4, so I'm closing this issue :). You may need to remove .embuild/ and rebuild to pull in the fixes from the v4.4 branch.