/** * 1インデント=4スペースです。ごめんなさいごめんなさい */ #include #include #include #include #include #include #include /* for copy_to_user */ static struct inode *my_vfs_get_inode(struct super_block *sb, unsigned int ino); /** * でっち上げたディレクトリエントリ */ static const char *dir_entries[] = { "a", "b", "c", "d", "e", }; /** * このモジュールで内部的に使うだけの関数。 * dir_entries に、name で指定されたファイルがあるかどうかを調べて、あった場合は inode 番号を返す。 * * @param name 探すファイル名 * @param name_sz 探すファイル名の長さ * @return inode 番号 */ 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)) /* とりあえず文字列へのポインタ == inode 番号ということにしてしまう */ return (unsigned long)dir_entries[i]; } return 0; } /** * ディレクトリノード用のコールバック関数。read(2) / pread(2) に対応する処理。 * @param file ディレクトリを表す struct file へのポインタ * @param buf [user, out] 読み込み先であるユーザ空間のバッファへのポインタ * @param buf_sz バッファのサイズ * @param pos [in/out] ファイルポインタ */ static ssize_t my_vfs_dir_read(struct file *file, char __user *buf, size_t buf_sz, loff_t *pos) { /* ディレクトリに対する read 操作は無効 */ return -EISDIR; } /** * ディレクトリノード用のコールバック関数。readdir(3) に対応する処理。 * @param file ディレクトリを表す * @param dirent [out] ディレクトリエントリを格納するバッファへのポインタ * @param filldir 指定されたバッファにデータを追加するために呼び出し側から * 提供される実装のコールバック関数ポインタ */ 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; } /** * ディレクトリノード用のコールバック関数。lseek(2) に対応する処理。 * @param file ディレクトリを表す * @param off オフセット * @param origin SEEK_SET / SEEK_CUR / SEEK_END のいずれか */ static loff_t my_vfs_dir_lseek(struct file *file, loff_t off, int origin) { loff_t new_off = file->f_pos; switch (origin) { case SEEK_SET: new_off = off; break; case SEEK_CUR: new_off += off; break; default: return -EINVAL; } if (new_off > sizeof(dir_entries) / sizeof(*dir_entries)) { file->f_pos = sizeof(dir_entries) / sizeof(*dir_entries); } file->f_pos = new_off; return new_off; } /** * ディレクトリノード用のコールバック関数。open(2) か creat(2) に対応する処理。 * @param inode ディレクトリのノードを表す struct inode へのポインタ * @param dentry 作成されるファイルを表す struct dentry へのポインタ * @param mode ファイルモード * @param nd [TBD} */ static int my_vfs_dir_create(struct inode *inode, struct dentry *entry, int mode, struct nameidata *nd) { return -ENOSPC; } /** * ディレクトリノード用のコールバック関数。stat(2) に対応する処理。 * @param mnt マウントポイントを表す struct vfsmount へのポインタ * @param dentry 対象のファイルを表す struct dentry へのポインタ * @param mode [out] ファイルの属性を返す struct kstat へのポインタ */ static int my_vfs_dir_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { generic_fillattr(dentry->d_inode, stat); stat->nlink = 1; return 0; } /** * dentry 用のコールバック関数。 * @param dentry 対象の dentry を表すポインタ。 */ static int my_vfs_dir_dentry_delete(struct dentry *dentry) { return 1; } /* XXX: why not const'ified? */ static struct dentry_operations my_vfs_dir_dentry_operations = { .d_delete = my_vfs_dir_dentry_delete }; /** * ディレクトリノード用のコールバック関数。ディレクトリから指定されたエントリを探す。 * @param inode 検索対象のディレクトリノードを表す struct inode へのポインタ * @param entry 検索するエントリの名前 * @param nd [TBD] */ 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 int my_vfs_dir_symlink(struct inode *inode, struct dentry *entry, const char *name) { return -ENOSPC; } static int my_vfs_dir_mknod(struct inode *inode, struct dentry *entry, int mode, dev_t dev) { return -ENOSPC; } static int my_vfs_dir_mkdir(struct inode *inode, struct dentry *entry, int mode) { return -ENOSPC; } static int my_vfs_dir_rmdir(struct inode *inode, struct dentry *entry) { return -EPERM; } static int my_vfs_dir_link(struct dentry *old_entry, struct inode *inode, struct dentry *entry) { return -ENOSPC; } static int my_vfs_dir_unlink(struct inode *inode, struct dentry *entry) { return my_vfs_internal_lookup(entry->d_name.name, entry->d_name.len) ? -EPERM: -ENOENT; } static int my_vfs_dir_rename(struct inode *old_inode, struct dentry *old_entry, struct inode *new_inode, struct dentry *new_entry) { if (my_vfs_internal_lookup(old_entry->d_name.name, old_entry->d_name.len)) return -ENOENT; if (my_vfs_internal_lookup(new_entry->d_name.name, new_entry->d_name.len)) return -EEXIST; return -ENOENT; } static int my_vfs_dir_close(struct inode *inode, struct file *file) { return 0; } static int my_vfs_dir_open(struct inode *inode, struct file *file) { return 0; } static const struct file_operations my_vfs_dir_ops = { .llseek = my_vfs_dir_lseek, .read = my_vfs_dir_read, .readdir = my_vfs_dir_readdir, .release = my_vfs_dir_close, .open = my_vfs_dir_open }; static const struct inode_operations my_vfs_dir_inode_ops = { .create = my_vfs_dir_create, .getattr = my_vfs_dir_getattr, .lookup = my_vfs_dir_lookup, .mknod = my_vfs_dir_mknod, .link = my_vfs_dir_link, .unlink = my_vfs_dir_unlink, .symlink = my_vfs_dir_symlink, .rename = my_vfs_dir_rename, .mkdir = my_vfs_dir_mkdir, .rmdir = my_vfs_dir_rmdir }; static const char str[] = "quick brown fox jumps over the lazy dog"; static ssize_t my_vfs_file_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { if (*ppos >= sizeof(str) - 1) return 0; { const ssize_t nbytes = ((loff_t)(sizeof(str) - 1) < *ppos + count ? sizeof(str) - 1: *ppos + count) - *ppos; if (copy_to_user(buf, str + *ppos, nbytes)) return -EFAULT; *ppos += nbytes; return nbytes; } } static loff_t my_vfs_file_lseek(struct file *file, loff_t off, int origin) { loff_t new_off = file->f_pos; switch (origin) { case SEEK_SET: new_off = off; break; case SEEK_CUR: new_off += off; break; default: return -EINVAL; } if (new_off > sizeof(str) - 1) { file->f_pos = sizeof(str) - 1; } file->f_pos = new_off; return new_off; } static int my_vfs_file_release(struct inode *inode, struct file *file) { return 0; } static int my_vfs_file_open(struct inode *inode, struct file *file) { return 0; } static const struct file_operations my_vfs_file_ops = { .read = my_vfs_file_read, .llseek = my_vfs_file_lseek, .release = my_vfs_file_release, .open = my_vfs_file_open }; static const struct inode_operations my_vfs_file_inode_ops = {}; 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 = 0644; 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 = sizeof(str) - 1; } unlock_new_inode(retval); return retval; } static struct inode *my_vfs_get_root(struct super_block *sb) { return my_vfs_get_inode(sb, 1); } static void my_vfs_put_super(struct super_block *sb) { /* do nothing for now */ } static const struct super_operations my_vfs_ops = { .statfs = simple_statfs, .put_super = my_vfs_put_super }; 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 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) { 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) MODULE_LICENSE("GPL");