Linux kernel Linux伪目录触发错误";没有这样的文件或目录; 总结
我正在为Linux内核4.13创建一个伪文件系统,但是我的目录不能用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()来填
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,
或forreaddir
文件opls
能够获取名称数据
,但无法获取有关目录索引节点的统计信息(、大小、日期、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);