<--staticfs ^--VFS--^ staticfs files-->

How to write a Linux VFS filesystem module - StaticFS - inodes

March 12, 2004

StaticFS

Inodes

Time to write some inode functions. Let's start by looking again at the struct inode_operations structure from fs.h:
struct inode_operations {
        int (*create) (struct inode *,struct dentry *,int);
        struct dentry * (*lookup) (struct inode *,struct dentry *);
        int (*link) (struct dentry *,struct inode *,struct dentry *);
        int (*unlink) (struct inode *,struct dentry *);
        int (*symlink) (struct inode *,struct dentry *,const char *);
        int (*mkdir) (struct inode *,struct dentry *,int);
        int (*rmdir) (struct inode *,struct dentry *);
        int (*mknod) (struct inode *,struct dentry *,int,int);
        int (*rename) (struct inode *, struct dentry *,
                        struct inode *, struct dentry *);
        int (*readlink) (struct dentry *, char *,int);
        int (*follow_link) (struct dentry *, struct nameidata *);
        void (*truncate) (struct inode *);
        int (*permission) (struct inode *, int);
        int (*revalidate) (struct dentry *);
        int (*setattr) (struct dentry *, struct iattr *);
        int (*getattr) (struct dentry *, struct iattr *);
};
This should be easy, considering we've got a read-only filesystem. romfs only implements one of the functions above, lookup, but we'll look at them all just to be sure.

create, mkdir, link, symlink and mknod all create objects, which we don't allow in our filesystem, so we can leave them blank. unlink and rmdir remove inodes, which we also don't allow. rename changes and/or moves inodes, neither that we allow.

So what about the fields we haven't seen before? readlink and follow_link are for symbolic links, so we don't need those. truncate? No idea. Yet. permission seems to be used to determine whether the inode is readable by ... the user? Great. Another thing we don't know. Oh well, we skip it now, because ramfs and romfs do! revalidate seems to be used to ask the VFS module to make sure the inode is correct -- useful if your filesystem sits elsewhere, say over a network. And lets finish it off with two more we don't understand: setattr and getattr. Definitely something to learn for a future VFS module. But not this one.

So what's left? Just that lookup function. From reading romfs and ramfs, I'm still not sure what's going on. As we saw in ramfs, all it did was

        d_add(dentry, NULL);
        return NULL;
So it took the dentry passed to it, added it to the dentry cache, and returned back with a NULL. Reading the romfs code, though, it does a few more things (thank goodness). It gets the name of the filename we're interested in from the dentry object passed in, at it checks for it in the directory specified by the inode passed in. If it finds the entry name in the directory, it grabs that entry's inode and adds the dentry into the dcache within. The comments in the romfs code also say that in the case of an unfound entry, it should call d_add() with a NULL inode. Let's follow that lead.
static struct dentry *staticfs_lookup(struct inode *dir, struct dentry *dentry) {
  int ino;
  struct inode *inode;
  const char *name;

  ino = -1;
  inode = NULL;
  name = dentry->d_name.name;

  switch (dir->i_ino) {
  case 0:
    if (strcmp(name,"a")==0) ino=1;
    if (strcmp(name,"b")==0) ino=2;
    break;
  case 2:
    if (strcmp(name,"c")==0) ino=3;
    break;
  }

  if (ino>=0) {
    inode=iget(dir->i_sb,ino);
  }

  d_add(dentry,inode);
  return NULL;
}
Phew. That's a lot of coding at once! I assume that the inode passed in has already been determined to be a directory, so we're not going to check it. This means that it's either inode 0, the root directory, or inode 2, the b directory. Since our directory entries are static, we don't have any records to look into, and just have a fixed switch statement. If the right name is found in the right place, we set ino to the inode number of that file. If ino comes out of the switch statement with -1, then inode stays NULL.

Rereading the comments in ramfs and romfs, I think I finally understand them. From romfs:

    /*
         * it's a bit funky, _lookup needs to return an error code
         * (negative) or a NULL, both as a dentry.  ENOENT should not
         * be returned, instead we need to create a negative dentry by
         * d_add(dentry, NULL); and return 0 as no error.
         * (Although as I see, it only matters on writable file
         * systems).
         */
and ramfs:
/*
 * Lookup the data. This is trivial - if the dentry didn't already
 * exist, we know it is negative.
 */
It's the use of "negative" that threw me off. A "negative" dentry. ramfs says that "if it didn't already exist", meaning that if it isn't already in the dcache. That's a shortcut for ramfs because the file can't exist unless it was made in this session, and if it was made in this session, then it's in the cache. This means it can just take any dentry given and throw it away, because if VFS is asking, it's not there. romfs has to do a little more work, because it is possible there (and likely in any other module) that VFS hasn't referred to a given file yet.

With all (one) of our inode functions written, we'll now put our struct inode_operations together.

static struct inode_operations staticfs_inode_operations = {
  lookup:staticfs_lookup,
};
And we're done with inodes!
<--staticfs ^--VFS--^ staticfs files-->
©2002-2017 Wayne Pearson