Tuesday | 05 NOV 2024
[ previous ]
[ next ]

Rust, Fuse, Sqlite - 02 readdir

Title:
Date: 2023-02-07
Tags:  

Interestingly rust seems to make a distinction between files and directories. In C both are queried by getattr while in rust getattr is for directories and lookup is for files. Very curious.

use std::time::{Duration, UNIX_EPOCH};
use std::ffi::OsStr;
use fuser::{
    FileAttr, FileType, Filesystem, ReplyAttr, ReplyDirectory, ReplyEntry, Request,
};

struct RFS;

const ENOENT :i32 = 2;

const TTL: Duration = Duration::from_secs(1);

const DEFAULT_DIR_ATTR: FileAttr = FileAttr {
    ino: 1,
    size: 4096,
    blocks: 0,
    atime: UNIX_EPOCH,
    mtime: UNIX_EPOCH,
    ctime: UNIX_EPOCH,
    crtime: UNIX_EPOCH,
    kind: FileType::Directory,
    perm: 0o755,
    nlink: 2,
    uid: 1000,
    gid: 1000,
    rdev: 0,
    flags: 0,
    blksize: 512,
    padding: 0,
};

const DEFAULT_FILE_ATTR: FileAttr = FileAttr {
    ino: 2,
    size: 4096,
    blocks: 1,
    atime: UNIX_EPOCH, 
    mtime: UNIX_EPOCH,
    ctime: UNIX_EPOCH,
    crtime: UNIX_EPOCH,
    kind: FileType::RegularFile,
    perm: 0o644,
    nlink: 1,
    uid: 1000,
    gid: 1000,
    rdev: 0,
    flags: 0,
    blksize: 512,
    padding: 0,
};

impl Filesystem for RFS {
    fn lookup(&mut self, _req: &Request, _parent: u64, name: &OsStr, reply: ReplyEntry) {
        if name.to_str() == Some("hello.txt") {
            reply.entry(&TTL, &DEFAULT_FILE_ATTR, 0);
        } else {
            reply.error(ENOENT);
        }
    }

    fn getattr(&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
        if ino == 1 {
            reply.attr(&TTL, &DEFAULT_DIR_ATTR);
        } else {
            reply.attr(&TTL, &DEFAULT_FILE_ATTR);
        }
    }

    fn readdir(&mut self, _req: &Request, ino: u64, _fh: u64, offset: i64, mut reply: ReplyDirectory) {
        if ino != 1 {
            reply.error(ENOENT);
            return;
        }

        let entries = vec![
            (1, FileType::Directory, "."),
            (1, FileType::Directory, ".."),
            (2, FileType::RegularFile, "hello.txt"),
            (1, FileType::Directory, "Test"),
        ];

        for (i, entry) in entries.into_iter().enumerate().skip(offset as usize) {
            if reply.add(entry.0, (i + 1) as i64, entry.1, entry.2) {
                break;
            }
        }
        reply.ok();
    }
}

fn main() {
    let mountpoint = "dir";
    let options = vec![];
    fuser::mount2(RFS, mountpoint, &options).unwrap();
}