In this tutorial, I'll be discussing how to use fusexx to rewrite the Hello World module that's packaged with FUSE. To get started let's create a directory called "hello_world_module":
And then download the fusexx.hpp file into the directory.
cd hello_world_module
wget http://portal.itauth.com/fusexx.hpp
If you haven't taken a look at the
fusexx.hpp file yet, I suggest you take a brief glance at it to get a better feel for what it does. It's a simple interface that provides static member methods corresponding to the methods defined in the fuse_operations struct. What that means is that we'll inherit from the
fuse class and overload the methods that need to be customized for our module. For the Hello World module that means we'll overload the
getattr,
readdir,
open and
read methods.
Let's define the header file for our Hello World module:
hello.hpp
// hello.hpp
#include "fusexx.hpp"
#include <string>
class HelloWorld : public fusexx::fuse<HelloWorld> {
public:
HelloWorld (); // Constructor
// Overload the fuse methods
static int getattr (const char *, struct stat *);
static int readdir (const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *);
static int open (const char *, struct fuse_file_info *);
static int read (const char *, char *, size_t, off_t, struct fuse_file_info *);
private:
// Private variables
// Notice that they aren't static, i.e. they belong to an instantiated object
std::string m_strHelloWorld;
std::string m_strPath;
};
As you can see, we're deriving a HelloWorld class from the fusexx::fuse template and are overloading 4 FUSE methods. Notice how we're defining private member variables in the class which aren't static. Normally, static methods would have no way to access those member variables. But as we'll see later, there is a pointer called 'self', similar to the 'this' pointer, that can be used to access data from an object.
And now let's actually implement the HelloWorld class. Much of the code will be very similar to the example code in the FUSE package:
hello.cpp
// hello.cpp
#include "hello.hpp"
#include <string>
using namespace std;
// Constructor
HelloWorld::HelloWorld () :
m_strHelloWorld ("Hello World!\n"),
m_strPath ("/hello") {
// all we're doing is initialize the member variables
}
int HelloWorld::getattr (const char *path, struct stat *stbuf) {
int res = 0;
string strPath (path); // convert the path into a std::string
// Zero out the file stat buffer
memset (stbuf, 0, sizeof (struct stat));
if (strPath == "/") {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
}
// Compare the private variable of the HelloWorld object to the
// passed-in path parameter from fuse
else if (self->m_strPath == strPath) {
stbuf->st_mode = S_IFREG | 0444; // read-only
stbuf->st_nlink = 1;
stbuf->st_size = self->m_strHelloWorld.length();
}
else {
res = -ENOENT;
}
return res;
}
int HelloWorld::readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi) {
string strPath (path);
(void) offset;
(void) fi;
if(strPath != "/")
return -ENOENT;
filler(buf, ".", NULL, 0);
filler(buf, "..", NULL, 0);
// self->m_strPath contains the std::string "/hello"
// Applying .c_str() returns a const char * containing "/hello"
// Adding 1 to that pointer skips the '/' character and thus outputs
// just "hello"
filler(buf, self->m_strPath.c_str() + 1, NULL, 0);
return 0;
}
int HelloWorld::open(const char *path, struct fuse_file_info *fi) {
string strPath (path);
// The only file that exists is "/hello"
if(strPath != self->m_strPath)
return -ENOENT;
// Only allow read-only access
if((fi->flags & 3) != O_RDONLY)
return -EACCES;
return 0;
}
int HelloWorld::read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi) {
string strPath (path);
size_t len;
(void) fi;
if(strPath != self->m_strPath)
return -ENOENT;
len = self->m_strHelloWorld.length ();
if (offset < len) {
if (offset + size > len)
size = len - offset;
memcpy (buf, self->m_strHelloWorld.c_str () + offset, size);
} else
size = 0;
return size;
}
Much of the above code should look very familiar if you've done FUSE modules in C. The main thing I would like to point out is the usage of the 'self' pointer. As mentioned earlier, static methods can't directly access member variables or methods from instantiated objects. To get around that problem, the fusexx binding includes a 'self' pointer. It is exactly the same thing as the 'this' pointer, with the additional benefit that you can use it in static methods.
And finally, let's create the main function to instantiate an object and execute the main method of the fusexx::fuse class:
main.cpp
// main.cpp
#include "hello.hpp"
int main (int argc, char **argv) {
HelloWorld hello;
// The first 3 parameters are identical to the fuse_main function.
// The last parameter gives a pointer to a class instance, which is
// required for static methods to access instance variables/ methods.
return hello.main (argc, argv, NULL, &hello);
}
In this code we're instantiating a HelloWorld object and calling its static main method. As mentioned in the comments, the first 3 parameters are identical to fuse_main's parameters. The last parameter will be used as the 'self' pointer described earlier.
To compile the program, simply type:
g++ -o hello main.cpp hello.cpp `pkg-config fuse --cflags --libs`
And then create a mount point and execute the module itself:
$ mkdir tmp
$ ./hello tmp
$ ls -al tmp
total 4
drwxr-xr-x 2 root root 0 1969-12-31 16:00 .
drwxr-xr-x 3 achillean achillean 4096 2007-07-06 22:57 ..
-r--r--r-- 1 root root 13 1969-12-31 16:00 hello
$ cat tmp/hello
Hello World!
I hope this tutorial has given you a better understanding of how to use fusexx to write FUSE modules in C++.
Post new comment