Pregunta

I am trying to implement a FUSE filesystem, and after reading around. I think I have found out how to implement it. I would state out what my idea is of the implementation. Please let me know if it is right or wrong.

One implements the operations that is required to be implemented by the filesystem using whatever functions one sees fit (say xmp_getattributes for getattr function) and then maps those functions to the corresponding functions in a struct of type fuse_operations

static struct fuse_operations xmp_oper = 
{
    .getattr = xmp_getattributes,
    //more declarations...
};

Now, if I have used a class ExFuse to implement a FUSE filesystem and I have a method ExFuse::getAttributes which does the job of getattr in the implementation. Then, my declaration would change as

static struct fuse_operations xmp_oper = 
{
    .getattr = ExFuse::getAttributes,
    //more declarations...
};

Am I correct? Also, is there any other way of implementing FUSE in C++ which is better than the static struct declaration?

¿Fue útil?

Solución

if I have used a class ExFuse to implement a FUSE filesystem and I have a method ExFuse::getAttributes which does the job of getattr in the implementation. Then, my declaration would change as

static struct fuse_operations xmp_oper = 
{
    .getattr = ExFuse::getAttributes,
    //more declarations...
};

This is wrong. FUSE expects a pointer to function, but ExFuse::getAttributes is a pointer to member method (since you didn't explicitly say that it was a static method, I can only assume it's a member method). They are not the same thing.

You either need to use plain functions, or static methods.

is there any other way of implementing FUSE in C++ which is better than the static struct declaration?

I'm afraid not. FUSE is written in C and expects you to stick to the "C way". This gives you very little leeway to use C++ paradigms as far as the public interface is concerned (of course, you can do pretty much what you want in the private implementation, as long as you don't throw, but this won't help you writing the interface to FUSE).

Otros consejos

Also already stated above, all methods of your class would have to be "static". This is because when you have a class like this in C++:

class example
{
  example ()
  int method (int i) {cout << i << '\n';}
};

int main (int argc, char **argv)
{
  example ex;

  ex.method (1);
}

What is really happening when you call ex.method(1) above is this call:

{NameMangling}method (&ex, 1);

Unless "method" is declared static, it's passing around the "this" pointer behind the scenes. All your prototypes will be wrong for the FUSE interface.

You can use C++ just fine within FUSE, but there's little point to using classes to define the structures. You can freely make use of vectors, strings, etc - but consider the over-head of using C++ as well (which won't be any if you do it properly) - but unless you are familiar with the C++->C translation (C++ used to basically be a C pre-processor in the bad old days), try to stick with C semantics as much as possible.

Disclaimer: I haven't tested any of the assumptions made in the following answer

As per @user6269400, the main issue of using C++ class methods for the FUSE operations, is what is the this pointer pointing to - or more to the point, how do we get class methods implementing FUSE operations to be called in the correct context, i.e. as method calls on the C++ class instance.

An easy answer to this is to use the FUSE context's private_data field to store a pointer to the instance and then use some glue code to cast it to your class type.

A "library" (and I use the term very loosely here) implementing (some of) the glue code for a C++ FUSE implementation can be found here: https://github.com/jachappell/Fusepp . The way it works is by hooking correctly named non-static class methods to the FUSE operations table, then initializing FUSE with the class instance address as the "private_data". As @syam alluded to, doing this will have FUSE invoke your class methods with incorrect (possibly null) this address and the result will be a very unhappy C++ code. Fusepp tries to solve this by providing a this_() method that does the above mentioned casting for you, so your code might look like this:

const bool ExFuse::resolve_path(const std::string &path, struct stat* stbuf) {
    // ...
}

int ExFuse::getattr(const char *path, struct stat *stbuf, struct fuse_file_info *)
{
    memset(stbuf, 0, sizeof(struct stat));
    if (!this_()->resolve_path(stbuf))
        return -ENOENT;
    return 0;
}

So you must use this_() to access your instance members and there is no implicit this - if you try to call resolve_path() directly (without dereferncing through this_()) you'd get undefined behavior (which at best will be a segmentation fault).

Just so we are clear - I don't recommend using FusePP: the resulting code will not be C++ but will look similar enough to C++ to fool compilers and humans with possibly catastrophic results. But taking the same approach and using a bit more glue code to intercept a FUSE operations call, cast the context private_data to a class instance pointer, then call the C++ method through the instance pointer would be very workable, I believe.

I started work on a C++ wrapper for Fuse that uses C++ paradigms, at https://github.com/xloem/fusexx . If you look at how it works, it may be pretty intuitive. It stashes a this pointer and translates the static functions to virtual functions using boilerplate forwarding functions.

The API of this library may change if I develop it more.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top