Question

I'm writing a filesystem in C for homework that interfaces with FUSE. So far I can mount my filesystem, create and delete files, list files in the directory, etc., but I'm having a few problems with the read() and write() functions.

My assignment stipulates that my filesystem keep two kinds of open file tables, one per process (so each process with an open file has its own table of open files), and one systemwide (that each entry in the per process table links to for information that can be shared). When a call to read() or write() is made, the appropriate table entry is looked up with pid of the calling process and the file descriptor parameter passed in the system call.

My issue is that FUSE does not pass file descriptors in calls to read() or write(), only the path of the file, so I cannot look up the appropriate entry in my open file tables. As suggested here, I can put the file decriptor returned by open() in the file handle field of the struct fuse_file_info argument, but I believe the struct fuse_file_info has a one-to-one relationship with my file, so then the file descriptor in there will always be the one made by the most recent call to open(), which is not necessarily the one related to a subsequent call to read() or write().

The only way I can think to solve the problem is to force calls to my filesystem's open() and close() functions to create and remove table entries for every call to the FUSE operations read() and write(). Then I can pass the file descriptor returned by that call to open() to my filesystem's read() or write() function, but this hardly seems reasonable. If possible, I'd like calls to my filesystem's open() and close() to have a one-to-one relationship with the FUSE operations open() and release().

My main fuse interface is listed below, and if interested the rest can be found here:

#include "abfs.h"

static int abfs_unlink(const char* name) {
    struct fuse_context* fc = fuse_get_context();

    return fs_unlink(name, fc->uid, fc->gid);
}

static int abfs_readdir(const char* name, void* buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info* fi) {
    if(strcmp(name, "/"))
        return -ENOENT;

    filler(buf, ".", NULL, 0);
    filler(buf, "..", NULL, 0);

    for (int i = 0; i < DIRECTORY_SIZE; i++) {
        GList* l = fileSystem->directory[i];
        while (l) {
            printf("i == %d\n", i);
            struct stat st;
            MetaDataNode* mdn = &fileSystem->storage[GPOINTER_TO_UINT(l->data)].mdn;
            abfs_getattr(mdn->name, &st);
            /* call to fs_access?*/
            if (filler(buf, mdn->name + 1, &st, 0))
                return -ENOMEM;
            l = l->next;
        }
    }

    return 0;
}

static int abfs_getattr(const char* name, struct stat* buf) {
    memset(buf, 0, sizeof *buf);

    if (!strcmp(name, "/")) {
        buf->st_mode = S_IFDIR | 0755;
        buf->st_nlink = 2;
    } else {
        MetaDataNode* mdn = findFile(name, NULL, NULL);
        if (!mdn)
            return -ENOENT;

        buf->st_mode = mdn->fileMode;
        buf->st_nlink = mdn->linkCount;
        buf->st_uid = mdn->uid;
        buf->st_gid = mdn->gid;
        buf->st_size = mdn->size;
        buf->st_atime = mdn->atime;
        buf->st_mtime = mdn->mtime;
        buf->st_ctime = mdn->ctime;
    }

    return 0;
}

static int abfs_create(const char* name, mode_t mode, struct fuse_file_info* fi) {
    struct fuse_context* fc = fuse_get_context();

    int i = fs_create(name, mode, fc->uid, fc->gid);
    if (i < 0)
        return i;

    // int fd = fs_open(name, fi->flags, fc->uid, fc->gid, fc->pid);
    // fi->fh = fd;

    return i;
}

static int abfs_access(const char* name, int mask) {
    struct fuse_context* fc = fuse_get_context();

    return fs_access(name, mask, fc->uid, fc->gid);
}

static int abfs_open(const char* name, struct fuse_file_info* fi) {
    struct fuse_context* fc = fuse_get_context();

    int fd = fs_open(name, fi->flags, fc->uid, fc->gid, fc->pid);

    if (fd >= 0) {
        fi->fh = fd;
        return 0;
    } else
        return fd;
}

static int abfs_read(const char* name, char* buf, size_t size, off_t offset, struct fuse_file_info* fi) {
    struct fuse_context* fc = fuse_get_context();

    return fs_read(fi->fh, buf, size, fc->pid);
}

static int abfs_write(const char* name, const char* buf, size_t size, off_t offset, struct fuse_file_info* fi) {
    struct fuse_context* fc = fuse_get_context();

    return fs_write(fi->fh, buf, size, fc->pid);
}

static int abfs_release(const char* name, struct fuse_file_info* fi) {
    struct fuse_context* fc = fuse_get_context();

    return fs_release(name, fi->fh, fc->pid);
}

static struct fuse_operations abfs_oper = {
    .getattr = abfs_getattr,
    .readdir = abfs_readdir,
    .create = abfs_create,
    .access = abfs_access,
    .unlink = abfs_unlink,
    .open = abfs_open,
    .destroy = freeFileSystem,
    .release = abfs_release,
    .read = abfs_read,
    .write = abfs_write,
};

int main(int argc, char *argv[])
{
    newFileSystem();
    // printf("size mdn: %lud\n", sizeof (MetaDataNode));

    return fuse_main(argc, argv, &abfs_oper, NULL);
}

I was wondering if anyone could offer any insight into my problem or about implementing open file tables with FUSE in general. Many thanks in advance.

Was it helpful?

Solution

I just double checked the FUSE source, and it looks like FUSE allocates fuse_file_info once per open file handle, so that it is one to one with an open handle, not a file path.

However, in the spirit of teaching you to fish, there is an easy way for you to check it yourself. Write yourself a small C test application. Open the same file twice in your test, and put two different numbers in the fh field in FUSE, then read from each of the open file handles, and see what is in the fh field.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top