/** * Simplest */ void simplefs_destory_inode(struct inode *inode) { struct simplefs_inode *sfs_inode = SIMPLEFS_INODE(inode); printk(KERN_INFO "Freeing private data of inode %p (%lu)\n", sfs_inode, inode->i_ino); kmem_cache_free(sfs_inode_cachep, sfs_inode); }
struct dentry *simplefs_lookup(struct inode *parent_inode, struct dentry *child_dentry, unsigned int flags) { struct simplefs_inode *parent = SIMPLEFS_INODE(parent_inode); struct super_block *sb = parent_inode->i_sb; struct buffer_head *bh; struct simplefs_dir_record *record; int i; bh = (struct buffer_head *)sb_bread(sb, parent->data_block_number); 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; /* FIXME: This simplefs_inode is leaking */ sfs_inode = simplefs_get_inode(sb, record->inode_no); /* FIXME: This inode is leaking */ inode = new_inode(sb); inode->i_ino = record->inode_no; inode_init_owner(inode, parent_inode, sfs_inode->mode); inode->i_sb = sb; inode->i_op = &simplefs_inode_ops; if (S_ISDIR(inode->i_mode)) inode->i_fop = &simplefs_dir_operations; else if (S_ISREG(inode->i_mode)) inode->i_fop = &simplefs_file_operations; else printk(KERN_ERR "Unknown inode type. Neither a directory nor a file"); /* FIXME: We should store these times to disk and retrieve them */ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; inode->i_private = sfs_inode; d_add(child_dentry, inode); return NULL; } record++; } printk(KERN_ERR "No inode found for the filename [%s]\n", child_dentry->d_name.name); return NULL; }
static int simplefs_readdir(struct file *filp, void *dirent, filldir_t filldir) #endif { loff_t pos; struct inode *inode; struct super_block *sb; struct buffer_head *bh; struct simplefs_inode *sfs_inode; struct simplefs_dir_record *record; int i; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) pos = ctx->pos; #else pos = filp->f_pos; #endif inode = filp->f_dentry->d_inode; sb = inode->i_sb; if (pos) { /* FIXME: We use a hack of reading pos to figure if we have filled in all data. * We should probably fix this to work in a cursor based model and * use the tokens correctly to not fill too many data in each cursor based call */ return 0; } sfs_inode = SIMPLEFS_INODE(inode); if (unlikely(!S_ISDIR(sfs_inode->mode))) { printk(KERN_ERR "inode [%llu][%lu] for fs object [%s] not a directory\n", sfs_inode->inode_no, inode->i_ino, filp->f_dentry->d_name.name); return -ENOTDIR; } bh = sb_bread(sb, sfs_inode->data_block_number); BUG_ON(!bh); record = (struct simplefs_dir_record *)bh->b_data; for (i = 0; i < sfs_inode->dir_children_count; i++) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) dir_emit(ctx, record->filename, SIMPLEFS_FILENAME_MAXLEN, record->inode_no, DT_UNKNOWN); ctx->pos += sizeof(struct simplefs_dir_record); #else filldir(dirent, record->filename, SIMPLEFS_FILENAME_MAXLEN, pos, record->inode_no, DT_UNKNOWN); filp->f_pos += sizeof(struct simplefs_dir_record); #endif pos += sizeof(struct simplefs_dir_record); record++; } brelse(bh); return 0; }
static void simplefs_destroy_inode(struct inode *vfs_inode) { struct simple_fs_inode_i *inode = SIMPLEFS_INODE(vfs_inode); struct simple_fs_sb_i *sb = SIMPLEFS_SB(vfs_inode->i_sb); if (inode->indirect_block) { if(!buffer_uptodate(inode->indirect_block)) sync_dirty_buffer(inode->indirect_block); bforget(inode->indirect_block); } kmem_cache_free(sb->inode_cachep,inode); }
ssize_t simplefs_read(struct file * filp, char __user * buf, size_t len, loff_t * ppos) { /* Hack to make sure that we answer the read call only once and not loop infinitely. * We need to implement support for filesize in inode to remove this hack */ static int done = 0; /* After the commit dd37978c5 in the upstream linux kernel, * we can use just filp->f_inode instead of the * f->f_path.dentry->d_inode redirection */ struct simplefs_inode *inode = SIMPLEFS_INODE(filp->f_path.dentry->d_inode); struct buffer_head *bh; char *buffer; int nbytes; if (done) { done = 0; return 0; } if (*ppos >= inode->file_size) { /* Read request with offset beyond the filesize */ 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((size_t) inode->file_size, len); if (copy_to_user(buf, buffer, nbytes)) { brelse(bh); printk(KERN_ERR "Error copying file contents to the userspace buffer\n"); return -EFAULT; } brelse(bh); *ppos += nbytes; done = 1; return nbytes; }
struct dentry *simplefs_lookup(struct inode *parent_inode, struct dentry *child_dentry, unsigned int flags) { struct simplefs_inode *parent = SIMPLEFS_INODE(parent_inode); struct super_block *sb = parent_inode->i_sb; struct buffer_head *bh; struct simplefs_dir_record *record; int i; bh = sb_bread(sb, parent->data_block_number); BUG_ON(!bh); sfs_trace("Lookup in: ino=%llu, b=%llu\n", parent->inode_no, parent->data_block_number); record = (struct simplefs_dir_record *)bh->b_data; for (i = 0; i < parent->dir_children_count; i++) { sfs_trace("Have file: '%s' (ino=%llu)\n", record->filename, record->inode_no); 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 uninitialized inode */ struct inode *inode = simplefs_iget(sb, record->inode_no); inode_init_owner(inode, parent_inode, SIMPLEFS_INODE(inode)->mode); d_add(child_dentry, inode); return NULL; } record++; } printk(KERN_ERR "No inode found for the filename [%s]\n", child_dentry->d_name.name); return NULL; }
ssize_t simplefs_read(struct file * filp, char __user * buf, size_t len, loff_t * ppos) { /* After the commit dd37978c5 in the upstream linux kernel, * we can use just filp->f_inode instead of the * f->f_path.dentry->d_inode redirection */ struct simplefs_inode *inode = SIMPLEFS_INODE(filp->f_path.dentry->d_inode); struct buffer_head *bh; char *buffer; int nbytes; if (*ppos >= inode->file_size) { /* Read request with offset beyond the filesize */ return 0; } bh = 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((size_t) inode->file_size, len); if (copy_to_user(buf, buffer, nbytes)) { brelse(bh); printk(KERN_ERR "Error copying file contents to the userspace buffer\n"); return -EFAULT; } brelse(bh); *ppos += nbytes; return nbytes; }
static int simplefs_write_inode(struct inode *vfs_inode, struct writeback_control *wbc) { /* * We just need to write the inode here not it's pages. */ struct simple_fs_inode_i *minode = SIMPLEFS_INODE(vfs_inode); struct simple_fs_sb_i *msblk = SIMPLEFS_SB(vfs_inode->i_sb); int inodes_per_block = SIMPLEFS_INODE_SIZE/msblk->sb.block_size; /* * Find the inode table where we need to write this inode. */ struct buffer_head *inode_table = msblk->inode_table[(minode->inode.inode_no - 1)/inodes_per_block]; struct simplefs_inode *disk_inode = (struct simplefs_inode*)(inode_table->b_data + (minode->inode.inode_no - 1) % inodes_per_block); minode->inode.m_time = timespec_to_ns(vfs_inode.m_time); minode->inode.m_time = cpu_to_le64(minode->inode.m_time); if(!(vfs_inode->i_mode & S_IFDIR)) { minode->inode.file_size = i_size_read(vfs_inode); minode->inode.file_size = cpu_to_le64(minode->inode.file_size); } memcpy(disk_inode,&minode->inode,sizeof(struct simplefs_inode)); mark_buffer_dirty(inode_table); if(wbc->sync_mode == WB_SYNC_ALL) { SFSDBG("[SFS] Writeback control was to sync all in %s \n",__FUNCTION__); sync_dirty_buffer(inode_table); } /* * Perhaps we should sync dirty buffer here, * but let's see how far we can go. The inode * may actually not be written if we don't force * sync and let the flush thread do it for us. */ SFSDBG("Not syncing in %s\n",__FUNCTION__); }
static int simplefs_create_fs_object(struct inode *dir, struct dentry *dentry, umode_t mode) { struct inode *inode; struct simplefs_inode *sfs_inode; struct super_block *sb; 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)) { sfs_trace("Failed to acquire mutex lock\n"); return -EINTR; } sb = dir->i_sb; ret = simplefs_sb_get_objects_count(sb, &count); if (ret < 0) { mutex_unlock(&simplefs_directory_children_update_lock); 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"); mutex_unlock(&simplefs_directory_children_update_lock); return -ENOSPC; } if (!S_ISDIR(mode) && !S_ISREG(mode)) { printk(KERN_ERR "Creation request but for neither a file nor a directory"); mutex_unlock(&simplefs_directory_children_update_lock); return -EINVAL; } inode = new_inode(sb); if (!inode) { mutex_unlock(&simplefs_directory_children_update_lock); 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 = (count + SIMPLEFS_START_INO - SIMPLEFS_RESERVED_INODES + 1); sfs_inode = kmem_cache_alloc(sfs_inode_cachep, GFP_KERNEL); sfs_inode->inode_no = inode->i_ino; inode->i_private = sfs_inode; sfs_inode->mode = mode; if (S_ISDIR(mode)) { printk(KERN_INFO "New directory creation request\n"); sfs_inode->dir_children_count = 0; inode->i_fop = &simplefs_dir_operations; } else if (S_ISREG(mode)) { printk(KERN_INFO "New file creation request\n"); sfs_inode->file_size = 0; 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"); mutex_unlock(&simplefs_directory_children_update_lock); return ret; } simplefs_inode_add(sb, sfs_inode); parent_dir_inode = SIMPLEFS_INODE(dir); bh = sb_bread(sb, parent_dir_inode->data_block_number); BUG_ON(!bh); 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; dir_contents_datablock->inode_no = sfs_inode->inode_no; strcpy(dir_contents_datablock->filename, dentry->d_name.name); mark_buffer_dirty(bh); sync_dirty_buffer(bh); brelse(bh); if (mutex_lock_interruptible(&simplefs_inodes_mgmt_lock)) { mutex_unlock(&simplefs_directory_children_update_lock); sfs_trace("Failed to acquire mutex lock\n"); return -EINTR; } parent_dir_inode->dir_children_count++; ret = simplefs_inode_save(sb, parent_dir_inode); if (ret) { mutex_unlock(&simplefs_inodes_mgmt_lock); mutex_unlock(&simplefs_directory_children_update_lock); /* TODO: Remove the newly created inode from the disk and in-memory inode store * and also update the superblock, freemaps etc. to reflect the same. * Basically, Undo all actions done during this create call */ return ret; } mutex_unlock(&simplefs_inodes_mgmt_lock); mutex_unlock(&simplefs_directory_children_update_lock); inode_init_owner(inode, dir, mode); d_add(dentry, inode); return 0; }
/* FIXME: The write support is rudimentary. I have not figured out a way to do writes * from particular offsets (even though I have written some untested code for this below) efficiently. */ ssize_t simplefs_write(struct file * filp, const char __user * buf, size_t len, loff_t * ppos) { /* After the commit dd37978c5 in the upstream linux kernel, * we can use just filp->f_inode instead of the * f->f_path.dentry->d_inode redirection */ struct inode *inode; struct simplefs_inode *sfs_inode; struct buffer_head *bh; struct super_block *sb; struct simplefs_super_block *sfs_sb; handle_t *handle; char *buffer; int retval; sb = filp->f_path.dentry->d_inode->i_sb; sfs_sb = SIMPLEFS_SB(sb); handle = jbd2_journal_start(sfs_sb->journal, 1); if (IS_ERR(handle)) return PTR_ERR(handle); retval = generic_write_checks(filp, ppos, &len, 0); if (retval) return retval; inode = filp->f_path.dentry->d_inode; sfs_inode = SIMPLEFS_INODE(inode); bh = sb_bread(filp->f_path.dentry->d_inode->i_sb, sfs_inode->data_block_number); if (!bh) { printk(KERN_ERR "Reading the block number [%llu] failed.", sfs_inode->data_block_number); return 0; } buffer = (char *)bh->b_data; /* Move the pointer until the required byte offset */ buffer += *ppos; retval = jbd2_journal_get_write_access(handle, bh); if (WARN_ON(retval)) { brelse(bh); sfs_trace("Can't get write access for bh\n"); return retval; } if (copy_from_user(buffer, buf, len)) { brelse(bh); printk(KERN_ERR "Error copying file contents from the userspace buffer to the kernel space\n"); return -EFAULT; } *ppos += len; retval = jbd2_journal_dirty_metadata(handle, bh); if (WARN_ON(retval)) { brelse(bh); return retval; } handle->h_sync = 1; retval = jbd2_journal_stop(handle); if (WARN_ON(retval)) { brelse(bh); return retval; } mark_buffer_dirty(bh); sync_dirty_buffer(bh); brelse(bh); /* Set new size * sfs_inode->file_size = max(sfs_inode->file_size, *ppos); * * FIXME: What to do if someone writes only some parts in between ? * The above code will also fail in case a file is overwritten with * a shorter buffer */ if (mutex_lock_interruptible(&simplefs_inodes_mgmt_lock)) { sfs_trace("Failed to acquire mutex lock\n"); return -EINTR; } sfs_inode->file_size = *ppos; retval = simplefs_inode_save(sb, sfs_inode); if (retval) { len = retval; } mutex_unlock(&simplefs_inodes_mgmt_lock); return len; }
static int allocate_data_blocks(struct inode *vfs_inode,int nr_blocks) { struct simple_fs_sb_i *msblk = SIMPLEFS_SB(vfs_inode->i_sb); struct simple_fs_inode_i *minode = SIMPLEFS_INODE(vfs_inode); struct buffer_head *sb_buffer_bitmap = NULL; char *bitmap_buffer = NULL; int block_start = 0,block_no = 0; int bitmap_index = 0; int blocks_alloced = 0; int buffer_offset = 0; /*How many b_this_page has been done on a single bh*/ int block_start_buffer_offset = 0; int block_start_bitmap_index = 0; if(!nr_blocks) return 0; mutex_lock(&msblk->sb_mutex); new_bitmap_buffer: sb_buffer_bitmap = msblk->block_bitmap[bitmap_index]; if(!sb_buffer_bitmap) goto out_failed; allocate_block: bitmap_buffer = sb_buffer_bitmap->b_data; if( nr_blocks && (block_no = alloc_bmap(bitmap_buffer,sb_buffer_bitmap->b_size)) < 0) { sb_buffer_bitmap = sb_buffer_bitmap->b_this_page; if(sb_buffer_bitmap == mbslk->block_bitmap[bitmap_index]) { bitmap_index++; /*Move to next buffer head in the array*/ buffer_offset = 0; goto new_bitmap_buffer; } else buffer_offset++; goto allocate_block; } else if(block_no >= 0) { nr_blocks--; blocks_alloced++; if(!block_start) { block_start = block_no + (((sb_buffer_bitmap->b_size * buffer_offset) + (PAGE_SIZE*bitmap_index)) << 3); block_start_buffer_offset = buffer_offset; block_start_bitmap_index = bitmap_index; } if(buffer_uptodate(sb_buffer_bitmap)) mark_buffer_dirty(sb_buffer_bitmap); block_no = -1; if(nr_blocks) goto allocate_block; } simplefs_sync_metadata(sb); mutex_unlock(&msblk->sb_mutex); return block_start; /*Return starting block number of the allocated blocks*/ out_failed: if(blocks_alloced) { /* * Get the starting buffer head from where allocations * were started. */ sb_buffer_bitmap = msblk->block_bitmap[block_start_bitmap_index]; /* * Get the starting block number relative to the buffer head. */ block_no = block_start - (( (sb_buffer_bitmap->b_size * block_start_buffer_offset) +(PAGE_SIZE * block_start_bitmap_index))) * 8; /* * Move to the correct buffer head within the page. */ while(block_start_buffer_offset) { sb_buffer_bitmap = sb_buffer_bitmap->b_this_page; block_start_buffer_offset--; } bitmap_buffer = sb_buffer_bitmap->b_data; while(blocks_alloced) { if(free_bmap(bitmap_buffer,sb_buffer_bitmap->b_size,block_no++)){ blocks_alloced--; if(buffer_uptodate(sb_buffer_bitmap)) mark_buffer_dirty(sb_buffer_bitmap); } /* * There was no freeing of block because this block_no didn't * belonged the starting buffer head. We need to move the * buffer head to new buffer or perhaps we might need to * get a new msblk->block_bitmap[j]. */ else { sb_buffer_bitmap = sb_buffer_bitmap->b_this_page; if( sb_buffer_bitmap == msblk->block_bitmap[block_start_bitmap_index] ) sb_buffer_bitmap = msblk->block_bitmap[++block_start_bitmap_index]; block_no = 0; /*This is relative to the buffer head*/ bitmap_buffer = sb_buffer_bitmap->b_data; } } } simplefs_sync_metadata(sb); mutex_unlock(msblk->sb_mutex); return 0; }
static int simplefs_get_block(struct inode *vfs_inode, sector_t iblock, struct buffer_head *bh_result, int create) { struct simple_fs_sb_i *msblk = SIMPLEFS_SB(vfs_inode->i_sb); struct simple_fs_inode_i *minode = SIMPLEFS_INODE(vfs_inode); uint64_t mapped_block = -1; if(iblock > msblk->sb.block_size/sizeof(uint64_t)) goto fail_get_block; if(create) { /* Do we already have allocated the indirect block. * If yes then all we need to do is check the block location * for being 0 within that. */ if(iblock) { allocate_indirect_block: if(minode->inode.indirect_block_number) { if(!minode->indirect_block) { minode->indirect_block = sb_bread(vfs_inode->i_sb, minode->inode.indirect_block_number); if(!minode->indirect_block) goto fail_get_block; } uint64_t *block_offset = ((uint64_t*)(minode->indirect_block->b_data) + (iblock-1)); mapped_block = le64_to_cpu(*block_offset); if(!mapped_block) { mapped_block = allocate_data_blocks(vfs_inode,1); if (!mapped_block) { SFSDBG(KERN_INFO "Error allocating indirect data block %s %d\n" ,__FUNCTION__,__LINE__); goto fail_get_block; } } *block_offset = cpu_to_le64(mapped_block); mark_buffer_dirty(minode->indirect_block); mapped_block = block_offset; } else { /*Allocate that indirect block and the block within*/ minode->inode.indirect_block_number = allocate_data_blocks(vfs_inode,1); if(!minode->inode.indirect_block_number) { SFSDBG(KERN_INFO "Error allocating indirect block %s %d\n" ,__FUNCTION__,__LINE__); goto fail_get_block; } else goto allocate_indirect_block; } } else { /*This is the first block for the file*/ if( minode->inode.data_block_number ){ mapped_block = le64_to_cpu(minode->inode.data_block_number); } else { minode->inode.data_block_number = allocate_data_blocks(vfs_inode,1); if(!minode->inode.data_block_number) { SFSDBG(KERN_INFO "Error allocating direct block %s %d\n" ,__FUNCTION__,__LINE__); goto fail_get_block; } mapped_block = minode->inode.data_block_number; minode->inode.data_block_number = cpu_to_le64( minode->inode.data_block_number); } } } else { /* * Find the mapping but don't create it. */ if(iblock) { if(minode->inode.indirect_block_number) { if(!minode->indirect_block) { minode->indirect_block = sb_bread(vfs_inode->i_sb, minode->inode.indirect_block_number); if(!minode->indirect_block) goto fail_get_block; } uint64_t *block_offset = ((uint64_t*)(minode->indirect_block->b_data) + (iblock-1)); mapped_block = le64_to_cpu(*block_offset); if(!mapped_block) goto fail_get_block; } else goto fail_get_block; } else { if(!minode->inode.data_block_number) goto fail_get_block; mapped_block = le64_to_cpu(minode->inode.data_block_number); } } set_buffer_new(bh_result); map_bh(bh_result,vfs_inode->i_sb,mapped_block); return 0; fail_get_block: return -EOF; }
static int simplefs_create_fs_object(struct inode *dir, struct dentry *dentry, umode_t mode) { struct inode *inode; struct simplefs_inode *sfs_inode; struct simplefs_inode *inode_iterator; 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) { mutex_unlock(&simplefs_directory_children_update_lock); 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"); mutex_unlock(&simplefs_directory_children_update_lock); return -ENOSPC; } if (!S_ISDIR(mode) && !S_ISREG(mode)) { printk(KERN_ERR "Creation request but for neither a file nor a directory"); mutex_unlock(&simplefs_directory_children_update_lock); return -EINVAL; } inode = new_inode(sb); if (!inode) { mutex_unlock(&simplefs_directory_children_update_lock); 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)) { /* inode inode->i_ino already exists */ 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; sfs_inode->mode = mode; if (S_ISDIR(mode)) { printk(KERN_INFO "New directory creation request\n"); sfs_inode->dir_children_count = 0; inode->i_fop = &simplefs_dir_operations; } else if (S_ISREG(mode)) { printk(KERN_INFO "New file creation request\n"); sfs_inode->file_size = 0; 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"); mutex_unlock(&simplefs_directory_children_update_lock); 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); brelse(bh); if (mutex_lock_interruptible(&simplefs_inodes_mgmt_lock)) { mutex_unlock(&simplefs_directory_children_update_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); 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 -EINTR; } count = 0; while (inode_iterator->inode_no != parent_dir_inode->inode_no && count < SIMPLEFS_SB(sb)->inodes_count) { count++; inode_iterator++; } if (likely(inode_iterator->inode_no == parent_dir_inode->inode_no)) { parent_dir_inode->dir_children_count++; inode_iterator->dir_children_count = parent_dir_inode->dir_children_count; /* Updated the parent inode's dir count to reflect the new child too */ mark_buffer_dirty(bh); sync_dirty_buffer(bh); } else { printk(KERN_ERR "The updated childcount could not be stored to the dir inode."); /* TODO: Remove the newly created inode from the disk and in-memory inode store * and also update the superblock, freemaps etc. to reflect the same. * Basically, Undo all actions done during this create call */ } brelse(bh); mutex_unlock(&simplefs_sb_lock); mutex_unlock(&simplefs_inodes_mgmt_lock); mutex_unlock(&simplefs_directory_children_update_lock); inode_init_owner(inode, dir, mode); d_add(dentry, inode); return 0; }
/* FIXME: The write support is rudimentary. I have not figured out a way to do writes * from particular offsets (even though I have written some untested code for this below) efficiently. */ ssize_t simplefs_write(struct file * filp, const char __user * buf, size_t len, loff_t * ppos) { /* After the commit dd37978c5 in the upstream linux kernel, * we can use just filp->f_inode instead of the * f->f_path.dentry->d_inode redirection */ struct inode *inode; struct simplefs_inode *sfs_inode; struct simplefs_inode *inode_iterator; struct buffer_head *bh; struct super_block *sb; char *buffer; int count; inode = filp->f_path.dentry->d_inode; sfs_inode = SIMPLEFS_INODE(inode); sb = inode->i_sb; if (*ppos + len >= SIMPLEFS_DEFAULT_BLOCK_SIZE) { printk(KERN_ERR "File size write will exceed a block"); return -ENOSPC; } bh = (struct buffer_head *)sb_bread(filp->f_path.dentry->d_inode->i_sb, sfs_inode->data_block_number); if (!bh) { printk(KERN_ERR "Reading the block number [%llu] failed.", sfs_inode->data_block_number); return 0; } buffer = (char *)bh->b_data; /* Move the pointer until the required byte offset */ buffer += *ppos; if (copy_from_user(buffer, buf, len)) { brelse(bh); printk(KERN_ERR "Error copying file contents from the userspace buffer to the kernel space\n"); return -EFAULT; } *ppos += len; mark_buffer_dirty(bh); sync_dirty_buffer(bh); brelse(bh); /* Set new size * sfs_inode->file_size = max(sfs_inode->file_size, *ppos); * * FIXME: What to do if someone writes only some parts in between ? * The above code will also fail in case a file is overwritten with * a shorter buffer */ if (mutex_lock_interruptible(&simplefs_inodes_mgmt_lock)) { printk(KERN_ERR "Failed to acquire mutex lock %s +%d\n", __FILE__, __LINE__); return -EINTR; } /* Save the modified inode */ bh = (struct buffer_head *)sb_bread(sb, SIMPLEFS_INODESTORE_BLOCK_NUMBER); sfs_inode->file_size = *ppos; 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 -EINTR; } count = 0; while (inode_iterator->inode_no != sfs_inode->inode_no && count < SIMPLEFS_SB(sb)->inodes_count) { count++; inode_iterator++; } if (likely(count < SIMPLEFS_SB(sb)->inodes_count)) { inode_iterator->file_size = sfs_inode->file_size; printk(KERN_INFO "The new filesize that is written is: [%llu] and len was: [%lu]\n", sfs_inode->file_size, len); mark_buffer_dirty(bh); sync_dirty_buffer(bh); } else { printk(KERN_ERR "The new filesize could not be stored to the inode."); len = -EIO; } brelse(bh); mutex_unlock(&simplefs_sb_lock); mutex_unlock(&simplefs_inodes_mgmt_lock); return len; }