mirror of
https://github.com/psankar/simplefs.git
synced 2025-07-22 21:44:30 +02:00
create file support
This commit is contained in:
parent
356bceb273
commit
6c591e9eb3
@ -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));
|
||||
|
||||
|
292
simple.c
292
simple.c
@ -10,9 +10,135 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/random.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
|
12
simple.h
12
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))];
|
||||
|
Loading…
x
Reference in New Issue
Block a user