diff --git a/mkfs-simplefs.c b/mkfs-simplefs.c index 44a8955..b75a651 100644 --- a/mkfs-simplefs.c +++ b/mkfs-simplefs.c @@ -9,8 +9,8 @@ #include "simple.h" -const uint64_t WELCOMEFILE_DATABLOCK_NUMBER = 3; -const uint64_t WELCOMEFILE_INODE_NUMBER = 2; +#define WELCOMEFILE_DATABLOCK_NUMBER (SIMPLEFS_LAST_RESERVED_BLOCK + 1) +#define WELCOMEFILE_INODE_NUMBER (SIMPLEFS_LAST_RESERVED_INODE + 1) static int write_superblock(int fd) { @@ -18,10 +18,9 @@ static int write_superblock(int fd) .version = 1, .magic = SIMPLEFS_MAGIC, .block_size = SIMPLEFS_DEFAULT_BLOCK_SIZE, - /* One inode for rootdirectory and another for a welcome file that we are going to create */ - .inodes_count = 2, + .inodes_count = WELCOMEFILE_INODE_NUMBER, /* FIXME: Free blocks management is not implemented yet */ - .free_blocks = (~0) & ~(1 << WELCOMEFILE_DATABLOCK_NUMBER), + .free_blocks = (~0) & ~(1 << SIMPLEFS_LAST_RESERVED_BLOCK), }; ssize_t ret; @@ -37,7 +36,7 @@ static int write_superblock(int fd) return 0; } -static int write_inode_store(int fd) +static int write_root_inode(int fd) { ssize_t ret; @@ -59,8 +58,26 @@ static int write_inode_store(int fd) printf("root directory inode written succesfully\n"); return 0; } +static int write_journal_inode(int fd) +{ + ssize_t ret; -static int write_inode(int fd, const struct simplefs_inode *i) + struct simplefs_inode journal; + + journal.inode_no = SIMPLEFS_JOURNAL_INODE_NUMBER; + journal.data_block_number = SIMPLEFS_JOURNAL_BLOCK_NUMBER; + + ret = write(fd, &journal, sizeof(journal)); + + if (ret != sizeof(journal)) { + printf("Error while writing journal inode. Retry your mkfs\n"); + return -1; + } + + printf("journal inode written succesfully\n"); + return 0; +} +static int write_welcome_inode(int fd, const struct simplefs_inode *i) { off_t nbytes; ssize_t ret; @@ -73,7 +90,7 @@ static int write_inode(int fd, const struct simplefs_inode *i) } printf("welcomefile inode written succesfully\n"); - nbytes = SIMPLEFS_DEFAULT_BLOCK_SIZE - sizeof(*i) - sizeof(*i); + nbytes = SIMPLEFS_DEFAULT_BLOCK_SIZE - (sizeof(*i) * 3); ret = lseek(fd, nbytes, SEEK_CUR); if (ret == (off_t)-1) { printf @@ -82,9 +99,23 @@ static int write_inode(int fd, const struct simplefs_inode *i) } printf - ("inode store padding bytes (after the two inodes) written sucessfully\n"); + ("inode store padding bytes (after the three inodes) written sucessfully\n"); return 0; } + +int write_journal(int fd) +{ + ssize_t ret; + ret = lseek(fd, SIMPLEFS_DEFAULT_BLOCK_SIZE * SIMPLEFS_JOURNAL_BLOCKS, SEEK_CUR); + if (ret == (off_t)-1) { + printf("Can't write journal. Retry you mkfs\n"); + return -1; + } + + printf("Journal written successfully\n"); + return 0; +} + int write_dirent(int fd, const struct simplefs_dir_record *record) { ssize_t nbytes = sizeof(*record), ret; @@ -154,11 +185,17 @@ int main(int argc, char *argv[]) do { if (write_superblock(fd)) break; - if (write_inode_store(fd)) + + if (write_root_inode(fd)) + break; + if (write_journal_inode(fd)) + break; + if (write_welcome_inode(fd, &welcome)) break; - if (write_inode(fd, &welcome)) + if (write_journal(fd)) break; + if (write_dirent(fd, &record)) break; if (write_block(fd, welcomefile_body, welcome.file_size)) diff --git a/simple-test.sh b/simple-test.sh index 5985566..f499066 100755 --- a/simple-test.sh +++ b/simple-test.sh @@ -15,10 +15,18 @@ set -e +echo 1 >| /sys/module/jbd2/parameters/jbd2_debug + root_pwd="$PWD" test_dir="test-dir-$RANDOM" test_mount_point="test-mount-point-$RANDOM" +test_journal_dev="" +function create_journal() +{ + dd bs=1M count=10 if=/dev/zero of="$1" + mke2fs -b 4096 -O journal_dev "$1" +} function create_test_image() { dd bs=4096 count=100 if=/dev/zero of="$1" @@ -27,12 +35,14 @@ function create_test_image() function mount_fs_image() { insmod simplefs.ko - mount -o loop,owner,group,users -t simplefs "$1" "$2" + test_journal_dev=$(losetup -f --show "$1") + mount -o loop,owner,group,users,journal_path="$test_journal_dev" -t simplefs "$2" "$3" dmesg | tail -n20 } function unmount_fs() { umount "$1" + losetup -d $test_journal_dev rmmod simplefs.ko dmesg | tail -n20 } @@ -106,15 +116,16 @@ cleanup trap cleanup SIGINT EXIT mkdir "$test_dir" "$test_mount_point" create_test_image "$test_dir/image" +create_journal "$test_dir/journal" # 1 -mount_fs_image "$test_dir/image" "$test_mount_point" +mount_fs_image "$test_dir/journal" "$test_dir/image" "$test_mount_point" do_some_operations "$test_mount_point" cd "$root_pwd" unmount_fs "$test_mount_point" # 2 -mount_fs_image "$test_dir/image" "$test_mount_point" +mount_fs_image "$test_dir/journal" "$test_dir/image" "$test_mount_point" do_read_operations "$test_mount_point" cd "$root_pwd" unmount_fs "$test_mount_point" diff --git a/simple.c b/simple.c index 53c384a..c2bb5e2 100644 --- a/simple.c +++ b/simple.c @@ -10,10 +10,14 @@ #include #include #include +#include #include #include #include #include +#include +#include +#include #include "super.h" @@ -358,19 +362,25 @@ ssize_t simplefs_write(struct file * filp, const char __user * buf, size_t len, 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) { + if (retval) return retval; - } inode = filp->f_path.dentry->d_inode; sfs_inode = SIMPLEFS_INODE(inode); - sb = inode->i_sb; bh = sb_bread(filp->f_path.dentry->d_inode->i_sb, sfs_inode->data_block_number); @@ -385,6 +395,13 @@ ssize_t simplefs_write(struct file * filp, const char __user * buf, size_t len, /* 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 @@ -393,6 +410,18 @@ ssize_t simplefs_write(struct file * filp, const char __user * buf, size_t len, } *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); @@ -584,6 +613,35 @@ static int simplefs_create(struct inode *dir, struct dentry *dentry, return simplefs_create_fs_object(dir, dentry, mode); } +static struct inode *simplefs_iget(struct super_block *sb, int ino) +{ + struct inode *inode; + struct simplefs_inode *sfs_inode; + + sfs_inode = simplefs_get_inode(sb, ino); + + inode = new_inode(sb); + inode->i_ino = ino; + inode->i_sb = sb; + inode->i_op = &simplefs_inode_ops; + + if (S_ISDIR(sfs_inode->mode)) + inode->i_fop = &simplefs_dir_operations; + else if (S_ISREG(sfs_inode->mode) || ino == SIMPLEFS_JOURNAL_INODE_NUMBER) + 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; + + return inode; +} + struct dentry *simplefs_lookup(struct inode *parent_inode, struct dentry *child_dentry, unsigned int flags) { @@ -595,9 +653,14 @@ struct dentry *simplefs_lookup(struct inode *parent_inode, 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 @@ -605,31 +668,8 @@ struct dentry *simplefs_lookup(struct inode *parent_inode, * with the filename that we are comparing above, then we * will use an invalid uninitialized inode */ - struct inode *inode; - struct simplefs_inode *sfs_inode; - - sfs_inode = simplefs_get_inode(sb, record->inode_no); - - 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; - + 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; } @@ -656,10 +696,130 @@ void simplefs_destory_inode(struct inode *inode) kmem_cache_free(sfs_inode_cachep, sfs_inode); } +static void simplefs_put_super(struct super_block *sb) +{ + struct simplefs_super_block *sfs_sb = SIMPLEFS_SB(sb); + if (sfs_sb->journal) + WARN_ON(jbd2_journal_destroy(sfs_sb->journal) < 0); + sfs_sb->journal = NULL; +} + static const struct super_operations simplefs_sops = { .destroy_inode = simplefs_destory_inode, + .put_super = simplefs_put_super, }; +static int simplefs_load_journal(struct super_block *sb, int devnum) +{ + struct journal_s *journal; + char b[BDEVNAME_SIZE]; + dev_t dev; + struct block_device *bdev; + int hblock, blocksize, len; + struct simplefs_super_block *sfs_sb = SIMPLEFS_SB(sb); + + dev = new_decode_dev(devnum); + printk(KERN_INFO "Journal device is: %s\n", __bdevname(dev, b)); + + bdev = blkdev_get_by_dev(dev, FMODE_READ|FMODE_WRITE|FMODE_EXCL, sb); + if (IS_ERR(bdev)) + return 1; + blocksize = sb->s_blocksize; + hblock = bdev_logical_block_size(bdev); + len = SIMPLEFS_MAX_FILESYSTEM_OBJECTS_SUPPORTED; + + journal = jbd2_journal_init_dev(bdev, sb->s_bdev, 1, -1, blocksize); + if (!journal) { + printk(KERN_ERR "Can't load journal\n"); + return 1; + } + journal->j_private = sb; + + sfs_sb->journal = journal; + + return 0; +} +static int simplefs_sb_load_journal(struct super_block *sb, struct inode *inode) +{ + struct journal_s *journal; + struct simplefs_super_block *sfs_sb = SIMPLEFS_SB(sb); + + journal = jbd2_journal_init_inode(inode); + if (!journal) { + printk(KERN_ERR "Can't load journal\n"); + return 1; + } + journal->j_private = sb; + + sfs_sb->journal = journal; + + return 0; +} + +#define SIMPLEFS_OPT_JOURNAL_DEV 1 +#define SIMPLEFS_OPT_JOURNAL_PATH 2 +static const match_table_t tokens = { + {SIMPLEFS_OPT_JOURNAL_DEV, "journal_dev=%u"}, + {SIMPLEFS_OPT_JOURNAL_PATH, "journal_path=%s"}, +}; +static int simplefs_parse_options(struct super_block *sb, char *options) +{ + substring_t args[MAX_OPT_ARGS]; + int token, ret, arg; + char *p; + + while ((p = strsep(&options, ",")) != NULL) { + if (!*p) + continue; + + args[0].to = args[0].from = NULL; + token = match_token(p, tokens, args); + + switch (token) { + case SIMPLEFS_OPT_JOURNAL_DEV: + if (args->from && match_int(args, &arg)) + return 1; + printk(KERN_INFO "Loading journal devnum: %i\n", arg); + if ((ret = simplefs_load_journal(sb, arg))) + return ret; + break; + + case SIMPLEFS_OPT_JOURNAL_PATH: + { + char *journal_path; + struct inode *journal_inode; + struct path path; + + BUG_ON(!(journal_path = match_strdup(&args[0]))); + ret = kern_path(journal_path, LOOKUP_FOLLOW, &path); + if (ret) { + printk(KERN_ERR "could not find journal device path: error %d\n", ret); + kfree(journal_path); + } + + journal_inode = path.dentry->d_inode; + + path_put(&path); + kfree(journal_path); + + if (S_ISBLK(journal_inode->i_mode)) { + unsigned long journal_devnum = new_encode_dev(journal_inode->i_rdev); + if ((ret = simplefs_load_journal(sb, journal_devnum))) + return ret; + } else { + /** Seems didn't work properly */ + if ((ret = simplefs_sb_load_journal(sb, journal_inode))) + return ret; + } + + break; + } + } + } + + return 0; +} + /* This function, as the name implies, Makes the super_block valid and * fills filesystem specific information in the super block */ int simplefs_fill_super(struct super_block *sb, void *data, int silent) @@ -688,6 +848,8 @@ int simplefs_fill_super(struct super_block *sb, void *data, int silent) "simplefs seem to be formatted using a non-standard block size."); goto release; } + /** XXX: Avoid this hack, by adding one more sb wrapper, but non-disk */ + sb_disk->journal = NULL; printk(KERN_INFO "simplefs filesystem of version [%llu] formatted with a block size of [%llu] detected in the device.\n", @@ -728,7 +890,18 @@ int simplefs_fill_super(struct super_block *sb, void *data, int silent) goto release; } - ret = 0; + if ((ret = simplefs_parse_options(sb, data))) + goto release; + + if (!sb_disk->journal) { + struct inode *journal_inode; + journal_inode = simplefs_iget(sb, SIMPLEFS_JOURNAL_INODE_NUMBER); + + ret = simplefs_sb_load_journal(sb, journal_inode); + goto release; + } + ret = jbd2_journal_load(sb_disk->journal); + release: brelse(bh); diff --git a/simple.h b/simple.h index e7f1f96..a6e7aa1 100644 --- a/simple.h +++ b/simple.h @@ -1,6 +1,8 @@ #define SIMPLEFS_MAGIC 0x10032013 +#define SIMPLEFS_JOURNAL_MAGIC = 0x20032013 + #define SIMPLEFS_DEFAULT_BLOCK_SIZE 4096 #define SIMPLEFS_FILENAME_MAXLEN 255 #define SIMPLEFS_START_INO 10 @@ -32,9 +34,17 @@ const int SIMPLEFS_SUPERBLOCK_BLOCK_NUMBER = 0; /* The disk block where the inodes are stored */ const int SIMPLEFS_INODESTORE_BLOCK_NUMBER = 1; +/** Journal settings */ +const int SIMPLEFS_JOURNAL_INODE_NUMBER = 2; +const int SIMPLEFS_JOURNAL_BLOCK_NUMBER = 2; +const int SIMPLEFS_JOURNAL_BLOCKS = 2; + /* The disk block where the name+inode_number pairs of the * contents of the root directory are stored */ -const int SIMPLEFS_ROOTDIR_DATABLOCK_NUMBER = 2; +const int SIMPLEFS_ROOTDIR_DATABLOCK_NUMBER = 4; + +#define SIMPLEFS_LAST_RESERVED_BLOCK SIMPLEFS_ROOTDIR_DATABLOCK_NUMBER +#define SIMPLEFS_LAST_RESERVED_INODE SIMPLEFS_JOURNAL_INODE_NUMBER /* The name+inode_number pair for each file in a directory. * This gets stored as the data for a directory */ @@ -63,6 +73,8 @@ const int SIMPLEFS_MAX_FILESYSTEM_OBJECTS_SUPPORTED = 64; /* 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 journal_s; struct simplefs_super_block { uint64_t version; uint64_t magic; @@ -73,5 +85,8 @@ struct simplefs_super_block { uint64_t free_blocks; - char padding[SIMPLEFS_DEFAULT_BLOCK_SIZE - (5 * sizeof(uint64_t))]; + /** FIXME: move this into separate struct */ + struct journal_s *journal; + + char padding[4048]; };