diff --git a/mkfs-simplefs.c b/mkfs-simplefs.c index 1729742..e322a61 100644 --- a/mkfs-simplefs.c +++ b/mkfs-simplefs.c @@ -47,6 +47,7 @@ int main(int argc, char *argv[]) /* FIXME: Free blocks management is not implemented yet */ sb.free_blocks = ~0; + sb.free_blocks &= ~(1 << WELCOMEFILE_DATABLOCK_NUMBER); ret = write(fd, (char *)&sb, sizeof(sb)); diff --git a/simple.c b/simple.c index 19804f7..5c709b1 100644 --- a/simple.c +++ b/simple.c @@ -10,9 +10,135 @@ #include #include #include +#include #include "super.h" +/* A super block lock that must be used for any critical section operation on the sb, + * such as: updating the free_blocks, inodes_count etc. */ +static DEFINE_MUTEX(simplefs_sb_lock); +static DEFINE_MUTEX(simplefs_inodes_mgmt_lock); + +/* FIXME: This can be moved to an in-memory structure of the simplefs_inode. + * Because of the global nature of this lock, we cannot create + * new children (without locking) in two different dirs at a time. + * They will get sequentially created. If we move the lock + * to a directory-specific way (by moving it inside inode), the + * insertion of two children in two different directories can be + * done in parallel */ +static DEFINE_MUTEX(simplefs_directory_children_update_lock); + +void simplefs_sb_sync(struct super_block *vsb) +{ + struct buffer_head *bh; + struct simplefs_super_block *sb = SIMPLEFS_SB(vsb); + + bh = (struct buffer_head *)sb_bread(vsb, + SIMPLEFS_SUPERBLOCK_BLOCK_NUMBER); + bh->b_data = (char *)sb; + mark_buffer_dirty(bh); + sync_dirty_buffer(bh); +} + +void simplefs_inode_add(struct super_block *vsb, struct simplefs_inode *inode) +{ + struct simplefs_super_block *sb = SIMPLEFS_SB(vsb); + struct buffer_head *bh; + struct simplefs_inode *inode_iterator; + + if (mutex_lock_interruptible(&simplefs_inodes_mgmt_lock)) { + printk(KERN_ERR "Failed to acquire mutex lock %s +%d\n", + __FILE__, __LINE__); + return; + } + + bh = (struct buffer_head *)sb_bread(vsb, + SIMPLEFS_INODESTORE_BLOCK_NUMBER); + + inode_iterator = (struct simplefs_inode *)bh->b_data; + + if (mutex_lock_interruptible(&simplefs_sb_lock)) { + printk(KERN_ERR "Failed to acquire mutex lock %s +%d\n", + __FILE__, __LINE__); + return; + } + + /* Append the new inode in the end in the inode store */ + inode_iterator += sb->inodes_count; + + memcpy(inode_iterator, inode, sizeof(struct simplefs_inode)); + sb->inodes_count++; + + mark_buffer_dirty(bh); + simplefs_sb_sync(vsb); + + mutex_unlock(&simplefs_sb_lock); + mutex_unlock(&simplefs_inodes_mgmt_lock); +} + +/* This function returns a blocknumber which is free. + * The block will be removed from the freeblock list. + * + * In an ideal, production-ready filesystem, we will not be dealing with blocks, + * and instead we will be using extents + * + * If for some reason, the file creation/deletion failed, the block number + * will still be marked as non-free. You need fsck to fix this.*/ +int simplefs_sb_get_a_freeblock(struct super_block *vsb, uint64_t * out) +{ + struct simplefs_super_block *sb = SIMPLEFS_SB(vsb); + int i; + int ret = 0; + + if (mutex_lock_interruptible(&simplefs_sb_lock)) { + printk(KERN_ERR "Failed to acquire mutex lock %s +%d\n", + __FILE__, __LINE__); + ret = -EINTR; + goto end; + } + + /* Loop until we find a free block. We start the loop from 3, + * as all prior blocks will always be in use */ + for (i = 3; i < SIMPLEFS_MAX_FILESYSTEM_OBJECTS_SUPPORTED; i++) { + if (sb->free_blocks & (1 << i)) { + break; + } + } + + if (unlikely(i == SIMPLEFS_MAX_FILESYSTEM_OBJECTS_SUPPORTED)) { + printk(KERN_ERR "No more free blocks available"); + ret = -ENOSPC; + goto end; + } + + *out = i; + + /* Remove the identified block from the free list */ + sb->free_blocks &= ~(1 << i); + + simplefs_sb_sync(vsb); + +end: + mutex_unlock(&simplefs_sb_lock); + return ret; +} + +static int simplefs_sb_get_objects_count(struct super_block *vsb, + uint64_t * out) +{ + struct simplefs_super_block *sb = SIMPLEFS_SB(vsb); + + if (mutex_lock_interruptible(&simplefs_inodes_mgmt_lock)) { + printk(KERN_ERR "Failed to acquire mutex lock %s +%d\n", + __FILE__, __LINE__); + return -EINTR; + } + *out = sb->inodes_count; + mutex_unlock(&simplefs_inodes_mgmt_lock); + + return 0; +} + static int simplefs_readdir(struct file *filp, void *dirent, filldir_t filldir) { loff_t pos = filp->f_pos; @@ -70,6 +196,13 @@ struct simplefs_inode *simplefs_get_inode(struct super_block *sb, SIMPLEFS_INODESTORE_BLOCK_NUMBER); sfs_inode = (struct simplefs_inode *)bh->b_data; +#if 0 + if (mutex_lock_interruptible(&simplefs_inodes_mgmt_lock)) { + printk(KERN_ERR "Failed to acquire mutex lock %s +%d\n", + __FILE__, __LINE__); + return NULL; + } +#endif for (i = 0; i < sfs_sb->inodes_count; i++) { if (sfs_inode->inode_no == inode_no) { /* FIXME: bh->b_data is probably leaking */ @@ -77,6 +210,7 @@ struct simplefs_inode *simplefs_get_inode(struct super_block *sb, } sfs_inode++; } +// mutex_unlock(&simplefs_inodes_mgmt_lock); return NULL; } @@ -103,8 +237,21 @@ ssize_t simplefs_read(struct file * filp, char __user * buf, size_t len, return 0; } + if (*ppos >= inode->file_size) { + printk(KERN_INFO + "Read request with offset beyond the filesize\n"); + return 0; + } + bh = (struct buffer_head *)sb_bread(filp->f_path.dentry->d_inode->i_sb, inode->data_block_number); + + if (!bh) { + printk(KERN_ERR "Reading the block number [%llu] failed.", + inode->data_block_number); + return 0; + } + buffer = (char *)bh->b_data; nbytes = min(strlen(buffer), len); @@ -135,10 +282,145 @@ const struct file_operations simplefs_dir_operations = { struct dentry *simplefs_lookup(struct inode *parent_inode, struct dentry *child_dentry, unsigned int flags); +static int simplefs_create(struct inode *dir, struct dentry *dentry, + umode_t mode, bool excl); + static struct inode_operations simplefs_inode_ops = { + .create = simplefs_create, .lookup = simplefs_lookup, }; +static int simplefs_create(struct inode *dir, struct dentry *dentry, + umode_t mode, bool excl) +{ + struct inode *inode; + struct simplefs_inode *sfs_inode; + struct super_block *sb; + struct simplefs_dir_record *record; + struct simplefs_inode *parent_dir_inode; + struct buffer_head *bh; + struct simplefs_dir_record *dir_contents_datablock; + uint64_t count; + int ret; + + if (mutex_lock_interruptible(&simplefs_directory_children_update_lock)) { + printk(KERN_ERR "Failed to acquire mutex lock %s +%d\n", + __FILE__, __LINE__); + return -EINTR; + } + sb = dir->i_sb; + + ret = simplefs_sb_get_objects_count(sb, &count); + if (ret < 0) { + return ret; + } + + if (unlikely(count >= SIMPLEFS_MAX_FILESYSTEM_OBJECTS_SUPPORTED)) { + /* The above condition can be just == insted of the >= */ + printk(KERN_ERR + "Maximum number of objects supported by simplefs is already reached"); + return -ENOSPC; + } + + if (!S_ISDIR(mode) && !S_ISREG(mode)) { + printk(KERN_ERR + "Creation request but for neither a file nor a directory"); + return -EINVAL; + } + + inode = new_inode(sb); + if (!inode) + return -ENOMEM; + + inode->i_sb = sb; + inode->i_op = &simplefs_inode_ops; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode->i_ino = 10; + + /* Loop until we get an unique inode number */ + while (simplefs_get_inode(sb, inode->i_ino)) { + printk(KERN_INFO "inode [%lu] already exists\n", inode->i_ino); + inode->i_ino++; + } + printk(KERN_INFO "Got new unique inode number [%lu]\n", inode->i_ino); + + /* FIXME: This is leaking. We need to free all in-memory inodes sometime */ + sfs_inode = kmalloc(sizeof(struct simplefs_inode), GFP_KERNEL); + sfs_inode->inode_no = inode->i_ino; + inode->i_private = &sfs_inode; + + if (S_ISDIR(mode)) { + printk(KERN_INFO "New directory creation request\n"); + sfs_inode->dir_children_count = 0; + sfs_inode->mode = S_IFDIR; + inode->i_fop = &simplefs_dir_operations; + } else if (S_ISREG(mode)) { + printk(KERN_INFO "New file creation request\n"); + sfs_inode->file_size = 0; + sfs_inode->mode = S_IFREG; + inode->i_fop = &simplefs_file_operations; + } + + /* First get a free block and update the free map, + * Then add inode to the inode store and update the sb inodes_count, + * Then update the parent directory's inode with the new child. + * + * The above ordering helps us to maintain fs consistency + * even in most crashes + */ + ret = simplefs_sb_get_a_freeblock(sb, &sfs_inode->data_block_number); + if (ret < 0) { + printk(KERN_ERR "simplefs could not get a freeblock"); + return ret; + } + + simplefs_inode_add(sb, sfs_inode); + + record = kmalloc(sizeof(struct simplefs_dir_record), GFP_KERNEL); + record->inode_no = sfs_inode->inode_no; + strcpy(record->filename, dentry->d_name.name); + + parent_dir_inode = SIMPLEFS_INODE(dir); + bh = sb_bread(sb, parent_dir_inode->data_block_number); + dir_contents_datablock = (struct simplefs_dir_record *)bh->b_data; + + /* Navigate to the last record in the directory contents */ + dir_contents_datablock += parent_dir_inode->dir_children_count; + + memcpy(dir_contents_datablock, record, + sizeof(struct simplefs_dir_record)); + + mark_buffer_dirty(bh); + sync_dirty_buffer(bh); + + parent_dir_inode->dir_children_count++; + + if (mutex_lock_interruptible(&simplefs_inodes_mgmt_lock)) { + printk(KERN_ERR "Failed to acquire mutex lock %s +%d\n", + __FILE__, __LINE__); + return -EINTR; + } + + bh = (struct buffer_head *)sb_bread(sb, + SIMPLEFS_INODESTORE_BLOCK_NUMBER); + + mark_buffer_dirty(bh); + sync_dirty_buffer(bh); + + mutex_unlock(&simplefs_inodes_mgmt_lock); + + mutex_unlock(&simplefs_directory_children_update_lock); + + printk(KERN_INFO "Returning success after creating the file\n"); + + inode_init_owner(inode, dir, mode); + d_add(dentry, inode); + + /* This sleep is necessary to update the dentry cache */ + msleep(5); + return 0; +} + struct dentry *simplefs_lookup(struct inode *parent_inode, struct dentry *child_dentry, unsigned int flags) { @@ -152,6 +434,11 @@ struct dentry *simplefs_lookup(struct inode *parent_inode, record = (struct simplefs_dir_record *)bh->b_data; for (i = 0; i < parent->dir_children_count; i++) { if (!strcmp(record->filename, child_dentry->d_name.name)) { + /* FIXME: There is a corner case where if an allocated inode, + * is not written to the inode store, but the inodes_count is + * incremented. Then if the random string on the disk matches + * with the filename that we are comparing above, then we + * will use an invalid unintialized inode */ struct inode *inode; struct simplefs_inode *sfs_inode; @@ -183,8 +470,13 @@ struct dentry *simplefs_lookup(struct inode *parent_inode, d_add(child_dentry, inode); return NULL; } + record++; } + printk(KERN_ERR + "No inode found for the filename [%s] under the directory\n", + child_dentry->d_name.name); + return NULL; } diff --git a/simple.h b/simple.h index a25aee8..2c317fa 100644 --- a/simple.h +++ b/simple.h @@ -33,11 +33,23 @@ struct simplefs_inode { }; }; +const int SIMPLEFS_MAX_FILESYSTEM_OBJECTS_SUPPORTED = 64; +/* min ( + SIMPLEFS_DEFAULT_BLOCK_SIZE / sizeof(struct simplefs_inode), + sizeof(uint64_t) //The free_blocks tracker in the sb + ); */ + +/* FIXME: Move the struct to its own file and not expose the members + * Always access using the simplefs_sb_* functions and + * do not access the members directly */ struct simplefs_super_block { uint64_t version; uint64_t magic; uint64_t block_size; + + /* FIXME: This should be moved to the inode store and not part of the sb */ uint64_t inodes_count; + uint64_t free_blocks; char padding[SIMPLEFS_DEFAULT_BLOCK_SIZE - (5 * sizeof(uint64_t))];