仮想ファイルシステムサンプル
ネタ元のファイルシステムに読み込み、書き込みの処理を追加しました。
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/dcache.h>
#include <linux/string.h>
#include <linux/pagemap.h>
#include <asm/uaccess.h> /* for copy_to_user */
// ファイル名
static char *dir_entries[] = { "a", "b", "c", "d", "e", };
// ファイルの中身
static char dir_msg[5][20];
static struct inode *my_vfs_get_inode(struct super_block *sb, unsigned int ino);
static unsigned long my_vfs_internal_lookup(const char *name, size_t name_sz)
{
int i;
for (i = 0; i < sizeof(dir_entries) / sizeof(*dir_entries); i++) {
if (0 == strncmp(dir_entries[i], name, name_sz))
return i + 100;
}
return 0;
}
static int my_vfs_dir_readdir(struct file *file, void *dirent, filldir_t filldir)
{
unsigned int i = file->f_pos;
while (i < 2 + sizeof(dir_entries) / sizeof(*dir_entries)) {
if (i == 0) {
/* "." の処理 */
if (filldir(dirent, ".", sizeof(".") - 1, i, 1, DT_DIR))
break;
} else if (i == 1) {
/* ".." の処理 */
if (filldir(dirent, "..", sizeof("..") - 1, i,
parent_ino(file->f_path.dentry), DT_DIR))
break;
} else {
/* それ以外 */
if (filldir(dirent, dir_entries[i - 2], strlen(dir_entries[i - 2]),
i, (unsigned long)dir_entries[i - 2], DT_REG))
break;
}
++i;
}
file->f_pos = i;
return 0;
}
// 本関数が無いとumountでセグメントエラー
static int my_vfs_dir_dentry_delete(struct dentry *dentry)
{
return 1;
}
static struct dentry_operations my_vfs_dir_dentry_operations = {
.d_delete = my_vfs_dir_dentry_delete
};
static struct dentry *my_vfs_dir_lookup(struct inode *inode, struct dentry *entry, struct nameidata *nd)
{
(void)nd; /* unused */
if (entry->d_name.len > NAME_MAX)
return ERR_PTR(-ENAMETOOLONG);
{
unsigned long file_inode_n;
struct inode *file_inode;
file_inode_n = my_vfs_internal_lookup(entry->d_name.name,
entry->d_name.len);
if (!file_inode_n)
return ERR_PTR(-ENOENT);
file_inode = my_vfs_get_inode(inode->i_sb, file_inode_n);
if (!file_inode)
return ERR_PTR(-EINVAL);
entry->d_op = &my_vfs_dir_dentry_operations;
d_add(entry, file_inode);
}
return NULL;
}
static const struct file_operations my_vfs_dir_ops = {
.readdir = my_vfs_dir_readdir,
};
static const struct inode_operations my_vfs_dir_inode_ops = {
.lookup = my_vfs_dir_lookup,
};
// 通常countはページサイズ? 4096
static ssize_t my_vfs_file_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
ssize_t nbytes;
int msglen;
int i;
for (i = 0; i < 5; i++) {
if (!strcmp(file->f_path.dentry->d_name.name, dir_entries[i])) {
msglen = strlen(dir_msg[i]);
if (*ppos >= msglen)
return 0;
nbytes = ((loff_t)(msglen) < *ppos + count ? msglen:
*ppos + count) - *ppos;
if (copy_to_user(buf, dir_msg[i] + *ppos, nbytes))
return -EFAULT;
*ppos += nbytes;
break;
}
}
return nbytes;
}
static ssize_t my_vfs_file_write(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
ssize_t nbytes = 0;
int msglen;
int i;
for (i = 0; i < 5; i++) {
if (!strcmp(file->f_path.dentry->d_name.name, dir_entries[i])) {
if (copy_from_user(dir_msg[i], buf, count))
return -EFAULT;
dir_msg[i][count] = 0;
*ppos += count;
break;
}
}
return count;
}
// これが無いとviで書き込みできなかった。
static int my_vfs_file_fsync(struct file *file, struct dentry *dentry, int datasync)
{
return 0;
}
static const struct file_operations my_vfs_file_ops = {
.read = my_vfs_file_read,
.write = my_vfs_file_write,
.fsync = my_vfs_file_fsync
};
static const struct inode_operations my_vfs_file_inode_ops = {
};
static struct inode *my_vfs_get_root(struct super_block *sb)
{
return my_vfs_get_inode(sb, 1);
}
static const struct super_operations my_vfs_ops = {
.statfs = simple_statfs,
};
static int my_vfs_fill_super(struct super_block *sb, void *data, int silent)
{
sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
sb->s_magic = 0x484f4745; /* 'HOGE' */
sb->s_op = &my_vfs_ops;
// sb->s_flags |= MS_RDONLY;
{
struct inode *inode = my_vfs_get_root(sb);
struct dentry *root = 0;
if (!inode)
return -ENOMEM;
root = d_alloc_root(inode);
if (!root) {
iput(inode);
return -ENOMEM;
}
sb->s_root = root;
}
return 0;
}
static struct inode *my_vfs_get_inode(struct super_block *sb, unsigned int ino)
{
struct inode *retval;
retval = iget_locked(sb, ino);
if (!retval)
return 0;
retval->i_uid = 0;
retval->i_gid = 0;
retval->i_mode = 0666;
if (ino == 1) {
retval->i_fop = &my_vfs_dir_ops;
retval->i_op = &my_vfs_dir_inode_ops;
retval->i_mode |= 0111 | S_IFDIR;
} else {
retval->i_fop = &my_vfs_file_ops;
retval->i_op = &my_vfs_file_inode_ops;
retval->i_mode |= S_IFREG;
retval->i_size = 10; // ファイルサイズは適当 ls -l で10で表示される
}
unlock_new_inode(retval);
return retval;
}
static int my_vfs_get_sb(struct file_system_type *self,
int flags, const char *dev_name, void *data, struct vfsmount *mnt)
{
return get_sb_nodev(self, flags, data, my_vfs_fill_super, mnt);
}
static struct file_system_type my_vfs_type = {
.name = "my_vfs",
.get_sb = my_vfs_get_sb,
.kill_sb = kill_litter_super,
.owner = THIS_MODULE
};
static int __init my_vfs_module_init(void)
{
// 初期ファイルデータ a->1000,b->1001,c->10002....
int i;
for (i = 0; i < 5; i++) {
sprintf(dir_msg[i], "%d", i + 1000);
}
register_filesystem(&my_vfs_type);
return 0;
}
static void __exit my_vfs_module_exit(void)
{
unregister_filesystem(&my_vfs_type);
}
module_init(my_vfs_module_init)
module_exit(my_vfs_module_exit)