static int tux3_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { struct inode *inode; int err; change_begin(tux_sb(dir->i_sb)); inode = tux_create_inode(dir, S_IFLNK | S_IRWXUGO, 0); err = PTR_ERR(inode); if (!IS_ERR(inode)) { err = page_symlink(inode, symname, strlen(symname) + 1); if (!err) { err = tux_add_dirent(dir, dentry, inode); if (!err) goto out; } inode_dec_link_count(inode); iput(inode); } out: change_end(tux_sb(dir->i_sb)); return err; }
static void tux3_write_super(struct super_block *sb) { struct buffer_head *bh; BUG_ON(SB_LOC < sb->s_blocksize); bh = sb_bread(sb, SB_LOC >> sb->s_blocksize_bits); if (!bh) { printk(KERN_ERR "TUX3: unable to read superblock\n"); return; } pack_sb(tux_sb(sb), bufdata(bh)); brelse_dirty(bh); sb->s_dirt = 0; }
/* Modify atom refcount */ static int atomref(struct inode *atable, atom_t atom, int use) { struct sb *sb = tux_sb(atable->i_sb); unsigned shift = sb->blockbits - ATOMREF_BLKBITS; unsigned block = sb->atomref_base + ATOMREF_SIZE * (atom >> shift); unsigned offset = atom & ~(-1 << shift), kill = 0; struct buffer_head *buffer; __be16 *refcount; int err; buffer = blockread(mapping(atable), block); if (!buffer) return -EIO; refcount = bufdata(buffer); int low = be16_to_cpu(refcount[offset]) + use; trace("inc atom %x by %d, offset %x[%x], low = %d", atom, use, block, offset, low); /* This releases buffer */ err = update_refcount(sb, buffer, offset, low); if (err) return err; if (!low || (low & (-1 << 16))) { buffer = blockread(mapping(atable), block + 1); if (!buffer) return -EIO; refcount = bufdata(buffer); int high = be16_to_cpu(refcount[offset]); if (!low) blockput(buffer); else { trace("carry %d, offset %x[%x], high = %d", (low >> 16), block, offset, high); high += (low >> 16); assert(high >= 0); /* paranoia check */ /* This releases buffer */ err = update_refcount(sb, buffer, offset, high); if (err) { /* FIXME: better set a flag that atomref broke * or something! */ return err; } } kill = !(low | high); }
static int __tux3_mknod(struct inode *dir, struct dentry *dentry, struct tux_iattr *iattr, dev_t rdev) { if(DEBUG_MODE_K==1) { printf("\t\t\t\t%25s[K] %25s %4d #in\n",__FILE__,__func__,__LINE__); } struct inode *inode; int err, is_dir = S_ISDIR(iattr->mode); if (!huge_valid_dev(rdev)) return -EINVAL; if (is_dir && dir->i_nlink >= TUX_LINK_MAX) return -EMLINK; change_begin(tux_sb(dir->i_sb)); inode = tux_create_inode(dir, iattr, rdev); err = PTR_ERR(inode); if (!IS_ERR(inode)) { err = tux_add_dirent(dir, dentry, inode); if (!err) { unlock_new_inode(inode); if (is_dir) inode_inc_link_count(dir); goto out; } clear_nlink(inode); tux3_mark_inode_dirty(inode); unlock_new_inode(inode); iput(inode); } out: change_end(tux_sb(dir->i_sb)); return err; }
static void tux3_put_super(struct super_block *sb) { struct sb *sbi = tux_sb(sb); /* FIXME: remove this, then use sb->s_dirt instead */ tux3_write_super(sb); empty_stash(&sbi->defree); iput(sbi->atable); iput(sbi->bitmap); iput(sbi->volmap); iput(sbi->logmap); sb->s_fs_info = NULL; kfree(sbi); }
/* Convert atom to name */ static int unatom(struct inode *atable, atom_t atom, char *name, unsigned size) { if(DEBUG_MODE_K==1) { printf("\t\t\t\t%25s[K] %25s %4d #in\n",__FILE__,__func__,__LINE__); } struct sb *sb = tux_sb(atable->i_sb); struct buffer_head *buffer; int err; loff_t where = unatom_dict_read(atable, atom); if (where < 0) { err = where; goto error; } buffer = blockread(mapping(atable), where >> sb->blockbits); if (!buffer) { err = -EIO; goto error; } tux_dirent *entry = bufdata(buffer) + (where & sb->blockmask); if (entry_atom(entry) != atom) { tux3_fs_error(sb, "atom %x reverse entry broken", atom); err = -EIO; goto error_blockput; } unsigned len = entry->name_len; if (size) { if (len > size) { err = -ERANGE; goto error_blockput; } memcpy(name, entry->name, len); } blockput(buffer); return len; error_blockput: blockput(buffer); error: return err; }
/* Find atom of name */ static int find_atom(struct inode *atable, const char *name, unsigned len, atom_t *atom) { struct sb *sb = tux_sb(atable->i_sb); struct buffer_head *buffer; struct tux3_dirent *entry; entry = tux_find_entry(atable, name, len, &buffer, sb->atomdictsize); if (IS_ERR(entry)) { int err = PTR_ERR(entry); if (err == -ENOENT) return -ENODATA; return err; } *atom = entry_atom(entry); blockput(buffer); return 0; }
/* * Mark inode dirty to delete. (called from ->drop_inode()). * Caller must hold inode->i_lock. */ void tux3_mark_inode_to_delete(struct inode *inode) { struct sb *sb = tux_sb(inode->i_sb); struct tux3_inode *tuxnode = tux_inode(inode); unsigned delta; /* inode has dead mark already */ if (tux3_inode_is_dead(tuxnode)) return; change_begin_atomic(sb); delta = tux3_inode_delta(inode); __tux3_mark_inode_to_delete(inode, delta); /* * Hack: this is called under inode->i_lock. So, we have to * release inode->i_lock to call mark_inode_dirty_sync(). * * FIXME: we want to set I_DIRTY_SYNC (I_DIRTY_SYNC will * prevent the indo is freed) and wakeup flusher if need, * while preventing inode is freed. Need better way to do. */ if (!(tux3_dirty_flags(inode, delta) & I_DIRTY_SYNC)) { /* FIXME: I_REFERENCED can't prevent completely */ //inode->i_state |= I_REFERENCED; /* FIXME: I_WILL_FREE will bother igrab() grabs reference */ inode->i_state |= I_WILL_FREE; spin_unlock(&inode->i_lock); /* Tell dead inode to backend by marking as dirty. */ tux3_mark_inode_dirty_sync(inode); spin_lock(&inode->i_lock); inode->i_state &= ~I_WILL_FREE; #ifdef __KERNEL__ wake_up_bit(&inode->i_state, __I_NEW); #endif } change_end_atomic(sb); }
static void tux3_put_super(struct super_block *sb) { struct sb *sbi = tux_sb(sb); /* FIXME: remove this, then use sb->s_dirt instead */ tux3_write_super(sb); destroy_defer_bfree(&sbi->new_decycle); destroy_defer_bfree(&sbi->decycle); destroy_defer_bfree(&sbi->derollup); destroy_defer_bfree(&sbi->defree); iput(sbi->atable); iput(sbi->bitmap); iput(sbi->volmap); iput(sbi->logmap); BUG_ON(!list_empty(&sbi->alloc_inodes)); sb->s_fs_info = NULL; kfree(sbi); }
static int tux3_statfs(struct dentry *dentry, struct kstatfs *buf) { struct super_block *sb = dentry->d_sb; struct sb *sbi = tux_sb(sb); buf->f_type = sb->s_magic; buf->f_bsize = sbi->blocksize; buf->f_blocks = sbi->volblocks; buf->f_bfree = sbi->freeblocks; buf->f_bavail = sbi->freeblocks; #if 0 buf->f_files = buf->f_blocks << (sbi->clus_bits - EXFAT_CHUNK_BITS) / 3; buf->f_ffree = buf->f_blocks << (sbi->clus_bits - EXFAT_CHUNK_BITS) / 3; buf->f_fsid.val[0] = sbi->serial_number; /*buf->f_fsid.val[1];*/ #endif buf->f_namelen = TUX_NAME_LEN; // buf->f_frsize = sbi->blocksize; return 0; }
static int __tux3_symlink(struct inode *dir, struct dentry *dentry, struct tux_iattr *iattr, const char *symname) { struct sb *sb = tux_sb(dir->i_sb); struct inode *inode; unsigned len = strlen(symname) + 1; int err, err2; /* FIXME: We want more length? */ if (len > PAGE_CACHE_SIZE) return -ENAMETOOLONG; change_begin(sb); inode = tux_new_inode(dir, iattr, 0); err = PTR_ERR(inode); if (!IS_ERR(inode)) { err = tux_create_dirent(dir, &dentry->d_name, inode); if (!err) { /* FIXME: we may want to initialize symlink earlier */ err = page_symlink(inode, symname, len); if (!err) { d_instantiate(dentry, inode); unlock_new_inode(inode); goto out; } err2 = tux_del_dirent(dir, dentry); if (err2) tux3_fs_error(sb, "Failed to recover dir entry (err %d)", err2); clear_nlink(inode); tux3_mark_inode_dirty(inode); unlock_new_inode(inode); iput(inode); } } out: change_end(sb); return err; }
int tux_dir_is_empty(struct inode *dir) { struct sb *sb = tux_sb(dir->i_sb); block_t block, blocks = dir->i_size >> sb->blockbits; __be64 self = cpu_to_be64(tux_inode(dir)->inum); struct buffer_head *buffer; for (block = 0; block < blocks; block++) { buffer = blockread(mapping(dir), block); if (!buffer) return -EIO; tux_dirent *entry = bufdata(buffer); tux_dirent *limit = bufdata(buffer) + sb->blocksize - TUX_REC_LEN(1); for (; entry <= limit; entry = next_entry(entry)) { if (!entry->rec_len) { blockput(buffer); tux_zero_len_error(dir, block); return -EIO; } if (is_deleted(entry)) continue; if (entry->name[0] != '.') goto not_empty; if (entry->name_len > 2) goto not_empty; if (entry->name_len < 2) { if (entry->inum != self) goto not_empty; } else if (entry->name[1] != '.') goto not_empty; } blockput(buffer); } return 0; not_empty: blockput(buffer); return -ENOTEMPTY; }
static struct dentry *tux3_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { struct buffer_head *buffer; struct inode *inode; tux_dirent *entry; inum_t inum; entry = tux_find_dirent(dir, &dentry->d_name, &buffer); if (IS_ERR(entry)) { if (PTR_ERR(entry) != -ENOENT) return ERR_CAST(entry); inode = NULL; goto out; } inum = be64_to_cpu(entry->inum); blockput(buffer); inode = tux3_iget(tux_sb(dir->i_sb), inum); out: return d_splice_alias(inode, dentry); }
/* Find atom of name */ static int find_atom(struct inode *atable, const char *name, unsigned len, atom_t *atom) { if(DEBUG_MODE_K==1) { printf("\t\t\t\t%25s[K] %25s %4d #in\n",__FILE__,__func__,__LINE__); } struct sb *sb = tux_sb(atable->i_sb); struct buffer_head *buffer; tux_dirent *entry; entry = tux_find_entry(atable, name, len, &buffer, sb->atomdictsize); if (IS_ERR(entry)) { int err = PTR_ERR(entry); if (err == -ENOENT) return -ENODATA; return err; } *atom = entry_atom(entry); blockput(buffer); return 0; }
/* Find free atom */ static int get_freeatom(struct inode *atable, atom_t *atom) { struct sb *sb = tux_sb(atable->i_sb); atom_t freeatom = sb->freeatom; if (!freeatom) { *atom = sb->atomgen++; return 0; } loff_t next = unatom_dict_read(atable, freeatom); if (next < 0) return next; if (!is_free_unatom(next)) { tux3_fs_error(sb, "something horrible happened"); return -EIO; } *atom = freeatom; sb->freeatom = next & ~UNATOM_FREE_MASK; return 0; }
static int __tux3_symlink(struct inode *dir, struct dentry *dentry, struct tux_iattr *iattr, const char *symname) { if(DEBUG_MODE_K==1) { printf("\t\t\t\t%25s[K] %25s %4d #in\n",__FILE__,__func__,__LINE__); } struct sb *sb = tux_sb(dir->i_sb); struct inode *inode; unsigned len = strlen(symname) + 1; int err; /* FIXME: We want more length? */ if (len > PAGE_CACHE_SIZE) return -ENAMETOOLONG; change_begin(sb); inode = tux_create_inode(dir, iattr, 0); err = PTR_ERR(inode); if (!IS_ERR(inode)) { err = page_symlink(inode, symname, len); if (!err) { err = tux_add_dirent(dir, dentry, inode); if (!err) { unlock_new_inode(inode); goto out; } } inode_dec_link_count(inode); unlock_new_inode(inode); iput(inode); } out: change_end(sb); return err; }
/* Make atom for name */ static int make_atom(struct inode *atable, const char *name, unsigned len, atom_t *atom) { if(DEBUG_MODE_K==1) { printf("\t\t\t\t%25s[K] %25s %4d #in\n",__FILE__,__func__,__LINE__); } struct sb *sb = tux_sb(atable->i_sb); int err; err = find_atom(atable, name, len, atom); if (!err) return 0; if (err != -ENODATA) return err; err = get_freeatom(atable, atom); if (err) return err; loff_t where = tux_create_entry(atable, name, len, *atom, 0, &sb->atomdictsize); if (where < 0) { /* FIXME: better set a flag that unatom broke or something!!! */ return where; } /* Enter into reverse map - maybe verify zero refs? */ where = unatom_dict_write(atable, *atom, where); if (where < 0) { /* FIXME: better set a flag that unatom broke or something!!! */ return where; } return 0; }
static int tux3_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) { struct inode *inode = old_dentry->d_inode; struct sb *sb = tux_sb(inode->i_sb); int err; if (inode->i_nlink >= TUX_LINK_MAX) return -EMLINK; change_begin(sb); tux3_iattrdirty(inode); inode->i_ctime = gettime(); inode_inc_link_count(inode); ihold(inode); err = tux_add_dirent(dir, dentry, inode); if (err) { inode_dec_link_count(inode); iput(inode); } change_end(sb); return err; }
/* Make atom for name */ static int make_atom(struct inode *atable, const char *name, unsigned len, atom_t *atom) { struct sb *sb = tux_sb(atable->i_sb); struct buffer_head *buffer; loff_t where; int err; err = find_atom(atable, name, len, atom); if (!err) return 0; if (err != -ENODATA) return err; err = get_freeatom(atable, atom); if (err) return err; where = tux_alloc_entry(atable, name, len, &sb->atomdictsize, &buffer); if (where < 0) { /* FIXME: better set a flag that unatom broke or something!!! */ return where; } /* This releases buffer */ tux_set_entry(buffer, bufdata(buffer) + (where & sb->blockmask), *atom, 0); /* Enter into reverse map - maybe verify zero refs? */ where = unatom_dict_write(atable, *atom, where); if (where < 0) { /* FIXME: better set a flag that unatom broke or something!!! */ return where; } return 0; }
static int tux3_fill_super(struct super_block *sb, void *data, int silent) { struct sb *sbi; int err, blocksize; sbi = kzalloc(sizeof(struct sb), GFP_KERNEL); if (!sbi) return -ENOMEM; sbi->vfs_sb = sb; sb->s_fs_info = sbi; sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_magic = TUX3_SUPER_MAGIC; sb->s_op = &tux3_super_ops; sb->s_time_gran = 1; mutex_init(&sbi->loglock); INIT_LIST_HEAD(&sbi->alloc_inodes); err = -EIO; blocksize = sb_min_blocksize(sb, BLOCK_SIZE); if (!blocksize) { if (!silent) printk(KERN_ERR "TUX3: unable to set blocksize\n"); goto error; } if ((err = load_sb(tux_sb(sb)))) { if (!silent) { if (err == -EINVAL) warn("invalid superblock [%Lx]", (L)from_be_u64(*(be_u64 *)sbi->super.magic)); else warn("Unable to read superblock"); } goto error; } if (sbi->blocksize != blocksize) { if (!sb_set_blocksize(sb, sbi->blocksize)) { printk(KERN_ERR "TUX3: blocksize too small for device.\n"); goto error; } } warn("s_blocksize %lu", sb->s_blocksize); err = -ENOMEM; sbi->volmap = tux_new_volmap(tux_sb(sb)); if (!sbi->volmap) goto error; insert_inode_hash(sbi->volmap); sbi->logmap = tux_new_logmap(tux_sb(sb)); if (!sbi->logmap) goto error_logmap; err = load_itable(sbi); if (err) goto error_bitmap; // struct inode *vtable; sbi->bitmap = tux3_iget(sb, TUX_BITMAP_INO); err = PTR_ERR(sbi->bitmap); if (IS_ERR(sbi->bitmap)) goto error_bitmap; sbi->rootdir = tux3_iget(sb, TUX_ROOTDIR_INO); err = PTR_ERR(sbi->rootdir); if (IS_ERR(sbi->rootdir)) goto error_rootdir; sbi->atable = tux3_iget(sb, TUX_ATABLE_INO); err = PTR_ERR(sbi->atable); if (IS_ERR(sbi->atable)) goto error_atable; sb->s_root = d_alloc_root(sbi->rootdir); if (!sb->s_root) goto error_alloc_root; return 0; error_alloc_root: iput(sbi->atable); error_atable: iput(sbi->rootdir); error_rootdir: iput(sbi->bitmap); error_bitmap: iput(sbi->logmap); error_logmap: iput(sbi->volmap); error: kfree(sbi); return err; }
static int tux3_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { struct inode *old_inode = old_dentry->d_inode; struct inode *new_inode = new_dentry->d_inode; struct buffer_head *old_buffer, *new_buffer; tux_dirent *old_entry, *new_entry; int err, new_subdir = 0; old_entry = tux_find_dirent(old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_buffer); if (IS_ERR(old_entry)) return PTR_ERR(old_entry); /* FIXME: is this needed? */ BUG_ON(from_be_u64(old_entry->inum) != tux_inode(old_inode)->inum); change_begin(tux_sb(old_inode->i_sb)); if (new_inode) { int old_is_dir = S_ISDIR(old_inode->i_mode); if (old_is_dir) { err = tux_dir_is_empty(new_inode); if (err) goto error; } new_entry = tux_find_dirent(new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_buffer); if (IS_ERR(new_entry)) { BUG_ON(PTR_ERR(new_entry) == -ENOENT); err = PTR_ERR(new_entry); goto error; } /* this releases new_buffer */ tux_update_dirent(new_buffer, new_entry, old_inode); new_inode->i_ctime = new_dir->i_ctime; if (old_is_dir) drop_nlink(new_inode); inode_dec_link_count(new_inode); } else { new_subdir = S_ISDIR(old_inode->i_mode) && new_dir != old_dir; if (new_subdir) { if (new_dir->i_nlink >= TUX_LINK_MAX) { err = -EMLINK; goto error; } } err = __tux_add_dirent(new_dir, new_dentry, old_inode); if (err) goto error; if (new_subdir) inode_inc_link_count(new_dir); } old_inode->i_ctime = new_dir->i_ctime; mark_inode_dirty(old_inode); err = tux_delete_dirent(old_buffer, old_entry); if (err) { printk(KERN_ERR "TUX3: %s: couldn't delete old entry (%Lu)\n", __func__, (L)tux_inode(old_inode)->inum); /* FIXME: now, we have hardlink even if it's dir. */ inode_inc_link_count(old_inode); } if (!err && new_subdir) inode_dec_link_count(old_dir); change_end(tux_sb(old_inode->i_sb)); return err; error: change_end(tux_sb(old_inode->i_sb)); brelse(old_buffer); return err; }
static int tux3_fill_super(struct super_block *sb, void *data, int silent) { static struct tux_iattr iattr; struct sb *sbi; struct root iroot; int err, blocksize; sbi = kzalloc(sizeof(struct sb), GFP_KERNEL); if (!sbi) return -ENOMEM; sbi->vfs_sb = sb; sb->s_fs_info = sbi; sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_magic = 0x54555833; sb->s_op = &tux3_super_ops; sb->s_time_gran = 1; mutex_init(&sbi->loglock); err = -EIO; blocksize = sb_min_blocksize(sb, BLOCK_SIZE); if (!blocksize) { if (!silent) printk(KERN_ERR "TUX3: unable to set blocksize\n"); goto error; } err = tux_load_sb(sb, &iroot, silent); if (err) goto error; printk("%s: depth %Lu, block %Lu\n", __func__, (L)iroot.depth, (L)iroot.block); printk("%s: blocksize %u, blockbits %u, blockmask %08x\n", __func__, sbi->blocksize, sbi->blockbits, sbi->blockmask); printk("%s: volblocks %Lu, freeblocks %Lu, nextalloc %Lu\n", __func__, sbi->volblocks, sbi->freeblocks, sbi->nextalloc); printk("%s: freeatom %u, atomgen %u\n", __func__, sbi->freeatom, sbi->atomgen); if (sbi->blocksize != blocksize) { if (!sb_set_blocksize(sb, sbi->blocksize)) { printk(KERN_ERR "TUX3: blocksize too small for device.\n"); goto error; } } printk("%s: s_blocksize %lu\n", __func__, sb->s_blocksize); err = -ENOMEM; sbi->volmap = tux_new_volmap(tux_sb(sb)); if (!sbi->volmap) goto error; /* Initialize itable btree */ init_btree(itable_btree(sbi), sbi, iroot, &itable_ops); // struct inode *vtable; sbi->bitmap = tux3_iget(sb, TUX_BITMAP_INO); err = PTR_ERR(sbi->bitmap); if (IS_ERR(sbi->bitmap)) goto error_bitmap; sbi->rootdir = tux3_iget(sb, TUX_ROOTDIR_INO); err = PTR_ERR(sbi->rootdir); if (IS_ERR(sbi->rootdir)) goto error_rootdir; sbi->atable = tux3_iget(sb, TUX_ATABLE_INO); err = PTR_ERR(sbi->atable); if (IS_ERR(sbi->atable)) goto error_atable; err = -ENOMEM; sbi->logmap = tux_new_inode(sbi->rootdir, &iattr, 0); if (!sbi->logmap) goto error_logmap; sb->s_root = d_alloc_root(sbi->rootdir); if (!sb->s_root) goto error_alloc_root; return 0; error_alloc_root: iput(sbi->logmap); error_logmap: iput(sbi->atable); error_atable: iput(sbi->rootdir); error_rootdir: iput(sbi->bitmap); error_bitmap: iput(sbi->volmap); error: kfree(sbi); return err; }
static int tux3_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { struct inode *inode = file_inode(vma->vm_file); struct sb *sb = tux_sb(inode->i_sb); struct page *clone, *page = vmf->page; void *ptr; int ret; sb_start_pagefault(inode->i_sb); retry: down_read(&tux_inode(inode)->truncate_lock); lock_page(page); if (page->mapping != mapping(inode)) { unlock_page(page); ret = VM_FAULT_NOPAGE; goto out; } /* * page fault can be happened while holding change_begin/end() * (e.g. copy of user data between ->write_begin and * ->write_end for write(2)). * * So, we use nested version here. */ change_begin_atomic_nested(sb, &ptr); /* * FIXME: Caller releases vmf->page (old_page) unconditionally. * So, this takes additional refcount to workaround it. */ if (vmf->page == page) page_cache_get(page); clone = pagefork_for_blockdirty(page, tux3_get_current_delta()); if (IS_ERR(clone)) { /* Someone did page fork */ pgoff_t index = page->index; change_end_atomic_nested(sb, ptr); unlock_page(page); page_cache_release(page); up_read(&tux_inode(inode)->truncate_lock); switch (PTR_ERR(clone)) { case -EAGAIN: page = find_get_page(inode->i_mapping, index); assert(page); goto retry; case -ENOMEM: ret = VM_FAULT_OOM; break; default: ret = VM_FAULT_SIGBUS; break; } goto out; } file_update_time(vma->vm_file); /* Assign buffers to dirty */ if (!page_has_buffers(clone)) create_empty_buffers(clone, sb->blocksize, 0); /* * We mark the page dirty already here so that when freeze is in * progress, we are guaranteed that writeback during freezing will * see the dirty page and writeprotect it again. */ tux3_set_page_dirty(clone); #if 1 /* FIXME: Caller doesn't see the changed vmf->page */ vmf->page = clone; change_end_atomic_nested(sb, ptr); /* FIXME: caller doesn't know about pagefork */ unlock_page(clone); page_cache_release(clone); ret = 0; // ret = VM_FAULT_LOCKED; #endif out: up_read(&tux_inode(inode)->truncate_lock); sb_end_pagefault(inode->i_sb); return ret; }
static int tux3_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { struct tux_iattr iattr = { .uid = current_fsuid(), .gid = current_fsgid(), .mode = S_IFLNK | S_IRWXUGO, }; return __tux3_symlink(dir, dentry, &iattr, symname); } #endif /* !__KERNEL__ */ static int tux3_unlink(struct inode *dir, struct dentry *dentry) { struct inode *inode = dentry->d_inode; struct sb *sb = tux_sb(inode->i_sb); change_begin(sb); int err = tux_del_dirent(dir, dentry); if (!err) { tux3_iattrdirty(inode); inode->i_ctime = dir->i_ctime; /* FIXME: we shouldn't write inode for i_nlink = 0? */ inode_dec_link_count(inode); } change_end(sb); return err; } static int tux3_rmdir(struct inode *dir, struct dentry *dentry) { struct sb *sb = tux_sb(dir->i_sb); struct inode *inode = dentry->d_inode; int err = tux_dir_is_empty(inode); if (!err) { change_begin(sb); err = tux_del_dirent(dir, dentry); if (!err) { tux3_iattrdirty(inode); inode->i_ctime = dir->i_ctime; /* FIXME: we need to do this for POSIX? */ /* inode->i_size = 0; */ clear_nlink(inode); tux3_mark_inode_dirty_sync(inode); inode_dec_link_count(dir); } change_end(sb); } return err; } static int tux3_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { struct inode *old_inode = old_dentry->d_inode; struct inode *new_inode = new_dentry->d_inode; struct sb *sb = tux_sb(old_inode->i_sb); struct buffer_head *old_buffer, *new_buffer, *clone; tux_dirent *old_entry, *new_entry; void *olddata; int err, new_subdir = 0; unsigned delta; old_entry = tux_find_dirent(old_dir, &old_dentry->d_name, &old_buffer); if (IS_ERR(old_entry)) return PTR_ERR(old_entry); /* FIXME: is this needed? */ assert(be64_to_cpu(old_entry->inum) == tux_inode(old_inode)->inum); change_begin(sb); delta = tux3_get_current_delta(); if (new_inode) { int old_is_dir = S_ISDIR(old_inode->i_mode); if (old_is_dir) { err = tux_dir_is_empty(new_inode); if (err) goto error; } new_entry = tux_find_dirent(new_dir, &new_dentry->d_name, &new_buffer); if (IS_ERR(new_entry)) { assert(PTR_ERR(new_entry) != -ENOENT); err = PTR_ERR(new_entry); goto error; } /* * The directory is protected by i_mutex. * blockdirty() should never return -EAGAIN. */ olddata = bufdata(new_buffer); clone = blockdirty(new_buffer, delta); if (IS_ERR(clone)) { assert(PTR_ERR(clone) != -EAGAIN); blockput(new_buffer); err = PTR_ERR(clone); goto error; } new_entry = ptr_redirect(new_entry, olddata, bufdata(clone)); /* this releases new_buffer */ tux_update_dirent(new_dir, clone, new_entry, old_inode); tux3_iattrdirty(new_inode); new_inode->i_ctime = new_dir->i_ctime; if (old_is_dir) drop_nlink(new_inode); inode_dec_link_count(new_inode); } else { new_subdir = S_ISDIR(old_inode->i_mode) && new_dir != old_dir; if (new_subdir) { if (new_dir->i_nlink >= TUX_LINK_MAX) { err = -EMLINK; goto error; } } err = tux_create_dirent(new_dir, &new_dentry->d_name, old_inode); if (err) goto error; if (new_subdir) inode_inc_link_count(new_dir); } tux3_iattrdirty(old_inode); old_inode->i_ctime = new_dir->i_ctime; tux3_mark_inode_dirty(old_inode); /* * The new entry can be on same buffer with old_buffer, and * may did buffer fork in the above path. So if old_buffer is * forked buffer, we update the old_buffer in here. */ if (buffer_forked(old_buffer)) { clone = blockget(mapping(old_dir), bufindex(old_buffer)); assert(clone); old_entry = ptr_redirect(old_entry, bufdata(old_buffer), bufdata(clone)); blockput(old_buffer); old_buffer = clone; } err = tux_delete_dirent(old_dir, old_buffer, old_entry); if (err) { tux3_fs_error(sb, "couldn't delete old entry (%Lu)", tux_inode(old_inode)->inum); /* FIXME: now, we have hardlink even if it's dir. */ inode_inc_link_count(old_inode); } if (!err && new_subdir) inode_dec_link_count(old_dir); change_end(sb); return err; error: change_end(sb); blockput(old_buffer); return err; } #ifdef __KERNEL__ const struct file_operations tux_dir_fops = { .llseek = generic_file_llseek, .read = generic_read_dir, .readdir = tux_readdir, .fsync = tux3_sync_file, }; const struct inode_operations tux_dir_iops = { .create = tux3_create, .lookup = tux3_lookup, .link = tux3_link, .unlink = tux3_unlink, .symlink = tux3_symlink, .mkdir = tux3_mkdir, .rmdir = tux3_rmdir, .mknod = tux3_mknod, .rename = tux3_rename, .setattr = tux3_setattr, .getattr = tux3_getattr // .setxattr = generic_setxattr, // .getxattr = generic_getxattr, // .listxattr = ext3_listxattr, // .removexattr = generic_removexattr, // .permission = ext3_permission, /* FIXME: why doesn't ext4 support this for directory? */ // .fallocate = ext4_fallocate, // .fiemap = ext4_fiemap, };
int main(int argc, char *argv[]) { unsigned abits = DATA_BTREE_BIT|CTIME_SIZE_BIT|MODE_OWNER_BIT|LINK_COUNT_BIT|MTIME_BIT; struct dev *dev = &(struct dev){ .bits = 8, .fd = open(argv[1], O_CREAT|O_RDWR, S_IRUSR|S_IWUSR) }; assert(!ftruncate(dev->fd, 1 << 24)); init_buffers(dev, 1 << 20, 0); struct sb *sb = rapid_sb(dev, .version = 0, .atomref_base = 1 << 10, .unatom_base = 1 << 11, .atomgen = 1); struct inode *inode = rapid_open_inode(sb, NULL, S_IFDIR | 0x666, .present = abits, .i_uid = 0x12121212, .i_gid = 0x34343434, .i_ctime = spectime(0xdec0debeadULL), .i_mtime = spectime(0xbadfaced00dULL)); inode->btree = (struct btree){ .root = { .block = 0xcaba1f00dULL, .depth = 3 } }; sb->atable = inode; for (int i = 0; i < 2; i++) { struct buffer_head *buffer = blockget(mapping(inode), tux_sb(inode->i_sb)->atomref_base + i); memset(bufdata(buffer), 0, sb->blocksize); blockput_dirty(buffer); } if (1) { warn("---- test positive and negative refcount carry ----"); use_atom(inode, 6, 1 << 15); use_atom(inode, 6, (1 << 15)); use_atom(inode, 6, -(1 << 15)); use_atom(inode, 6, -(1 << 15)); } warn("---- test atom table ----"); printf("atom = %Lx\n", (L)make_atom(inode, "foo", 3)); printf("atom = %Lx\n", (L)make_atom(inode, "foo", 3)); printf("atom = %Lx\n", (L)make_atom(inode, "bar", 3)); printf("atom = %Lx\n", (L)make_atom(inode, "foo", 3)); printf("atom = %Lx\n", (L)make_atom(inode, "bar", 3)); warn("---- test inode xattr cache ----"); int err; err = xcache_update(inode, 0x666, "hello", 5, 0); if (err) printf("err %d\n", err); err = xcache_update(inode, 0x777, "world!", 6, 0); if (err) printf("err %d\n", err); xcache_dump(inode); struct xattr *xattr = xcache_lookup(tux_inode(inode)->xcache, 0x777); if (!IS_ERR(xattr)) printf("atom %x => %.*s\n", xattr->atom, xattr->size, xattr->body); err = xcache_update(inode, 0x111, "class", 5, 0); if (err) printf("err %d\n", err); err = xcache_update(inode, 0x666, NULL, 0, 0); if (err) printf("err %d\n", err); err = xcache_update(inode, 0x222, "boooyah", 7, 0); if (err) printf("err %d\n", err); xcache_dump(inode); warn("---- test xattr inode table encode and decode ----"); char attrs[1000] = { }; char *top = encode_xattrs(inode, attrs, sizeof(attrs)); hexdump(attrs, top - attrs); printf("predicted size = %x, encoded size = %Lx\n", encode_xsize(inode), (L)(top - attrs)); inode->xcache->size = offsetof(struct xcache, xattrs); char *newtop = decode_attrs(inode, attrs, top - attrs); printf("predicted size = %x, xcache size = %x\n", decode_xsize(inode, attrs, top - attrs), inode->xcache->size); assert(top == newtop); xcache_dump(inode); free(inode->xcache); inode->xcache = NULL; warn("---- xattr update ----"); set_xattr(inode, "hello", 5, "world!", 6, 0); set_xattr(inode, "empty", 5, "zot", 0, 0); set_xattr(inode, "foo", 3, "foobar", 6, 0); xcache_dump(inode); warn("---- xattr remove ----"); // del_xattr(inode, "hello", 5); xcache_dump(inode); warn("---- xattr lookup ----"); for (int i = 0, len; i < 3; i++) { char *namelist[] = { "hello", "foo", "world" }, *name = namelist[i]; char data[100]; int size = get_xattr(inode, name, len = strlen(name), data, sizeof(data)); if (size < 0) printf("xattr %.*s not found (%s)\n", len, name, strerror(-size)); else printf("found xattr %.*s => %.*s\n", len, name, size, data); } warn("---- list xattrs ----"); int len = xattr_list(inode, attrs, sizeof(attrs)); printf("xattr list length = %i\n", xattr_list(inode, NULL, 0)); hexdump(attrs, len); warn("---- atom reverse map ----"); for (int i = 0; i < 5; i++) { unsigned atom = i, offset; struct buffer_head *buffer = blockread_unatom(inode, atom, &offset); loff_t where = from_be_u64(((be_u64 *)bufdata(buffer))[offset]); blockput_dirty(buffer); buffer = blockread(mapping(inode), where >> sb->blockbits); printf("atom %.3Lx at dirent %.4Lx, ", (L)atom, (L)where); hexdump(bufdata(buffer) + (where & sb->blockmask), 16); blockput(buffer); } warn("---- atom recycle ----"); set_xattr(inode, "hello", 5, NULL, 0, 0); show_freeatoms(sb); printf("got free atom %x\n", get_freeatom(inode)); printf("got free atom %x\n", get_freeatom(inode)); printf("got free atom %x\n", get_freeatom(inode)); warn("---- dump atom table ----"); dump_atoms(inode); show_buffers(inode->map); exit(0); }
loff_t tux_create_entry(struct inode *dir, const char *name, unsigned len, inum_t inum, umode_t mode, loff_t *size) { unsigned delta = tux3_get_current_delta(); struct sb *sb = tux_sb(dir->i_sb); tux_dirent *entry; struct buffer_head *buffer, *clone; unsigned reclen = TUX_REC_LEN(len), rec_len, name_len, offset; unsigned blocksize = sb->blocksize; block_t block, blocks = *size >> sb->blockbits; void *olddata; for (block = 0; block < blocks; block++) { buffer = blockread(mapping(dir), block); if (!buffer) return -EIO; entry = bufdata(buffer); tux_dirent *limit = bufdata(buffer) + blocksize - reclen; while (entry <= limit) { if (entry->rec_len == 0) { blockput(buffer); tux_zero_len_error(dir, block); return -EIO; } name_len = TUX_REC_LEN(entry->name_len); rec_len = tux_rec_len_from_disk(entry->rec_len); if (is_deleted(entry) && rec_len >= reclen) goto create; if (rec_len >= name_len + reclen) goto create; entry = (void *)entry + rec_len; } blockput(buffer); } entry = NULL; buffer = blockget(mapping(dir), block); assert(!buffer_dirty(buffer)); create: /* * The directory is protected by i_mutex. * blockdirty() should never return -EAGAIN. */ olddata = bufdata(buffer); clone = blockdirty(buffer, delta); if (IS_ERR(clone)) { assert(PTR_ERR(clone) != -EAGAIN); blockput(buffer); return PTR_ERR(clone); } if (!entry) { /* Expanding the directory size. Initialize block. */ entry = bufdata(clone); memset(entry, 0, blocksize); entry->rec_len = tux_rec_len_to_disk(blocksize); assert(is_deleted(entry)); *size += blocksize; } else { entry = ptr_redirect(entry, olddata, bufdata(clone)); if (!is_deleted(entry)) { tux_dirent *newent = (void *)entry + name_len; unsigned rest_rec_len = rec_len - name_len; newent->rec_len = tux_rec_len_to_disk(rest_rec_len); entry->rec_len = tux_rec_len_to_disk(name_len); entry = newent; } } entry->name_len = len; memcpy(entry->name, name, len); offset = (void *)entry - bufdata(clone); /* this releases buffer */ tux_update_entry(clone, entry, inum, mode); return (block << sb->blockbits) + offset; /* only for xattr create */ }
/* * Almost copy of generic_file_splice_write() (added changed_begin/end, * tux3_iattrdirty()). */ static ssize_t tux3_file_splice_write(struct pipe_inode_info *pipe, struct file *out, loff_t *ppos, size_t len, unsigned int flags) { if(DEBUG_MODE_K==1) { printk(KERN_INFO"%25s %25s %4d #in\n",__FILE__,__func__,__LINE__); } struct address_space *mapping = out->f_mapping; struct inode *inode = mapping->host; struct sb *sb = tux_sb(inode->i_sb); struct splice_desc sd = { .total_len = len, .flags = flags, .pos = *ppos, .u.file = out, }; ssize_t ret; sb_start_write(inode->i_sb); pipe_lock(pipe); splice_from_pipe_begin(&sd); do { ret = splice_from_pipe_next(pipe, &sd); if (ret <= 0) break; mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD); /* For each ->write_end() calls change_end(). */ change_begin(sb); /* For timestamp. FIXME: convert this to ->update_time * handler? */ tux3_iattrdirty(inode); ret = file_remove_suid(out); if (!ret) { ret = file_update_time(out); if (!ret) ret = splice_from_pipe_feed(pipe, &sd, pipe_to_file); } change_end_if_needed(sb); mutex_unlock(&inode->i_mutex); } while (ret > 0); splice_from_pipe_end(pipe, &sd); pipe_unlock(pipe); if (sd.num_spliced) ret = sd.num_spliced; if (ret > 0) { int err; err = generic_write_sync(out, *ppos, ret); if (err) ret = err; else *ppos += ret; balance_dirty_pages_ratelimited(mapping); } sb_end_write(inode->i_sb); return ret; }