Giorgos Xou
Giorgos Xou

Reputation: 2204

FUSE3 - Inability to create inode-based virtual-file-system

Intro - context

I'm trying to develop a FUSE3 inode-based Virtual-File-System (VFS), where:

  1. A set of inodes from an external ext4 file-system are mapped into a hashmap as <inode>:<filename>
  2. Attributes are provided (via the hashmap &) through ext2fs_read_inode(fs, <inode>, &<inode>)
  3. Other file-system operation are managed via inodes too. [but that's not my priority as for now]

[1]: Specifically, using a NoSQL-database (kyotocabinet). But this could be anything of key-value nature

Issue

In my attempt to create such a system either via fuse_lowlevel.h or fuse.h, I was unable to figure out how to cache\preset\prefill inodes in readdir, due to the fact that I stumbled upon a lot of overwhelming-uncertainties\problems (primarily [due to my luck of knowledge and] the documentation) such as:

1. readdir_ino:

While documentation states that readdir_ino:

If use_ino option is not given, still try to fill in the d_ino field in readdir(2). If the name was previously looked up, and is still in the cache, the inode number found there will be used. Otherwise it will be set to -1. If use_ino option is given, this option is ignored.

I see no (dentry)-d_ino being set\filled as an example anywhere, so I assume they meant (stat)-st_ino and this might be a typo?

... If use_ino option is given, this option is ignored.

(Assuming it means that readdir_ino is ignored [based on what said in the begining "If use_ino..."]) then I don't get why in the example I provide below setting use_ino=1 does the opposite (displays no inodes via ls), based on the fact that documentation about use_ino states that:

... This value is used to fill in the st_ino field ...

2. filler:

Even though filler in readdir provides a struct stat entry & documentation states all of these about cfg->readdir_ino still st_ino seems to be ignored even when remember=-1 is used.

and so on ...

Questions

So, my questions are: is there any way of pre-filling\caching the inodes in readdir (or anywhere) for the entirety of the program, while maintaining access to them (inodes) afterwords at gettattr (eg. via stat(mountpoint + path))? and if so, how am I supposed to get the inode from getattr if cached in readdir?

Example

Here's an oversimplification of what I'm trying to do:

#define FUSE_USE_VERSION 31
#include <fuse3/fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>


#define FILE_CONTENT "Hello, World!\n"
#define FILE_NAME "/example.txt"
static char *file_buffer = NULL;
static size_t file_size = 0;


// Function to handle file attributes
static int my_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) 
{
    (void) fi;

    
    // NOTE: Shouldn't `lstat(mountpoint + path)` return at least the inode prefilled\set at `readdir`? instead it freezes
    
    // instead of using paths i'd like instead to use their repsective inodes here
    if (strcmp(path, "/") == 0) {
        stbuf->st_mode = S_IFDIR | 0755;
        stbuf->st_nlink = 2;
        stbuf->st_ino = 1;  // Assign inode for root directory
    } else if (strcmp(path, FILE_NAME) == 0) {
        stbuf->st_mode = S_IFREG | 0644;
        stbuf->st_nlink = 1;
        stbuf->st_size = file_size;
        // stbuf->st_ino = 9465403; // if not set , no inode :(
        // stbuf->st_ino = st1.st_ino; // :(
    } else {
        return -ENOENT;
    }

    return 0;
}

// Function to list files in a directory, here I'd like to set the inodes once and for all
static int my_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags) {
    (void) offset;
    (void) fi;
    (void) flags;

    if (strcmp(path, "/") != 0)
        return -ENOENT;

    printf("READDIR: %lu %s\n", fi->fh, path);
    struct stat st; memset(&st, 0, sizeof(st));

    // Add current directory and parent directory
    st.st_ino = 1;
    st.st_mode = S_IFDIR | 0755;
    filler(buf, ".", &st, 0, 0);
    filler(buf, "..", &st, 0, 0);

    // Add the example.txt file
    st.st_ino = 9465403; // NOTE: prefill inode - assuming it will be cached and later be retrived via stat in `getattr`
    st.st_mode = S_IFREG | 0644;
    filler(buf, "example.txt", &st, 0, FUSE_FILL_DIR_PLUS);

    return 0;
}

void *my_init(struct fuse_conn_info  *conn, struct fuse_config *cfg) 
{
    printf("Filesystem initialized.\n");
    // cfg->parallel_direct_writes = 1;
    // cfg->nullpath_ok = 1;
    // cfg->use_ino = 1; // NOTE: uncommenting disables auto-prefill for inodes ???? but also ignores `readdir_ino`????
    cfg->readdir_ino=1;
    cfg->remember=-1;
    cfg->kernel_cache = 1;
    // cfg->entry_timeout = 999999; // Disable entry cache for passthrough-like behavior
    // cfg->attr_timeout = 0;
    // cfg->negative_timeout = 0;
    return 0;
}


// Define FUSE operations
static struct fuse_operations my_oper = {
    .getattr = my_getattr,
    .readdir = my_readdir,
    .init    = my_init
};

int main(int argc, char *argv[]) {
    // Initialize the file content buffer with default content
    file_size = strlen(FILE_CONTENT);
    file_buffer = malloc(file_size);
    memcpy(file_buffer, FILE_CONTENT, file_size);

    // Run FUSE main loop
    return fuse_main(argc, argv, &my_oper, NULL);
}
gcc -Wall -o simple_fs simple_fs.c `pkg-config fuse3 --cflags --libs`
./simple_fs ./mountpoint -f

Use ls -i ./mountpoint to see inodes later.

Goal

My goal is to achieve access to specific parts\files of an external ext file-system via their inodes (for use in an upcoming project of mine).

What can I try next?

Upvotes: 1

Views: 190

Answers (1)

Oren Kishon
Oren Kishon

Reputation: 767

The way to pre-fill and cache inode numbers is in fact to use use_ino = 1, and to assign your inode values in the implementation of .readdir and .getattr.

The advice I would give you is to copy one of the passthrough examples and start developing your program from there, checking in each modification that you haven't messed up anything.

I didn't run your program but I did gave a look at the inodes that are given when running libfuse passthrough example. It uses use_ino = 1. And indeed the inodes are consistent with the underlying file system's inodes.

I have also modified the code to use_ino = 0. What I got were inodes values created on the fly in a monotonous increasing order, more or less (1, 2, 3, ...). They were also consistent while browsing different folders, repeating some folders once in a while.

Upvotes: 0

Related Questions