The intent of this page is to give a little bit of extra information
on calls that seem a little obscure in the FUSE documentation. Two
cases requiring extra explanation have come up so far:
readdir()
, and FUSE's handling of the file creation flags
in open()
.
readdir()
FUSE provides a mechanism to place entries in a directory structure. The directory structure itself is opaque, so the basic mechanism is to create the data and call a FUSE-supplied function to put it in the structure.
When your readdir()
callback is invoked, one of
the parameters is a function called filler()
. The
purpose of this function is to insert directory entries into the
directory structure, which is also passed to your callback as
buf
.
filler()
's prototype looks like this:
int fuse_fill_dir_t(void *buf, const char *name,
const struct stat *stbuf, off_t off);
You insert an entry into buf
(the same buffer that
is passed to readdir()
) by calling
filler()
with the filename and optionally a
pointer to a
struct stat
containing the file type.
bb_readdir()
uses filler()
in as
simple a way as possible to just copy the underlying directory's
filenames into the mounted directory. Notice that the offset passed
to bb_readdir()
is ignored, and an offset of 0 is
passed to filler()
. This tells filler()
to manage the offsets into the directory structure for itself. Here's
the code:
int bb_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset,
struct fuse_file_info *fi)
{
int retstat = 0;
DIR *dp;
struct dirent *de;
log_msg("bb_readdir(path=\"%s\", buf=0x%08x, filler=0x%08x, offset=%lld, fi=0x%08x)\n",
path, (int) buf, (int) filler, offset, (int) fi);
dp = (DIR *) (uintptr_t) fi->fh;
// Every directory contains at least two entries: . and .. If my
// first call to the system readdir() returns NULL I've got an
// error; near as I can tell, that's the only condition under
// which I can get an error from readdir()
de = readdir(dp);
if (de == 0)
return -errno;
// This will copy the entire directory into the buffer. The loop exits
// when either the system readdir() returns NULL, or filler()
// returns something non-zero. The first case just means I've
// read the whole directory; the second means the buffer is full.
do {
log_msg("calling filler with name %s\n", de->d_name);
if (filler(buf, de->d_name, NULL, 0) != 0)
return -ENOMEM;
} while ((de = readdir(dp)) != NULL);
log_fi(fi);
return retstat;
}
The open()
system call is documented as taking both file
access mode and file creation flags (the file creation flags are
O_CREAT
, O_EXCL
, O_NOCTTY
, and
O_TRUNC
). fuse.h
documents that
O_CREAT
and O_EXCL
are not passed to your
open()
function, and further that O_TRUNC
is
not passed by default.
The reason for this turns out to be that FUSE handles these flags
internally, and modifies which of your functions are called depending
on their status, and on the results of a call to your
getattr()
(your getattr()
is always called
before the file is opened). If those flags are set, the call is handled
as follows:
O_CREAT
create()
function is called instead of your open()
function
(if it did exist, then your open()
is called). After
the call to your create()
function, your
fgetattr()
function is called, though I haven't been
able to determine why. One possible use is that you could use
this to modify the semantics of creating a file that you yourself
don't have access to (note that the standard semantics will only
apply the file access mode you specify to subsequent
open()
s).
If the file did not exist, and the flag is not set, FUSE only
calls your getattr()
function (so neither your
create()
nor your open()
function is
called in this case).
O_EXCL
O_CREAT
is also specified. If the file did not
previously exist your create()
and
fgetatter()
functions are called; if it did, FUSE returns
failure after the getattr()
call (so neither your
open()
nor your create()
is called in this
case).
O_NOCTTY
O_TRUNC
-o atomic_o_trunc
flag. If not, then FUSE will call your truncate()
function before calling your open()
. If the
atomic_o_trunc
option was set, the flag is passed to
your open()
function instead (note that this means I
don't have any code that explicitly handles the flag: if it gets
passed to bb_open()
, I just pass it along to
open()
.
Security Considerations and Race Conditions