title: Linux pseudo directory triggers the error "no such file or directory"
body: <h2>Summary</h2>
  
  <p>I'm creating a pseudo filesystem for Linux kernel 4.13, but my directories can't be listed with <code>ls</code>.  I keep getting the message:</p>
  
  <pre><code>ls: cannot access 'mountedfs/data': No such file or directory.
  </code></pre>
  
  <h2>Details</h2>
  
  <p>Creation and registration of the <code>super_block</code> and root <code>dentry</code> structures goes well.  I can mount the file system without trouble, but I can't list the contents of my filesystem.  When I try I get the error "No such file or directory".  </p>
  
  <p>After creating the <code>super_block</code> and root <code>dentry</code>, I call <code>womfs_create_files()</code> to populate the tree.  Here is the full source code of the module.  As you can see, I haven't even bothered with the file operations yet.  I'm still stuck on inode operations.</p>
  
  <pre><code>#include &lt;linux/kernel.h&gt;
  #include &lt;linux/fs.h&gt;       /* libfs and most file-related headers. */
  #include &lt;linux/dcache.h&gt;
  #include &lt;linux/init.h&gt;
  #include &lt;linux/module.h&gt;
  #include &lt;linux/pagemap.h&gt;  /* PAGE_SIZE */
  #include &lt;linux/atomic.h&gt;
  #include &lt;linux/time.h&gt;
  #include &lt;linux/string.h&gt;
  #include &lt;linux/sched.h&gt;
  #include &lt;linux/parser.h&gt;
  #include &lt;linux/magic.h&gt;
  #include &lt;linux/slab.h&gt;
  #include &lt;linux/uaccess.h&gt;
  #include "wombat.h"
  /*
   *  Wombat directories are all basic.  They just contain stuff, you can't link to them,
   *  you can't delete them, and you can't modify them.  Anyone can list them, and
   *  they're owned by root.
   *
   *  The file inodes are more specialized: they have to be linked to information about the
   *  keys they represent and the operations that can be performed on those keys.
   *  The same key data will be used by several inodes, but each inode needs to understand
   *  its particular purpose.  (eg: "&lt;key&gt;/pub_key" lets you retrieve the public key;
   *  "&lt;key&gt;/sign" lets you sign data with the key.)
   *
   *  Open file nodes need state information for processing reads, writes, ioctl,
   *  etc..
   */
  
  /*
   * Boilerplate stuff.
   */
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("CJ Holmes");
  
  #define WOMFS_NAME "womfs"
  #define WOMFS_MAGIC 0x00ff0019 // Lear Red
  
  /*
   * Operations keygen file that uses the machine-specific KEK to create
   * a shrouded key for data encryption.
   */
  
  int womfs_keygen_open(struct inode *inode, struct file *filp)
  {
      return 0;
  }
  
  ssize_t womfs_keygen_read(struct file *filp, char __user *buf,
          size_t count, loff_t *offset)
  {
    return 0;
  }
  
  int womfs_keygen_release(struct inode *inode, struct file *filp)
  {
    return 0;
  }
  
  static struct file_operations keygen_ops = {
    .open = womfs_keygen_open,
    .release = womfs_keygen_release,
    .read = womfs_keygen_read,
  };
  
  /*
   *  Operations for encryption endpoints.  The key and semantics depend on
   *  the key info in i_private and calls to ioctl.
   */
  
  int womfs_encrypt_open(struct inode *inode, struct file *filp)
  {
    return 0;
  }
  
  ssize_t womfs_encrypt_read(struct file *filp, char *buf,
          size_t count, loff_t *offset)
  {
     return 0;
  }
  
  ssize_t womfs_encrypt_write(struct file *filp, const char __user * buf, size_t len, loff_t *pos) {
      return 0;
  }
  
  int womfs_encrypt_release(struct inode *inode, struct file *filp)
  {
     return 0;
  }
  
  long womfs_encrypt_ioctl(struct file *filp, unsigned int cmd, unsigned long data)
  {
     return 0;
  }
  
  static struct file_operations encrypt_ops = {
    .open = womfs_encrypt_open,
    .release = womfs_encrypt_release,
    .read = womfs_encrypt_read,
    .write = womfs_encrypt_write,
    .unlocked_ioctl = womfs_encrypt_ioctl,
  };
  
  /*
   *  Stuff for building our FS structure.
   */
  
  // Implementation borrowed from fs/stat.c:vsf_getattr_nosec()
  int womfs_getattr(const struct path *path, struct kstat *stat, u32 request_mask,
      unsigned int query_flags) {
          struct inode *inode;
  
          inode = d_inode(path-&gt;dentry);
          printk(KERN_WARNING WOMFS_NAME " womfs_getattr(%pd4) --&gt; %p\n", path-&gt;dentry, inode);
  
          memset(stat, 0, sizeof(*stat));
          stat-&gt;result_mask |= STATX_BASIC_STATS;
          if(inode != NULL)
              generic_fillattr(inode, stat); // fs/stat.c
          return 0;
  }
  
  const struct inode_operations womfs_dir_inode_operations = {
      .lookup     = simple_lookup,
      .getattr  = womfs_getattr,
  };
  
  const struct inode_operations womfs_inode_operations = {
      .getattr = womfs_getattr,
  };
  
   static struct inode *womfs_make_inode(struct super_block *sb, struct wombat_key_info *key,
     kgid_t group, char *domain, int mode, struct file_operations *fops)
  {
      struct inode *ret = new_inode(sb);
      struct timespec now = current_kernel_time();
  
      if (ret) {
          ret-&gt;i_mode = mode;
          ret-&gt;i_uid = KUIDT_INIT(0);
          ret-&gt;i_gid = group;
          ret-&gt;i_blocks = 0;
          ret-&gt;i_atime = now;
          ret-&gt;i_mtime = now;
          ret-&gt;i_ctime = now;
  
      if (mode &amp; S_IFDIR) {
        ret-&gt;i_op = &amp;womfs_dir_inode_operations;
        ret-&gt;i_fop = (fops == NULL) ? &amp;simple_dir_operations : fops ;
        inc_nlink(ret);
      } else {
              ret-&gt;i_op = &amp;womfs_inode_operations;
        ret-&gt;i_fop = fops;
      }
  
      ret-&gt;i_private = key;
    }
  
    return ret;
  }
  
  
  struct dentry *womfs_add_node_to_dir(struct dentry *parent, struct inode *node, const char *name)
  {
    struct dentry *child;
    struct qstr qname;
  
    qname.name = name;
    qname.len = strlen(name);
    qname.hash = full_name_hash(NULL, name, qname.len);
  
    child = d_alloc(parent, &amp;qname);
    if (child != NULL)
    {
          // d_instantiate(child, node);
      d_add(child, node);
          inode_inc_link_count(node);
    }
  
    return child;
  }
  
  
  static void womfs_create_files (struct super_block *sb, struct dentry *root)
  {
    struct dentry *subdir;
    struct dentry *fentry;
    struct inode *node;
    kgid_t group;
  
    /*  This will eventually be a loop through all of the slots provided by the SNVS.
        For now, we can just add the KEK entry. */
  
    struct wombat_key_info *ki = kzalloc(sizeof(struct wombat_key_info), GFP_KERNEL);
    ki-&gt;type = wombat_key_kek;
    ki-&gt;slot = 0;
    strcpy(ki-&gt;name, "data");
    // leave group and domain blank, pending further implementation.
    group = KGIDT_INIT(0);
  
    /*  This can be cleaned up, perhaps by combining womfs_make_inode()
        and womfs_add_node_to_dir()
    */
    node = womfs_make_inode(sb, ki, group, NULL, S_IFDIR | 0555, NULL);
    if(node != NULL) {
  
      subdir = womfs_add_node_to_dir(root, node, ki-&gt;name);
      if (subdir != NULL) {
        printk(KERN_WARNING WOMFS_NAME " %pd4", subdir);
        switch(ki-&gt;type) {
          case wombat_key_kek:
            /* create the data/keygen file */
            node = womfs_make_inode(sb, ki, group, NULL, S_IFREG | 0444, &amp;keygen_ops);
            if(node != NULL) {
              fentry = womfs_add_node_to_dir(subdir, node, "keygen");
              if(fentry != NULL)
                printk(KERN_WARNING WOMFS_NAME " %pd4", fentry);
              else
                iput(node);
            }
            /*  create the data/encrypt file */
            node = womfs_make_inode(sb, ki, group, NULL, S_IFREG | 0666, &amp;encrypt_ops);
            if( node != NULL) {
              fentry = womfs_add_node_to_dir(subdir, node, "encrypt");
              if(fentry != NULL)
                printk(KERN_WARNING WOMFS_NAME " %pd4", fentry);
              else
                iput(node);
            }
            break;
          default:
            /* Show some error here. */
            break;
        }
      } else {
        iput(node);
      }
    }
  }
  
  /*
   * Superblock stuff.  This is all boilerplate to give the vfs something
   * that looks like a filesystem to work with.
   */
  
  /*
   * Our superblock operations, both of which are generic kernel ops
   * that we don't have to write ourselves.
   */
  static struct super_operations womfs_s_ops = {
      .statfs     = simple_statfs,
      .drop_inode = generic_delete_inode,
  };
  
  /*
   * "Fill" a superblock with mundane stuff.
   */
  static int womfs_fill_super (struct super_block *sb, void *data, int silent)
  {
    int retval = 0;
      struct inode *root = NULL;
      struct dentry *root_dentry = NULL;
    kgid_t gid = KGIDT_INIT(0);
  
    /*
     * Basic parameters.
     */
      sb-&gt;s_blocksize = PAGE_SIZE;
      sb-&gt;s_blocksize_bits = PAGE_SHIFT;
      sb-&gt;s_magic = WOMFS_MAGIC;
      sb-&gt;s_op = &amp;womfs_s_ops;
    sb-&gt;s_time_gran = 1;
  
  /*
   * We need to conjure up an inode to represent the root directory
   * of this filesystem.  Its operations all come from libfs, so we
   * don't have to mess with actually *doing* things inside this
   * directory.
   */
  
    root = womfs_make_inode(sb, NULL, gid, NULL, S_IFDIR | 0555, NULL);
      if (root != NULL) {
  
      // make the root directory entry.
      root_dentry = d_make_root(root);
      if (root_dentry != NULL) {
        sb-&gt;s_root = root_dentry;
        womfs_create_files (sb, root_dentry);
        printk(KERN_WARNING WOMFS_NAME " setup complete\n");
      } else {
        retval = -ENOMEM;
      }
    } else {
      retval = -ENOMEM;
    }
  
    if (retval != 0) {
      // clean up our inode and dirent
      if (root != NULL) {
        iput(root);
      }
      if (root_dentry != NULL) {
        dput(root_dentry);
      }
    }
  
      return retval;
  }
  
  
  /*
   * Stuff to pass in when registering the filesystem.
   */
  struct dentry *womfs_mount(struct file_system_type *fst,
          int flags, const char *devname, void *data)
  {
      return mount_nodev(fst, flags, data, womfs_fill_super);
  }
  
  
  static struct file_system_type womfs_type = {
      .owner    = THIS_MODULE,
      .name         = WOMFS_NAME,
      .mount    = womfs_mount,
      .kill_sb    = kill_litter_super,
  };
  
  
  /*
   * Get things set up.
   */
  static int __init womfs_init(void)
  {
      return register_filesystem(&amp;womfs_type);
  }
  
  static void __exit womfs_exit(void)
  {
      unregister_filesystem(&amp;womfs_type);
  }
  
  module_init(womfs_init);
  module_exit(womfs_exit);
  </code></pre>
  
  <p>When I load the module and mount my filesystem, I see the following lines in my syslog:</p>
  
  <pre><code>Apr 17 11:43:59 felix kernel: [ 7024.360872] womfs data
  Apr 17 11:43:59 felix kernel: [ 7024.360873] womfs data/keygen
  Apr 17 11:43:59 felix kernel: [ 7024.360874] womfs data/encrypt
  Apr 17 11:43:59 felix kernel: [ 7024.360875] womfs setup complete
  </code></pre>
  
  <p>The <code>ls</code> command shows my mounted file system:</p>
  
  <pre><code>cholmes@felix:~/leardev/womfs$ ls -l
  total 668
  -rw-rw-r-- 1 cholmes cholmes    317 Apr 12 10:38 Makefile
  -rw-rw-r-- 1 cholmes cholmes     46 Apr 17 11:28 modules.order
  -rw-rw-r-- 1 cholmes cholmes      0 Apr 16 15:50 Module.symvers
  -rw-rw-r-- 1 cholmes cholmes   6860 Apr 12 11:39 README.md
      ... etc ...
  dr-xr-xr-x 2 root    root         0 Apr 17 11:28 mountedfs
  </code></pre>
  
  <p>But listing the contents of <code>mountedfs</code> is a disaster:</p>
  
  <pre><code>cholmes@felix:~/leardev/wombat$ ls -l mountedfs
  ls: cannot access 'mountedfs/data': No such file or directory
  total 0
  d????????? ? ? ? ?            ? data
  </code></pre>
  
  <p>It seems obvious I'm forgetting something pretty simple.  It just isn't obvious to <strong>me</strong> yet.</p>
  
  <h2>Update 1</h2>
  
  <p>The <code>data</code> directory uses <code>simple_dir_inode_operations</code> and <code>simple_dir_operations</code> pointers from <code>libfs</code>.</p>
  
  <p>The two regular files have very minimal ops structures, but I'll post them after I remove all the truly interesting bits ;-)</p>
  
  <p>An <code>strace ls -l mountedfs</code> command displays:</p>
  
  <pre><code>... all the usual linking to run ls ...
  open("mountedfs/", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
  fstat(3, {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
  getdents(3, /* 3 entries */, 32768)     = 72
  lstat("mountedfs/data", 0xaa01a0)       = -1 ENOENT (No such file or directory)
  open("/usr/share/locale/en_US/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
  open("/usr/share/locale/en/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
  ... the rest is all about printing the error message ...
  </code></pre>
  
  <h2>Update 2</h2>
  
  <p>The missing <code>getattr()</code> hook doesn't seem to be the problem.  VFS provides a default implementation that copies attribute information from the inode, so you don't really need to implement <code>getattr()</code> unless you have to deal with syncing to disk or similar issues.</p>
  
  <p>What I discovered was that my getattr() methods aren't even being called for subdirectories.  Here's the new bits of my implementation:</p>
  
  <pre><code>// Implementation borrowed from fs/stat.c:vsf_getattr_nosec()
  int womfs_getattr(const struct path *path, struct kstat *stat, u32 request_mask,
      unsigned int query_flags) {
          struct inode *inode;
  
          inode = d_inode(path-&gt;dentry);
          printk(KERN_WARNING WOMFS_NAME " womfs_getattr(%pd4) --&gt; %p\n", path-&gt;dentry, inode);
  
          memset(stat, 0, sizeof(*stat));
          stat-&gt;result_mask |= STATX_BASIC_STATS;
          if(inode != NULL)
              generic_fillattr(inode, stat); // fs/stat.c
          return 0;
  }
  
  const struct inode_operations womfs_dir_inode_operations = {
      .lookup     = simple_lookup,
      .getattr  = womfs_getattr,
  };
  
  const struct inode_operations womfs_inode_operations = {
      .getattr = womfs_getattr,
  };
  </code></pre>
  
  <p>I add these ops to the inodes in the expected way.  When I mount my file system I get <code>printk()</code> messages as expected:</p>
  
  <pre><code>Apr 18 14:19:21 felix kernel: [20777.116214] womfs /data
  Apr 18 14:19:21 felix kernel: [20777.116216] womfs /data/keygen
  Apr 18 14:19:21 felix kernel: [20777.116217] womfs /data/encrypt
  Apr 18 14:19:21 felix kernel: [20777.116218] womfs setup complete
  </code></pre>
  
  <p>And when I do an <code>ls</code> on the filesystem I get the same error message as before, plus some syslog messages telling me that <code>womfs_getattr()</code> was invoked.</p>
  
  <pre><code>Apr 18 14:19:27 felix kernel: [20782.880473] womfs womfs_getattr(/) --&gt; ffff9e957c902960
  Apr 18 14:19:27 felix kernel: [20782.880696] womfs womfs_getattr(/) --&gt; ffff9e957c902960
  </code></pre>
  
  <p>So only the root of the filesystem is being stat'ed.  It seems like my inode isn't being properly added to the dentry.  </p>
  
  <p>After reading more examples, I switched out my call to <code>d_add()</code> with <code>d_instantiate()</code> instead.  Now the error messsage is gone, but the file system shows as being empty:</p>
  
  <pre><code>cholmes@felix:~/leardev/wombat$ ls -l mountedfs/
  total 0
  </code></pre>
  
  <p>And now my syslog shows 5 accesses:</p>
  
  <pre><code> Apr 18 14:45:06 felix kernel: [22321.799115] womfs womfs_getattr(/) --&gt; ffff9e95f95a04b0
   Apr 18 14:45:06 felix kernel: [22321.800553] womfs womfs_getattr(/) --&gt; ffff9e95f95a04b0
   Apr 18 14:45:06 felix kernel: [22321.800554] womfs womfs_getattr(/) --&gt; ffff9e95f95a04b0
   Apr 18 14:45:06 felix kernel: [22322.406112] womfs womfs_getattr(/) --&gt; ffff9e95f95a04b0
   Apr 18 14:45:06 felix kernel: [22322.406341] womfs womfs_getattr(/) --&gt; ffff9e95f95a04b0
  </code></pre>
  
  <p>And strace says:</p>
  
  <pre><code>open("mountedfs/", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
  fstat(3, {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
  getdents(3, /* 2 entries */, 32768)     = 48
  getdents(3, /* 0 entries */, 32768)     = 0
  close(3)                                = 0
  </code></pre>
  
  <p>At this point I'm completely confused and just trying semi-random things to see what will happen.</p>