Linux kernel Linux伪目录触发错误";没有这样的文件或目录; 总结

Linux kernel Linux伪目录触发错误";没有这样的文件或目录; 总结,linux-kernel,linux-device-driver,Linux Kernel,Linux Device Driver,我正在为Linux内核4.13创建一个伪文件系统,但是我的目录不能用ls列出。我一直得到这样的信息: ls: cannot access 'mountedfs/data': No such file or directory. 细节 super_块和根dentry结构的创建和注册进展顺利。我可以毫不费力地挂载文件系统,但我不能列出文件系统的内容。当我尝试时,我得到错误“没有这样的文件或目录” 在创建super\u块和根dentry之后,我调用womfs\u create\u files()来填

我正在为Linux内核4.13创建一个伪文件系统,但是我的目录不能用
ls
列出。我一直得到这样的信息:

ls: cannot access 'mountedfs/data': No such file or directory.
细节
super_块
和根
dentry
结构的创建和注册进展顺利。我可以毫不费力地挂载文件系统,但我不能列出文件系统的内容。当我尝试时,我得到错误“没有这样的文件或目录”

在创建
super\u块
和根
dentry
之后,我调用
womfs\u create\u files()
来填充树。下面是模块的完整源代码。正如您所看到的,我甚至还没有考虑过文件操作。我仍然坚持使用inode操作

#include <linux/kernel.h>
#include <linux/fs.h>       /* libfs and most file-related headers. */
#include <linux/dcache.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pagemap.h>  /* PAGE_SIZE */
#include <linux/atomic.h>
#include <linux/time.h>
#include <linux/string.h>
#include <linux/sched.h>
#include <linux/parser.h>
#include <linux/magic.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#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: "<key>/pub_key" lets you retrieve the public key;
 *  "<key>/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->dentry);
        printk(KERN_WARNING WOMFS_NAME " womfs_getattr(%pd4) --> %p\n", path->dentry, inode);

        memset(stat, 0, sizeof(*stat));
        stat->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->i_mode = mode;
        ret->i_uid = KUIDT_INIT(0);
        ret->i_gid = group;
        ret->i_blocks = 0;
        ret->i_atime = now;
        ret->i_mtime = now;
        ret->i_ctime = now;

    if (mode & S_IFDIR) {
      ret->i_op = &womfs_dir_inode_operations;
      ret->i_fop = (fops == NULL) ? &simple_dir_operations : fops ;
      inc_nlink(ret);
    } else {
            ret->i_op = &womfs_inode_operations;
      ret->i_fop = fops;
    }

    ret->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, &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->type = wombat_key_kek;
  ki->slot = 0;
  strcpy(ki->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->name);
    if (subdir != NULL) {
      printk(KERN_WARNING WOMFS_NAME " %pd4", subdir);
      switch(ki->type) {
        case wombat_key_kek:
          /* create the data/keygen file */
          node = womfs_make_inode(sb, ki, group, NULL, S_IFREG | 0444, &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, &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->s_blocksize = PAGE_SIZE;
    sb->s_blocksize_bits = PAGE_SHIFT;
    sb->s_magic = WOMFS_MAGIC;
    sb->s_op = &womfs_s_ops;
  sb->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->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(&womfs_type);
}

static void __exit womfs_exit(void)
{
    unregister_filesystem(&womfs_type);
}

module_init(womfs_init);
module_exit(womfs_exit);
ls
命令显示我装载的文件系统:

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
但列出
mountedfs
的内容是一场灾难:

cholmes@felix:~/leardev/wombat$ ls -l mountedfs
ls: cannot access 'mountedfs/data': No such file or directory
total 0
d????????? ? ? ? ?            ? data
很明显,我忘记了一些很简单的事情。这对我来说还不明显

更新1
data
目录使用
simple\u dir\u inode\u操作
simple\u dir\u操作
来自
libfs
的指针

这两个常规文件具有非常小的ops结构,但我将在删除所有真正有趣的位之后发布它们;-)

strace ls-l mountedfs命令显示:

... 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 ...
更新2 缺少的
getattr()
hook似乎不是问题所在。VFS提供了一个默认实现,可以从inode复制属性信息,因此您实际上不需要实现
getattr()
,除非您必须处理同步到磁盘或类似问题

我发现我的getattr()方法甚至没有被子目录调用。以下是我的实现的新内容:

// 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->dentry);
        printk(KERN_WARNING WOMFS_NAME " womfs_getattr(%pd4) --> %p\n", path->dentry, inode);

        memset(stat, 0, sizeof(*stat));
        stat->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,
};
我以预期的方式将这些操作添加到inode。当我挂载文件系统时,会收到预期的
printk()
消息:

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
当我在文件系统上执行
ls
时,我会得到与以前相同的错误消息,再加上一些系统日志消息,告诉我调用了
womfs\u getattr()

Apr 18 14:19:27 felix kernel: [20782.880473] womfs womfs_getattr(/) --> ffff9e957c902960
Apr 18 14:19:27 felix kernel: [20782.880696] womfs womfs_getattr(/) --> ffff9e957c902960
所以只有文件系统的根被统计。看起来我的inode没有被正确地添加到dentry中

在阅读了更多的示例之后,我将调用切换到
d_add()
,而改为
d_instantiate()
。现在错误消息已消失,但文件系统显示为空:

