diff --git a/Makefile b/Makefile index 8d10f24..067be34 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ obj-m += simplefs.o -simplefs-objs := fs.o super.o inode.o file.o dir.o extent.o +simplefs-objs := fs.o super.o inode.o file.o dir.o extent.o hash.o KDIR ?= /lib/modules/$(shell uname -r)/build diff --git a/dir.c b/dir.c index e96484f..95fb7ba 100644 --- a/dir.c +++ b/dir.c @@ -62,9 +62,10 @@ static int simplefs_iterate(struct file *dir, struct dir_context *ctx) for (; remained_nr_files && ei < SIMPLEFS_MAX_EXTENTS; ei++) { if (eblock->extents[ei].ee_start == 0) continue; - + int ei_nr = eblock->extents[ei].nr_files; /* Iterate over blocks in one extent */ - for (bi = 0; bi < eblock->extents[ei].ee_len && remained_nr_files; + for (bi = 0; + bi < eblock->extents[ei].ee_len && remained_nr_files && ei_nr; bi++) { bh2 = sb_bread(sb, eblock->extents[ei].ee_start + bi); if (!bh2) { @@ -80,12 +81,13 @@ static int simplefs_iterate(struct file *dir, struct dir_context *ctx) continue; } - for (fi = 0; fi < SIMPLEFS_FILES_PER_BLOCK;) { + for (fi = 0; fi < SIMPLEFS_FILES_PER_BLOCK && dblock->nr_files;) { if (dblock->files[fi].inode != 0) { if (offset) { offset--; } else { remained_nr_files--; + ei_nr--; if (!dir_emit(ctx, dblock->files[fi].filename, SIMPLEFS_FILENAME_LEN, dblock->files[fi].inode, DT_UNKNOWN)) { diff --git a/hash.c b/hash.c new file mode 100644 index 0000000..e19a15e --- /dev/null +++ b/hash.c @@ -0,0 +1,9 @@ +#include +#include +#include "simplefs.h" + +uint32_t simplefs_hash(struct dentry *dentry) +{ + return full_name_hash(dentry, dentry->d_name.name, + strlen(dentry->d_name.name)); +} diff --git a/inode.c b/inode.c index 4ba8b73..3bcd57a 100644 --- a/inode.c +++ b/inode.c @@ -110,6 +110,77 @@ struct inode *simplefs_iget(struct super_block *sb, unsigned long ino) return ERR_PTR(ret); } +static int __file_lookup(struct inode *dir, + struct dentry *dentry, + int *ei, + int *bi, + int *fi) +{ + struct super_block *sb = dir->i_sb; + struct simplefs_inode_info *ci_dir = SIMPLEFS_INODE(dir); + struct buffer_head *bh = NULL, *bh2 = NULL; + struct simplefs_file_ei_block *eblock = NULL; + struct simplefs_dir_block *dblock = NULL; + struct simplefs_file *f = NULL; + int _ei, _bi, _fi, ret = 0; + int idx_ei, idx_bi; + uint32_t hash_code; + /* Check filename length */ + + /* Read the directory block on disk */ + bh = sb_bread(sb, ci_dir->ei_block); + if (!bh) + return -EIO; + eblock = (struct simplefs_file_ei_block *) bh->b_data; + hash_code = simplefs_hash(dentry) % + (SIMPLEFS_MAX_EXTENTS * SIMPLEFS_MAX_BLOCKS_PER_EXTENT); + _ei = hash_code / SIMPLEFS_MAX_BLOCKS_PER_EXTENT; + _bi = hash_code % SIMPLEFS_MAX_BLOCKS_PER_EXTENT; + + for (idx_ei = 0; idx_ei < SIMPLEFS_MAX_EXTENTS; _ei++, idx_ei++) { + if (unlikely(_ei >= SIMPLEFS_MAX_EXTENTS)) + _ei %= SIMPLEFS_MAX_EXTENTS; + + if (!eblock->extents[_ei].ee_start) + continue; + + /* Iterate blocks in extent */ + for (idx_bi = 0; idx_bi < eblock->extents[_ei].ee_len; + _bi++, idx_bi++) { + if (unlikely(_bi >= eblock->extents[_ei].ee_len)) + _bi %= eblock->extents[_ei].ee_len; + + bh2 = sb_bread(sb, eblock->extents[_ei].ee_start + _bi); + if (!bh2) { + ret = -EIO; + goto file_search_end; + } + + + dblock = (struct simplefs_dir_block *) bh2->b_data; + /* Search file in ei_block */ + for (_fi = 0; _fi < dblock->nr_files;) { + f = &dblock->files[_fi]; + if (f->inode && !strncmp(f->filename, dentry->d_name.name, + SIMPLEFS_FILENAME_LEN)) { + *ei = _ei; + *bi = _bi; + *fi = _fi; + + brelse(bh); + brelse(bh2); + return 1; + } + _fi += dblock->files[_fi].nr_blk; + } + brelse(bh2); + } + } +file_search_end: + brelse(bh); + return ret; +} + /* Search for a dentry in dir. * Fills dentry with NULL if not found in dir, or with the corresponding inode * if found. @@ -126,51 +197,32 @@ static struct dentry *simplefs_lookup(struct inode *dir, struct buffer_head *bh = NULL, *bh2 = NULL; struct simplefs_file_ei_block *eblock = NULL; struct simplefs_dir_block *dblock = NULL; - struct simplefs_file *f = NULL; - int ei, bi, fi; + int ei, bi, fi, chk; /* Check filename length */ if (dentry->d_name.len > SIMPLEFS_FILENAME_LEN) return ERR_PTR(-ENAMETOOLONG); /* Read the directory block on disk */ + + chk = __file_lookup(dir, dentry, &ei, &bi, &fi); + if (chk != 1) { + if (!chk) + return ERR_PTR(-chk); + goto search_end; + } + bh = sb_bread(sb, ci_dir->ei_block); if (!bh) return ERR_PTR(-EIO); - eblock = (struct simplefs_file_ei_block *) bh->b_data; - - /* Search for the file in directory */ - for (ei = 0; ei < SIMPLEFS_MAX_EXTENTS; ei++) { - if (!eblock->extents[ei].ee_start) - break; - /* Iterate blocks in extent */ - for (bi = 0; bi < eblock->extents[ei].ee_len; bi++) { - bh2 = sb_bread(sb, eblock->extents[ei].ee_start + bi); - if (!bh2) - return ERR_PTR(-EIO); + eblock = (struct simplefs_file_ei_block *) bh->b_data; + bh2 = sb_bread(sb, eblock->extents[ei].ee_start + bi); + if (!bh2) + return ERR_PTR(-EIO); - dblock = (struct simplefs_dir_block *) bh2->b_data; - - /* Search file in ei_block */ - for (fi = 0; fi < dblock->nr_files;) { - f = &dblock->files[fi]; - if (!f->inode) { - brelse(bh2); - goto search_end; - } - if (!strncmp(f->filename, dentry->d_name.name, - SIMPLEFS_FILENAME_LEN)) { - inode = simplefs_iget(sb, f->inode); - brelse(bh2); - goto search_end; - } - fi += dblock->files[fi].nr_blk; - } - brelse(bh2); - bh2 = NULL; - } - } + dblock = (struct simplefs_dir_block *) bh2->b_data; + inode = simplefs_iget(sb, (&dblock->files[fi])->inode); search_end: brelse(bh); @@ -314,26 +366,26 @@ static struct inode *simplefs_new_inode(struct inode *dir, mode_t mode) } static uint32_t simplefs_get_available_ext_idx( - int *dir_nr_files, - struct simplefs_file_ei_block *eblock) + struct simplefs_file_ei_block *eblock, + uint32_t hash_code) { - int ei = 0; + int ei = 0, idx; uint32_t first_empty_blk = -1; - for (ei = 0; ei < SIMPLEFS_MAX_EXTENTS; ei++) { - if (eblock->extents[ei].ee_start && - eblock->extents[ei].nr_files != SIMPLEFS_FILES_PER_EXT) { + + ei = hash_code / SIMPLEFS_MAX_BLOCKS_PER_EXTENT; + + for (idx = 0; idx < SIMPLEFS_MAX_EXTENTS; ei++, idx++) { + if (unlikely(ei >= SIMPLEFS_MAX_EXTENTS)) + ei %= SIMPLEFS_MAX_EXTENTS; + + if (!eblock->extents[ei].ee_start) { first_empty_blk = ei; break; - } else if (!eblock->extents[ei].ee_start) { - if (first_empty_blk == -1) - first_empty_blk = ei; - } else { - *dir_nr_files -= eblock->extents[ei].nr_files; - if (first_empty_blk == -1 && !*dir_nr_files) - first_empty_blk = ei + 1; - } - if (!*dir_nr_files) + } else if (eblock->extents[ei].ee_start && + eblock->extents[ei].nr_files != SIMPLEFS_FILES_PER_EXT) { + first_empty_blk = ei; break; + } } return first_empty_blk; } @@ -427,12 +479,12 @@ static int simplefs_create(struct inode *dir, struct simplefs_dir_block *dblock; char *fblock; struct buffer_head *bh, *bh2; - uint32_t dir_nr_files = 0, avail; + uint32_t avail; #if SIMPLEFS_AT_LEAST(6, 6, 0) && SIMPLEFS_LESS_EQUAL(6, 7, 0) struct timespec64 cur_time; #endif - int ret = 0, alloc = false; - int bi = 0; + int ret = 0, alloc = false, hash_code; + int bi = 0, idx_bi; /* Check filename length */ if (strlen(dentry->d_name.name) > SIMPLEFS_FILENAME_LEN) @@ -472,11 +524,11 @@ static int simplefs_create(struct inode *dir, mark_buffer_dirty(bh2); brelse(bh2); - dir_nr_files = eblock->nr_files; - avail = simplefs_get_available_ext_idx(&dir_nr_files, eblock); - + hash_code = simplefs_hash(dentry) % + (SIMPLEFS_MAX_EXTENTS * SIMPLEFS_MAX_BLOCKS_PER_EXTENT); + avail = simplefs_get_available_ext_idx(eblock, hash_code); /* if there is not any empty space, alloc new one */ - if (!dir_nr_files && !eblock->extents[avail].ee_start) { + if (!eblock->extents[avail].ee_start) { ret = simplefs_put_new_ext(sb, avail, eblock); switch (ret) { case -ENOSPC: @@ -491,7 +543,11 @@ static int simplefs_create(struct inode *dir, /* TODO: fix from 8 to dynamic value */ /* Find which simplefs_dir_block has free space */ - for (bi = 0; bi < eblock->extents[avail].ee_len; bi++) { + bi = hash_code % eblock->extents[avail].ee_len; + for (idx_bi = 0; idx_bi < eblock->extents[avail].ee_len; bi++, idx_bi++) { + if (unlikely(bi >= eblock->extents[avail].ee_len)) + bi %= eblock->extents[avail].ee_len; + bh2 = sb_bread(sb, eblock->extents[avail].ee_start + bi); if (!bh2) { ret = -EIO; @@ -551,44 +607,60 @@ static int simplefs_create(struct inode *dir, return ret; } -static int simplefs_remove_from_dir(struct inode *dir, struct dentry *dentry) +static int simplefs_remove_from_dir(struct inode *dir, + struct dentry *dentry, + int *ret_ei) { struct super_block *sb = dir->i_sb; struct inode *inode = d_inode(dentry); struct buffer_head *bh = NULL, *bh2 = NULL; struct simplefs_file_ei_block *eblock = NULL; struct simplefs_dir_block *dirblk = NULL; - int ei = 0, bi = 0, fi = 0; - int ret = 0, found = false; - + int ei = 0, bi = 0, fi = 0, idx_bi; + int ret = 0, found = false, i, blk_nr_files, dir_nr_files; + uint32_t hash_code; /* Read parent directory index */ bh = sb_bread(sb, SIMPLEFS_INODE(dir)->ei_block); if (!bh) return -EIO; eblock = (struct simplefs_file_ei_block *) bh->b_data; + dir_nr_files = eblock->nr_files; + + + hash_code = simplefs_hash(dentry) % + (SIMPLEFS_MAX_EXTENTS * SIMPLEFS_MAX_BLOCKS_PER_EXTENT); + ei = hash_code / SIMPLEFS_MAX_BLOCKS_PER_EXTENT; + bi = hash_code % SIMPLEFS_MAX_BLOCKS_PER_EXTENT; + + for (; dir_nr_files; ei++) { + if (unlikely(ei >= SIMPLEFS_MAX_EXTENTS)) + ei %= SIMPLEFS_MAX_EXTENTS; - int dir_nr_files = eblock->nr_files; - for (ei = 0; dir_nr_files; ei++) { if (eblock->extents[ei].ee_start) { dir_nr_files -= eblock->extents[ei].nr_files; - for (bi = 0; bi < eblock->extents[ei].ee_len; bi++) { + /* simplefs_extent */ + for (idx_bi = 0; idx_bi < eblock->extents[ei].ee_len; + bi++, idx_bi++) { + if (unlikely(bi >= eblock->extents[ei].ee_len)) + bi %= eblock->extents[ei].ee_len; + bh2 = sb_bread(sb, eblock->extents[ei].ee_start + bi); if (!bh2) { ret = -EIO; goto release_bh; } + /* simplefs_dir_block */ dirblk = (struct simplefs_dir_block *) bh2->b_data; - int blk_nr_files = dirblk->nr_files; + blk_nr_files = dirblk->nr_files; for (fi = 0; blk_nr_files && fi < SIMPLEFS_FILES_PER_BLOCK;) { if (dirblk->files[fi].inode) { if (dirblk->files[fi].inode == inode->i_ino && !strcmp(dirblk->files[fi].filename, dentry->d_name.name)) { - found = true; dirblk->files[fi].inode = 0; /* merge the empty data */ - for (int i = fi - 1; i >= 0; i--) { + for (i = fi - 1; i >= 0; i--) { if (dirblk->files[i].inode != 0 || i == 0) { dirblk->files[i].nr_blk += dirblk->files[fi].nr_blk; @@ -601,6 +673,7 @@ static int simplefs_remove_from_dir(struct inode *dir, struct dentry *dentry) mark_buffer_dirty(bh2); brelse(bh2); found = true; + *ret_ei = ei; goto found_data; } blk_nr_files--; @@ -633,7 +706,7 @@ static int simplefs_unlink(struct inode *dir, struct dentry *dentry) struct simplefs_sb_info *sbi = SIMPLEFS_SB(sb); struct inode *inode = d_inode(dentry); struct buffer_head *bh = NULL, *bh2 = NULL; - struct simplefs_file_ei_block *file_block = NULL; + struct simplefs_file_ei_block *eblk = NULL; char *block; #if SIMPLEFS_AT_LEAST(6, 6, 0) && SIMPLEFS_LESS_EQUAL(6, 7, 0) struct timespec64 cur_time; @@ -644,9 +717,20 @@ static int simplefs_unlink(struct inode *dir, struct dentry *dentry) uint32_t ino = inode->i_ino; uint32_t bno = 0; - ret = simplefs_remove_from_dir(dir, dentry); + ret = simplefs_remove_from_dir(dir, dentry, &ei); + if (ret != 0) return ret; + /* if extent[i] file number is 0, reclaim the extent[i] block*/ + bh = sb_bread(sb, SIMPLEFS_INODE(dir)->ei_block); + if (!bh) + return -EIO; + + eblk = (struct simplefs_file_ei_block *) bh->b_data; + if (!eblk->extents[ei].nr_files) { + put_blocks(sbi, eblk->extents[ei].ee_start, eblk->extents[ei].ee_len); + memset(&eblk->extents[ei], 0, sizeof(struct simplefs_extent)); + } if (S_ISLNK(inode->i_mode)) goto clean_inode; @@ -683,18 +767,16 @@ static int simplefs_unlink(struct inode *dir, struct dentry *dentry) bh = sb_bread(sb, bno); if (!bh) goto clean_inode; - file_block = (struct simplefs_file_ei_block *) bh->b_data; - + eblk = (struct simplefs_file_ei_block *) bh->b_data; for (ei = 0; ei < SIMPLEFS_MAX_EXTENTS; ei++) { - if (!file_block->extents[ei].ee_start) + if (!eblk->extents[ei].ee_start) break; - put_blocks(sbi, file_block->extents[ei].ee_start, - file_block->extents[ei].ee_len); + put_blocks(sbi, eblk->extents[ei].ee_start, eblk->extents[ei].ee_len); /* Scrub the extent */ - for (bi = 0; bi < file_block->extents[ei].ee_len; bi++) { - bh2 = sb_bread(sb, file_block->extents[ei].ee_start + bi); + for (bi = 0; bi < eblk->extents[ei].ee_len; bi++) { + bh2 = sb_bread(sb, eblk->extents[ei].ee_start + bi); if (!bh2) continue; block = (char *) bh2->b_data; @@ -705,7 +787,7 @@ static int simplefs_unlink(struct inode *dir, struct dentry *dentry) } /* Scrub index block */ - memset(file_block, 0, SIMPLEFS_BLOCK_SIZE); + memset(eblk, 0, SIMPLEFS_BLOCK_SIZE); mark_buffer_dirty(bh); brelse(bh); @@ -741,186 +823,187 @@ static int simplefs_unlink(struct inode *dir, struct dentry *dentry) #if SIMPLEFS_AT_LEAST(6, 3, 0) static int simplefs_rename(struct mnt_idmap *id, - struct inode *old_dir, - struct dentry *old_dentry, - struct inode *new_dir, - struct dentry *new_dentry, + struct inode *src_dir, + struct dentry *src_dentry, + struct inode *dest_dir, + struct dentry *dest_dentry, unsigned int flags) #elif SIMPLEFS_AT_LEAST(5, 12, 0) static int simplefs_rename(struct user_namespace *ns, - struct inode *old_dir, - struct dentry *old_dentry, - struct inode *new_dir, - struct dentry *new_dentry, + struct inode *src_dir, + struct dentry *src_dentry, + struct inode *dest_dir, + struct dentry *dest_dentry, unsigned int flags) #else -static int simplefs_rename(struct inode *old_dir, - struct dentry *old_dentry, - struct inode *new_dir, - struct dentry *new_dentry, +static int simplefs_rename(struct inode *src_dir, + struct dentry *src_dentry, + struct inode *dest_dir, + struct dentry *dest_dentry, unsigned int flags) #endif { - struct super_block *sb = old_dir->i_sb; - struct simplefs_inode_info *ci_new = SIMPLEFS_INODE(new_dir); - struct inode *src = d_inode(old_dentry); - struct buffer_head *bh_new = NULL, *bh2 = NULL; - struct simplefs_file_ei_block *eblock_new = NULL; + struct super_block *sb = src_dir->i_sb; + struct simplefs_sb_info *sbi = SIMPLEFS_SB(sb); + struct simplefs_inode_info *ci_src = SIMPLEFS_INODE(src_dir); + struct simplefs_inode_info *ci_dest = SIMPLEFS_INODE(dest_dir); + struct inode *src_in = d_inode(src_dentry); + struct buffer_head *bh_fei_blk_src = NULL, *bh_fei_blk_dest = NULL, + *bh_ext = NULL; + struct simplefs_file_ei_block *eblk_src = NULL, *eblk_dest = NULL; struct simplefs_dir_block *dblock = NULL; #if SIMPLEFS_AT_LEAST(6, 6, 0) && SIMPLEFS_LESS_EQUAL(6, 7, 0) struct timespec64 cur_time; #endif - int new_pos = -1, ret = 0; - int ei = 0, bi = 0, fi = 0, bno = 0; + int ret = 0, new_pos = 0, idx, chk; + int src_ei = 0, dest_ei = 0, bi = 0, fi = 0; /* fail with these unsupported flags */ if (flags & (RENAME_EXCHANGE | RENAME_WHITEOUT)) return -EINVAL; /* Check if filename is not too long */ - if (strlen(new_dentry->d_name.name) > SIMPLEFS_FILENAME_LEN) + if (strlen(dest_dentry->d_name.name) > SIMPLEFS_FILENAME_LEN) return -ENAMETOOLONG; - /* Fail if new_dentry exists or if new_dir is full */ - bh_new = sb_bread(sb, ci_new->ei_block); - if (!bh_new) + /* Fail if dest_dentry exists or if dest_dir is full */ + bh_fei_blk_dest = sb_bread(sb, ci_dest->ei_block); + if (!bh_fei_blk_dest) return -EIO; - eblock_new = (struct simplefs_file_ei_block *) bh_new->b_data; - for (ei = 0; new_pos < 0 && ei < SIMPLEFS_MAX_EXTENTS; ei++) { - if (!eblock_new->extents[ei].ee_start) - break; - - for (bi = 0; new_pos < 0 && bi < eblock_new->extents[ei].ee_len; bi++) { - bh2 = sb_bread(sb, eblock_new->extents[ei].ee_start + bi); - if (!bh2) { - ret = -EIO; - goto release_new; - } - - dblock = (struct simplefs_dir_block *) bh2->b_data; - int blk_nr_files = dblock->nr_files; - for (fi = 0; blk_nr_files;) { - /* src and target are the same dir (inode is same) */ - if (new_dir == old_dir) { - if (dblock->files[fi].inode && - !strncmp(dblock->files[fi].filename, - old_dentry->d_name.name, - SIMPLEFS_FILENAME_LEN)) { - strncpy(dblock->files[fi].filename, - new_dentry->d_name.name, SIMPLEFS_FILENAME_LEN); - mark_buffer_dirty(bh2); - brelse(bh2); - goto release_new; - } - } else { - /* src and target are different, then check if the - same name in the target directory */ - if (dblock->files[fi].inode && - !strncmp(dblock->files[fi].filename, - new_dentry->d_name.name, - SIMPLEFS_FILENAME_LEN)) { - brelse(bh2); - ret = -EEXIST; - goto release_new; - } - /* find the empty index in target directory */ - if (new_pos < 0 && dblock->files[fi].nr_blk != 1) { - new_pos = fi + 1; - break; - } - } - blk_nr_files--; - fi += dblock->files[fi].nr_blk; - } - brelse(bh2); + eblk_dest = (struct simplefs_file_ei_block *) bh_fei_blk_dest->b_data; + /* Search for the file in directory */ + chk = __file_lookup(dest_dir, dest_dentry, &dest_ei, &bi, &fi); + if (chk != 0) { + if (chk == 1) { + ret = -EEXIST; + } else if (chk < 0) { + ret = chk; } + goto release_new; } + /* If new directory is full, fail */ - if (new_pos < 0 && eblock_new->nr_files == SIMPLEFS_FILES_PER_EXT) { + if (eblk_dest->nr_files == SIMPLEFS_MAX_SUBFILES) { ret = -EMLINK; goto release_new; } /* insert in new parent directory */ /* Get new freeblocks for extent if needed*/ - if (new_pos < 0) { - bno = get_free_blocks(sb, SIMPLEFS_MAX_BLOCKS_PER_EXTENT); - if (!bno) { + if (!eblk_dest->extents[dest_ei].ee_start) { + ret = simplefs_put_new_ext(sb, dest_ei, eblk_dest); + switch (ret) { + case -ENOSPC: ret = -ENOSPC; goto release_new; + case -EIO: + ret = -EIO; + goto release_new; } - eblock_new->extents[ei].ee_start = bno; - eblock_new->extents[ei].ee_len = SIMPLEFS_MAX_BLOCKS_PER_EXTENT; - eblock_new->extents[ei].ee_block = - ei ? eblock_new->extents[ei - 1].ee_block + - eblock_new->extents[ei - 1].ee_len - : 0; - bh2 = sb_bread(sb, eblock_new->extents[ei].ee_start + 0); - if (!bh2) { + mark_buffer_dirty(bh_fei_blk_dest); + new_pos = 1; + } else { + for (idx = 0; idx < SIMPLEFS_MAX_EXTENTS; dest_ei++, idx++) { + if (unlikely(dest_ei >= SIMPLEFS_MAX_EXTENTS)) + dest_ei %= SIMPLEFS_MAX_EXTENTS; + + if (eblk_dest->extents[dest_ei].nr_files != SIMPLEFS_FILES_PER_EXT) + break; + } + } + /* copy src info into new directory*/ + for (bi = 0; bi < eblk_dest->extents[dest_ei].ee_len; bi++) { + bh_ext = sb_bread(sb, eblk_dest->extents[dest_ei].ee_start + bi); + if (!bh_ext) { ret = -EIO; goto put_block; } - dblock = (struct simplefs_dir_block *) bh2->b_data; - mark_buffer_dirty(bh_new); - new_pos = 0; + dblock = (struct simplefs_dir_block *) bh_ext->b_data; + + /* check if dir block is full*/ + if (dblock->nr_files != SIMPLEFS_FILES_PER_BLOCK) { + simplefs_set_file_into_dir(dblock, src_in->i_ino, + dest_dentry->d_name.name); + eblk_dest->extents[dest_ei].nr_files++; + eblk_dest->nr_files++; + mark_buffer_dirty(bh_ext); + mark_buffer_dirty(bh_fei_blk_dest); + brelse(bh_ext); + break; + } + brelse(bh_ext); + } + + /* remove target from old parent directory */ + ret = simplefs_remove_from_dir(src_dir, src_dentry, &src_ei); + if (ret != 0) + goto rm_new; + + /* if extent[i] file number is 0, reclaim the extent[i] block*/ + bh_fei_blk_src = sb_bread(sb, ci_src->ei_block); + if (!bh_fei_blk_src) { + ret = -EIO; + goto rm_new; + } + + eblk_src = (struct simplefs_file_ei_block *) bh_fei_blk_src->b_data; + if (!eblk_src->extents[src_ei].nr_files) { + put_blocks(sbi, eblk_src->extents[src_ei].ee_start, + eblk_src->extents[src_ei].ee_len); + memset(&eblk_src->extents[src_ei], 0, sizeof(struct simplefs_extent)); + mark_buffer_dirty(bh_fei_blk_src); } - dblock->files[new_pos].inode = src->i_ino; - strncpy(dblock->files[new_pos].filename, new_dentry->d_name.name, - SIMPLEFS_FILENAME_LEN); - mark_buffer_dirty(bh2); - brelse(bh2); /* Update new parent inode metadata */ #if SIMPLEFS_AT_LEAST(6, 7, 0) - simple_inode_init_ts(new_dir); + simple_inode_init_ts(dest_dir); #elif SIMPLEFS_AT_LEAST(6, 6, 0) - cur_time = current_time(new_dir); - new_dir->i_atime = new_dir->i_mtime = cur_time; - inode_set_ctime_to_ts(new_dir, cur_time); + cur_time = current_time(dest_dir); + dest_dir->i_atime = dest_dir->i_mtime = cur_time; + inode_set_ctime_to_ts(dest_dir, cur_time); #else - new_dir->i_atime = new_dir->i_ctime = new_dir->i_mtime = - current_time(new_dir); + dest_dir->i_atime = dest_dir->i_ctime = dest_dir->i_mtime = + current_time(dest_dir); #endif - if (S_ISDIR(src->i_mode)) - inc_nlink(new_dir); - mark_inode_dirty(new_dir); - - /* remove target from old parent directory */ - ret = simplefs_remove_from_dir(old_dir, old_dentry); - if (ret != 0) - goto release_new; + if (S_ISDIR(src_in->i_mode)) + inc_nlink(dest_dir); + mark_inode_dirty(dest_dir); - /* Update old parent inode metadata */ + /* Update old parent inode metadata */ #if SIMPLEFS_AT_LEAST(6, 7, 0) - simple_inode_init_ts(old_dir); + simple_inode_init_ts(src_dir); #elif SIMPLEFS_AT_LEAST(6, 6, 0) - cur_time = current_time(old_dir); - old_dir->i_atime = old_dir->i_mtime = cur_time; - inode_set_ctime_to_ts(old_dir, cur_time); + cur_time = current_time(src_dir); + src_dir->i_atime = src_dir->i_mtime = cur_time; + inode_set_ctime_to_ts(src_dir, cur_time); #else - old_dir->i_atime = old_dir->i_ctime = old_dir->i_mtime = - current_time(old_dir); + src_dir->i_atime = src_dir->i_ctime = src_dir->i_mtime = + current_time(src_dir); #endif - if (S_ISDIR(src->i_mode)) - drop_nlink(old_dir); - mark_inode_dirty(old_dir); + if (S_ISDIR(src_in->i_mode)) + drop_nlink(src_dir); + mark_inode_dirty(src_dir); - return ret; + goto release_new; + +rm_new: + simplefs_remove_from_dir(dest_dir, dest_dentry, &dest_ei); put_block: - if (eblock_new->extents[ei].ee_start) { - put_blocks(SIMPLEFS_SB(sb), eblock_new->extents[ei].ee_start, - eblock_new->extents[ei].ee_len); - memset(&eblock_new->extents[ei], 0, sizeof(struct simplefs_extent)); + if (eblk_dest->extents[dest_ei].ee_start && new_pos) { + put_blocks(SIMPLEFS_SB(sb), eblk_dest->extents[dest_ei].ee_start, + eblk_dest->extents[dest_ei].ee_len); + memset(&eblk_dest->extents[dest_ei], 0, sizeof(struct simplefs_extent)); } + release_new: - brelse(bh_new); + brelse(bh_fei_blk_dest); return ret; } @@ -975,19 +1058,19 @@ static int simplefs_rmdir(struct inode *dir, struct dentry *dentry) return simplefs_unlink(dir, dentry); } -static int simplefs_link(struct dentry *old_dentry, +static int simplefs_link(struct dentry *src_dentry, struct inode *dir, struct dentry *dentry) { - struct inode *old_inode = d_inode(old_dentry); + struct inode *old_inode = d_inode(src_dentry); struct super_block *sb = old_inode->i_sb; struct simplefs_inode_info *ci_dir = SIMPLEFS_INODE(dir); struct simplefs_file_ei_block *eblock = NULL; struct simplefs_dir_block *dblock; struct buffer_head *bh = NULL, *bh2 = NULL; int ret = 0, alloc = false; - int ei = 0, bi = 0; - uint32_t avail; + int ei = 0, bi = 0, idx_bi; + uint32_t avail, hash_code; bh = sb_bread(sb, ci_dir->ei_block); if (!bh) @@ -1000,11 +1083,13 @@ static int simplefs_link(struct dentry *old_dentry, goto end; } - int dir_nr_files = eblock->nr_files; - avail = simplefs_get_available_ext_idx(&dir_nr_files, eblock); + + hash_code = simplefs_hash(dentry) % + (SIMPLEFS_MAX_EXTENTS * SIMPLEFS_MAX_BLOCKS_PER_EXTENT); + avail = simplefs_get_available_ext_idx(eblock, hash_code); /* if there is not any empty space, alloc new one */ - if (!dir_nr_files && !eblock->extents[avail].ee_start) { + if (!eblock->extents[avail].ee_start) { ret = simplefs_put_new_ext(sb, avail, eblock); switch (ret) { case -ENOSPC: @@ -1019,12 +1104,17 @@ static int simplefs_link(struct dentry *old_dentry, /* TODO: fix from 8 to dynamic value */ /* Find which simplefs_dir_block has free space */ - for (bi = 0; bi < eblock->extents[avail].ee_len; bi++) { + bi = hash_code % eblock->extents[avail].ee_len; + for (idx_bi = 0; idx_bi < eblock->extents[avail].ee_len; bi++, idx_bi++) { + if (unlikely(bi >= eblock->extents[avail].ee_len)) + bi %= eblock->extents[avail].ee_len; + bh2 = sb_bread(sb, eblock->extents[avail].ee_start + bi); if (!bh2) { ret = -EIO; goto put_block; } + dblock = (struct simplefs_dir_block *) bh2->b_data; if (dblock->nr_files != SIMPLEFS_FILES_PER_BLOCK) break; @@ -1034,7 +1124,7 @@ static int simplefs_link(struct dentry *old_dentry, /* write the file info into simplefs_dir_block */ simplefs_set_file_into_dir(dblock, old_inode->i_ino, dentry->d_name.name); - + eblock->extents[avail].nr_files++; eblock->nr_files++; mark_buffer_dirty(bh2); mark_buffer_dirty(bh); @@ -1082,8 +1172,8 @@ static int simplefs_symlink(struct inode *dir, struct simplefs_dir_block *dblock = NULL; struct buffer_head *bh = NULL, *bh2 = NULL; int ret = 0, alloc = false; - int ei = 0, bi = 0; - uint32_t avail; + int ei = 0, bi = 0, idx_bi; + uint32_t avail, hash_code; /* Check if symlink content is not too long */ if (l > sizeof(ci->i_data)) @@ -1101,11 +1191,13 @@ static int simplefs_symlink(struct inode *dir, goto end; } - int dir_nr_files = eblock->nr_files; - avail = simplefs_get_available_ext_idx(&dir_nr_files, eblock); + + hash_code = simplefs_hash(dentry) % + (SIMPLEFS_MAX_EXTENTS * SIMPLEFS_MAX_BLOCKS_PER_EXTENT); + avail = simplefs_get_available_ext_idx(eblock, hash_code); /* if there is not any empty space, alloc new one */ - if (!dir_nr_files && !eblock->extents[avail].ee_start) { + if (!eblock->extents[avail].ee_start) { ret = simplefs_put_new_ext(sb, avail, eblock); switch (ret) { case -ENOSPC: @@ -1120,12 +1212,17 @@ static int simplefs_symlink(struct inode *dir, /* TODO: fix from 8 to dynamic value */ /* Find which simplefs_dir_block has free space */ - for (bi = 0; bi < eblock->extents[avail].ee_len; bi++) { + bi = hash_code % eblock->extents[avail].ee_len; + for (idx_bi = 0; idx_bi < eblock->extents[avail].ee_len; bi++, idx_bi++) { + if (unlikely(bi >= eblock->extents[avail].ee_len)) + bi %= eblock->extents[avail].ee_len; + bh2 = sb_bread(sb, eblock->extents[avail].ee_start + bi); if (!bh2) { ret = -EIO; goto put_block; } + dblock = (struct simplefs_dir_block *) bh2->b_data; if (dblock->nr_files != SIMPLEFS_FILES_PER_BLOCK) break; @@ -1136,6 +1233,7 @@ static int simplefs_symlink(struct inode *dir, /* write the file info into simplefs_dir_block */ simplefs_set_file_into_dir(dblock, inode->i_ino, dentry->d_name.name); + eblock->extents[avail].nr_files++; eblock->nr_files++; mark_buffer_dirty(bh2); mark_buffer_dirty(bh); diff --git a/simplefs.h b/simplefs.h index f99353f..66f9d88 100644 --- a/simplefs.h +++ b/simplefs.h @@ -115,6 +115,9 @@ struct dentry *simplefs_mount(struct file_system_type *fs_type, const char *dev_name, void *data); +/* hash.c */ +uint32_t simplefs_hash(struct dentry *dentry); + /* file functions */ extern const struct file_operations simplefs_file_ops; extern const struct file_operations simplefs_dir_ops;