├── Documentation └── WritingFS4Fun.pdf ├── Makefile ├── README.md ├── dir.c ├── dummy_fs.h ├── dummyfs.c ├── file.c ├── hdummy.h ├── inode.c ├── mkfs_dummyfs.c └── super.c /Documentation/WritingFS4Fun.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gotoco/dummyfs/132b379ec8b508477b27885ec20645b66b0c64fa/Documentation/WritingFS4Fun.pdf -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGET = dmyfs 2 | 3 | # Specify here your kernel dir: 4 | KDIR := /root/kernel/linux-4.15.13 5 | PWD := $(shell pwd) 6 | 7 | # To implement some macros declaration after satement is required 8 | ccflags-y := -std=gnu99 -Wno-declaration-after-statement 9 | 10 | obj-m += $(TARGET).o 11 | 12 | dmyfs-objs := dummyfs.o super.o inode.o dir.o file.o 13 | 14 | default: 15 | make -C $(KDIR) SUBDIRS=$(shell pwd) modules 16 | 17 | clean: 18 | make -C $(KDIR) SUBDIRS=$(shell pwd) clean 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple implementation of the Linux filesystem 2 | DummyFS is simple implementation of FileSystem with basic on-disk layout. 3 | The main goal of this FS is educational puropse. 4 | 5 | 6 | ## How to start? 7 | Clone repository then specify Linux kernel dir `KDIR` inside Makefile. 8 | 9 | ```bash 10 | # to create kernel modules 11 | make 12 | 13 | # to make mkfs 14 | gcc -o mkfs_dmfs mkfs_dummyfs.c -g 15 | 16 | # to create initial filesystem on device i.e. for device /dev/sdb use mkfs 17 | ./mkfs_dmfs /dev/sdb 18 | 19 | # you can see disk layout in the hex if you will, to do so: 20 | dd if=/dev/sdb bs=1k count=20 | hexdump 21 | 22 | # insert kernel module 23 | # (Please mind the fact that it is learning module, 24 | # so good idea is to run it on VM or save all data before!) 25 | insmod ./dmyfs.ko 26 | 27 | # Then mount filesystem (again given device /dev/sdb, and mount point /mnt): 28 | mount -t dummyfs /dev/sdb /mnt 29 | 30 | # verfy if the fs is mounted properly 31 | mount | grep dummyfs 32 | 33 | ls -al /mnt 34 | ... 35 | 36 | # create file in dummyfs 37 | touch /mnt/test 38 | 39 | # do simple 'hello word' 40 | echo hello_from_dummyfs > /mnt/test 41 | cat /mnt/test 42 | ... 43 | ``` 44 | 45 | ## More resources: 46 | See slide-deck from my presentation "Writing Linux FS4Fun" 47 | ```bash 48 | ./Documentation/WrittingFS4Fun.pdf 49 | ``` 50 | -------------------------------------------------------------------------------- /dir.c: -------------------------------------------------------------------------------- 1 | #include "hdummy.h" 2 | 3 | int dummy_readdir(struct file *filp, struct dir_context *ctx) 4 | { 5 | loff_t pos = ctx->pos; 6 | struct inode *inode = file_inode(filp); 7 | struct super_block *sb = inode->i_sb; 8 | struct buffer_head *bh; 9 | struct dm_inode *dinode; 10 | struct dm_dir_entry *dir_rec; 11 | u32 j = 0, i = 0; 12 | 13 | dinode = inode->i_private; 14 | 15 | if (pos) 16 | return 0; 17 | WARN_ON(!S_ISDIR(dinode->i_mode)); 18 | 19 | /* For each extends from file */ 20 | for (i = 0; i < DM_INODE_TSIZE; ++i) { 21 | u32 b = dinode->i_addrb[i] , e = dinode->i_addre[i]; 22 | u32 blk = b; 23 | while (blk < e) { 24 | 25 | bh = sb_bread(sb, blk); 26 | BUG_ON(!bh); 27 | dir_rec = (struct dm_dir_entry *)(bh->b_data); 28 | 29 | for (j = 0; j < sb->s_blocksize; j += sizeof(*dir_rec)) { 30 | /* We mark empty/free inodes */ 31 | if (dir_rec->inode_nr == 0xdeeddeed) { 32 | break; 33 | } 34 | dir_emit(ctx, dir_rec->name, dir_rec->name_len, 35 | dir_rec->inode_nr, DT_UNKNOWN); 36 | filp->f_pos += sizeof(*dir_rec); 37 | ctx->pos += sizeof(*dir_rec); 38 | dir_rec++; 39 | } 40 | 41 | /* Move to another block */ 42 | blk++; 43 | bforget(bh); 44 | } 45 | } 46 | 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /dummy_fs.h: -------------------------------------------------------------------------------- 1 | #ifndef _LINUX_DUMMYFS_H 2 | #define _LINUX_DUMMYFS_H 3 | /** 4 | #ifdef __KERNEL__ 5 | #include 6 | #include 7 | #endif 8 | **/ 9 | 10 | /* Layout version */ 11 | #define DM_LAYOUT_VER 1 12 | 13 | 14 | /* FS SIZE/OFFSET CONST */ 15 | #define DM_INODE_TSIZE 3 16 | #define DM_SUPER_OFFSET 0 17 | #define DM_DEFAULT_BSIZE 4096 18 | #define DM_OLT_OFFSET (DM_SUPER_OFFSET + 1) 19 | #define DM_INODE_TABLE_OFFSET (DM_OLT_OFFSET + 1) 20 | #define DM_INODE_BITMAP_OFFSET (DM_INODE_TABLE_OFFSET + DM_INODE_TABLE_SIZE + 1) 21 | #define DM_ROOT_INODE_OFFSET (DM_INODE_BITMAP_OFFSET + 1) 22 | #define DM_ROOT_IN_EXT_OFF (DM_ROOT_INODE_OFFSET + 1) 23 | #define DM_LF_INODE_OFFSET (DM_ROOT_IN_EXT_OFF + DM_DEF_ALLOC) 24 | /* Default place where FS will start using after mkfs (all above are used for mkfs) */ 25 | #define DM_FS_SPACE_START (DM_LF_INODE_OFFSET + DM_DEF_ALLOC) 26 | 27 | /* FS constants */ 28 | #define DM_MAGIC_NR 0xf00dbeef 29 | #define DM_INODE_SIZE 512 30 | #define DM_INODE_NUMBER_TABLE 128 31 | #define DM_INODE_TABLE_SIZE (DM_INODE_NUMBER_TABLE * DM_INODE_SIZE)/DM_DEFAULT_BSIZE 32 | #define DM_EMPTY_ENTRY 0xdeeddeed 33 | 34 | #define DM_NAME_LEN 255 35 | #define DM_DEF_ALLOC 4 /* By default alloc N blocks per extend */ 36 | 37 | /* 38 | * Special inode numbers 39 | */ 40 | #define DM_BAD_INO 1 /* Bad blocks inode */ 41 | #define DM_ROOT_INO 2 /* Root inode nr */ 42 | #define DM_LAF_INO 3 /* Lost and Found inode nr */ 43 | 44 | /** 45 | * The on-disk superblock 46 | */ 47 | struct dm_superblock { 48 | uint32_t s_magic; /* magic number */ 49 | uint32_t s_version; /* fs version */ 50 | uint32_t s_blocksize; /* fs block size */ 51 | uint32_t s_block_olt; /* Object location table block */ 52 | uint32_t s_inode_cnt; /* number of inodes in inode table */ 53 | uint32_t s_last_blk; /* just move forward with allocation */ 54 | }; 55 | 56 | /** 57 | * Object Location Table 58 | */ 59 | struct dm_olt { 60 | uint32_t inode_table; /* inode_table block location */ 61 | uint32_t inode_cnt; /* number of inodes */ 62 | uint32_t inode_bitmap; /* inode bitmap block */ 63 | }; 64 | 65 | /** 66 | * The on Disk inode 67 | */ 68 | struct dm_inode { 69 | uint8_t i_version; /* inode version */ 70 | uint8_t i_flags; /* inode flags: TYPE */ 71 | uint32_t i_mode; /* File mode */ 72 | uint32_t i_ino; /* inode number */ 73 | uint16_t i_uid; /* owner's user id */ 74 | uint16_t i_hrd_lnk; /* number of hard links */ 75 | uint32_t i_ctime; /* Creation time */ 76 | uint32_t i_mtime; /* Modification time*/ 77 | uint32_t i_size; /* Number of bytes in file */ 78 | /* address begin - end block, range exclusive: addres end (last block) does not belogs to extend! */ 79 | uint32_t i_addrb[DM_INODE_TSIZE]; /* Start block of extend ranges */ 80 | uint32_t i_addre[DM_INODE_TSIZE]; /* End block of extend ranges */ 81 | }; 82 | 83 | struct dm_dir_entry { 84 | uint32_t inode_nr; /* inode number */ 85 | uint32_t name_len; /* Name length */ 86 | char name[256]; /* File name, up to DM_NAME_LEN */ 87 | }; 88 | 89 | #endif /* _LINUX_DUMMYFS_H */ 90 | -------------------------------------------------------------------------------- /dummyfs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "hdummy.h" 15 | 16 | MODULE_LICENSE("GPL"); 17 | MODULE_AUTHOR("gotoc"); 18 | MODULE_DESCRIPTION("Dummy FS"); 19 | 20 | struct file_system_type dummyfs_type = { 21 | .owner = THIS_MODULE, 22 | .name = "dummyfs", 23 | .mount = dummyfs_mount, 24 | .kill_sb = dummyfs_kill_superblock, 25 | .fs_flags = FS_REQUIRES_DEV 26 | }; 27 | 28 | const struct inode_operations dummy_inode_ops = { 29 | .create = dm_create, 30 | .mkdir = dm_mkdir, 31 | .lookup = dm_lookup, 32 | }; 33 | 34 | const struct file_operations dummy_file_ops = { 35 | .read_iter = dummy_read, 36 | .write_iter = dummy_write, 37 | }; 38 | 39 | const struct super_operations dummy_sb_ops = { 40 | .destroy_inode = dm_destroy_inode, 41 | .put_super = dummyfs_put_super, 42 | }; 43 | 44 | const struct file_operations dummy_dir_ops = { 45 | .owner = THIS_MODULE, 46 | .iterate_shared = dummy_readdir, 47 | }; 48 | 49 | struct kmem_cache *dmy_inode_cache = NULL; 50 | 51 | static int __init dummy_init(void) 52 | { 53 | int ret = 0; 54 | 55 | dmy_inode_cache = kmem_cache_create("dmy_inode_cache", 56 | sizeof(struct dm_inode), 57 | 0, 58 | (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), 59 | NULL); 60 | 61 | if (!dmy_inode_cache) 62 | return -ENOMEM; 63 | 64 | ret = register_filesystem(&dummyfs_type); 65 | 66 | if (ret == 0) 67 | printk(KERN_INFO "Sucessfully registered dummyfs\n"); 68 | else 69 | printk(KERN_ERR "Failed to register dummyfs. Error code: %d\n", ret); 70 | 71 | return ret; 72 | } 73 | module_init(dummy_init); 74 | 75 | static void __exit dummy_exit(void) 76 | { 77 | int ret; 78 | 79 | ret = unregister_filesystem(&dummyfs_type); 80 | kmem_cache_destroy(dmy_inode_cache); 81 | 82 | if (!ret) 83 | printk(KERN_INFO "Unregister dummy FS success\n"); 84 | else 85 | printk(KERN_ERR "Failed to unregister dummy FS\n"); 86 | 87 | } 88 | module_exit(dummy_exit); 89 | -------------------------------------------------------------------------------- /file.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "hdummy.h" 4 | 5 | ssize_t dm_get_loffset(struct dm_inode *di, loff_t off) 6 | { 7 | ssize_t ret = DM_EMPTY_ENTRY; 8 | loff_t add = 0; 9 | u32 i = 0; 10 | 11 | if (off > DM_DEFAULT_BSIZE) 12 | add += DM_DEFAULT_BSIZE % off; 13 | 14 | for (i = 0; i < DM_INODE_TSIZE; ++i) { 15 | if (di->i_addrb[i] + off > di->i_addre[i]) { 16 | off -= (di->i_addre[i] - di->i_addrb[i]); 17 | } else { 18 | ret = di->i_addrb[i] + off; 19 | break; 20 | } 21 | } 22 | 23 | BUG_ON(ret == 0xdeadbeef); 24 | 25 | return ret; 26 | } 27 | 28 | ssize_t dummy_read(struct kiocb *iocb, struct iov_iter *to) 29 | { 30 | struct super_block *sb; 31 | struct inode *inode; 32 | struct dm_inode *dinode; 33 | struct buffer_head *bh; 34 | char *buffer; 35 | void *buf = to->iov->iov_base; 36 | int nbytes; 37 | size_t count = iov_iter_count(to); 38 | loff_t off = iocb->ki_pos; 39 | loff_t end = off + count; 40 | size_t blk = 0; 41 | 42 | 43 | inode = iocb->ki_filp->f_path.dentry->d_inode; 44 | sb = inode->i_sb; 45 | dinode = inode->i_private; 46 | if (off) { 47 | return 0; 48 | } 49 | 50 | /* calculate datablock number here */ 51 | blk = dm_get_loffset(dinode, off); 52 | bh = sb_bread(sb, blk); 53 | if (!bh) { 54 | printk(KERN_ERR "Failed to read data block %lu\n", blk); 55 | return 0; 56 | } 57 | 58 | buffer = (char *)bh->b_data + (off % DM_DEFAULT_BSIZE); 59 | nbytes = min((size_t)(dinode->i_size - off), count); 60 | 61 | if (copy_to_user(buf, buffer, nbytes)) { 62 | brelse(bh); 63 | printk(KERN_ERR 64 | "Error copying file content to userspace buffer\n"); 65 | return -EFAULT; 66 | } 67 | 68 | brelse(bh); 69 | iocb->ki_pos += nbytes; 70 | 71 | return nbytes; 72 | } 73 | 74 | ssize_t dm_alloc_if_necessary(struct dm_superblock *sb, struct dm_inode *di, 75 | loff_t off, size_t cnt) 76 | { 77 | // Mock it until using bitmap 78 | return 0; 79 | } 80 | 81 | ssize_t dummy_write(struct kiocb *iocb, struct iov_iter *from) 82 | { 83 | struct super_block *sb; 84 | struct inode *inode; 85 | struct dm_inode *dinode; 86 | struct buffer_head *bh; 87 | struct dm_superblock *dsb; 88 | void *buf = from->iov->iov_base; 89 | loff_t off = iocb->ki_pos; 90 | size_t count = iov_iter_count(from); 91 | size_t blk = 0; 92 | size_t boff = 0; 93 | char *buffer; 94 | int ret; 95 | 96 | inode = iocb->ki_filp->f_path.dentry->d_inode; 97 | sb = inode->i_sb; 98 | dinode = inode->i_private; 99 | dsb = sb->s_fs_info; 100 | 101 | ret = generic_write_checks(iocb, from); 102 | if (ret <= 0) { 103 | printk(KERN_INFO "DummyFS: generic_write_checks Failed: %d", ret); 104 | return ret; 105 | } 106 | 107 | /* calculate datablock to write alloc if necessary */ 108 | blk = dm_alloc_if_necessary(dsb, dinode, off, count); 109 | /* dummy files are contigous so offset can be easly calculated */ 110 | boff = dm_get_loffset(dinode, off); 111 | bh = sb_bread(sb, boff); 112 | if (!bh) { 113 | printk(KERN_ERR "Failed to read data block %lu\n", blk); 114 | return 0; 115 | } 116 | 117 | buffer = (char *)bh->b_data + (off % DM_DEFAULT_BSIZE); 118 | if (copy_from_user(buffer, buf, count)) { 119 | brelse(bh); 120 | printk(KERN_ERR 121 | "Error copying file content from userspace buffer " 122 | "to kernel space\n"); 123 | return -EFAULT; 124 | } 125 | 126 | iocb->ki_pos += count; 127 | 128 | mark_buffer_dirty(bh); 129 | sync_dirty_buffer(bh); 130 | brelse(bh); 131 | 132 | dinode->i_size = max((size_t)(dinode->i_size), count); 133 | 134 | dm_store_inode(sb, dinode); 135 | 136 | return count; 137 | } 138 | 139 | -------------------------------------------------------------------------------- /hdummy.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERN_DUMMYFS_H 2 | #define _KERN_DUMMYFS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "dummy_fs.h" 16 | 17 | extern const struct super_operations dummy_sb_ops; 18 | extern const struct inode_operations dummy_inode_ops; 19 | extern const struct file_operations dummy_dir_ops; 20 | extern const struct file_operations dummy_file_ops; 21 | 22 | extern struct kmem_cache *dmy_inode_cache; 23 | 24 | struct dentry *dummyfs_mount(struct file_system_type *ft, int f, const char *dev, void *d); 25 | int dm_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode); 26 | struct dentry *dm_lookup(struct inode *dir, struct dentry *child_dentry, unsigned int flags); 27 | 28 | /* file.c */ 29 | ssize_t dummy_read(struct kiocb *iocb, struct iov_iter *to); 30 | ssize_t dummy_write(struct kiocb *iocb, struct iov_iter *from); 31 | 32 | /* dir.c */ 33 | int dummy_readdir(struct file *filp, struct dir_context *ctx); 34 | 35 | /* inode.c */ 36 | int isave_intable(struct super_block *sb, struct dm_inode *dmi, u32 i_block); 37 | void dm_destroy_inode(struct inode *inode); 38 | void dm_fill_inode(struct super_block *sb, struct inode *des, struct dm_inode *src); 39 | int dm_create_inode(struct inode *dir, struct dentry *dentry, umode_t mode); 40 | int dm_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl); 41 | void dm_store_inode(struct super_block *sb, struct dm_inode *dmi); 42 | 43 | /* inode cache */ 44 | struct dm_inode *cache_get_inode(void); 45 | void cache_put_inode(struct dm_inode **di); 46 | 47 | /* super.c */ 48 | void dummyfs_put_super(struct super_block *sb); 49 | void dummyfs_kill_superblock(struct super_block *sb); 50 | 51 | #endif /* _KERN_DUMMYFS_H */ 52 | -------------------------------------------------------------------------------- /inode.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "hdummy.h" 6 | 7 | #define FOREACH_BLK_IN_EXT(dmi, blk) \ 8 | u32 _ix = 0, b = 0, e = 0; \ 9 | for (_ix = 0, b = dmi->i_addrb[0], e = dmi->i_addre[0], blk = b-1; \ 10 | _ix < DM_INODE_TSIZE; \ 11 | ++_ix, b = dmi->i_addrb[_ix], e = dmi->i_addre[_ix], blk = b-1) \ 12 | while (++blk < e) 13 | 14 | void dump_dminode(struct dm_inode *dmi) 15 | { 16 | printk(KERN_INFO "----------dump_dm_inode-------------"); 17 | printk(KERN_INFO "dm_inode addr: %p", dmi); 18 | printk(KERN_INFO "dm_inode->i_version: %u", dmi->i_version); 19 | printk(KERN_INFO "dm_inode->i_flags: %u", dmi->i_flags); 20 | printk(KERN_INFO "dm_inode->i_mode: %u", dmi->i_mode); 21 | printk(KERN_INFO "dm_inode->i_ino: %u", dmi->i_ino); 22 | printk(KERN_INFO "dm_inode->i_hrd_lnk: %u", dmi->i_hrd_lnk); 23 | printk(KERN_INFO "dm_inode->i_addrb[0]: %u", dmi->i_addrb[0]); 24 | printk(KERN_INFO "dm_inode->i_addre[0]: %u", dmi->i_addre[0]); 25 | printk(KERN_INFO "----------[end of dump]-------------"); 26 | } 27 | 28 | struct dm_inode *cache_get_inode(void) 29 | { 30 | struct dm_inode *di; 31 | 32 | di = kmem_cache_alloc(dmy_inode_cache, GFP_KERNEL); 33 | printk(KERN_INFO "#: dummyfs cache_get_inode : di=%p\n", di); 34 | 35 | return di; 36 | } 37 | 38 | void cache_put_inode(struct dm_inode **di) 39 | { 40 | printk(KERN_INFO "#: dummyfs cache_put_inode : di=%p\n", *di); 41 | kmem_cache_free(dmy_inode_cache, *di); 42 | *di = NULL; 43 | } 44 | 45 | void dm_destroy_inode(struct inode *inode) { 46 | struct dm_inode *di = inode->i_private; 47 | 48 | printk(KERN_INFO "#: dummyfs freeing private data of inode %p (%lu)\n", 49 | di, inode->i_ino); 50 | cache_put_inode(&di); 51 | } 52 | 53 | void dm_store_inode(struct super_block *sb, struct dm_inode *dmi) 54 | { 55 | struct buffer_head *bh; 56 | struct dm_inode *in_core; 57 | uint32_t blk = dmi->i_addrb[0] - 1; 58 | 59 | /* put in-core inode */ 60 | /* Change me: here we just use fact that current allocator is cont. 61 | * With smarter allocator the position should be found from itab 62 | */ 63 | bh = sb_bread(sb, blk); 64 | BUG_ON(!bh); 65 | 66 | in_core = (struct dm_inode *)(bh->b_data); 67 | memcpy(in_core, dmi, sizeof(*in_core)); 68 | 69 | mark_buffer_dirty(bh); 70 | sync_dirty_buffer(bh); 71 | brelse(bh); 72 | } 73 | 74 | /* Here introduce allocation for directory... */ 75 | int dm_add_dir_record(struct super_block *sb, struct inode *dir, 76 | struct dentry *dentry, struct inode *inode) 77 | { 78 | struct buffer_head *bh; 79 | struct dm_inode *parent, *dmi; 80 | struct dm_dir_entry *dir_rec; 81 | u32 blk, j; 82 | 83 | parent = dir->i_private; 84 | dmi = parent; 85 | 86 | // Find offset, in dir in extends 87 | FOREACH_BLK_IN_EXT(parent, blk) { 88 | bh = sb_bread(sb, blk); 89 | BUG_ON(!bh); 90 | dir_rec = (struct dm_dir_entry *)(bh->b_data); 91 | for (j = 0; j < sb->s_blocksize; ++j) { 92 | /* We found free space */ 93 | if (dir_rec->inode_nr == DM_EMPTY_ENTRY) { 94 | dir_rec->inode_nr = inode->i_ino; 95 | dir_rec->name_len = strlen(dentry->d_name.name); 96 | memset(dir_rec->name, 0, 256); 97 | strcpy(dir_rec->name, dentry->d_name.name); 98 | mark_buffer_dirty(bh); 99 | sync_dirty_buffer(bh); 100 | brelse(bh); 101 | parent->i_size += sizeof(*dir_rec); 102 | return 0; 103 | } 104 | dir_rec++; 105 | } 106 | /* Move to another block */ 107 | bforget(bh); 108 | } 109 | 110 | printk(KERN_ERR "Unable to put entry to directory"); 111 | return -ENOSPC; 112 | } 113 | 114 | int alloc_inode(struct super_block *sb, struct dm_inode *dmi) 115 | { 116 | struct dm_superblock *dsb; 117 | u32 i; 118 | 119 | dsb = sb->s_fs_info; 120 | dsb->s_inode_cnt += 1; 121 | dmi->i_ino = dsb->s_inode_cnt; 122 | dmi->i_version = DM_LAYOUT_VER; 123 | dmi->i_flags = 0; 124 | dmi->i_mode = 0; 125 | dmi->i_size = 0; 126 | 127 | /* TODO: check if there is any space left on the device */ 128 | /* First block is allocated for in-core inode struct */ 129 | /* Then 4 block for extends: that mean dmi struct is in i_addrb[0]-1 */ 130 | dmi->i_addrb[0] = dsb->s_last_blk + 1; 131 | dmi->i_addre[0] = dsb->s_last_blk += 4; 132 | for (i = 1; i < DM_INODE_TSIZE; ++i) { 133 | dmi->i_addre[i] = 0; 134 | dmi->i_addrb[i] = 0; 135 | } 136 | 137 | dm_store_inode(sb, dmi); 138 | isave_intable(sb, dmi, (dmi->i_addrb[0] - 1)); 139 | /* TODO: update inode block bitmap */ 140 | 141 | return 0; 142 | } 143 | 144 | struct inode *dm_new_inode(struct inode *dir, struct dentry *dentry, 145 | umode_t mode) 146 | { 147 | struct super_block *sb; 148 | struct dm_superblock *dsb; 149 | struct dm_inode *di; 150 | struct inode *inode; 151 | int ret; 152 | 153 | sb = dir->i_sb; 154 | dsb = sb->s_fs_info; 155 | 156 | di = cache_get_inode(); 157 | 158 | /* allocate space dmy way: 159 | * sb has last block on it just use it 160 | */ 161 | ret = alloc_inode(sb, di); 162 | 163 | if (ret) { 164 | cache_put_inode(&di); 165 | printk(KERN_ERR "Unable to allocate disk space for inode"); 166 | return NULL; 167 | } 168 | di->i_mode = mode; 169 | 170 | BUG_ON(!S_ISREG(mode) && !S_ISDIR(mode)); 171 | 172 | /* Create VFS inode */ 173 | inode = new_inode(sb); 174 | 175 | dm_fill_inode(sb, inode, di); 176 | 177 | /* Add new inode to parent dir */ 178 | ret = dm_add_dir_record(sb, dir, dentry, inode); 179 | 180 | return inode; 181 | } 182 | 183 | int dm_add_ondir(struct inode *inode, struct inode *dir, struct dentry *dentry, 184 | umode_t mode) 185 | { 186 | inode_init_owner(inode, dir, mode); 187 | d_add(dentry, inode); 188 | 189 | return 0; 190 | } 191 | 192 | int dm_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl) 193 | { 194 | return dm_create_inode(dir, dentry, mode); 195 | } 196 | 197 | int dm_create_inode(struct inode *dir, struct dentry *dentry, umode_t mode) 198 | { 199 | struct inode *inode; 200 | 201 | /* allocate space 202 | * create incore inode 203 | * create VFS inode 204 | * finally ad inode to parent dir 205 | */ 206 | inode = dm_new_inode(dir, dentry, mode); 207 | 208 | if (!inode) 209 | return -ENOSPC; 210 | 211 | /* add new inode to parent dir */ 212 | return dm_add_ondir(inode, dir, dentry, mode); 213 | } 214 | 215 | int dm_mkdir(struct inode *dir, struct dentry *dentry, 216 | umode_t mode) 217 | { 218 | int ret = 0; 219 | 220 | ret = dm_create_inode(dir, dentry, S_IFDIR | mode); 221 | 222 | if (ret) { 223 | printk(KERN_ERR "Unable to allocate dir"); 224 | return -ENOSPC; 225 | } 226 | 227 | dir->i_op = &dummy_inode_ops; 228 | dir->i_fop = &dummy_dir_ops; 229 | 230 | return 0; 231 | } 232 | 233 | void dm_put_inode(struct inode *inode) 234 | { 235 | struct dm_inode *ip = inode->i_private; 236 | 237 | cache_put_inode(&ip); 238 | } 239 | 240 | int isave_intable(struct super_block *sb, struct dm_inode *dmi, u32 i_block) 241 | { 242 | struct buffer_head *bh; 243 | struct dm_inode *itab; 244 | u32 blk = 0; 245 | u32 *ptr; 246 | 247 | /* get inode table 'file' */ 248 | bh = sb_bread(sb, DM_INODE_TABLE_OFFSET); 249 | itab = (struct dm_inode*)(bh->b_data); 250 | /* right now we just allocated one itable extend for files */ 251 | blk = itab->i_addrb[0]; 252 | bforget(bh); 253 | 254 | bh = sb_bread(sb, blk); 255 | /* Get block of ino inode*/ 256 | ptr = (u32 *)(bh->b_data); 257 | /* inodes starts from index 1: -2 offset */ 258 | *(ptr + dmi->i_ino - 2) = i_block; 259 | 260 | mark_buffer_dirty(bh); 261 | sync_dirty_buffer(bh); 262 | brelse(bh); 263 | 264 | return 0; 265 | } 266 | 267 | struct dm_inode *dm_iget(struct super_block *sb, ino_t ino) 268 | { 269 | struct buffer_head *bh; 270 | struct dm_inode *ip; 271 | struct dm_inode *dinode; 272 | struct dm_inode *itab; 273 | u32 blk = 0; 274 | u32 *ptr; 275 | 276 | /* get inode table 'file' */ 277 | bh = sb_bread(sb, DM_INODE_TABLE_OFFSET); 278 | itab = (struct dm_inode*)(bh->b_data); 279 | /* right now we just allocated one itable extend for files */ 280 | blk = itab->i_addrb[0]; 281 | bforget(bh); 282 | 283 | bh = sb_bread(sb, blk); 284 | /* Get block of ino inode*/ 285 | ptr = (u32 *)(bh->b_data); 286 | /* inodes starts from index 1: -2 offset */ 287 | blk = *(ptr + ino - 2); 288 | bforget(bh); 289 | 290 | bh = sb_bread(sb, blk); 291 | ip = (struct dm_inode*)bh->b_data; 292 | if (ip->i_ino == DM_EMPTY_ENTRY) 293 | return NULL; 294 | dinode = cache_get_inode(); 295 | memcpy(dinode, ip, sizeof(*ip)); 296 | bforget(bh); 297 | 298 | return dinode; 299 | } 300 | 301 | void dm_fill_inode(struct super_block *sb, struct inode *des, struct dm_inode *src) 302 | { 303 | des->i_mode = src->i_mode; 304 | des->i_flags = src->i_flags; 305 | des->i_sb = sb; 306 | des->i_atime = des->i_ctime = des->i_mtime = current_time(des); 307 | des->i_ino = src->i_ino; 308 | des->i_private = src; 309 | des->i_op = &dummy_inode_ops; 310 | 311 | if (S_ISDIR(des->i_mode)) 312 | des->i_fop = &dummy_dir_ops; 313 | else if (S_ISREG(des->i_mode)) 314 | des->i_fop = &dummy_file_ops; 315 | else { 316 | des->i_fop = NULL; 317 | } 318 | 319 | WARN_ON(!des->i_fop); 320 | } 321 | 322 | struct dentry *dm_lookup(struct inode *dir, 323 | struct dentry *child_dentry, 324 | unsigned int flags) 325 | { 326 | struct dm_inode *dparent = dir->i_private; 327 | struct dm_inode *dchild; 328 | struct super_block *sb = dir->i_sb; 329 | struct buffer_head *bh; 330 | struct dm_dir_entry *dir_rec; 331 | struct inode *ichild; 332 | u32 j = 0, i = 0; 333 | 334 | /* Here we should use cache instead but dummyfs is doing stuff in dummy way.. */ 335 | for (i = 0; i < DM_INODE_TSIZE; ++i) { 336 | u32 b = dparent->i_addrb[i] , e = dparent->i_addre[i]; 337 | u32 blk = b; 338 | while (blk < e) { 339 | 340 | bh = sb_bread(sb, blk); 341 | BUG_ON(!bh); 342 | dir_rec = (struct dm_dir_entry *)(bh->b_data); 343 | 344 | for (j = 0; j < sb->s_blocksize; ++j) { 345 | if (dir_rec->inode_nr == DM_EMPTY_ENTRY) { 346 | break; 347 | } 348 | 349 | if (0 == strcmp(dir_rec->name, child_dentry->d_name.name)) { 350 | dchild = dm_iget(sb, dir_rec->inode_nr); 351 | ichild = new_inode(sb); 352 | if (!dchild) { 353 | return NULL; 354 | } 355 | dm_fill_inode(sb, ichild, dchild); 356 | inode_init_owner(ichild, dir, dchild->i_mode); 357 | d_add(child_dentry, ichild); 358 | 359 | } 360 | dir_rec++; 361 | } 362 | 363 | /* Move to another block */ 364 | blk++; 365 | bforget(bh); 366 | } 367 | } 368 | return NULL; 369 | } 370 | 371 | -------------------------------------------------------------------------------- /mkfs_dummyfs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "dummy_fs.h" 12 | //#include 13 | 14 | #define CLEAN_SIZE_OFF 0x10000 15 | 16 | /* Global variables */ 17 | char wipe_g[] = {0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb}; 18 | char zero_g[] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; 19 | char laf[] = {'.', 'l', 'o', 's', 't', '+', 'f', 'o', 'u', 'n', 'd', '\0'}; 20 | char dotdot[] = {'.', '.', '\0'}; 21 | char dot[] = {'.', '\0'}; 22 | 23 | // dm_ctime is used for inode time fields as create date. 24 | time_t dm_ctime; 25 | 26 | int wipe_out_device(int fd, int flag) 27 | { 28 | int ret = 0; 29 | uint32_t zero_s = sizeof(zero_g); 30 | uint32_t wipe_s = sizeof(wipe_g); 31 | 32 | // write either zeros or debug cb to beggining of device CLEAN_SIZE lines 33 | for (int i = 0; i < CLEAN_SIZE_OFF; ++i) { 34 | if (flag == 0) 35 | if (wipe_s != write(fd, &wipe_g, wipe_s)) { 36 | ret = -1; 37 | break; 38 | } 39 | else 40 | if (zero_s != write(fd, &zero_g, zero_s)) { 41 | ret = -1; 42 | break; 43 | } 44 | } 45 | 46 | return ret; 47 | } 48 | 49 | int write_superblock(int fd) 50 | { 51 | int ret = 0; 52 | 53 | // construct superblock: 54 | struct dm_superblock sb = { 55 | .s_version = 1, 56 | .s_magic = DM_MAGIC_NR, 57 | .s_blocksize = DM_DEFAULT_BSIZE, 58 | .s_block_olt = DM_OLT_OFFSET, 59 | .s_last_blk = DM_FS_SPACE_START, 60 | .s_inode_cnt = 3, 61 | }; 62 | 63 | lseek(fd, DM_SUPER_OFFSET, SEEK_SET); 64 | 65 | // write super block 66 | if (sizeof(sb) != write(fd, &sb, sizeof(sb))) 67 | ret = -1; 68 | 69 | return ret; 70 | } 71 | 72 | int write_metadata(int fd) 73 | { 74 | int ret = 0; 75 | 76 | // construct object location table: 77 | struct dm_olt olt = { 78 | .inode_table = DM_INODE_TABLE_OFFSET, 79 | .inode_cnt = 2, 80 | .inode_bitmap = DM_INODE_BITMAP_OFFSET, 81 | }; 82 | 83 | lseek(fd, DM_OLT_OFFSET*DM_DEFAULT_BSIZE, SEEK_SET); 84 | if (sizeof(olt) != write(fd, &olt, sizeof(olt))) { 85 | ret = -1; 86 | } 87 | 88 | return ret; 89 | } 90 | 91 | int write_inode_table(int fd) 92 | { 93 | int ret = 0; 94 | uint32_t it_s = (DM_INODE_NUMBER_TABLE * DM_INODE_SIZE * sizeof(uint32_t)); 95 | 96 | // construct inode table (it is a inode) 97 | struct dm_inode inode_table = { 98 | .i_version = 1, 99 | .i_flags = 0, 100 | .i_mode = 0, 101 | .i_uid = 0, 102 | .i_ctime = dm_ctime, 103 | .i_mtime = dm_ctime, 104 | .i_size = 0, 105 | .i_addrb = {DM_INODE_TABLE_OFFSET+1, 0, 0}, 106 | .i_addre = {DM_INODE_TABLE_OFFSET+1+DM_INODE_TABLE_SIZE, 0, 0}, 107 | }; 108 | 109 | lseek(fd, DM_INODE_TABLE_OFFSET * DM_DEFAULT_BSIZE, SEEK_SET); 110 | if (sizeof(inode_table) != write(fd, &inode_table, sizeof(inode_table))) { 111 | ret = -1; 112 | } 113 | 114 | lseek(fd, (DM_INODE_TABLE_OFFSET + 1) * DM_DEFAULT_BSIZE, SEEK_SET); 115 | // clear Extension first 116 | for (int i = 0; i < (it_s)/sizeof(zero_g); ++i) 117 | if (sizeof(zero_g) != write(fd, &zero_g, sizeof(zero_g))) { 118 | ret = -1; 119 | } 120 | 121 | // will Root and Lost+found inodes after they went written to the disk 122 | return ret; 123 | } 124 | 125 | int write_root_inode(int fd) 126 | { 127 | int ret = 0; 128 | uint32_t dummy = 0; 129 | 130 | // construct root inode 131 | struct dm_inode root_inode = { 132 | .i_version = 1, 133 | .i_flags = 0, 134 | .i_mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH, 135 | .i_uid = 0, 136 | .i_ctime = dm_ctime, 137 | .i_mtime = dm_ctime, 138 | .i_size = 0, 139 | .i_hrd_lnk = 1, 140 | .i_ino = DM_ROOT_INO, 141 | .i_addrb = {DM_ROOT_INODE_OFFSET+1, 0, 0}, 142 | .i_addre = {DM_ROOT_INODE_OFFSET+DM_DEF_ALLOC+1, 0, 0}, 143 | }; 144 | 145 | lseek(fd, DM_ROOT_INODE_OFFSET*DM_DEFAULT_BSIZE, SEEK_SET); 146 | if (sizeof(root_inode) != write(fd, &root_inode, sizeof(root_inode))) { 147 | return -1; 148 | } 149 | 150 | //Fill Root Inode Table Extension with 0xdeeddeed 151 | lseek(fd, (DM_ROOT_INODE_OFFSET + 1) * DM_DEFAULT_BSIZE, SEEK_SET); 152 | dummy = 0xdeeddeed; 153 | for (int i = 0; i < (DM_DEF_ALLOC * DM_DEFAULT_BSIZE)/sizeof(uint32_t); ++i) 154 | if (sizeof(dummy) != write(fd, &dummy, sizeof(dummy))) { 155 | return -1; 156 | } 157 | 158 | // Write '..' reference to the root folder 159 | struct dm_dir_entry dot_dentry = { 160 | .inode_nr = DM_ROOT_INO, 161 | .name_len = sizeof(dotdot)/sizeof(char), 162 | .name = {0}, 163 | }; 164 | 165 | memcpy(dot_dentry.name, dotdot, sizeof(dotdot) / sizeof(char)); 166 | lseek(fd, (DM_ROOT_INODE_OFFSET + 1) * DM_DEFAULT_BSIZE, SEEK_SET); 167 | 168 | if (sizeof(dot_dentry) != write(fd, &dot_dentry, sizeof(dot_dentry))) { 169 | perror("Error: root dotdot FAILURE!\n"); 170 | ret = -1; 171 | } 172 | 173 | // Write '.' reference to the root folder 174 | struct dm_dir_entry sdot_dentry = { 175 | .inode_nr = DM_ROOT_INO, 176 | .name_len = sizeof(dot)/sizeof(char), 177 | .name = {0}, 178 | }; 179 | 180 | memcpy(sdot_dentry.name, dot, sizeof(dot) / sizeof(char)); 181 | lseek(fd, (DM_ROOT_INODE_OFFSET + 1) * DM_DEFAULT_BSIZE + sizeof(sdot_dentry), SEEK_SET); 182 | 183 | if (sizeof(sdot_dentry) != write(fd, &sdot_dentry, sizeof(sdot_dentry))) { 184 | perror("Error: root dot FAILURE!\n"); 185 | ret = -1; 186 | } 187 | return ret; 188 | } 189 | 190 | int write_lostfound_inode(int fd) 191 | { 192 | int ret = 0; 193 | uint32_t dummy = 0xdeeddeed; 194 | 195 | // construct Lost and Found inode 196 | struct dm_inode laf_inode = { 197 | .i_version = 1, 198 | .i_flags = 0, 199 | .i_mode = S_IFDIR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, 200 | .i_ino = DM_LAF_INO, 201 | .i_uid = 0, 202 | .i_hrd_lnk = 1, 203 | .i_ctime = dm_ctime, 204 | .i_mtime = dm_ctime, 205 | .i_size = 0, 206 | .i_addrb = {DM_LF_INODE_OFFSET+1, 0, 0}, 207 | .i_addre = {DM_LF_INODE_OFFSET+DM_DEF_ALLOC+1, 0, 0}, 208 | }; 209 | 210 | lseek(fd, DM_LF_INODE_OFFSET * DM_DEFAULT_BSIZE, SEEK_SET); 211 | if (sizeof(laf_inode) != write(fd, &laf_inode, sizeof(laf_inode))) { 212 | return -1; 213 | } 214 | 215 | // Fill Inode Table Extension with 0xdeeddeed 216 | lseek(fd, (DM_LF_INODE_OFFSET + 1) * DM_DEFAULT_BSIZE, SEEK_SET); 217 | for (int i = 0; i < (DM_DEF_ALLOC * DM_DEFAULT_BSIZE)/sizeof(uint32_t); ++i) 218 | if (sizeof(dummy) != write(fd, &dummy, sizeof(dummy))) { 219 | return -1; 220 | } 221 | 222 | // Write '..' reference to the lost+found folder as a root 223 | struct dm_dir_entry dot_dentry = { 224 | .inode_nr = DM_ROOT_INO, 225 | .name_len = sizeof(dotdot)/sizeof(char), 226 | .name = {0}, 227 | }; 228 | 229 | memcpy(dot_dentry.name, dotdot, sizeof(dotdot) / sizeof(char)); 230 | lseek(fd, (DM_LF_INODE_OFFSET + 1) * DM_DEFAULT_BSIZE, SEEK_SET); 231 | 232 | if (sizeof(dot_dentry) != write(fd, &dot_dentry, sizeof(dot_dentry))) { 233 | perror("Error: lost+found dotdot FAILURE!\n"); 234 | ret = -1; 235 | } 236 | 237 | // Write '.' reference to the root folder 238 | struct dm_dir_entry sdot_dentry = { 239 | .inode_nr = DM_ROOT_INO, 240 | .name_len = sizeof(dot)/sizeof(char), 241 | .name = {0}, 242 | }; 243 | 244 | memcpy(sdot_dentry.name, dot, sizeof(dot) / sizeof(char)); 245 | lseek(fd, (DM_LF_INODE_OFFSET + 1) * DM_DEFAULT_BSIZE + sizeof(sdot_dentry), SEEK_SET); 246 | 247 | if (sizeof(sdot_dentry) != write(fd, &sdot_dentry, sizeof(sdot_dentry))) { 248 | perror("Error: root dot FAILURE!\n"); 249 | ret = -1; 250 | } 251 | 252 | // Write lost and found folder name to root node 253 | struct dm_dir_entry laf_dentry = { 254 | .inode_nr = DM_LAF_INO, 255 | .name_len = sizeof(laf)/sizeof(char), 256 | .name = {0}, 257 | }; 258 | syncfs(fd); 259 | 260 | uint32_t off = (DM_ROOT_INODE_OFFSET + 1) * DM_DEFAULT_BSIZE + 2 * sizeof(laf_dentry); 261 | memcpy(laf_dentry.name, laf, sizeof(laf) / sizeof(char)); 262 | lseek(fd, off, SEEK_SET); 263 | if (sizeof(laf_dentry) != write(fd, &laf_dentry, sizeof(laf_dentry))) { 264 | perror("Error: write_lostfound_inode FAILURE!\n"); 265 | ret = -1; 266 | } 267 | 268 | return ret; 269 | } 270 | 271 | int write_root2itable(int fd) 272 | { 273 | int ret = 0; 274 | uint32_t blk = DM_ROOT_INODE_OFFSET; 275 | 276 | // write root_inode to the inodetable as a first entry 277 | lseek(fd, (DM_INODE_TABLE_OFFSET + 1) * DM_DEFAULT_BSIZE, SEEK_SET); 278 | if (sizeof(blk) != write(fd, &blk, sizeof(uint32_t))) { 279 | perror("Error: write_root2itable FAILURE!\n"); 280 | ret = -1; 281 | } 282 | 283 | return ret; 284 | } 285 | 286 | int write_laf2itable(int fd) 287 | { 288 | int ret = 0; 289 | uint32_t off = sizeof(uint32_t), blk = DM_LF_INODE_OFFSET; 290 | 291 | // write lost+found to the inode table on index[1] 292 | lseek(fd, (DM_INODE_TABLE_OFFSET + 1) * DM_DEFAULT_BSIZE + off, SEEK_SET); 293 | if (off != write(fd, &blk, sizeof(blk))) { 294 | perror("Error: write_laf2itable FAILURE!\n"); 295 | ret = -1; 296 | } 297 | 298 | return ret; 299 | } 300 | 301 | int main(int argc, char *argv[]) 302 | { 303 | int fd; 304 | ssize_t ret; 305 | 306 | time(&dm_ctime); 307 | 308 | fd = open(argv[1], O_RDWR); 309 | if (fd == -1) { 310 | perror("Error: cannot open the device!\n"); 311 | return -1; 312 | } 313 | 314 | // wipe out device before writing new on disk structure 315 | if (wipe_out_device(fd, 1)) { 316 | perror("Error: wipe_out_device failed!\n"); 317 | ret = -1; 318 | goto werror; 319 | } 320 | 321 | if (write_superblock(fd)) { 322 | perror("Error: write_superblock failed!\n"); 323 | ret = -1; 324 | goto werror; 325 | } 326 | 327 | if (write_metadata(fd)) { 328 | perror("Error: write_metadata failed!\n"); 329 | ret = -1; 330 | goto werror; 331 | } 332 | 333 | if (write_inode_table(fd)) { 334 | perror("Error: write_inode_table failed!\n"); 335 | ret = -1; 336 | goto werror; 337 | } 338 | 339 | if (write_root_inode(fd)) { 340 | perror("Error: write_root_inode failed!\n"); 341 | ret = -1; 342 | goto werror; 343 | } 344 | 345 | if (write_lostfound_inode(fd)) { 346 | perror("Error: write_lostfound_inode failed!\n"); 347 | ret = -1; 348 | goto werror; 349 | } 350 | 351 | if (write_root2itable(fd) || write_laf2itable(fd)) { 352 | perror("Error: write_root2itable && write_laf2itable failed!\n"); 353 | ret = -1; 354 | goto werror; 355 | } 356 | 357 | close(fd); 358 | return 0; 359 | 360 | werror: 361 | perror("Error occured! closing...\n"); 362 | close(fd); 363 | return ret; 364 | } 365 | -------------------------------------------------------------------------------- /super.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "hdummy.h" 15 | 16 | static int dummyfs_fill_super(struct super_block *sb, void *data, int silent) 17 | { 18 | struct dm_superblock *d_sb; 19 | struct buffer_head *bh; 20 | struct inode *root_inode; 21 | struct dm_inode *root_dminode, *rbuf; 22 | int ret = 0; 23 | 24 | bh = sb_bread(sb, DM_SUPER_OFFSET); 25 | BUG_ON(!bh); 26 | d_sb = (struct dm_superblock *)bh->b_data; 27 | 28 | sb->s_magic = d_sb->s_magic; 29 | sb->s_blocksize = d_sb->s_blocksize; 30 | sb->s_op = &dummy_sb_ops; 31 | sb->s_fs_info = d_sb; 32 | bforget(bh); 33 | 34 | bh = sb_bread(sb, DM_ROOT_INODE_OFFSET); 35 | BUG_ON(!bh); 36 | 37 | rbuf = (struct dm_inode *)bh->b_data; 38 | root_dminode = cache_get_inode(); 39 | memcpy(root_dminode, rbuf, sizeof(*rbuf)); 40 | root_inode = new_inode(sb); 41 | 42 | /* Fill inode with dmy info */ 43 | root_inode->i_mode = root_dminode->i_mode; 44 | 45 | root_inode->i_flags = root_dminode->i_flags; 46 | root_inode->i_ino = root_dminode->i_ino; 47 | root_inode->i_sb = sb; 48 | root_inode->i_atime = current_time(root_inode); 49 | root_inode->i_ctime = current_time(root_inode); 50 | root_inode->i_mtime = current_time(root_inode); 51 | root_inode->i_ino = DM_ROOT_INO; 52 | root_inode->i_op = &dummy_inode_ops; 53 | root_inode->i_fop = &dummy_dir_ops; 54 | root_inode->i_private = root_dminode; 55 | 56 | sb->s_root = d_make_root(root_inode); 57 | if (!sb->s_root) { 58 | ret = -ENOMEM; 59 | goto release; 60 | } 61 | 62 | release: 63 | brelse(bh); 64 | return ret; 65 | } 66 | 67 | struct dentry *dummyfs_mount(struct file_system_type *fs_type, 68 | int flags, const char *dev_name, 69 | void *data) 70 | { 71 | struct dentry *ret; 72 | ret = mount_bdev(fs_type, flags, dev_name, data, dummyfs_fill_super); 73 | printk(KERN_INFO "#: Mounting dummyfs! \n"); 74 | 75 | if (IS_ERR(ret)) 76 | printk(KERN_ERR "Error mounting dummyfs.\n"); 77 | else 78 | printk(KERN_INFO "Dummyfs is succesfully mounted on: %s\n", 79 | dev_name); 80 | 81 | return ret; 82 | } 83 | 84 | void dummyfs_kill_superblock(struct super_block *sb) 85 | { 86 | printk(KERN_INFO "#: Dummyfs. Unmount succesful.\n"); 87 | kill_block_super(sb); 88 | } 89 | 90 | void dummyfs_save_sb(struct super_block *sb) { 91 | struct buffer_head *bh; 92 | struct dm_superblock *d_sb = sb->s_fs_info; 93 | 94 | bh = sb_bread(sb, DM_SUPER_OFFSET); 95 | BUG_ON(!bh); 96 | 97 | bh->b_data = (char *)d_sb; 98 | mark_buffer_dirty(bh); 99 | sync_dirty_buffer(bh); 100 | brelse(bh); 101 | } 102 | 103 | void dummyfs_put_super(struct super_block *sb) { 104 | return; 105 | } 106 | --------------------------------------------------------------------------------