cholmes@felix:~/leardev/wombat$ ls -l mountedfs/
total 0
现在,我的系统日志显示了5次访问:

 Apr 18 14:45:06 felix kernel: [22321.799115] womfs womfs_getattr(/) --> ffff9e95f95a04b0
 Apr 18 14:45:06 felix kernel: [22321.800553] womfs womfs_getattr(/) --> ffff9e95f95a04b0
 Apr 18 14:45:06 felix kernel: [22321.800554] womfs womfs_getattr(/) --> ffff9e95f95a04b0
 Apr 18 14:45:06 felix kernel: [22322.406112] womfs womfs_getattr(/) --> ffff9e95f95a04b0
 Apr 18 14:45:06 felix kernel: [22322.406341] womfs womfs_getattr(/) --> ffff9e95f95a04b0
斯特拉斯说:

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
在这一点上,我完全困惑,只是尝试半随机的事情,看看会发生什么

ls
这样的输出显示子目录名,但不显示文件模式、uid、gid或日期/时间。这可能是在有一些正确的syscall返回目录名时发生的;另一个系统调用在哪里获取关于inode的附加信息,但失败了

您应该检查
strace ls-l
输出,以获得准确的系统调用名称和返回值,但我认为有/要读取
数据
,并且失败了(stat64,fstat,lstat,…)

在代码中,您为以下片段中的inode(目录类型)设置了fops:

   if (mode & S_IFDIR) {
     ret->i_op = &simple_dir_inode_operations;
     ret->i_fop = &simple_dir_operations;
但是简单的文件/inode操作没有定义任何方法来提供stat实现:

// 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->dentry);
        printk(KERN_WARNING WOMFS_NAME " womfs_getattr(%pd4) --> %p\n", path->dentry, inode);

        memset(stat, 0, sizeof(*stat));
        stat->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,
};

我认为,
struct inode_operations
getattr
字段用于实现
stat
。如果没有实现(使用简单的inode操作),stat syscall无法将任何有用的数据填充到
struct kstat

getattr的一些示例:在

文件:

ls
来源证明stat返回码无效导致
ls:无法访问
消息

/* If true, the file listing format requires that stat be called on
   each file.  */
...
format_needs_stat =  ... || format == long_format || ...
...
 dereference = ... DEREF_NEVER;
...
gobble_file (char const *name, enum filetype type, ino_t inode,
...
if ( ... || format_needs_stat  || ...) {
...
        default: /* DEREF_NEVER */
          err = lstat (full_name, &f->stat);
          do_deref = false;
          break;
        }

      if (err != 0)
        {
          /* Failure to stat a command line argument leads to
             an exit status of 2.  For other files, stat failure
             provokes an exit status of 1.  */
          file_failure (command_line_arg,
                        _("cannot access %s"), full_name);

什么是ops(带函数指针的数组)?在当前的形式下,这是不容易回答的,因为我们无法复制您的设置并得到相同的错误。您可以从
strace ls-l
开始获取失败的系统调用的名称和准确的错误代码,然后在vfs和fs中检查相应的操作。一些内核级跟踪(ftrace/trace cmd)可能会有所帮助。使用readdir检查此示例:
const struct file_operations aufs_dir_ops=。iterate=aufs_readdir,
或for
readdir
文件op
ls
能够获取名称
数据
,但无法获取有关目录索引节点的统计信息(、大小、日期、uid、gid、模式、a/m/ctime…)。您是否实现了任何方法来填充
struct kstat
?类似于
fuse\u fillattr
/
fuse\u do\u getattr
,它是从
struct inode\u操作中间接引用的。getattr=fuse\u getattr,
请为“
$ls-l mountedfs/total 0”添加新的strace日志。在我使用d\u实例化的情况下,我从
ls
中没有错误。strace输出已经修复(我之前添加了错误的输出)。我不知道我以前是如何设法不找到vfs.txt文档并将其添加到书签的——尤其是它位于我的linux源代码树中!当我把解决方案编码后,我会把它贴出来。不标记为答案,因为这似乎不是问题所在。请参阅问题的更新。还在努力…@slashingweapon,为什么是-15?新的
strace
输出和更新的源在哪里?为什么不使用ftrace/trace cmd(甚至lttng)跟踪器来获取有关调用内容和时间的更多信息?调用getattr并不是什么神奇的事情;它是
ls
执行stat和vfs实现stat(您也可以从fuse示例开始…)我只是将这个答案标记为解决方案,因为getattr()似乎不是问题所在。如果我被证明是错的,我会恢复它。看起来dentry并没有保留inode数据。dentry在那里并且有一个名称,但是inode数据丢失。我已经更新了OP中的代码,使其更加完整。
const struct file_operations simple_dir_operations = {
    .open       = dcache_dir_open,
    .release    = dcache_dir_close,
    .llseek     = dcache_dir_lseek,
    .read       = generic_read_dir,
    .iterate    = dcache_readdir,
    .fsync      = noop_fsync,
};

const struct inode_operations simple_dir_inode_operations = {
    .lookup     = simple_lookup,
};
struct inode_operations

    int (*getattr) (const struct path *, struct kstat *, u32, unsigned int);

  getattr: called by the VFS to get attributes of a file. This method
    is called by stat(2) and related system calls.
/* If true, the file listing format requires that stat be called on
   each file.  */
...
format_needs_stat =  ... || format == long_format || ...
...
 dereference = ... DEREF_NEVER;
...
gobble_file (char const *name, enum filetype type, ino_t inode,
...
if ( ... || format_needs_stat  || ...) {
...
        default: /* DEREF_NEVER */
          err = lstat (full_name, &f->stat);
          do_deref = false;
          break;
        }

      if (err != 0)
        {
          /* Failure to stat a command line argument leads to
             an exit status of 2.  For other files, stat failure
             provokes an exit status of 1.  */
          file_failure (command_line_arg,
                        _("cannot access %s"), full_name);