Zbr's days.
December
Sun Mon Tue Wed Thu Fri Sat
           
19
         
2007
Months
Dec

About TODO Blog RSS Old blog Projects Gallery Notes

Wed, 19 Dec 2007

Anatomy of the filesystem. ->readdir() callback.

Here I will write simple notes about how some callbacks are used in linux VFS and what filesystem write should implement to be correctly understood by VFS layer.

Let's start from essentially the first callback invoked by FS after fs has been mounted. As name suggests, ->readdir() is used to read directory content. Its prototype looks like this:

static int pohmelfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
where filp is a file structure which is connected to the root inode (which you have to initialize in ->fill_super() callback to be able to mount fs). Dirent is a magic structure, which hosts all directory content you will read, and filldir is a function, which transforms directory names into dirent structure.
Its prototype looks like this:
int filldir(void * __buf, const char * name, int namlen, loff_t offset,
	u64 ino, unsigned int d_type)
and is invoked this way:
size = 1;
if (filldir(dirent, ".", size, filp->f_pos, inode->i_ino, DT_DIR) < 0)
	return -1;
filp->f_pos += size;
I think every step is very straightforward, except last two entries: the former is inode number, which is unique id of the structure, every filesystem has to store it on disk for every inode, obviously in Unix '.' refers to curent dir, so its inode number should be taken from the current dir inode. For '..' directory, which is a parent for given one, filldir() is executed by the following way:
size = 2;
if (filldir(dirent, "..", size, filp->f_pos, parent_ino(filp->f_path.dentry), DT_DIR) < 0)
	return -1;
filp->f_pos += size;
and for some other dir:
size = 8;
if (filldir(dirent, "test_dir", size, filp->f_pos, 14, DT_DIR) < 0)
	return -1;
filp->f_pos += size;
where '14' is inode number for 'test_dir' subdir.
Directory listing for this filesystem will look like this (data from live pohmelfs setup):
# ls -la /mnt/
total 9
drwxr-xr-x  1 root root 4096 1969-12-31 20:02 .
drwxr-xr-x 21 root root 1024 2007-02-08 15:04 ..
drwxr-xr-x  1 root root 4096 1969-12-31 20:02 test_dir

# mount | grep mnt
/dev/hdb1 on /mnt type pohmel (rw)
The last parameter of the filldir() is type of the directory entry, DT_DIR is for directories and it corresponds to 12-15 bits of the stat.st_mode returned from stat() call.

Note, that ->readdir() will be invoked (by ls -la at least) until filp->f_pos stops changing, so after you filled your directory entry and properly updated filp->f_pos, you have to check, that provided filp->f_pos exceeds or not size of the directory (here I mean overall size used by every copied directory entry), and if it does (or is equal), just return 0.

So, how network filesystem should behave here? Answer is pretty simple: it should just send a request to remote server to provide directory listing, copy answer to the allocated buffer and fill directory with provided data. It is possible to cache that data here, but each subsequent ->readdir() has to check on server that data is still valid and was not changed.

With this work pohmelfs becomes a network filesystem, with many interesting features I have in mind, but will open when they got implemented.
This is not intended for mainline inclusion, since Zach Brown's work was first and likely will be more stable and/or feature complete when this stuff become ready.

But nevertheless, stay tuned..

/devel/fs :: Link / Comments (0)