diff --git a/tools/labs/skels/Kbuild b/tools/labs/skels/Kbuild new file mode 100644 index 00000000000000..0ed2c6be723111 --- /dev/null +++ b/tools/labs/skels/Kbuild @@ -0,0 +1,4 @@ +# autogenerated, do not edit +ccflags-y += -Wno-unused-function -Wno-unused-label -Wno-unused-variable +obj-m += ./filesystems/minfs/kernel/ +obj-m += ./filesystems/myfs/ diff --git a/tools/labs/skels/filesystems/minfs/kernel/Kbuild b/tools/labs/skels/filesystems/minfs/kernel/Kbuild new file mode 100644 index 00000000000000..b243430771727e --- /dev/null +++ b/tools/labs/skels/filesystems/minfs/kernel/Kbuild @@ -0,0 +1,3 @@ +EXTRA_CFLAGS = -Wall -g -Wno-unused + +obj-m = minfs.o diff --git a/tools/labs/skels/filesystems/minfs/kernel/minfs.c b/tools/labs/skels/filesystems/minfs/kernel/minfs.c new file mode 100644 index 00000000000000..2f625254d5ff22 --- /dev/null +++ b/tools/labs/skels/filesystems/minfs/kernel/minfs.c @@ -0,0 +1,616 @@ +/* + * SO2 Lab - Filesystem drivers + * Exercise #2 (dev filesystem) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "minfs.h" + +MODULE_DESCRIPTION("Simple filesystem"); +MODULE_AUTHOR("SO2"); +MODULE_LICENSE("GPL"); + +#define LOG_LEVEL KERN_ALERT + + +struct minfs_sb_info { + __u8 version; + unsigned long imap; + struct buffer_head *sbh; +}; + +struct minfs_inode_info { + __u16 data_block; + struct inode vfs_inode; +}; + +/* declarations of functions that are part of operation structures */ + +static int minfs_readdir(struct file *filp, struct dir_context *ctx); +static struct dentry *minfs_lookup(struct inode *dir, + struct dentry *dentry, unsigned int flags); +static int minfs_create(struct inode *dir, struct dentry *dentry, + umode_t mode, bool excl); + +/* dir and inode operation structures */ + +static const struct file_operations minfs_dir_operations = { + .read = generic_read_dir, + .iterate = minfs_readdir, +}; + +static const struct inode_operations minfs_dir_inode_operations = { + .lookup = minfs_lookup, + /* TODO 7: Use minfs_create as the create function. */ + .create = minfs_create, +}; + +static const struct address_space_operations minfs_aops = { + .readpage = simple_readpage, + .write_begin = simple_write_begin, + .write_end = simple_write_end, +}; + +static const struct file_operations minfs_file_operations = { + .read_iter = generic_file_read_iter, + .write_iter = generic_file_write_iter, + .mmap = generic_file_mmap, + .llseek = generic_file_llseek, +}; + +static const struct inode_operations minfs_file_inode_operations = { + .getattr = simple_getattr, +}; + +static struct inode *minfs_iget(struct super_block *s, unsigned long ino) +{ + struct minfs_inode *mi; + struct buffer_head *bh; + struct inode *inode; + struct minfs_inode_info *mii; + + /* Allocate VFS inode. */ + inode = iget_locked(s, ino); + if (inode == NULL) { + printk(LOG_LEVEL "error aquiring inode\n"); + return ERR_PTR(-ENOMEM); + } + + /* Return inode from cache */ + if (!(inode->i_state & I_NEW)) + return inode; + + /* TODO 4: Read block with inodes. It's the second block on + * the device, i.e. the block with the index 1. This is the index + * to be passed to sb_bread(). + */ + if (!(bh = sb_bread(s, MINFS_INODE_BLOCK))) + goto out_bad_sb; + + /* TODO 4: Get inode with index ino from the block. */ + mi = ((struct minfs_inode *) bh->b_data) + ino; + + /* TODO 4: fill VFS inode */ + inode->i_mode = mi->mode; + i_uid_write(inode, mi->uid); + i_gid_write(inode, mi->gid); + inode->i_size = mi->size; + inode->i_blocks = 0; + inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); + + /* TODO 7: Fill address space operations (inode->i_mapping->a_ops) */ + inode->i_mapping->a_ops = &minfs_aops; + + if (S_ISDIR(inode->i_mode)) { + /* TODO 4: Fill dir inode operations. */ + inode->i_op = &simple_dir_inode_operations; + inode->i_fop = &simple_dir_operations; + + /* TODO 5: Use minfs_dir_inode_operations for i_op + * and minfs_dir_operations for i_fop. */ + inode->i_op = &minfs_dir_inode_operations; + inode->i_fop = &minfs_dir_operations; + + /* TODO 4: Directory inodes start off with i_nlink == 2. + * (use inc_link) */ + inc_nlink(inode); + } + + /* TODO 7: Fill inode and file operations for regular files + * (i_op and i_fop). Use the S_ISREG macro. + */ + if (S_ISREG(inode->i_mode)) { + inode->i_op = &minfs_file_inode_operations; + inode->i_fop = &minfs_file_operations; + } + + /* fill data for mii */ + mii = container_of(inode, struct minfs_inode_info, vfs_inode); + + /* TODO 4: uncomment after the minfs_inode is initialized */ + mii->data_block = mi->data_block; + + /* Free resources. */ + /* TODO 4: uncomment after the buffer_head is initialized */ + brelse(bh); + unlock_new_inode(inode); + + return inode; + +out_bad_sb: + iget_failed(inode); + return NULL; +} + +static int minfs_readdir(struct file *filp, struct dir_context *ctx) +{ + struct buffer_head *bh; + struct minfs_dir_entry *de; + struct minfs_inode_info *mii; + struct inode *inode; + struct super_block *sb; + int over; + int err = 0; + + /* TODO 5: Get inode of directory and container inode. */ + inode = file_inode(filp); + mii = container_of(inode, struct minfs_inode_info, vfs_inode); + + /* TODO 5: Get superblock from inode (i_sb). */ + sb = inode->i_sb; + + /* TODO 5: Read data block for directory inode. */ + bh = sb_bread(sb, mii->data_block); + if (bh == NULL) { + printk(LOG_LEVEL "could not read block\n"); + err = -ENOMEM; + goto out_bad_sb; + } + + for (; ctx->pos < MINFS_NUM_ENTRIES; ctx->pos++) { + /* TODO 5: Data block contains an array of + * "struct minfs_dir_entry". Use `de' for storing. + */ + de = (struct minfs_dir_entry *) bh->b_data + ctx->pos; + + /* TODO 5: Step over empty entries (de->ino == 0). */ + if (de->ino == 0) { + continue; + } + + /* + * Use `over` to store return value of dir_emit and exit + * if required. + */ + over = dir_emit(ctx, de->name, MINFS_NAME_LEN, de->ino, + DT_UNKNOWN); + if (over) { + printk(KERN_DEBUG "Read %s from folder %s, ctx->pos: %lld\n", + de->name, + filp->f_path.dentry->d_name.name, + ctx->pos); + ctx->pos++; + goto done; + } + } + +done: + brelse(bh); +out_bad_sb: + return err; +} + +/* + * Find dentry in parent folder. Return parent folder's data buffer_head. + */ + +static struct minfs_dir_entry *minfs_find_entry(struct dentry *dentry, + struct buffer_head **bhp) +{ + struct buffer_head *bh; + struct inode *dir = dentry->d_parent->d_inode; + struct minfs_inode_info *mii = container_of(dir, + struct minfs_inode_info, vfs_inode); + struct super_block *sb = dir->i_sb; + const char *name = dentry->d_name.name; + struct minfs_dir_entry *final_de = NULL; + struct minfs_dir_entry *de; + int i; + + /* TODO 6: Read parent folder data block (contains dentries). + * Fill bhp with return value. + */ + bh = sb_bread(sb, mii->data_block); + if (bh == NULL) { + printk(LOG_LEVEL "could not read block\n"); + return NULL; + } + *bhp = bh; + + for (i = 0; i < MINFS_NUM_ENTRIES; i++) { + /* TODO 6: Traverse all entries, find entry by name + * Use `de' to traverse. Use `final_de' to store dentry + * found, if existing. + */ + de = ((struct minfs_dir_entry *) bh->b_data) + i; + if (de->ino != 0) { + /* found it */ + if (strcmp(name, de->name) == 0) { + printk(KERN_DEBUG "Found entry %s on position: %zd\n", + name, i); + final_de = de; + break; + } + } + } + + /* bh needs to be released by caller. */ + return final_de; +} + +static struct dentry *minfs_lookup(struct inode *dir, + struct dentry *dentry, unsigned int flags) +{ + /* TODO 6: Comment line. */ + // return simple_lookup(dir, dentry, flags); + + struct super_block *sb = dir->i_sb; + struct minfs_dir_entry *de; + struct buffer_head *bh = NULL; + struct inode *inode = NULL; + + dentry->d_op = sb->s_root->d_op; + + de = minfs_find_entry(dentry, &bh); + if (de != NULL) { + printk(KERN_DEBUG "getting entry: name: %s, ino: %d\n", + de->name, de->ino); + inode = minfs_iget(sb, de->ino); + if (IS_ERR(inode)) + return ERR_CAST(inode); + } + + d_add(dentry, inode); + brelse(bh); + + printk(KERN_DEBUG "looked up dentry %s\n", dentry->d_name.name); + + return NULL; +} + +static struct inode *minfs_alloc_inode(struct super_block *s) +{ + struct minfs_inode_info *mii; + + /* TODO 3: Allocate minfs_inode_info. */ + mii = kzalloc(sizeof(struct minfs_inode_info), GFP_KERNEL); + if (mii == NULL) + return NULL; + + /* TODO 3: init VFS inode in minfs_inode_info */ + inode_init_once(&mii->vfs_inode); + + return &mii->vfs_inode; +} + +static void minfs_destroy_inode(struct inode *inode) +{ + /* TODO 3: free minfs_inode_info */ + kfree(container_of(inode, struct minfs_inode_info, vfs_inode)); +} + +/* + * Create a new VFS inode. Do basic initialization and fill imap. + */ + +static struct inode *minfs_new_inode(struct inode *dir) +{ + struct super_block *sb = dir->i_sb; + struct minfs_sb_info *sbi = sb->s_fs_info; + struct inode *inode; + int idx; + + /* TODO 7: Find first available inode. */ + idx = find_first_zero_bit(&sbi->imap, MINFS_NUM_INODES); + if (idx == MINFS_NUM_INODES) { + printk(LOG_LEVEL "no space left in imap\n"); + return NULL; + } + + /* TODO 7: Mark the inode as used in the bitmap and mark + * the superblock buffer head as dirty. + */ + __test_and_set_bit(idx, &sbi->imap); + mark_buffer_dirty(sbi->sbh); + + /* TODO 7: Call new_inode(), fill inode fields + * and insert inode into inode hash table. + */ + inode = new_inode(sb); + inode->i_uid = current_fsuid(); + inode->i_gid = current_fsgid(); + inode->i_ino = idx; + inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); + inode->i_blocks = 0; + + /* Actual writing to the disk will be done in minfs_write_inode, + * which will be called at a later time. + */ + + return inode; +} + +/* + * Add dentry link on parent inode disk structure. + */ + +static int minfs_add_link(struct dentry *dentry, struct inode *inode) +{ + struct buffer_head *bh; + struct inode *dir; + struct super_block *sb; + struct minfs_inode_info *mii; + struct minfs_dir_entry *de; + int i; + int err = 0; + + /* TODO 7: Get: directory inode (in inode); containing inode (in mii); superblock (in sb). */ + dir = dentry->d_parent->d_inode; + mii = container_of(dir, struct minfs_inode_info, vfs_inode); + sb = dir->i_sb; + + /* TODO 7: Read dir data block (use sb_bread). */ + bh = sb_bread(sb, mii->data_block); + + /* TODO 7: Find first free dentry (de->ino == 0). */ + for (i = 0; i < MINFS_NUM_ENTRIES; i++) { + de = (struct minfs_dir_entry *) bh->b_data + i; + if (de->ino == 0) + break; + } + + if (i == MINFS_NUM_ENTRIES) { + err = -ENOSPC; + goto out; + } + + /* TODO 7: Place new entry in the available slot. Mark buffer_head + * as dirty. */ + de->ino = inode->i_ino; + memcpy(de->name, dentry->d_name.name, MINFS_NAME_LEN); + dir->i_mtime = dir->i_ctime = current_time(inode); + + mark_buffer_dirty(bh); + +out: + brelse(bh); + + return err; +} + +/* + * Create a VFS file inode. Use minfs_file_... operations. + */ + +static int minfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, + bool excl) +{ + struct inode *inode; + struct minfs_inode_info *mii; + int err; + + inode = minfs_new_inode(dir); + if (inode == NULL) { + printk(LOG_LEVEL "error allocating new inode\n"); + err = -ENOMEM; + goto err_new_inode; + } + + inode->i_mode = mode; + inode->i_op = &minfs_file_inode_operations; + inode->i_fop = &minfs_file_operations; + mii = container_of(inode, struct minfs_inode_info, vfs_inode); + mii->data_block = MINFS_FIRST_DATA_BLOCK + inode->i_ino; + + err = minfs_add_link(dentry, inode); + if (err != 0) + goto err_add_link; + + d_instantiate(dentry, inode); + mark_inode_dirty(inode); + + printk(KERN_DEBUG "new file inode created (ino = %lu)\n", + inode->i_ino); + + return 0; + +err_add_link: + inode_dec_link_count(inode); + iput(inode); +err_new_inode: + return err; +} + +/* + * Write VFS inode contents to disk inode. + */ + +static int minfs_write_inode(struct inode *inode, + struct writeback_control *wbc) +{ + struct super_block *sb = inode->i_sb; + struct minfs_inode *mi; + struct minfs_inode_info *mii = container_of(inode, + struct minfs_inode_info, vfs_inode); + struct buffer_head *bh; + int err = 0; + + bh = sb_bread(sb, MINFS_INODE_BLOCK); + if (bh == NULL) { + printk(LOG_LEVEL "could not read block\n"); + err = -ENOMEM; + goto out; + } + + mi = (struct minfs_inode *) bh->b_data + inode->i_ino; + + /* fill disk inode */ + mi->mode = inode->i_mode; + mi->uid = i_uid_read(inode); + mi->gid = i_gid_read(inode); + mi->size = inode->i_size; + mi->data_block = mii->data_block; + + printk(KERN_DEBUG "mode is %05o; data_block is %d\n", mi->mode, + mii->data_block); + + mark_buffer_dirty(bh); + brelse(bh); + + printk(KERN_DEBUG "wrote inode %lu\n", inode->i_ino); + +out: + return err; +} + +static void minfs_put_super(struct super_block *sb) +{ + struct minfs_sb_info *sbi = sb->s_fs_info; + + /* Free superblock buffer head. */ + mark_buffer_dirty(sbi->sbh); + brelse(sbi->sbh); + + printk(KERN_DEBUG "released superblock resources\n"); +} + +static const struct super_operations minfs_ops = { + .statfs = simple_statfs, + .put_super = minfs_put_super, + /* TODO 4: add alloc and destroy inode functions */ + .alloc_inode = minfs_alloc_inode, + .destroy_inode = minfs_destroy_inode, + /* TODO 7: = set write_inode function. */ + .write_inode = minfs_write_inode, +}; + +static int minfs_fill_super(struct super_block *s, void *data, int silent) +{ + struct minfs_sb_info *sbi; + struct minfs_super_block *ms; + struct inode *root_inode; + struct dentry *root_dentry; + struct buffer_head *bh; + int ret = -EINVAL; + + sbi = kzalloc(sizeof(struct minfs_sb_info), GFP_KERNEL); + if (!sbi) + return -ENOMEM; + s->s_fs_info = sbi; + + /* Set block size for superblock. */ + if (!sb_set_blocksize(s, MINFS_BLOCK_SIZE)) + goto out_bad_blocksize; + + /* TODO 2: Read block with superblock. It's the first block on + * the device, i.e. the block with the index 0. This is the index + * to be passed to sb_bread(). + */ + bh = sb_bread(s, MINFS_SUPER_BLOCK); + if (bh == NULL) + goto out_bad_sb; + + /* TODO 2: interpret read data as minfs_super_block */ + ms = (struct minfs_super_block *) bh->b_data; + + /* TODO 2: check magic number with value defined in minfs.h. jump to out_bad_magic if not suitable */ + if (ms->magic != MINFS_MAGIC) + goto out_bad_magic; + + /* TODO 2: fill super_block with magic_number, super_operations */ + s->s_magic = MINFS_MAGIC; + s->s_op = &minfs_ops; + + /* TODO 2: Fill sbi with rest of information from disk superblock + * (i.e. version). + */ + sbi->version = ms->version; + sbi->imap = ms->imap; + + /* allocate root inode and root dentry */ + /* TODO 2: use myfs_get_inode instead of minfs_iget */ + root_inode = minfs_iget(s, MINFS_ROOT_INODE); + if (!root_inode) + goto out_bad_inode; + + root_dentry = d_make_root(root_inode); + if (!root_dentry) + goto out_iput; + s->s_root = root_dentry; + + /* Store superblock buffer_head for further use. */ + sbi->sbh = bh; + + return 0; + +out_iput: + iput(root_inode); +out_bad_inode: + printk(LOG_LEVEL "bad inode\n"); +out_bad_magic: + printk(LOG_LEVEL "bad magic number\n"); + brelse(bh); +out_bad_sb: + printk(LOG_LEVEL "error reading buffer_head\n"); +out_bad_blocksize: + printk(LOG_LEVEL "bad block size\n"); + s->s_fs_info = NULL; + kfree(sbi); + return ret; +} + +static struct dentry *minfs_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) +{ + /* TODO 1: call superblock mount function */ + return mount_bdev(fs_type, flags, dev_name, data, minfs_fill_super); +} + +static struct file_system_type minfs_fs_type = { + .owner = THIS_MODULE, + .name = "minfs", + /* TODO 1: add mount, kill_sb and fs_flags */ + .mount = minfs_mount, + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV, +}; + +static int __init minfs_init(void) +{ + int err; + + err = register_filesystem(&minfs_fs_type); + if (err) { + printk(LOG_LEVEL "register_filesystem failed\n"); + return err; + } + + return 0; +} + +static void __exit minfs_exit(void) +{ + unregister_filesystem(&minfs_fs_type); +} + +module_init(minfs_init); +module_exit(minfs_exit); diff --git a/tools/labs/skels/filesystems/minfs/kernel/minfs.h b/tools/labs/skels/filesystems/minfs/kernel/minfs.h new file mode 100644 index 00000000000000..92285597a938ce --- /dev/null +++ b/tools/labs/skels/filesystems/minfs/kernel/minfs.h @@ -0,0 +1,45 @@ +#ifndef _MINFS_H +#define _MINFS_H 1 + +#define MINFS_MAGIC 0xDEADF00D +#define MINFS_NAME_LEN 16 +#define MINFS_BLOCK_SIZE 4096 +#define MINFS_NUM_INODES 32 +#define MINFS_NUM_ENTRIES 32 + +#define MINFS_ROOT_INODE 0 + +/* + * Filesystem layout: + * + * SB IZONE DATA + * ^ ^ (1 block) + * | | + * +-0 +-- 4096 + */ + +#define MINFS_SUPER_BLOCK 0 +#define MINFS_INODE_BLOCK 1 +#define MINFS_FIRST_DATA_BLOCK 2 + +struct minfs_super_block { + unsigned long magic; + __u8 version; + unsigned long imap; +}; + +struct minfs_dir_entry { + __u32 ino; + char name[MINFS_NAME_LEN]; +}; + +/* A minfs inode uses a single block. */ +struct minfs_inode { + __u32 mode; + __u32 uid; + __u32 gid; + __u32 size; + __u16 data_block; +}; + +#endif /* _MINFS_H */ diff --git a/tools/labs/skels/filesystems/minfs/user/.gitignore b/tools/labs/skels/filesystems/minfs/user/.gitignore new file mode 100644 index 00000000000000..970317ec7dd653 --- /dev/null +++ b/tools/labs/skels/filesystems/minfs/user/.gitignore @@ -0,0 +1 @@ +/mkfs.minfs diff --git a/tools/labs/skels/filesystems/minfs/user/Makefile b/tools/labs/skels/filesystems/minfs/user/Makefile new file mode 100644 index 00000000000000..b8e754a6b70fff --- /dev/null +++ b/tools/labs/skels/filesystems/minfs/user/Makefile @@ -0,0 +1,13 @@ +CFLAGS = -Wall -g -m32 +LDFLAGS = -static -m32 + +.PHONY: all clean + +all: mkfs.minfs + +mkfs.minfs: mkfs.minfs.o + +mkfs.minfs.o: mkfs.minfs.c ../kernel/minfs.h + +clean: + -rm -f *~ *.o mkfs.minfs diff --git a/tools/labs/skels/filesystems/minfs/user/mkfs.minfs.c b/tools/labs/skels/filesystems/minfs/user/mkfs.minfs.c new file mode 100644 index 00000000000000..c5e8f9132bb2c0 --- /dev/null +++ b/tools/labs/skels/filesystems/minfs/user/mkfs.minfs.c @@ -0,0 +1,81 @@ +#include +#include +#include + +#include +#include + +#include "../kernel/minfs.h" + +/* + * mk_minfs file + */ + +int main(int argc, char **argv) +{ + FILE *file; + char buffer[MINFS_BLOCK_SIZE]; + struct minfs_super_block msb; + struct minfs_inode root_inode; + struct minfs_inode file_inode; + struct minfs_dir_entry file_dentry; + int i; + + if (argc != 2) { + fprintf(stderr, "Usage: %s block_device_name\n", argv[0]); + exit(EXIT_FAILURE); + } + + file = fopen(argv[1], "w+"); + if (file == NULL) { + perror("fopen"); + exit(EXIT_FAILURE); + } + + memset(&msb, 0, sizeof(struct minfs_super_block)); + + msb.magic = MINFS_MAGIC; + msb.version = 1; + msb.imap = 0x03; + + /* zero disk */ + memset(buffer, 0, MINFS_BLOCK_SIZE); + for (i = 0; i < 128; i++) + fwrite(buffer, 1, MINFS_BLOCK_SIZE, file); + + fseek(file, 0, SEEK_SET); + + /* initialize super block */ + fwrite(&msb, sizeof(msb), 1, file); + + /* initialize root inode */ + memset(&root_inode, 0, sizeof(root_inode)); + root_inode.uid = 0; + root_inode.gid = 0; + root_inode.mode = S_IFDIR | 0755; + root_inode.size = 0; + root_inode.data_block = MINFS_FIRST_DATA_BLOCK; + + fseek(file, MINFS_INODE_BLOCK * MINFS_BLOCK_SIZE, SEEK_SET); + fwrite(&root_inode, sizeof(root_inode), 1, file); + + /* initialize new inode */ + memset(&file_inode, 0, sizeof(file_inode)); + file_inode.uid = 0; + file_inode.gid = 0; + file_inode.mode = S_IFREG | 0644; + file_inode.size = 0; + file_inode.data_block = MINFS_FIRST_DATA_BLOCK + 1; + fwrite(&file_inode, sizeof(file_inode), 1, file); + + /* add dentry information */ + memset(&file_dentry, 0, sizeof(file_dentry)); + file_dentry.ino = 1; + memcpy(file_dentry.name, "a.txt", 5); + fseek(file, MINFS_FIRST_DATA_BLOCK * MINFS_BLOCK_SIZE, SEEK_SET); + fwrite(&file_dentry, sizeof(file_dentry), 1, file); + + fclose(file); + + return 0; +} diff --git a/tools/labs/skels/filesystems/minfs/user/test-minfs-0.sh b/tools/labs/skels/filesystems/minfs/user/test-minfs-0.sh new file mode 100644 index 00000000000000..7da9597bc9c2f4 --- /dev/null +++ b/tools/labs/skels/filesystems/minfs/user/test-minfs-0.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +# load module +insmod ../kernel/minfs.ko + +# create mount point +mkdir -p /mnt/minfs + +# format partition +./mkfs.minfs /dev/vdb + +# mount filesystem +mount -t minfs /dev/vdb /mnt/minfs + +# show registered filesystems +cat /proc/filesystems + +# show mounted filesystems +cat /proc/mounts + +# umount filesystem +umount /mnt/minfs + +# unload module +rmmod minfs diff --git a/tools/labs/skels/filesystems/minfs/user/test-minfs-1.sh b/tools/labs/skels/filesystems/minfs/user/test-minfs-1.sh new file mode 100644 index 00000000000000..0a824ae9251c27 --- /dev/null +++ b/tools/labs/skels/filesystems/minfs/user/test-minfs-1.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +# load module +insmod ../kernel/minfs.ko + +# create mount point +mkdir -p /mnt/minfs + +# format partition +./mkfs.minfs /dev/vdb + +# mount filesystem +mount -t minfs /dev/vdb /mnt/minfs + +# list all filesystem files +cd /mnt/minfs +ls -la + +# unmount filesystem +cd .. +umount /mnt/minfs + +# unload module +rmmod minfs diff --git a/tools/labs/skels/filesystems/minfs/user/test-minfs-2.sh b/tools/labs/skels/filesystems/minfs/user/test-minfs-2.sh new file mode 100644 index 00000000000000..11970dfa0234cc --- /dev/null +++ b/tools/labs/skels/filesystems/minfs/user/test-minfs-2.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +pushd . > /dev/null 2>&1 + +# load module +insmod ../kernel/minfs.ko + +# create mount point +mkdir -p /mnt/minfs + +# format partition +./mkfs.minfs /dev/vdb + +# mount filesystem +mount -t minfs /dev/vdb /mnt/minfs + +# change to minfs root folder +cd /mnt/minfs + +# create new file +touch b.txt && echo "OK. File created." || echo "NOT OK. File creation failed." + +# unmount filesystem +cd .. +umount /mnt/minfs + +popd > /dev/null 2>&1 + +# mount filesystem +mount -t minfs /dev/vdb /mnt/minfs + +# check whether b.txt is still there +ls /mnt/minfs | grep b.txt && echo "OK. File b.txt exists " || echo "NOT OK. File b.txt does not exist." + +# unmount filesystem +umount /mnt/minfs + +# unload module +rmmod minfs diff --git a/tools/labs/skels/filesystems/minfs/user/test-minfs.sh b/tools/labs/skels/filesystems/minfs/user/test-minfs.sh new file mode 100644 index 00000000000000..ea704e07600c2e --- /dev/null +++ b/tools/labs/skels/filesystems/minfs/user/test-minfs.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +set -ex + +#load module +insmod ../kernel/minfs.ko + +#create mount point +mkdir -p /mnt/minfs + +#format partition +./mkfs.minfs /dev/vdb + +#mount filesystem +mount -t minfs /dev/vdb /mnt/minfs + +#show registered filesystems +cat /proc/filesystems | grep minfs + +#show mounted filesystems +cat /proc/mounts | grep minfs + +#show filesystem statistics +stat -f /mnt/minfs + +#list all filesystem files +cd /mnt/minfs +ls -la + +#unmount filesystem +cd .. +umount /mnt/minfs + +#unload module +rmmod minfs diff --git a/tools/labs/skels/filesystems/myfs/Kbuild b/tools/labs/skels/filesystems/myfs/Kbuild new file mode 100644 index 00000000000000..1b3fa9316852ee --- /dev/null +++ b/tools/labs/skels/filesystems/myfs/Kbuild @@ -0,0 +1,3 @@ +EXTRA_CFLAGS = -Wall -g -Wno-unused + +obj-m = myfs.o diff --git a/tools/labs/skels/filesystems/myfs/myfs.c b/tools/labs/skels/filesystems/myfs/myfs.c new file mode 100644 index 00000000000000..8b090f7046c02b --- /dev/null +++ b/tools/labs/skels/filesystems/myfs/myfs.c @@ -0,0 +1,230 @@ +/* + * SO2 Lab - Filesystem drivers + * Exercise #1 (no-dev filesystem) + */ + +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("Simple no-dev filesystem"); +MODULE_AUTHOR("SO2"); +MODULE_LICENSE("GPL"); + +#define MYFS_BLOCKSIZE 4096 +#define MYFS_BLOCKSIZE_BITS 12 +#define MYFS_MAGIC 0xbeefcafe +#define LOG_LEVEL KERN_ALERT + +/* declarations of functions that are part of operation structures */ + +static int myfs_mknod(struct inode *dir, + struct dentry *dentry, umode_t mode, dev_t dev); +static int myfs_create(struct inode *dir, struct dentry *dentry, + umode_t mode, bool excl); +static int myfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode); + +/* TODO 2: define super_operations structure */ +static const struct super_operations myfs_ops = { + .statfs = simple_statfs, + .drop_inode = generic_drop_inode, +}; + +static const struct inode_operations myfs_dir_inode_operations = { + /* TODO 5: Fill dir inode operations structure. */ + .create = myfs_create, + .lookup = simple_lookup, + .link = simple_link, + .unlink = simple_unlink, + .mkdir = myfs_mkdir, + .rmdir = simple_rmdir, + .mknod = myfs_mknod, + .rename = simple_rename, +}; + +static const struct file_operations myfs_file_operations = { + /* TODO 6: Fill file operations structure. */ + .read_iter = generic_file_read_iter, + .write_iter = generic_file_write_iter, + .mmap = generic_file_mmap, + .llseek = generic_file_llseek, +}; + +static const struct inode_operations myfs_file_inode_operations = { + /* TODO 6: Fill file inode operations structure. */ + .getattr = simple_getattr, +}; + +static const struct address_space_operations myfs_aops = { + /* TODO 6: Fill address space operations structure. */ + .readpage = simple_readpage, + .write_begin = simple_write_begin, + .write_end = simple_write_end, +}; + +struct inode *myfs_get_inode(struct super_block *sb, const struct inode *dir, + int mode) +{ + struct inode *inode = new_inode(sb); + + if (!inode) + return NULL; + + /* TODO 3: fill inode structure + * - mode + * - uid + * - gid + * - atime,ctime,mtime + * - ino + */ + inode_init_owner(inode, dir, mode); + inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode); + inode->i_ino = 1; + + /* TODO 5: Init i_ino using get_next_ino */ + inode->i_ino = get_next_ino(); + + /* TODO 6: Initialize address space operations. */ + inode->i_mapping->a_ops = &myfs_aops; + + if (S_ISDIR(mode)) { + /* TODO 3: set inode operations for dir inodes. */ + inode->i_op = &simple_dir_inode_operations; + inode->i_fop = &simple_dir_operations; + + /* TODO 5: use myfs_dir_inode_operations for inode + * operations (i_op). + */ + inode->i_op = &myfs_dir_inode_operations; + + /* TODO 3: directory inodes start off with i_nlink == 2 (for "." entry). + * Directory link count should be incremented (use inc_nlink). + */ + inc_nlink(inode); + } + + /* TODO 6: Set file inode and file operations for regular files + * (use the S_ISREG macro). + */ + if (S_ISREG(mode)) { + inode->i_op = &myfs_file_inode_operations; + inode->i_fop = &myfs_file_operations; + } + + return inode; +} + +/* TODO 5: Implement myfs_mknod, myfs_create, myfs_mkdir. */ +static int myfs_mknod(struct inode *dir, + struct dentry *dentry, umode_t mode, dev_t dev) +{ + struct inode *inode = myfs_get_inode(dir->i_sb, dir, mode); + + if (inode == NULL) + return -ENOSPC; + + d_instantiate(dentry, inode); + dget(dentry); + dir->i_mtime = dir->i_ctime = current_time(inode); + + return 0; +} + +static int myfs_create(struct inode *dir, struct dentry *dentry, + umode_t mode, bool excl) +{ + return myfs_mknod(dir, dentry, mode | S_IFREG, 0); +} + +static int myfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + int ret; + + ret = myfs_mknod(dir, dentry, mode | S_IFDIR, 0); + if (ret != 0) + return ret; + + inc_nlink(dir); + + return 0; +} + + +static int myfs_fill_super(struct super_block *sb, void *data, int silent) +{ + struct inode *root_inode; + struct dentry *root_dentry; + + /* TODO 2: fill super_block + * - blocksize, blocksize_bits + * - magic + * - super operations + * - maxbytes + */ + sb->s_maxbytes = MAX_LFS_FILESIZE; + sb->s_blocksize = MYFS_BLOCKSIZE; + sb->s_blocksize_bits = MYFS_BLOCKSIZE_BITS; + sb->s_magic = MYFS_MAGIC; + sb->s_op = &myfs_ops; + + /* mode = directory & access rights (755) */ + root_inode = myfs_get_inode(sb, NULL, + S_IFDIR | S_IRWXU | S_IRGRP | + S_IXGRP | S_IROTH | S_IXOTH); + + printk(LOG_LEVEL "root inode has %d link(s)\n", root_inode->i_nlink); + + if (!root_inode) + return -ENOMEM; + + root_dentry = d_make_root(root_inode); + if (!root_dentry) + goto out_no_root; + sb->s_root = root_dentry; + + return 0; + +out_no_root: + iput(root_inode); + return -ENOMEM; +} + +static struct dentry *myfs_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) +{ + /* TODO 1: call superblock mount function */ + return mount_nodev(fs_type, flags, data, myfs_fill_super); +} + +/* TODO 1: define file_system_type structure */ +static struct file_system_type myfs_fs_type = { + .owner = THIS_MODULE, + .name = "myfs", + .mount = myfs_mount, + .kill_sb = kill_litter_super, +}; + +static int __init myfs_init(void) +{ + int err; + + /* TODO 1: register */ + err = register_filesystem(&myfs_fs_type); + if (err) { + printk(LOG_LEVEL "register_filesystem failed\n"); + return err; + } + + return 0; +} + +static void __exit myfs_exit(void) +{ + /* TODO 1: unregister */ + unregister_filesystem(&myfs_fs_type); +} + +module_init(myfs_init); +module_exit(myfs_exit); diff --git a/tools/labs/skels/filesystems/myfs/test-myfs-1.sh b/tools/labs/skels/filesystems/myfs/test-myfs-1.sh new file mode 100644 index 00000000000000..26dffe0eb4bfc2 --- /dev/null +++ b/tools/labs/skels/filesystems/myfs/test-myfs-1.sh @@ -0,0 +1,53 @@ +#!/bin/sh + +set -x + +# load module +insmod myfs.ko + +# mount filesystem +mkdir -p /mnt/myfs +mount -t myfs none /mnt/myfs +ls -laid /mnt/myfs + +cd /mnt/myfs + +# create directory +mkdir mydir +ls -la + +# create subdirectory +cd mydir +mkdir mysubdir +ls -lai + +# rename subdirectory +mv mysubdir myrenamedsubdir +ls -lai + +# delete renamed subdirectory +rmdir myrenamedsubdir +ls -la + +# create file +touch myfile +ls -lai + +# rename file +mv myfile myrenamedfile +ls -lai + +# delete renamed file +rm myrenamedfile + +# delete directory +cd .. +rmdir mydir +ls -la + +# unmount filesystem +cd .. +umount /mnt/myfs + +# unload module +rmmod myfs diff --git a/tools/labs/skels/filesystems/myfs/test-myfs-2.sh b/tools/labs/skels/filesystems/myfs/test-myfs-2.sh new file mode 100644 index 00000000000000..17e92812599aba --- /dev/null +++ b/tools/labs/skels/filesystems/myfs/test-myfs-2.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +set -ex + +# load module +insmod myfs.ko + +# mount filesystem +mkdir -p /mnt/myfs +mount -t myfs none /mnt/myfs +ls -laid /mnt/myfs + +cd /mnt/myfs + +# create file +touch myfile +ls -lai + +# rename file +mv myfile myrenamedfile +ls -lai + +# create link to file +ln myrenamedfile mylink +ls -lai + +# read/write file +echo "message" > myrenamedfile +cat myrenamedfile + +# remove link to file +rm mylink +ls -la + +# delete file +rm -f myrenamedfile +ls -la + +# unmount filesystem +cd .. +umount /mnt/myfs + +# unload module +rmmod myfs diff --git a/tools/labs/skels/filesystems/myfs/test-myfs.sh b/tools/labs/skels/filesystems/myfs/test-myfs.sh new file mode 100644 index 00000000000000..c7bb43b5fb26d5 --- /dev/null +++ b/tools/labs/skels/filesystems/myfs/test-myfs.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +set -ex + +#load module +insmod myfs.ko + +#mount filesystem +mkdir -p /mnt/myfs +mount -t myfs none /mnt/myfs + +#show registered filesystems +cat /proc/filesystems | grep myfs + +#show mounted filesystems +cat /proc/mounts | grep myfs + +#show filesystem statistics +stat -f /mnt/myfs + +#list all filesystem files +cd /mnt/myfs +ls -la + +#unmount filesystem +cd .. +umount /mnt/myfs + +#unload module +rmmod myfs