【发布时间】:2018-04-19 06:41:11
【问题描述】:
总结
我正在为 Linux 内核 4.13 创建一个伪文件系统,但我的目录无法使用 ls 列出。我不断收到消息:
ls: cannot access 'mountedfs/data': No such file or directory.
详情
super_block 和根dentry 结构的创建和注册进展顺利。我可以毫无问题地挂载文件系统,但我无法列出文件系统的内容。当我尝试时,我收到错误“没有这样的文件或目录”。
创建super_block 和根dentry 后,我调用womfs_create_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);
当我加载模块并挂载我的文件系统时,我在 syslog 中看到以下行:
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
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 目录使用来自libfs 的simple_dir_inode_operations 和simple_dir_operations 指针。
这两个常规文件的操作结构非常少,但我会在删除所有真正有趣的部分后发布它们;-)
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() 钩子似乎不是问题所在。 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_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
strace 说:
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
此时我完全感到困惑,只是尝试半随机的事情来看看会发生什么。
【问题讨论】:
-
你的操作是什么(带有函数指针的数组)?以目前的形式,这不容易回答,因为我们无法重现您的设置并得到相同的错误。您可以从
strace ls -l开始获取失败的系统调用的名称和确切的错误代码,然后检查 vfs 和 fs 中的相应操作。一些内核级跟踪(ftrace / trace-cmd)可能会有所帮助。使用 readdir 检查此示例:github.com/krinkinmu/aufs/blob/master/kern/dir.cconst struct file_operations aufs_dir_ops = ... .iterate = aufs_readdir,或 geocities.ws/ravikiran_uvs/articles/rkfs.html forreaddir文件操作 -
ls能够获取名称data,但不能获取有关目录 inode。你有没有实现任何方法来填充struct kstat?像fuse_fillattrelixir.bootlin.com/linux/v3.18/source/fs/fuse/dir.c#L891 /fuse_do_getattr间接引用自struct inode_operations ... .getattr = fuse_getattr, -
请为“
$ ls -l mountedfs/ total 0”的情况添加新的strace日志 -
在我使用 d_instantiate 的情况下,
ls没有任何错误。 strace的输出已经修复(我之前加错输出了)
标签: linux-kernel linux-device-driver