diff -urN linux-2.4.16-reiserfspatches/fs/ext2/ialloc.c linux-2.4.16-reiserfspatches-immutable/fs/ext2/ialloc.c --- linux-2.4.16-reiserfspatches/fs/ext2/ialloc.c Mon Dec 10 13:12:48 2001 +++ linux-2.4.16-reiserfspatches-immutable/fs/ext2/ialloc.c Mon Dec 10 14:28:03 2001 @@ -389,7 +389,7 @@ inode->u.ext2_i.i_new_inode = 1; inode->u.ext2_i.i_flags = dir->u.ext2_i.i_flags; if (S_ISLNK(mode)) - inode->u.ext2_i.i_flags &= ~(EXT2_IMMUTABLE_FL|EXT2_APPEND_FL); + inode->u.ext2_i.i_flags &= ~(EXT2_IMMUTABLE_FILE_FL|EXT2_IMMUTABLE_LINK_FL|EXT2_APPEND_FL); inode->u.ext2_i.i_block_group = group; if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL) inode->i_flags |= S_SYNC; diff -urN linux-2.4.16-reiserfspatches/fs/ext2/inode.c linux-2.4.16-reiserfspatches-immutable/fs/ext2/inode.c --- linux-2.4.16-reiserfspatches/fs/ext2/inode.c Mon Dec 10 13:12:48 2001 +++ linux-2.4.16-reiserfspatches-immutable/fs/ext2/inode.c Mon Dec 10 14:28:03 2001 @@ -801,7 +801,7 @@ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) return; - if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + if (IS_APPEND(inode) || IS_IMMUTABLE_FILE(inode)) return; ext2_discard_prealloc(inode); @@ -1007,9 +1007,13 @@ inode->i_attr_flags |= ATTR_FLAG_APPEND; inode->i_flags |= S_APPEND; } - if (inode->u.ext2_i.i_flags & EXT2_IMMUTABLE_FL) { - inode->i_attr_flags |= ATTR_FLAG_IMMUTABLE; - inode->i_flags |= S_IMMUTABLE; + if (inode->u.ext2_i.i_flags & EXT2_IMMUTABLE_FILE_FL) { + inode->i_attr_flags |= ATTR_FLAG_IMMUTABLE_FILE; + inode->i_flags |= S_IMMUTABLE_FILE; + } + if (inode->u.ext2_i.i_flags & EXT2_IMMUTABLE_LINK_FL) { + inode->i_attr_flags |= ATTR_FLAG_IMMUTABLE_LINK; + inode->i_flags |= S_IMMUTABLE_LINK; } if (inode->u.ext2_i.i_flags & EXT2_NOATIME_FL) { inode->i_attr_flags |= ATTR_FLAG_NOATIME; diff -urN linux-2.4.16-reiserfspatches/fs/ext2/ioctl.c linux-2.4.16-reiserfspatches-immutable/fs/ext2/ioctl.c --- linux-2.4.16-reiserfspatches/fs/ext2/ioctl.c Wed Sep 27 21:41:33 2000 +++ linux-2.4.16-reiserfspatches-immutable/fs/ext2/ioctl.c Mon Dec 10 14:28:03 2001 @@ -44,7 +44,7 @@ * * This test looks nicer. Thanks to Pauline Middelink */ - if ((flags ^ oldflags) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) { + if ((flags ^ oldflags) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FILE_FL | EXT2_IMMUTABLE_LINK_FL)) { if (!capable(CAP_LINUX_IMMUTABLE)) return -EPERM; } @@ -61,10 +61,17 @@ inode->i_flags |= S_APPEND; else inode->i_flags &= ~S_APPEND; - if (flags & EXT2_IMMUTABLE_FL) - inode->i_flags |= S_IMMUTABLE; + + if (flags & EXT2_IMMUTABLE_FILE_FL) + inode->i_flags |= S_IMMUTABLE_FILE; + else + inode->i_flags &= ~S_IMMUTABLE_FILE; + + if (flags & EXT2_IMMUTABLE_LINK_FL) + inode->i_flags |= S_IMMUTABLE_LINK; else - inode->i_flags &= ~S_IMMUTABLE; + inode->i_flags &= ~S_IMMUTABLE_LINK; + if (flags & EXT2_NOATIME_FL) inode->i_flags |= S_NOATIME; else diff -urN linux-2.4.16-reiserfspatches/fs/ext3/ialloc.c linux-2.4.16-reiserfspatches-immutable/fs/ext3/ialloc.c --- linux-2.4.16-reiserfspatches/fs/ext3/ialloc.c Mon Dec 10 13:12:48 2001 +++ linux-2.4.16-reiserfspatches-immutable/fs/ext3/ialloc.c Mon Dec 10 14:28:03 2001 @@ -486,7 +486,7 @@ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; inode->u.ext3_i.i_flags = dir->u.ext3_i.i_flags & ~EXT3_INDEX_FL; if (S_ISLNK(mode)) - inode->u.ext3_i.i_flags &= ~(EXT3_IMMUTABLE_FL|EXT3_APPEND_FL); + inode->u.ext3_i.i_flags &= ~(EXT3_IMMUTABLE_FILE_FL|EXT3_IMMUTABLE_LINK_FL|EXT3_APPEND_FL); #ifdef EXT3_FRAGMENTS inode->u.ext3_i.i_faddr = 0; inode->u.ext3_i.i_frag_no = 0; diff -urN linux-2.4.16-reiserfspatches/fs/ext3/inode.c linux-2.4.16-reiserfspatches-immutable/fs/ext3/inode.c --- linux-2.4.16-reiserfspatches/fs/ext3/inode.c Mon Dec 10 13:12:48 2001 +++ linux-2.4.16-reiserfspatches-immutable/fs/ext3/inode.c Mon Dec 10 14:28:03 2001 @@ -1823,7 +1823,7 @@ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) return; - if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + if (IS_APPEND(inode) || IS_IMMUTABLE_FILE(inode)) return; ext3_discard_prealloc(inode); @@ -2127,9 +2127,13 @@ /* inode->i_attr_flags |= ATTR_FLAG_APPEND; unused */ inode->i_flags |= S_APPEND; } - if (inode->u.ext3_i.i_flags & EXT3_IMMUTABLE_FL) { - /* inode->i_attr_flags |= ATTR_FLAG_IMMUTABLE; unused */ - inode->i_flags |= S_IMMUTABLE; + if (inode->u.ext3_i.i_flags & EXT3_IMMUTABLE_FILE_FL) { + /* inode->i_attr_flags |= ATTR_FLAG_IMMUTABLE_FILE; unused */ + inode->i_flags |= S_IMMUTABLE_FILE; + } + if (inode->u.ext3_i.i_flags & EXT3_IMMUTABLE_LINK_FL) { + /* inode->i_attr_flags |= ATTR_FLAG_IMMUTABLE_FILE; unused */ + inode->i_flags |= S_IMMUTABLE_LINK; } if (inode->u.ext3_i.i_flags & EXT3_NOATIME_FL) { /* inode->i_attr_flags |= ATTR_FLAG_NOATIME; unused */ diff -urN linux-2.4.16-reiserfspatches/fs/ext3/ioctl.c linux-2.4.16-reiserfspatches-immutable/fs/ext3/ioctl.c --- linux-2.4.16-reiserfspatches/fs/ext3/ioctl.c Mon Dec 10 13:12:48 2001 +++ linux-2.4.16-reiserfspatches-immutable/fs/ext3/ioctl.c Mon Dec 10 14:28:03 2001 @@ -53,7 +53,7 @@ * * This test looks nicer. Thanks to Pauline Middelink */ - if ((flags ^ oldflags) & (EXT3_APPEND_FL | EXT3_IMMUTABLE_FL)) { + if ((flags ^ oldflags) & (EXT3_APPEND_FL | EXT3_IMMUTABLE_FILE_FL | EXT3_IMMUTABLE_LINK_FL)) { if (!capable(CAP_LINUX_IMMUTABLE)) return -EPERM; } @@ -89,10 +89,14 @@ inode->i_flags |= S_APPEND; else inode->i_flags &= ~S_APPEND; - if (flags & EXT3_IMMUTABLE_FL) - inode->i_flags |= S_IMMUTABLE; + if (flags & EXT3_IMMUTABLE_FILE_FL) + inode->i_flags |= S_IMMUTABLE_FILE; else - inode->i_flags &= ~S_IMMUTABLE; + inode->i_flags &= ~S_IMMUTABLE_FILE; + if (flags & EXT3_IMMUTABLE_LINK_FL) + inode->i_flags |= S_IMMUTABLE_LINK; + else + inode->i_flags &= ~S_IMMUTABLE_LINK; if (flags & EXT3_NOATIME_FL) inode->i_flags |= S_NOATIME; else diff -urN linux-2.4.16-reiserfspatches/fs/fat/file.c linux-2.4.16-reiserfspatches-immutable/fs/fat/file.c --- linux-2.4.16-reiserfspatches/fs/fat/file.c Sun Aug 12 18:56:56 2001 +++ linux-2.4.16-reiserfspatches-immutable/fs/fat/file.c Mon Dec 10 14:28:03 2001 @@ -119,7 +119,7 @@ /* Why no return value? Surely the disk could fail... */ if (IS_RDONLY (inode)) return /* -EPERM */; - if (IS_IMMUTABLE(inode)) + if (IS_IMMUTABLE_FILE(inode)) return /* -EPERM */; cluster = 1 << sbi->cluster_bits; /* diff -urN linux-2.4.16-reiserfspatches/fs/fat/inode.c linux-2.4.16-reiserfspatches-immutable/fs/fat/inode.c --- linux-2.4.16-reiserfspatches/fs/fat/inode.c Mon Dec 10 13:12:48 2001 +++ linux-2.4.16-reiserfspatches-immutable/fs/fat/inode.c Mon Dec 10 14:28:03 2001 @@ -947,7 +947,7 @@ } if(de->attr & ATTR_SYS) if (sbi->options.sys_immutable) - inode->i_flags |= S_IMMUTABLE; + inode->i_flags |= S_IMMUTABLE_FILE; MSDOS_I(inode)->i_attrs = de->attr & ATTR_UNUSED; /* this is as close to the truth as we can get ... */ inode->i_blksize = 1 << sbi->cluster_bits; diff -urN linux-2.4.16-reiserfspatches/fs/hpfs/file.c linux-2.4.16-reiserfspatches-immutable/fs/hpfs/file.c --- linux-2.4.16-reiserfspatches/fs/hpfs/file.c Mon Aug 13 01:37:53 2001 +++ linux-2.4.16-reiserfspatches-immutable/fs/hpfs/file.c Mon Dec 10 14:28:03 2001 @@ -60,7 +60,7 @@ void hpfs_truncate(struct inode *i) { - if (IS_IMMUTABLE(i)) return /*-EPERM*/; + if (IS_IMMUTABLE_FILE(i)) return /*-EPERM*/; i->i_hpfs_n_secs = 0; i->i_blocks = 1 + ((i->i_size + 511) >> 9); i->u.hpfs_i.mmu_private = i->i_size; diff -urN linux-2.4.16-reiserfspatches/fs/intermezzo/vfs.c linux-2.4.16-reiserfspatches-immutable/fs/intermezzo/vfs.c --- linux-2.4.16-reiserfspatches/fs/intermezzo/vfs.c Mon Dec 10 13:12:48 2001 +++ linux-2.4.16-reiserfspatches-immutable/fs/intermezzo/vfs.c Mon Dec 10 14:28:03 2001 @@ -89,7 +89,7 @@ if (IS_APPEND(dir)) return -EPERM; if (check_sticky(dir, victim->d_inode)||IS_APPEND(victim->d_inode)|| - IS_IMMUTABLE(victim->d_inode)) + IS_IMMUTABLE_LINK(victim->d_inode)) return -EPERM; if (isdir) { if (!S_ISDIR(victim->d_inode->i_mode)) @@ -190,7 +190,7 @@ return -EROFS; } - if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) { + if (IS_IMMUTABLE_FILE(inode) || IS_APPEND(inode)) { EXIT; return -EPERM; } @@ -231,7 +231,7 @@ return -EROFS; } - if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) { + if (IS_IMMUTABLE_FILE(inode) || IS_APPEND(inode)) { EXIT; return -EPERM; } @@ -609,7 +609,7 @@ * A link to an append-only or immutable file cannot be created. */ error = -EPERM; - if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) { + if (IS_APPEND(inode) || IS_IMMUTABLE_LINK(inode)) { EXIT; goto exit_lock; } @@ -2201,7 +2201,9 @@ return -EROFS; } - if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) { + /* Oh gawd. Should this be IS_IMMUTABLE_FILE or + IS_IMMUTABLE_LINK? */ + if (IS_IMMUTABLE_FILE(inode) || IS_APPEND(inode)) { EXIT; return -EPERM; } diff -urN linux-2.4.16-reiserfspatches/fs/namei.c linux-2.4.16-reiserfspatches-immutable/fs/namei.c --- linux-2.4.16-reiserfspatches/fs/namei.c Mon Dec 10 13:12:23 2001 +++ linux-2.4.16-reiserfspatches-immutable/fs/namei.c Mon Dec 10 14:28:03 2001 @@ -162,7 +162,7 @@ /* * Nobody gets write access to an immutable file. */ - if (IS_IMMUTABLE(inode)) + if (IS_IMMUTABLE_FILE(inode)) return -EACCES; } @@ -877,8 +877,7 @@ return error; if (IS_APPEND(dir)) return -EPERM; - if (check_sticky(dir, victim->d_inode)||IS_APPEND(victim->d_inode)|| - IS_IMMUTABLE(victim->d_inode)) + if (check_sticky(dir, victim->d_inode)||IS_APPEND(victim->d_inode)||IS_IMMUTABLE_LINK(victim->d_inode)) return -EPERM; if (isdir) { if (!S_ISDIR(victim->d_inode->i_mode)) @@ -1594,7 +1593,7 @@ * A link to an append-only or immutable file cannot be created. */ error = -EPERM; - if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + if (IS_APPEND(inode) || IS_IMMUTABLE_LINK(inode)) goto exit_lock; if (!dir->i_op || !dir->i_op->link) goto exit_lock; diff -urN linux-2.4.16-reiserfspatches/fs/nfsd/vfs.c linux-2.4.16-reiserfspatches-immutable/fs/nfsd/vfs.c --- linux-2.4.16-reiserfspatches/fs/nfsd/vfs.c Mon Dec 10 13:11:59 2001 +++ linux-2.4.16-reiserfspatches-immutable/fs/nfsd/vfs.c Mon Dec 10 14:28:03 2001 @@ -1480,7 +1480,7 @@ if (acc == MAY_NOP) return 0; #if 0 - dprintk("nfsd: permission 0x%x%s%s%s%s%s%s%s mode 0%o%s%s%s\n", + dprintk("nfsd: permission 0x%x%s%s%s%s%s%s%s mode 0%o%s%s%s%s\n", acc, (acc & MAY_READ)? " read" : "", (acc & MAY_WRITE)? " write" : "", @@ -1490,7 +1490,8 @@ (acc & MAY_LOCK)? " lock" : "", (acc & MAY_OWNER_OVERRIDE)? " owneroverride" : "", inode->i_mode, - IS_IMMUTABLE(inode)? " immut" : "", + IS_IMMUTABLE_FILE(inode)? " immut(F)" : "", + IS_IMMUTABLE_LINK(inode)? " immut(L)" : "", IS_APPEND(inode)? " append" : "", IS_RDONLY(inode)? " ro" : ""); dprintk(" owner %d/%d user %d/%d\n", @@ -1505,7 +1506,7 @@ if (acc & (MAY_WRITE | MAY_SATTR | MAY_TRUNC)) { if (EX_RDONLY(exp) || IS_RDONLY(inode)) return nfserr_rofs; - if (/* (acc & MAY_WRITE) && */ IS_IMMUTABLE(inode)) + if (/* (acc & MAY_WRITE) && */ IS_IMMUTABLE_FILE(inode)) return nfserr_perm; } if ((acc & MAY_TRUNC) && IS_APPEND(inode)) diff -urN linux-2.4.16-reiserfspatches/fs/open.c linux-2.4.16-reiserfspatches-immutable/fs/open.c --- linux-2.4.16-reiserfspatches/fs/open.c Mon Dec 10 13:12:23 2001 +++ linux-2.4.16-reiserfspatches-immutable/fs/open.c Mon Dec 10 14:28:03 2001 @@ -470,7 +470,7 @@ if (IS_RDONLY(inode)) goto out_putf; err = -EPERM; - if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) + if (IS_IMMUTABLE_FILE(inode) || IS_APPEND(inode)) goto out_putf; if (mode == (mode_t) -1) mode = inode->i_mode; @@ -501,7 +501,7 @@ goto dput_and_out; error = -EPERM; - if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) + if (IS_IMMUTABLE_FILE(inode) || IS_APPEND(inode)) goto dput_and_out; if (mode == (mode_t) -1) @@ -531,7 +531,7 @@ if (IS_RDONLY(inode)) goto out; error = -EPERM; - if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) + if (IS_IMMUTABLE_FILE(inode) || IS_APPEND(inode)) goto out; if (user == (uid_t) -1) user = inode->i_uid; diff -urN linux-2.4.16-reiserfspatches/fs/proc/base.c linux-2.4.16-reiserfspatches-immutable/fs/proc/base.c --- linux-2.4.16-reiserfspatches/fs/proc/base.c Mon Dec 10 13:12:02 2001 +++ linux-2.4.16-reiserfspatches-immutable/fs/proc/base.c Mon Dec 10 14:28:03 2001 @@ -976,7 +976,7 @@ inode->i_op = &proc_base_inode_operations; inode->i_fop = &proc_base_operations; inode->i_nlink = 3; - inode->i_flags|=S_IMMUTABLE; + inode->i_flags|=S_IMMUTABLE_FILE; dentry->d_op = &pid_base_dentry_operations; d_add(dentry, inode); diff -urN linux-2.4.16-reiserfspatches/fs/reiserfs/inode.c linux-2.4.16-reiserfspatches-immutable/fs/reiserfs/inode.c --- linux-2.4.16-reiserfspatches/fs/reiserfs/inode.c Mon Dec 10 13:57:11 2001 +++ linux-2.4.16-reiserfspatches-immutable/fs/reiserfs/inode.c Mon Dec 10 14:22:18 2001 @@ -1519,7 +1519,7 @@ /* symlink cannot be immutable or append only, right? */ if( S_ISLNK( inode -> i_mode ) ) - inode -> i_flags &= ~ ( S_IMMUTABLE | S_APPEND ); + inode -> i_flags &= ~ ( S_IMMUTABLE_FILE | S_IMMUTABLE_LINK | S_APPEND ); inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; inode->i_size = i_size; @@ -2049,10 +2049,14 @@ inode -> i_flags |= S_SYNC; else inode -> i_flags &= ~S_SYNC; - if( sd_attrs & REISERFS_IMMUTABLE_FL ) - inode -> i_flags |= S_IMMUTABLE; + if( sd_attrs & REISERFS_IMMUTABLE_FILE_FL ) + inode -> i_flags |= S_IMMUTABLE_FILE; else - inode -> i_flags &= ~S_IMMUTABLE; + inode -> i_flags &= ~S_IMMUTABLE_FILE; + if( sd_attrs & REISERFS_IMMUTABLE_LINK_FL ) + inode -> i_flags |= S_IMMUTABLE_LINK; + else + inode -> i_flags &= ~S_IMMUTABLE_LINK; if( sd_attrs & REISERFS_NOATIME_FL ) inode -> i_flags |= S_NOATIME; else @@ -2067,10 +2071,14 @@ void i_attrs_to_sd_attrs( struct inode *inode, __u16 *sd_attrs ) { if( !reiserfs_noattrs( inode -> i_sb ) ) { - if( inode -> i_flags & S_IMMUTABLE ) - *sd_attrs |= REISERFS_IMMUTABLE_FL; + if( inode -> i_flags & S_IMMUTABLE_FILE ) + *sd_attrs |= REISERFS_IMMUTABLE_FILE_FL; + else + *sd_attrs &= ~REISERFS_IMMUTABLE_FILE_FL; + if( inode -> i_flags & S_IMMUTABLE_LINK ) + *sd_attrs |= REISERFS_IMMUTABLE_LINK_FL; else - *sd_attrs &= ~REISERFS_IMMUTABLE_FL; + *sd_attrs &= ~REISERFS_IMMUTABLE_LINK_FL; if( inode -> i_flags & S_SYNC ) *sd_attrs |= REISERFS_SYNC_FL; else diff -urN linux-2.4.16-reiserfspatches/fs/reiserfs/inode.c~ linux-2.4.16-reiserfspatches-immutable/fs/reiserfs/inode.c~ --- linux-2.4.16-reiserfspatches/fs/reiserfs/inode.c~ Thu Jan 1 01:00:00 1970 +++ linux-2.4.16-reiserfspatches-immutable/fs/reiserfs/inode.c~ Mon Dec 10 13:57:11 2001 @@ -0,0 +1,2096 @@ +/* + * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README + */ + +#include +#include +#include +#include +#include +#include +#include + +/* args for the create parameter of reiserfs_get_block */ +#define GET_BLOCK_NO_CREATE 0 /* don't create new blocks or convert tails */ +#define GET_BLOCK_CREATE 1 /* add anything you need to find block */ +#define GET_BLOCK_NO_HOLE 2 /* return -ENOENT for file holes */ +#define GET_BLOCK_READ_DIRECT 4 /* read the tail if indirect item not found */ +#define GET_BLOCK_NO_ISEM 8 /* i_sem is not held, don't preallocate */ + +static int reiserfs_get_block (struct inode * inode, long block, + struct buffer_head * bh_result, int create); +// +// initially this function was derived from minix or ext2's analog and +// evolved as the prototype did +// +void reiserfs_delete_inode (struct inode * inode) +{ + int jbegin_count = JOURNAL_PER_BALANCE_CNT * 2; + int windex ; + struct reiserfs_transaction_handle th ; + + + lock_kernel() ; + + /* The = 0 happens when we abort creating a new inode for some reason like lack of space.. */ + if (INODE_PKEY(inode)->k_objectid != 0) { /* also handles bad_inode case */ + down (&inode->i_sem); + + journal_begin(&th, inode->i_sb, jbegin_count) ; + reiserfs_update_inode_transaction(inode) ; + windex = push_journal_writer("delete_inode") ; + + reiserfs_delete_object (&th, inode); + pop_journal_writer(windex) ; + + journal_end(&th, inode->i_sb, jbegin_count) ; + + up (&inode->i_sem); + + /* all items of file are deleted, so we can remove "save" link */ + remove_save_link (inode, 0/* not truncate */); + } else { + /* no object items are in the tree */ + ; + } + clear_inode (inode); /* note this must go after the journal_end to prevent deadlock */ + inode->i_blocks = 0; + unlock_kernel() ; +} + +static void _make_cpu_key (struct cpu_key * key, int version, __u32 dirid, __u32 objectid, + loff_t offset, int type, int length ) +{ + key->version = version; + + key->on_disk_key.k_dir_id = dirid; + key->on_disk_key.k_objectid = objectid; + set_cpu_key_k_offset (key, offset); + set_cpu_key_k_type (key, type); + key->key_length = length; +} + + +/* take base of inode_key (it comes from inode always) (dirid, objectid) and version from an inode, set + offset and type of key */ +void make_cpu_key (struct cpu_key * key, const struct inode * inode, loff_t offset, + int type, int length ) +{ + _make_cpu_key (key, get_inode_item_key_version (inode), le32_to_cpu (INODE_PKEY (inode)->k_dir_id), + le32_to_cpu (INODE_PKEY (inode)->k_objectid), + offset, type, length); +} + + +// +// when key is 0, do not set version and short key +// +inline void make_le_item_head (struct item_head * ih, const struct cpu_key * key, + int version, + loff_t offset, int type, int length, + int entry_count/*or ih_free_space*/) +{ + if (key) { + ih->ih_key.k_dir_id = cpu_to_le32 (key->on_disk_key.k_dir_id); + ih->ih_key.k_objectid = cpu_to_le32 (key->on_disk_key.k_objectid); + } + put_ih_version( ih, version ); + set_le_ih_k_offset (ih, offset); + set_le_ih_k_type (ih, type); + put_ih_item_len( ih, length ); + /* set_ih_free_space (ih, 0);*/ + // for directory items it is entry count, for directs and stat + // datas - 0xffff, for indirects - 0 + put_ih_entry_count( ih, entry_count ); +} + +static void add_to_flushlist(struct inode *inode, struct buffer_head *bh) { + struct inode *jinode = &(SB_JOURNAL(inode->i_sb)->j_dummy_inode) ; + + buffer_insert_inode_queue(bh, jinode) ; +} + +// +// FIXME: we might cache recently accessed indirect item (or at least +// first 15 pointers just like ext2 does + +// Ugh. Not too eager for that.... +// I cut the code until such time as I see a convincing argument (benchmark). +// I don't want a bloated inode struct..., and I don't like code complexity.... + +/* cutting the code is fine, since it really isn't in use yet and is easy +** to add back in. But, Vladimir has a really good idea here. Think +** about what happens for reading a file. For each page, +** The VFS layer calls reiserfs_readpage, who searches the tree to find +** an indirect item. This indirect item has X number of pointers, where +** X is a big number if we've done the block allocation right. But, +** we only use one or two of these pointers during each call to readpage, +** needlessly researching again later on. +** +** The size of the cache could be dynamic based on the size of the file. +** +** I'd also like to see us cache the location the stat data item, since +** we are needlessly researching for that frequently. +** +** --chris +*/ + +/* If this page has a file tail in it, and +** it was read in by get_block_create_0, the page data is valid, +** but tail is still sitting in a direct item, and we can't write to +** it. So, look through this page, and check all the mapped buffers +** to make sure they have valid block numbers. Any that don't need +** to be unmapped, so that block_prepare_write will correctly call +** reiserfs_get_block to convert the tail into an unformatted node +*/ +static inline void fix_tail_page_for_writing(struct page *page) { + struct buffer_head *head, *next, *bh ; + + if (page && page->buffers) { + head = page->buffers ; + bh = head ; + do { + next = bh->b_this_page ; + if (buffer_mapped(bh) && bh->b_blocknr == 0) { + reiserfs_unmap_buffer(bh) ; + } + bh = next ; + } while (bh != head) ; + } +} + + + + +/* we need to allocate a block for new unformatted node. Try to figure out + what point in bitmap reiserfs_new_blocknrs should start from. */ +static b_blocknr_t find_tag (struct buffer_head * bh, struct item_head * ih, + __u32 * item, int pos_in_item) +{ + __u32 block ; + if (!is_indirect_le_ih (ih)) + /* something more complicated could be here */ + return bh->b_blocknr; + + /* for indirect item: go to left and look for the first non-hole entry in + the indirect item */ + if (pos_in_item == I_UNFM_NUM (ih)) + pos_in_item --; + while (pos_in_item >= 0) { + block = get_block_num(item, pos_in_item) ; + if (block) + return block ; + pos_in_item --; + } + return bh->b_blocknr; +} + + +/* reiserfs_get_block does not need to allocate a block only if it has been + done already or non-hole position has been found in the indirect item */ +static inline int allocation_needed (int retval, b_blocknr_t allocated, + struct item_head * ih, + __u32 * item, int pos_in_item) +{ + if (allocated) + return 0; + if (retval == POSITION_FOUND && is_indirect_le_ih (ih) && + get_block_num(item, pos_in_item)) + return 0; + return 1; +} + +static inline int indirect_item_found (int retval, struct item_head * ih) +{ + return (retval == POSITION_FOUND) && is_indirect_le_ih (ih); +} + + +static inline void set_block_dev_mapped (struct buffer_head * bh, + b_blocknr_t block, struct inode * inode) +{ + bh->b_dev = inode->i_dev; + bh->b_blocknr = block; + bh->b_state |= (1UL << BH_Mapped); +} + + +// +// files which were created in the earlier version can not be longer, +// than 2 gb +// +static int file_capable (struct inode * inode, long block) +{ + if (get_inode_item_key_version (inode) != KEY_FORMAT_3_5 || // it is new file. + block < (1 << (31 - inode->i_sb->s_blocksize_bits))) // old file, but 'block' is inside of 2gb + return 1; + + return 0; +} + +/*static*/ void restart_transaction(struct reiserfs_transaction_handle *th, + struct inode *inode, struct path *path) { + struct super_block *s = th->t_super ; + int len = th->t_blocks_allocated ; + + pathrelse(path) ; + reiserfs_update_sd(th, inode) ; + journal_end(th, s, len) ; + journal_begin(th, s, len) ; + reiserfs_update_inode_transaction(inode) ; +} + +// it is called by get_block when create == 0. Returns block number +// for 'block'-th logical block of file. When it hits direct item it +// returns 0 (being called from bmap) or read direct item into piece +// of page (bh_result) + +// Please improve the english/clarity in the comment above, as it is +// hard to understand. + +static int _get_block_create_0 (struct inode * inode, long block, + struct buffer_head * bh_result, + int args) +{ + INITIALIZE_PATH (path); + struct cpu_key key; + struct buffer_head * bh; + struct item_head * ih, tmp_ih; + int fs_gen ; + int blocknr; + char * p = NULL; + int chars; + int ret ; + int done = 0 ; + unsigned long offset ; + + // prepare the key to look for the 'block'-th block of file + make_cpu_key (&key, inode, + (loff_t)block * inode->i_sb->s_blocksize + 1, TYPE_ANY, 3); + +research: + if (search_for_position_by_key (inode->i_sb, &key, &path) != POSITION_FOUND) { + pathrelse (&path); + if (p) + kunmap(bh_result->b_page) ; + if ((args & GET_BLOCK_NO_HOLE)) { + return -ENOENT ; + } + return 0 ; + } + + // + bh = get_last_bh (&path); + ih = get_ih (&path); + if (is_indirect_le_ih (ih)) { + __u32 * ind_item = (__u32 *)B_I_PITEM (bh, ih); + + /* FIXME: here we could cache indirect item or part of it in + the inode to avoid search_by_key in case of subsequent + access to file */ + blocknr = get_block_num(ind_item, path.pos_in_item) ; + ret = 0 ; + if (blocknr) { + bh_result->b_dev = inode->i_dev; + bh_result->b_blocknr = blocknr; + bh_result->b_state |= (1UL << BH_Mapped); + } else if ((args & GET_BLOCK_NO_HOLE)) { + ret = -ENOENT ; + } + pathrelse (&path); + if (p) + kunmap(bh_result->b_page) ; + return ret ; + } + + // requested data are in direct item(s) + if (!(args & GET_BLOCK_READ_DIRECT)) { + // we are called by bmap. FIXME: we can not map block of file + // when it is stored in direct item(s) + pathrelse (&path); + if (p) + kunmap(bh_result->b_page) ; + return -ENOENT; + } + + /* if we've got a direct item, and the buffer was uptodate, + ** we don't want to pull data off disk again. skip to the + ** end, where we map the buffer and return + */ + if (buffer_uptodate(bh_result)) { + goto finished ; + } + + // read file tail into part of page + offset = (cpu_key_k_offset(&key) - 1) & (PAGE_CACHE_SIZE - 1) ; + fs_gen = get_generation(inode->i_sb) ; + copy_item_head (&tmp_ih, ih); + + /* we only want to kmap if we are reading the tail into the page. + ** this is not the common case, so we don't kmap until we are + ** sure we need to. But, this means the item might move if + ** kmap schedules + */ + if (!p) { + p = (char *)kmap(bh_result->b_page) ; + if (fs_changed (fs_gen, inode->i_sb) && item_moved (&tmp_ih, &path)) { + goto research; + } + } + p += offset ; + memset (p, 0, inode->i_sb->s_blocksize); + do { + if (!is_direct_le_ih (ih)) { + BUG (); + } + /* make sure we don't read more bytes than actually exist in + ** the file. This can happen in odd cases where i_size isn't + ** correct, and when direct item padding results in a few + ** extra bytes at the end of the direct item + */ + if ((le_ih_k_offset(ih) + path.pos_in_item) > inode->i_size) + break ; + if ((le_ih_k_offset(ih) - 1 + ih_item_len(ih)) > inode->i_size) { + chars = inode->i_size - (le_ih_k_offset(ih) - 1) - path.pos_in_item; + done = 1 ; + } else { + chars = ih_item_len(ih) - path.pos_in_item; + } + memcpy (p, B_I_PITEM (bh, ih) + path.pos_in_item, chars); + + if (done) + break ; + + p += chars; + + if (PATH_LAST_POSITION (&path) != (B_NR_ITEMS (bh) - 1)) + // we done, if read direct item is not the last item of + // node FIXME: we could try to check right delimiting key + // to see whether direct item continues in the right + // neighbor or rely on i_size + break; + + // update key to look for the next piece + set_cpu_key_k_offset (&key, cpu_key_k_offset (&key) + chars); + if (search_for_position_by_key (inode->i_sb, &key, &path) != POSITION_FOUND) + // we read something from tail, even if now we got IO_ERROR + break; + bh = get_last_bh (&path); + ih = get_ih (&path); + } while (1); + + flush_dcache_page(bh_result->b_page) ; + kunmap(bh_result->b_page) ; + +finished: + pathrelse (&path); + bh_result->b_blocknr = 0 ; + bh_result->b_dev = inode->i_dev; + mark_buffer_uptodate (bh_result, 1); + bh_result->b_state |= (1UL << BH_Mapped); + return 0; +} + + +// this is called to create file map. So, _get_block_create_0 will not +// read direct item +int reiserfs_bmap (struct inode * inode, long block, + struct buffer_head * bh_result, int create) +{ + if (!file_capable (inode, block)) + return -EFBIG; + + lock_kernel() ; + /* do not read the direct item */ + _get_block_create_0 (inode, block, bh_result, 0) ; + unlock_kernel() ; + return 0; +} + +/* special version of get_block that is only used by grab_tail_page right +** now. It is sent to block_prepare_write, and when you try to get a +** block past the end of the file (or a block from a hole) it returns +** -ENOENT instead of a valid buffer. block_prepare_write expects to +** be able to do i/o on the buffers returned, unless an error value +** is also returned. +** +** So, this allows block_prepare_write to be used for reading a single block +** in a page. Where it does not produce a valid page for holes, or past the +** end of the file. This turns out to be exactly what we need for reading +** tails for conversion. +** +** The point of the wrapper is forcing a certain value for create, even +** though the VFS layer is calling this function with create==1. If you +** don't want to send create == GET_BLOCK_NO_HOLE to reiserfs_get_block, +** don't use this function. +*/ +static int reiserfs_get_block_create_0 (struct inode * inode, long block, + struct buffer_head * bh_result, int create) { + return reiserfs_get_block(inode, block, bh_result, GET_BLOCK_NO_HOLE) ; +} + +/* +** helper function for when reiserfs_get_block is called for a hole +** but the file tail is still in a direct item +** bh_result is the buffer head for the hole +** tail_offset is the offset of the start of the tail in the file +** +** This calls prepare_write, which will start a new transaction +** you should not be in a transaction, or have any paths held when you +** call this. +*/ +static int convert_tail_for_hole(struct inode *inode, + struct buffer_head *bh_result, + loff_t tail_offset) { + unsigned long index ; + unsigned long tail_end ; + unsigned long tail_start ; + struct page * tail_page ; + struct page * hole_page = bh_result->b_page ; + int retval = 0 ; + + if ((tail_offset & (bh_result->b_size - 1)) != 1) + return -EIO ; + + /* always try to read until the end of the block */ + tail_start = tail_offset & (PAGE_CACHE_SIZE - 1) ; + tail_end = (tail_start | (bh_result->b_size - 1)) + 1 ; + + index = tail_offset >> PAGE_CACHE_SHIFT ; + if (index != hole_page->index) { + tail_page = grab_cache_page(inode->i_mapping, index) ; + retval = -ENOMEM; + if (!tail_page) { + goto out ; + } + } else { + tail_page = hole_page ; + } + + /* we don't have to make sure the conversion did not happen while + ** we were locking the page because anyone that could convert + ** must first take i_sem. + ** + ** We must fix the tail page for writing because it might have buffers + ** that are mapped, but have a block number of 0. This indicates tail + ** data that has been read directly into the page, and block_prepare_write + ** won't trigger a get_block in this case. + */ + fix_tail_page_for_writing(tail_page) ; + retval = block_prepare_write(tail_page, tail_start, tail_end, + reiserfs_get_block) ; + if (retval) + goto unlock ; + + /* tail conversion might change the data in the page */ + flush_dcache_page(tail_page) ; + + retval = generic_commit_write(NULL, tail_page, tail_start, tail_end) ; + +unlock: + if (tail_page != hole_page) { + UnlockPage(tail_page) ; + page_cache_release(tail_page) ; + } +out: + return retval ; +} + +static inline int _allocate_block(struct reiserfs_transaction_handle *th, + struct inode *inode, + b_blocknr_t *allocated_block_nr, + unsigned long tag, + int flags) { + +#ifdef REISERFS_PREALLOCATE + if (!(flags & GET_BLOCK_NO_ISEM)) { + return reiserfs_new_unf_blocknrs2(th, inode, allocated_block_nr, tag); + } +#endif + return reiserfs_new_unf_blocknrs (th, allocated_block_nr, tag); +} +// +// initially this function was derived from ext2's analog and evolved +// as the prototype did. You'll need to look at the ext2 version to +// determine which parts are derivative, if any, understanding that +// there are only so many ways to code to a given interface. +// +static int reiserfs_get_block (struct inode * inode, long block, + struct buffer_head * bh_result, int create) +{ + int repeat, retval; + unsigned long tag; + b_blocknr_t allocated_block_nr = 0;// b_blocknr_t is unsigned long + INITIALIZE_PATH(path); + int pos_in_item; + struct cpu_key key; + struct buffer_head * bh, * unbh = 0; + struct item_head * ih, tmp_ih; + __u32 * item; + int done; + int fs_gen; + int windex ; + struct reiserfs_transaction_handle th ; + /* space reserved in transaction batch: + . 3 balancings in direct->indirect conversion + . 1 block involved into reiserfs_update_sd() + XXX in practically impossible worst case direct2indirect() + can incur (much) more that 3 balancings. */ + int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 1; + int version; + int transaction_started = 0 ; + loff_t new_offset = (((loff_t)block) << inode->i_sb->s_blocksize_bits) + 1 ; + + /* bad.... */ + lock_kernel() ; + th.t_trans_id = 0 ; + version = get_inode_item_key_version (inode); + + if (block < 0) { + unlock_kernel(); + return -EIO; + } + + if (!file_capable (inode, block)) { + unlock_kernel() ; + return -EFBIG; + } + + /* if !create, we aren't changing the FS, so we don't need to + ** log anything, so we don't need to start a transaction + */ + if (!(create & GET_BLOCK_CREATE)) { + int ret ; + /* find number of block-th logical block of the file */ + ret = _get_block_create_0 (inode, block, bh_result, + create | GET_BLOCK_READ_DIRECT) ; + unlock_kernel() ; + return ret; + } + + inode->u.reiserfs_i.i_flags |= i_pack_on_close_mask; + + windex = push_journal_writer("reiserfs_get_block") ; + + /* set the key of the first byte in the 'block'-th block of file */ + make_cpu_key (&key, inode, new_offset, + TYPE_ANY, 3/*key length*/); + if ((new_offset + inode->i_sb->s_blocksize - 1) > inode->i_size) { + journal_begin(&th, inode->i_sb, jbegin_count) ; + reiserfs_update_inode_transaction(inode) ; + transaction_started = 1 ; + } + research: + + retval = search_for_position_by_key (inode->i_sb, &key, &path); + if (retval == IO_ERROR) { + retval = -EIO; + goto failure; + } + + bh = get_last_bh (&path); + ih = get_ih (&path); + item = get_item (&path); + pos_in_item = path.pos_in_item; + + fs_gen = get_generation (inode->i_sb); + copy_item_head (&tmp_ih, ih); + + if (allocation_needed (retval, allocated_block_nr, ih, item, pos_in_item)) { + /* we have to allocate block for the unformatted node */ + tag = find_tag (bh, ih, item, pos_in_item); + if (!transaction_started) { + pathrelse(&path) ; + journal_begin(&th, inode->i_sb, jbegin_count) ; + reiserfs_update_inode_transaction(inode) ; + transaction_started = 1 ; + goto research ; + } + + repeat = _allocate_block(&th, inode, &allocated_block_nr, tag, create); + + if (repeat == NO_DISK_SPACE) { + /* restart the transaction to give the journal a chance to free + ** some blocks. releases the path, so we have to go back to + ** research if we succeed on the second try + */ + restart_transaction(&th, inode, &path) ; + repeat = _allocate_block(&th, inode,&allocated_block_nr,tag,create); + + if (repeat != NO_DISK_SPACE) { + goto research ; + } + retval = -ENOSPC; + goto failure; + } + + if (fs_changed (fs_gen, inode->i_sb) && item_moved (&tmp_ih, &path)) { + goto research; + } + } + + if (indirect_item_found (retval, ih)) { + b_blocknr_t unfm_ptr; + /* 'block'-th block is in the file already (there is + corresponding cell in some indirect item). But it may be + zero unformatted node pointer (hole) */ + unfm_ptr = get_block_num (item, pos_in_item); + if (unfm_ptr == 0) { + /* use allocated block to plug the hole */ + reiserfs_prepare_for_journal(inode->i_sb, bh, 1) ; + if (fs_changed (fs_gen, inode->i_sb) && item_moved (&tmp_ih, &path)) { + reiserfs_restore_prepared_buffer(inode->i_sb, bh) ; + goto research; + } + bh_result->b_state |= (1UL << BH_New); + put_block_num(item, pos_in_item, allocated_block_nr) ; + unfm_ptr = allocated_block_nr; + journal_mark_dirty (&th, inode->i_sb, bh); + inode->i_blocks += (inode->i_sb->s_blocksize / 512) ; + reiserfs_update_sd(&th, inode) ; + } + set_block_dev_mapped(bh_result, unfm_ptr, inode); + pathrelse (&path); + pop_journal_writer(windex) ; + if (transaction_started) + journal_end(&th, inode->i_sb, jbegin_count) ; + + unlock_kernel() ; + + /* the item was found, so new blocks were not added to the file + ** there is no need to make sure the inode is updated with this + ** transaction + */ + return 0; + } + + if (!transaction_started) { + /* if we don't pathrelse, we could vs-3050 on the buffer if + ** someone is waiting for it (they can't finish until the buffer + ** is released, we can start a new transaction until they finish) + */ + pathrelse(&path) ; + journal_begin(&th, inode->i_sb, jbegin_count) ; + reiserfs_update_inode_transaction(inode) ; + transaction_started = 1 ; + goto research; + } + + /* desired position is not found or is in the direct item. We have + to append file with holes up to 'block'-th block converting + direct items to indirect one if necessary */ + done = 0; + do { + if (is_statdata_le_ih (ih)) { + __u32 unp = 0; + struct cpu_key tmp_key; + + /* indirect item has to be inserted */ + make_le_item_head (&tmp_ih, &key, version, 1, TYPE_INDIRECT, + UNFM_P_SIZE, 0/* free_space */); + + if (cpu_key_k_offset (&key) == 1) { + /* we are going to add 'block'-th block to the file. Use + allocated block for that */ + unp = cpu_to_le32 (allocated_block_nr); + set_block_dev_mapped (bh_result, allocated_block_nr, inode); + bh_result->b_state |= (1UL << BH_New); + done = 1; + } + tmp_key = key; // ;) + set_cpu_key_k_offset (&tmp_key, 1); + PATH_LAST_POSITION(&path) ++; + + retval = reiserfs_insert_item (&th, &path, &tmp_key, &tmp_ih, (char *)&unp); + if (retval) { + reiserfs_free_block (&th, allocated_block_nr); + goto failure; // retval == -ENOSPC or -EIO or -EEXIST + } + if (unp) + inode->i_blocks += inode->i_sb->s_blocksize / 512; + //mark_tail_converted (inode); + } else if (is_direct_le_ih (ih)) { + /* direct item has to be converted */ + loff_t tail_offset; + + tail_offset = ((le_ih_k_offset (ih) - 1) & ~(inode->i_sb->s_blocksize - 1)) + 1; + if (tail_offset == cpu_key_k_offset (&key)) { + /* direct item we just found fits into block we have + to map. Convert it into unformatted node: use + bh_result for the conversion */ + set_block_dev_mapped (bh_result, allocated_block_nr, inode); + unbh = bh_result; + done = 1; + } else { + /* we have to padd file tail stored in direct item(s) + up to block size and convert it to unformatted + node. FIXME: this should also get into page cache */ + + pathrelse(&path) ; + journal_end(&th, inode->i_sb, jbegin_count) ; + transaction_started = 0 ; + + retval = convert_tail_for_hole(inode, bh_result, tail_offset) ; + if (retval) { + printk("clm-6004: convert tail failed inode %lu, error %d\n", inode->i_ino, retval) ; + if (allocated_block_nr) + reiserfs_free_block (&th, allocated_block_nr); + goto failure ; + } + goto research ; + } + retval = direct2indirect (&th, inode, &path, unbh, tail_offset); + /* it is important the mark_buffer_uptodate is done after + ** the direct2indirect. The buffer might contain valid + ** data newer than the data on disk (read by readpage, changed, + ** and then sent here by writepage). direct2indirect needs + ** to know if unbh was already up to date, so it can decide + ** if the data in unbh needs to be replaced with data from + ** the disk + */ + mark_buffer_uptodate (unbh, 1); + if (retval) { + reiserfs_free_block (&th, allocated_block_nr); + goto failure; + } + /* we've converted the tail, so we must + ** flush unbh before the transaction commits + */ + add_to_flushlist(inode, unbh) ; + + /* mark it dirty now to prevent commit_write from adding + ** this buffer to the inode's dirty buffer list + */ + __mark_buffer_dirty(unbh) ; + + //inode->i_blocks += inode->i_sb->s_blocksize / 512; + //mark_tail_converted (inode); + } else { + /* append indirect item with holes if needed, when appending + pointer to 'block'-th block use block, which is already + allocated */ + struct cpu_key tmp_key; + struct unfm_nodeinfo un = {0, 0}; + + RFALSE( pos_in_item != ih_item_len(ih) / UNFM_P_SIZE, + "vs-804: invalid position for append"); + /* indirect item has to be appended, set up key of that position */ + make_cpu_key (&tmp_key, inode, + le_key_k_offset (version, &(ih->ih_key)) + op_bytes_number (ih, inode->i_sb->s_blocksize), + //pos_in_item * inode->i_sb->s_blocksize, + TYPE_INDIRECT, 3);// key type is unimportant + + if (cpu_key_k_offset (&tmp_key) == cpu_key_k_offset (&key)) { + /* we are going to add target block to the file. Use allocated + block for that */ + un.unfm_nodenum = cpu_to_le32 (allocated_block_nr); + set_block_dev_mapped (bh_result, allocated_block_nr, inode); + bh_result->b_state |= (1UL << BH_New); + done = 1; + } else { + /* paste hole to the indirect item */ + } + retval = reiserfs_paste_into_item (&th, &path, &tmp_key, (char *)&un, UNFM_P_SIZE); + if (retval) { + reiserfs_free_block (&th, allocated_block_nr); + goto failure; + } + if (un.unfm_nodenum) + inode->i_blocks += inode->i_sb->s_blocksize / 512; + //mark_tail_converted (inode); + } + + if (done == 1) + break; + + /* this loop could log more blocks than we had originally asked + ** for. So, we have to allow the transaction to end if it is + ** too big or too full. Update the inode so things are + ** consistent if we crash before the function returns + ** + ** release the path so that anybody waiting on the path before + ** ending their transaction will be able to continue. + */ + if (journal_transaction_should_end(&th, th.t_blocks_allocated)) { + restart_transaction(&th, inode, &path) ; + } + /* inserting indirect pointers for a hole can take a + ** long time. reschedule if needed + */ + if (current->need_resched) + schedule() ; + + retval = search_for_position_by_key (inode->i_sb, &key, &path); + if (retval == IO_ERROR) { + retval = -EIO; + goto failure; + } + if (retval == POSITION_FOUND) { + reiserfs_warning ("vs-825: reiserfs_get_block: " + "%k should not be found\n", &key); + retval = -EEXIST; + if (allocated_block_nr) + reiserfs_free_block (&th, allocated_block_nr); + pathrelse(&path) ; + goto failure; + } + bh = get_last_bh (&path); + ih = get_ih (&path); + item = get_item (&path); + pos_in_item = path.pos_in_item; + } while (1); + + + retval = 0; + reiserfs_check_path(&path) ; + + failure: + if (transaction_started) { + reiserfs_update_sd(&th, inode) ; + journal_end(&th, inode->i_sb, jbegin_count) ; + } + pop_journal_writer(windex) ; + unlock_kernel() ; + reiserfs_check_path(&path) ; + return retval; +} + + +// +// BAD: new directories have stat data of new type and all other items +// of old type. Version stored in the inode says about body items, so +// in update_stat_data we can not rely on inode, but have to check +// item version directly +// + +// called by read_inode +static void init_inode (struct inode * inode, struct path * path) +{ + struct buffer_head * bh; + struct item_head * ih; + __u32 rdev; + //int version = ITEM_VERSION_1; + + bh = PATH_PLAST_BUFFER (path); + ih = PATH_PITEM_HEAD (path); + + + copy_key (INODE_PKEY (inode), &(ih->ih_key)); + inode->i_blksize = PAGE_SIZE; + + INIT_LIST_HEAD(&inode->u.reiserfs_i.i_prealloc_list) ; + + if (stat_data_v1 (ih)) { + struct stat_data_v1 * sd = (struct stat_data_v1 *)B_I_PITEM (bh, ih); + unsigned long blocks; + + set_inode_item_key_version (inode, KEY_FORMAT_3_5); + set_inode_sd_version (inode, STAT_DATA_V1); + inode->i_mode = sd_v1_mode(sd); + inode->i_nlink = sd_v1_nlink(sd); + inode->i_uid = sd_v1_uid(sd); + inode->i_gid = sd_v1_gid(sd); + inode->i_size = sd_v1_size(sd); + inode->i_atime = sd_v1_atime(sd); + inode->i_mtime = sd_v1_mtime(sd); + inode->i_ctime = sd_v1_ctime(sd); + + inode->i_blocks = sd_v1_blocks(sd); + inode->i_generation = le32_to_cpu (INODE_PKEY (inode)->k_dir_id); + blocks = (inode->i_size + 511) >> 9; + blocks = _ROUND_UP (blocks, inode->i_blksize >> 9); + if (inode->i_blocks > blocks) { + // there was a bug in <=3.5.23 when i_blocks could take negative + // values. Starting from 3.5.17 this value could even be stored in + // stat data. For such files we set i_blocks based on file + // size. Just 2 notes: this can be wrong for sparce files. On-disk value will be + // only updated if file's inode will ever change + inode->i_blocks = blocks; + } + + rdev = sd_v1_rdev(sd); + inode->u.reiserfs_i.i_first_direct_byte = sd_v1_first_direct_byte(sd); + /* nopack is initially zero for v1 objects. For v2 objects, + nopack is initialised from sd_attrs */ + inode->u.reiserfs_i.i_flags &= ~i_nopack_mask; + } else { + // new stat data found, but object may have old items + // (directories and symlinks) + struct stat_data * sd = (struct stat_data *)B_I_PITEM (bh, ih); + + /* both old and new directories have old keys */ + //version = (S_ISDIR (sd->sd_mode) ? ITEM_VERSION_1 : ITEM_VERSION_2); + + inode->i_mode = sd_v2_mode(sd); + inode->i_nlink = sd_v2_nlink(sd); + inode->i_uid = sd_v2_uid(sd); + inode->i_size = sd_v2_size(sd); + inode->i_gid = sd_v2_gid(sd); + inode->i_mtime = sd_v2_mtime(sd); + inode->i_atime = sd_v2_atime(sd); + inode->i_ctime = sd_v2_ctime(sd); + inode->i_blocks = sd_v2_blocks(sd); + rdev = sd_v2_rdev(sd); + if( S_ISCHR( inode -> i_mode ) || S_ISBLK( inode -> i_mode ) ) + inode->i_generation = le32_to_cpu (INODE_PKEY (inode)->k_dir_id); + else + inode->i_generation = sd_v2_generation(sd); + + if (S_ISDIR (inode->i_mode) || S_ISLNK (inode->i_mode)) + set_inode_item_key_version (inode, KEY_FORMAT_3_5); + else + set_inode_item_key_version (inode, KEY_FORMAT_3_6); + + /* read persistemt inode attributes from sd and initalise + generic inode flags from them */ + inode -> u.reiserfs_i.i_attrs = sd_v2_attrs( sd ); + sd_attrs_to_i_attrs( sd_v2_attrs( sd ), inode ); + } + + pathrelse (path); + if (S_ISREG (inode->i_mode)) { + inode->i_op = &reiserfs_file_inode_operations; + inode->i_fop = &reiserfs_file_operations; + inode->i_mapping->a_ops = &reiserfs_address_space_operations ; + } else if (S_ISDIR (inode->i_mode)) { + inode->i_op = &reiserfs_dir_inode_operations; + inode->i_fop = &reiserfs_dir_operations; + } else if (S_ISLNK (inode->i_mode)) { + inode->i_op = &page_symlink_inode_operations; + inode->i_mapping->a_ops = &reiserfs_address_space_operations; + } else { + inode->i_blocks = 0; + init_special_inode(inode, inode->i_mode, rdev) ; + } +} + + +// update new stat data with inode fields +static void inode2sd (void * sd, struct inode * inode) +{ + struct stat_data * sd_v2 = (struct stat_data *)sd; + __u16 flags; + + set_sd_v2_mode(sd_v2, inode->i_mode ); + set_sd_v2_nlink(sd_v2, inode->i_nlink ); + set_sd_v2_uid(sd_v2, inode->i_uid ); + set_sd_v2_size(sd_v2, inode->i_size ); + set_sd_v2_gid(sd_v2, inode->i_gid ); + set_sd_v2_mtime(sd_v2, inode->i_mtime ); + set_sd_v2_atime(sd_v2, inode->i_atime ); + set_sd_v2_ctime(sd_v2, inode->i_ctime ); + set_sd_v2_blocks(sd_v2, inode->i_blocks ); + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) + set_sd_v2_rdev(sd_v2, inode->i_rdev ); + else + set_sd_v2_generation(sd_v2, inode->i_generation); + flags = inode -> u.reiserfs_i.i_attrs; + i_attrs_to_sd_attrs( inode, &flags ); + set_sd_v2_attrs( sd_v2, flags ); +} + + +// used to copy inode's fields to old stat data +static void inode2sd_v1 (void * sd, struct inode * inode) +{ + struct stat_data_v1 * sd_v1 = (struct stat_data_v1 *)sd; + + set_sd_v1_mode(sd_v1, inode->i_mode ); + set_sd_v1_uid(sd_v1, inode->i_uid ); + set_sd_v1_gid(sd_v1, inode->i_gid ); + set_sd_v1_nlink(sd_v1, inode->i_nlink ); + set_sd_v1_size(sd_v1, inode->i_size ); + set_sd_v1_atime(sd_v1, inode->i_atime ); + set_sd_v1_ctime(sd_v1, inode->i_ctime ); + set_sd_v1_mtime(sd_v1, inode->i_mtime ); + + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) + set_sd_v1_rdev(sd_v1, inode->i_rdev ); + else + set_sd_v1_blocks(sd_v1, inode->i_blocks ); + + // Sigh. i_first_direct_byte is back + set_sd_v1_first_direct_byte(sd_v1, inode->u.reiserfs_i.i_first_direct_byte); +} + + +/* NOTE, you must prepare the buffer head before sending it here, +** and then log it after the call +*/ +static void update_stat_data (struct path * path, struct inode * inode) +{ + struct buffer_head * bh; + struct item_head * ih; + + bh = PATH_PLAST_BUFFER (path); + ih = PATH_PITEM_HEAD (path); + + if (!is_statdata_le_ih (ih)) + reiserfs_panic (inode->i_sb, "vs-13065: update_stat_data: key %k, found item %h", + INODE_PKEY (inode), ih); + + if (stat_data_v1 (ih)) { + // path points to old stat data + inode2sd_v1 (B_I_PITEM (bh, ih), inode); + } else { + inode2sd (B_I_PITEM (bh, ih), inode); + } + + return; +} + + +void reiserfs_update_sd (struct reiserfs_transaction_handle *th, + struct inode * inode) +{ + struct cpu_key key; + INITIALIZE_PATH(path); + struct buffer_head *bh ; + int fs_gen ; + struct item_head *ih, tmp_ih ; + int retval; + + make_cpu_key (&key, inode, SD_OFFSET, TYPE_STAT_DATA, 3);//key type is unimportant + + for(;;) { + int pos; + /* look for the object's stat data */ + retval = search_item (inode->i_sb, &key, &path); + if (retval == IO_ERROR) { + reiserfs_warning ("vs-13050: reiserfs_update_sd: " + "i/o failure occurred trying to update %K stat data", + &key); + return; + } + if (retval == ITEM_NOT_FOUND) { + pos = PATH_LAST_POSITION (&path); + pathrelse(&path) ; + if (inode->i_nlink == 0) { + /*printk ("vs-13050: reiserfs_update_sd: i_nlink == 0, stat data not found\n");*/ + return; + } + reiserfs_warning ("vs-13060: reiserfs_update_sd: " + "stat data of object %k (nlink == %d) not found (pos %d)\n", + INODE_PKEY (inode), inode->i_nlink, pos); + reiserfs_check_path(&path) ; + return; + } + + /* sigh, prepare_for_journal might schedule. When it schedules the + ** FS might change. We have to detect that, and loop back to the + ** search if the stat data item has moved + */ + bh = get_last_bh(&path) ; + ih = get_ih(&path) ; + copy_item_head (&tmp_ih, ih); + fs_gen = get_generation (inode->i_sb); + reiserfs_prepare_for_journal(inode->i_sb, bh, 1) ; + if (fs_changed (fs_gen, inode->i_sb) && item_moved(&tmp_ih, &path)) { + reiserfs_restore_prepared_buffer(inode->i_sb, bh) ; + continue ; /* Stat_data item has been moved after scheduling. */ + } + break; + } + update_stat_data (&path, inode); + journal_mark_dirty(th, th->t_super, bh) ; + pathrelse (&path); + return; +} + +void reiserfs_read_inode(struct inode *inode) { + make_bad_inode(inode) ; +} + + +// +// initially this function was derived from minix or ext2's analog and +// evolved as the prototype did +// + +/* looks for stat data in the tree, and fills up the fields of in-core + inode stat data fields */ +void reiserfs_read_inode2 (struct inode * inode, void *p) +{ + INITIALIZE_PATH (path_to_sd); + struct cpu_key key; + struct reiserfs_iget4_args *args = (struct reiserfs_iget4_args *)p ; + unsigned long dirino; + int retval; + + if (!p) { + make_bad_inode(inode) ; + return; + } + + dirino = args->objectid ; + + /* set version 1, version 2 could be used too, because stat data + key is the same in both versions */ + key.version = KEY_FORMAT_3_5; + key.on_disk_key.k_dir_id = dirino; + key.on_disk_key.k_objectid = inode->i_ino; + key.on_disk_key.u.k_offset_v1.k_offset = SD_OFFSET; + key.on_disk_key.u.k_offset_v1.k_uniqueness = SD_UNIQUENESS; + + /* look for the object's stat data */ + retval = search_item (inode->i_sb, &key, &path_to_sd); + if (retval == IO_ERROR) { + reiserfs_warning ("vs-13070: reiserfs_read_inode2: " + "i/o failure occurred trying to find stat data of %K\n", + &key); + make_bad_inode(inode) ; + return; + } + if (retval != ITEM_FOUND) { + /* a stale NFS handle can trigger this without it being an error */ + pathrelse (&path_to_sd); + make_bad_inode(inode) ; + return; + } + + init_inode (inode, &path_to_sd); + + /* It is possible that knfsd is trying to access inode of a file + that is being removed from the disk by some other thread. As we + update sd on unlink all that is required is to check for nlink + here. This bug was first found by Sizif when debugging + SquidNG/Butterfly, forgotten, and found again after Philippe + Gramoulle reproduced it. + + More logical fix would require changes in fs/inode.c:iput() to + remove inode from hash-table _after_ fs cleaned disk stuff up and + in iget() to return NULL if I_FREEING inode is found in + hash-table. */ + if( inode -> i_nlink == 0 ) { + reiserfs_warning( "vs-13075: reiserfs_read_inode2: " + "dead inode read from disk %K. " + "This is likely to be race with knfsd. Ignore\n", + &key ); + make_bad_inode( inode ); + } + + reiserfs_check_path(&path_to_sd) ; /* init inode should be relsing */ + +} + + +struct inode * reiserfs_iget (struct super_block * s, const struct cpu_key * key) +{ + struct inode * inode; + struct reiserfs_iget4_args args ; + + args.objectid = key->on_disk_key.k_dir_id ; + inode = iget4 (s, key->on_disk_key.k_objectid, 0, (void *)(&args)); + if (!inode) + return ERR_PTR(-ENOMEM) ; + + if (comp_short_keys (INODE_PKEY (inode), key) || is_bad_inode (inode)) { + /* either due to i/o error or a stale NFS handle */ + iput (inode); + inode = 0; + } + return inode; +} + +struct dentry *reiserfs_fh_to_dentry(struct super_block *sb, __u32 *data, + int len, int fhtype, int parent) { + struct cpu_key key ; + struct inode *inode = NULL ; + struct list_head *lp; + struct dentry *result; + + /* fhtype happens to reflect the number of u32s encoded. + * due to a bug in earlier code, fhtype might indicate there + * are more u32s then actually fitted. + * so if fhtype seems to be more than len, reduce fhtype. + * Valid types are: + * 2 - objectid + dir_id - legacy support + * 3 - objectid + dir_id + generation + * 4 - objectid + dir_id + objectid and dirid of parent - legacy + * 5 - objectid + dir_id + generation + objectid and dirid of parent + * 6 - as above plus generation of directory + * 6 does not fit in NFSv2 handles + */ + if (fhtype > len) { + if (fhtype != 6 || len != 5) + printk(KERN_WARNING "nfsd/reiserfs, fhtype=%d, len=%d - odd\n", + fhtype, len); + fhtype = 5; + } + if (fhtype < 2 || (parent && fhtype < 4)) + goto out ; + + if (! parent) { + /* this works for handles from old kernels because the default + ** reiserfs generation number is the packing locality. + */ + key.on_disk_key.k_objectid = data[0] ; + key.on_disk_key.k_dir_id = data[1] ; + inode = reiserfs_iget(sb, &key) ; + if (inode && !IS_ERR(inode) && (fhtype == 3 || fhtype >= 5) && + data[2] != inode->i_generation) { + iput(inode) ; + inode = NULL ; + } + } else { + key.on_disk_key.k_objectid = data[fhtype>=5?3:2] ; + key.on_disk_key.k_dir_id = data[fhtype>=5?4:3] ; + inode = reiserfs_iget(sb, &key) ; + if (inode && !IS_ERR(inode) && fhtype == 6 && + data[5] != inode->i_generation) { + iput(inode) ; + inode = NULL ; + } + } +out: + if (IS_ERR(inode)) + return ERR_PTR(PTR_ERR(inode)); + if (!inode) + return ERR_PTR(-ESTALE) ; + + /* now to find a dentry. + * If possible, get a well-connected one + */ + spin_lock(&dcache_lock); + for (lp = inode->i_dentry.next; lp != &inode->i_dentry ; lp=lp->next) { + result = list_entry(lp,struct dentry, d_alias); + if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) { + dget_locked(result); + result->d_vfs_flags |= DCACHE_REFERENCED; + spin_unlock(&dcache_lock); + iput(inode); + return result; + } + } + spin_unlock(&dcache_lock); + result = d_alloc_root(inode); + if (result == NULL) { + iput(inode); + return ERR_PTR(-ENOMEM); + } + result->d_flags |= DCACHE_NFSD_DISCONNECTED; + return result; + +} + +int reiserfs_dentry_to_fh(struct dentry *dentry, __u32 *data, int *lenp, int need_parent) { + struct inode *inode = dentry->d_inode ; + int maxlen = *lenp; + + if (maxlen < 3) + return 255 ; + + data[0] = inode->i_ino ; + data[1] = le32_to_cpu(INODE_PKEY (inode)->k_dir_id) ; + data[2] = inode->i_generation ; + *lenp = 3 ; + /* no room for directory info? return what we've stored so far */ + if (maxlen < 5 || ! need_parent) + return 3 ; + + inode = dentry->d_parent->d_inode ; + data[3] = inode->i_ino ; + data[4] = le32_to_cpu(INODE_PKEY (inode)->k_dir_id) ; + *lenp = 5 ; + if (maxlen < 6) + return 5 ; + data[5] = inode->i_generation ; + *lenp = 6 ; + return 6 ; +} + + +// +// initially this function was derived from minix or ext2's analog and +// evolved as the prototype did +// +/* looks for stat data, then copies fields to it, marks the buffer + containing stat data as dirty */ +/* reiserfs inodes are never really dirty, since the dirty inode call +** always logs them. This call allows the VFS inode marking routines +** to properly mark inodes for datasync and such, but only actually +** does something when called for a synchronous update. +*/ +void reiserfs_write_inode (struct inode * inode, int do_sync) { + struct reiserfs_transaction_handle th ; + int jbegin_count = 1 ; + + if (inode->i_sb->s_flags & MS_RDONLY) { + reiserfs_warning("clm-6005: writing inode %lu on readonly FS\n", + inode->i_ino) ; + return ; + } + /* memory pressure can sometimes initiate write_inode calls with sync == 1, + ** these cases are just when the system needs ram, not when the + ** inode needs to reach disk for safety, and they can safely be + ** ignored because the altered inode has already been logged. + */ + if (do_sync && !(current->flags & PF_MEMALLOC)) { + lock_kernel() ; + journal_begin(&th, inode->i_sb, jbegin_count) ; + reiserfs_update_sd (&th, inode); + journal_end_sync(&th, inode->i_sb, jbegin_count) ; + unlock_kernel() ; + } +} + +/* FIXME: no need any more. right? */ +int reiserfs_sync_inode (struct reiserfs_transaction_handle *th, struct inode * inode) +{ + int err = 0; + + reiserfs_update_sd (th, inode); + return err; +} + + +/* stat data of new object is inserted already, this inserts the item + containing "." and ".." entries */ +static int reiserfs_new_directory (struct reiserfs_transaction_handle *th, + struct item_head * ih, struct path * path, + const struct inode * dir) +{ + struct super_block * sb = th->t_super; + char empty_dir [EMPTY_DIR_SIZE]; + char * body = empty_dir; + struct cpu_key key; + int retval; + + _make_cpu_key (&key, KEY_FORMAT_3_5, le32_to_cpu (ih->ih_key.k_dir_id), + le32_to_cpu (ih->ih_key.k_objectid), DOT_OFFSET, TYPE_DIRENTRY, 3/*key length*/); + + /* compose item head for new item. Directories consist of items of + old type (ITEM_VERSION_1). Do not set key (second arg is 0), it + is done by reiserfs_new_inode */ + if (old_format_only (sb)) { + make_le_item_head (ih, 0, KEY_FORMAT_3_5, DOT_OFFSET, TYPE_DIRENTRY, EMPTY_DIR_SIZE_V1, 2); + + make_empty_dir_item_v1 (body, ih->ih_key.k_dir_id, ih->ih_key.k_objectid, + INODE_PKEY (dir)->k_dir_id, + INODE_PKEY (dir)->k_objectid ); + } else { + make_le_item_head (ih, 0, KEY_FORMAT_3_5, DOT_OFFSET, TYPE_DIRENTRY, EMPTY_DIR_SIZE, 2); + + make_empty_dir_item (body, ih->ih_key.k_dir_id, ih->ih_key.k_objectid, + INODE_PKEY (dir)->k_dir_id, + INODE_PKEY (dir)->k_objectid ); + } + + /* look for place in the tree for new item */ + retval = search_item (sb, &key, path); + if (retval == IO_ERROR) { + reiserfs_warning ("vs-13080: reiserfs_new_directory: " + "i/o failure occurred creating new directory\n"); + return -EIO; + } + if (retval == ITEM_FOUND) { + pathrelse (path); + reiserfs_warning ("vs-13070: reiserfs_new_directory: " + "object with this key exists (%k)", &(ih->ih_key)); + return -EEXIST; + } + + /* insert item, that is empty directory item */ + return reiserfs_insert_item (th, path, &key, ih, body); +} + + +/* stat data of object has been inserted, this inserts the item + containing the body of symlink */ +static int reiserfs_new_symlink (struct reiserfs_transaction_handle *th, + struct item_head * ih, + struct path * path, const char * symname, int item_len) +{ + struct super_block * sb = th->t_super; + struct cpu_key key; + int retval; + + _make_cpu_key (&key, KEY_FORMAT_3_5, + le32_to_cpu (ih->ih_key.k_dir_id), + le32_to_cpu (ih->ih_key.k_objectid), + 1, TYPE_DIRECT, 3/*key length*/); + + make_le_item_head (ih, 0, KEY_FORMAT_3_5, 1, TYPE_DIRECT, item_len, 0/*free_space*/); + + /* look for place in the tree for new item */ + retval = search_item (sb, &key, path); + if (retval == IO_ERROR) { + reiserfs_warning ("vs-13080: reiserfs_new_symlinik: " + "i/o failure occurred creating new symlink\n"); + return -EIO; + } + if (retval == ITEM_FOUND) { + pathrelse (path); + reiserfs_warning ("vs-13080: reiserfs_new_symlink: " + "object with this key exists (%k)", &(ih->ih_key)); + return -EEXIST; + } + + /* insert item, that is body of symlink */ + return reiserfs_insert_item (th, path, &key, ih, symname); +} + + +/* inserts the stat data into the tree, and then calls + reiserfs_new_directory (to insert ".", ".." item if new object is + directory) or reiserfs_new_symlink (to insert symlink body if new + object is symlink) or nothing (if new object is regular file) */ +struct inode * reiserfs_new_inode (struct reiserfs_transaction_handle *th, + const struct inode * dir, int mode, + const char * symname, + int i_size, /* 0 for regular, EMTRY_DIR_SIZE for dirs, + strlen (symname) for symlinks)*/ + struct dentry *dentry, struct inode *inode, int * err) +{ + struct super_block * sb; + INITIALIZE_PATH (path_to_key); + struct cpu_key key; + struct item_head ih; + struct stat_data sd; + int retval; + + if (!dir || !dir->i_nlink) { + *err = -EPERM; + iput(inode) ; + return NULL; + } + + sb = dir->i_sb; + inode -> u.reiserfs_i.i_attrs = + dir -> u.reiserfs_i.i_attrs & REISERFS_INHERIT_MASK; + sd_attrs_to_i_attrs( inode -> u.reiserfs_i.i_attrs, inode ); + + /* item head of new item */ + ih.ih_key.k_dir_id = INODE_PKEY (dir)->k_objectid; + ih.ih_key.k_objectid = cpu_to_le32 (reiserfs_get_unused_objectid (th)); + if (!ih.ih_key.k_objectid) { + iput(inode) ; + *err = -ENOMEM; + return NULL; + } + if (old_format_only (sb)) + /* not a perfect generation count, as object ids can be reused, but this + ** is as good as reiserfs can do right now. + ** note that the private part of inode isn't filled in yet, we have + ** to use the directory. + */ + inode->i_generation = le32_to_cpu (INODE_PKEY (dir)->k_objectid); + else +#if defined( USE_INODE_GENERATION_COUNTER ) + inode->i_generation = + le32_to_cpu( sb -> u.reiserfs_sb.s_rs -> s_inode_generation ); +#else + inode->i_generation = ++event; +#endif + if (old_format_only (sb)) + make_le_item_head (&ih, 0, KEY_FORMAT_3_5, SD_OFFSET, TYPE_STAT_DATA, SD_V1_SIZE, MAX_US_INT); + else + make_le_item_head (&ih, 0, KEY_FORMAT_3_6, SD_OFFSET, TYPE_STAT_DATA, SD_SIZE, MAX_US_INT); + + + /* key to search for correct place for new stat data */ + _make_cpu_key (&key, KEY_FORMAT_3_6, le32_to_cpu (ih.ih_key.k_dir_id), + le32_to_cpu (ih.ih_key.k_objectid), SD_OFFSET, TYPE_STAT_DATA, 3/*key length*/); + + /* find proper place for inserting of stat data */ + retval = search_item (sb, &key, &path_to_key); + if (retval == IO_ERROR) { + iput (inode); + *err = -EIO; + return NULL; + } + if (retval == ITEM_FOUND) { + pathrelse (&path_to_key); + iput (inode); + *err = -EEXIST; + return NULL; + } + + /* fill stat data */ + inode->i_mode = mode; + inode->i_nlink = (S_ISDIR (mode) ? 2 : 1); + inode->i_uid = current->fsuid; + if (dir->i_mode & S_ISGID) { + inode->i_gid = dir->i_gid; + if (S_ISDIR(mode)) + inode->i_mode |= S_ISGID; + } else + inode->i_gid = current->fsgid; + + /* symlink cannot be immutable or append only, right? */ + if( S_ISLNK( inode -> i_mode ) ) + inode -> i_flags &= ~ ( S_IMMUTABLE | S_APPEND ); + + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + inode->i_size = i_size; + inode->i_blocks = (inode->i_size + 511) >> 9; + inode->u.reiserfs_i.i_first_direct_byte = S_ISLNK(mode) ? 1 : + U32_MAX/*NO_BYTES_IN_DIRECT_ITEM*/; + + INIT_LIST_HEAD(&inode->u.reiserfs_i.i_prealloc_list) ; + + if (old_format_only (sb)) { + if (inode->i_uid & ~0xffff || inode->i_gid & ~0xffff) { + pathrelse (&path_to_key); + /* i_uid or i_gid is too big to be stored in stat data v3.5 */ + iput (inode); + *err = -EINVAL; + return NULL; + } + inode2sd_v1 (&sd, inode); + } else + inode2sd (&sd, inode); + + // these do not go to on-disk stat data + inode->i_ino = le32_to_cpu (ih.ih_key.k_objectid); + inode->i_blksize = PAGE_SIZE; + inode->i_dev = sb->s_dev; + + // store in in-core inode the key of stat data and version all + // object items will have (directory items will have old offset + // format, other new objects will consist of new items) + memcpy (INODE_PKEY (inode), &(ih.ih_key), KEY_SIZE); + if (old_format_only (sb) || S_ISDIR(mode) || S_ISLNK(mode)) + set_inode_item_key_version (inode, KEY_FORMAT_3_5); + else + set_inode_item_key_version (inode, KEY_FORMAT_3_6); + if (old_format_only (sb)) + set_inode_sd_version (inode, STAT_DATA_V1); + else + set_inode_sd_version (inode, STAT_DATA_V2); + + /* insert the stat data into the tree */ + retval = reiserfs_insert_item (th, &path_to_key, &key, &ih, (char *)(&sd)); + if (retval) { + iput (inode); + *err = retval; + reiserfs_check_path(&path_to_key) ; + return NULL; + } + + if (S_ISDIR(mode)) { + /* insert item with "." and ".." */ + retval = reiserfs_new_directory (th, &ih, &path_to_key, dir); + } + + if (S_ISLNK(mode)) { + /* insert body of symlink */ + if (!old_format_only (sb)) + i_size = ROUND_UP(i_size); + retval = reiserfs_new_symlink (th, &ih, &path_to_key, symname, i_size); + } + if (retval) { + inode->i_nlink = 0; + iput (inode); + *err = retval; + reiserfs_check_path(&path_to_key) ; + return NULL; + } + + insert_inode_hash (inode); + // we do not mark inode dirty: on disk content matches to the + // in-core one + reiserfs_check_path(&path_to_key) ; + + return inode; +} + +/* +** finds the tail page in the page cache, +** reads the last block in. +** +** On success, page_result is set to a locked, pinned page, and bh_result +** is set to an up to date buffer for the last block in the file. returns 0. +** +** tail conversion is not done, so bh_result might not be valid for writing +** check buffer_mapped(bh_result) and bh_result->b_blocknr != 0 before +** trying to write the block. +** +** on failure, nonzero is returned, page_result and bh_result are untouched. +*/ +static int grab_tail_page(struct inode *p_s_inode, + struct page **page_result, + struct buffer_head **bh_result) { + + /* we want the page with the last byte in the file, + ** not the page that will hold the next byte for appending + */ + unsigned long index = (p_s_inode->i_size-1) >> PAGE_CACHE_SHIFT ; + unsigned long pos = 0 ; + unsigned long start = 0 ; + unsigned long blocksize = p_s_inode->i_sb->s_blocksize ; + unsigned long offset = (p_s_inode->i_size) & (PAGE_CACHE_SIZE - 1) ; + struct buffer_head *bh ; + struct buffer_head *head ; + struct page * page ; + int error ; + + /* we know that we are only called with inode->i_size > 0. + ** we also know that a file tail can never be as big as a block + ** If i_size % blocksize == 0, our file is currently block aligned + ** and it won't need converting or zeroing after a truncate. + */ + if ((offset & (blocksize - 1)) == 0) { + return -ENOENT ; + } + page = grab_cache_page(p_s_inode->i_mapping, index) ; + error = -ENOMEM ; + if (!page) { + goto out ; + } + /* start within the page of the last block in the file */ + start = (offset / blocksize) * blocksize ; + + error = block_prepare_write(page, start, offset, + reiserfs_get_block_create_0) ; + if (error) + goto unlock ; + + kunmap(page) ; /* mapped by block_prepare_write */ + + head = page->buffers ; + bh = head; + do { + if (pos >= start) { + break ; + } + bh = bh->b_this_page ; + pos += blocksize ; + } while(bh != head) ; + + if (!buffer_uptodate(bh)) { + /* note, this should never happen, prepare_write should + ** be taking care of this for us. If the buffer isn't up to date, + ** I've screwed up the code to find the buffer, or the code to + ** call prepare_write + */ + reiserfs_warning("clm-6000: error reading block %lu on dev %s\n", + bh->b_blocknr, kdevname(bh->b_dev)) ; + error = -EIO ; + goto unlock ; + } + *bh_result = bh ; + *page_result = page ; + +out: + return error ; + +unlock: + UnlockPage(page) ; + page_cache_release(page) ; + return error ; +} + +/* +** vfs version of truncate file. Must NOT be called with +** a transaction already started. +** +** some code taken from block_truncate_page +*/ +void reiserfs_truncate_file(struct inode *p_s_inode, int update_timestamps) { + struct reiserfs_transaction_handle th ; + int windex ; + + /* we want the offset for the first byte after the end of the file */ + unsigned long offset = p_s_inode->i_size & (PAGE_CACHE_SIZE - 1) ; + unsigned blocksize = p_s_inode->i_sb->s_blocksize ; + unsigned length ; + struct page *page = NULL ; + int error ; + struct buffer_head *bh = NULL ; + + if (p_s_inode->i_size > 0) { + if ((error = grab_tail_page(p_s_inode, &page, &bh))) { + // -ENOENT means we truncated past the end of the file, + // and get_block_create_0 could not find a block to read in, + // which is ok. + if (error != -ENOENT) + reiserfs_warning("clm-6001: grab_tail_page failed %d\n", error); + page = NULL ; + bh = NULL ; + } + } + + /* so, if page != NULL, we have a buffer head for the offset at + ** the end of the file. if the bh is mapped, and bh->b_blocknr != 0, + ** then we have an unformatted node. Otherwise, we have a direct item, + ** and no zeroing is required on disk. We zero after the truncate, + ** because the truncate might pack the item anyway + ** (it will unmap bh if it packs). + */ + /* it is enough to reserve space in transaction for 2 balancings: + one for "save" link adding and another for the first + cut_from_item. 1 is for update_sd */ + journal_begin(&th, p_s_inode->i_sb, JOURNAL_PER_BALANCE_CNT * 2 + 1 ) ; + reiserfs_update_inode_transaction(p_s_inode) ; + windex = push_journal_writer("reiserfs_vfs_truncate_file") ; + if (update_timestamps) + /* we are doing real truncate: if the system crashes before the last + transaction of truncating gets committed - on reboot the file + either appears truncated properly or not truncated at all */ + add_save_link (&th, p_s_inode, 1); + reiserfs_do_truncate (&th, p_s_inode, page, update_timestamps) ; + pop_journal_writer(windex) ; + journal_end(&th, p_s_inode->i_sb, JOURNAL_PER_BALANCE_CNT * 2 + 1 ) ; + + if (update_timestamps) + remove_save_link (p_s_inode, 1/* truncate */); + + if (page) { + length = offset & (blocksize - 1) ; + /* if we are not on a block boundary */ + if (length) { + length = blocksize - length ; + memset((char *)kmap(page) + offset, 0, length) ; + flush_dcache_page(page) ; + kunmap(page) ; + if (buffer_mapped(bh) && bh->b_blocknr != 0) { + mark_buffer_dirty(bh) ; + } + } + UnlockPage(page) ; + page_cache_release(page) ; + } + + return ; +} + +static int map_block_for_writepage(struct inode *inode, + struct buffer_head *bh_result, + unsigned long block) { + struct reiserfs_transaction_handle th ; + int fs_gen ; + struct item_head tmp_ih ; + struct item_head *ih ; + struct buffer_head *bh ; + __u32 *item ; + struct cpu_key key ; + INITIALIZE_PATH(path) ; + int pos_in_item ; + int jbegin_count = JOURNAL_PER_BALANCE_CNT ; + loff_t byte_offset = (block << inode->i_sb->s_blocksize_bits) + 1 ; + int retval ; + int use_get_block = 0 ; + int bytes_copied = 0 ; + int copy_size ; + +start_over: + lock_kernel() ; + journal_begin(&th, inode->i_sb, jbegin_count) ; + reiserfs_update_inode_transaction(inode) ; + + make_cpu_key(&key, inode, byte_offset, TYPE_ANY, 3) ; + +research: + retval = search_for_position_by_key(inode->i_sb, &key, &path) ; + if (retval != POSITION_FOUND) { + use_get_block = 1; + goto out ; + } + + bh = get_last_bh(&path) ; + ih = get_ih(&path) ; + item = get_item(&path) ; + pos_in_item = path.pos_in_item ; + + /* we've found an unformatted node */ + if (indirect_item_found(retval, ih)) { + if (bytes_copied > 0) { + reiserfs_warning("clm-6002: bytes_copied %d\n", bytes_copied) ; + } + if (!get_block_num(item, pos_in_item)) { + /* crap, we are writing to a hole */ + use_get_block = 1; + goto out ; + } + set_block_dev_mapped(bh_result, get_block_num(item,pos_in_item),inode); + mark_buffer_uptodate(bh_result, 1); + } else if (is_direct_le_ih(ih)) { + char *p ; + p = page_address(bh_result->b_page) ; + p += (byte_offset -1) & (PAGE_CACHE_SIZE - 1) ; + copy_size = ih_item_len(ih) - pos_in_item; + + fs_gen = get_generation(inode->i_sb) ; + copy_item_head(&tmp_ih, ih) ; + reiserfs_prepare_for_journal(inode->i_sb, bh, 1) ; + if (fs_changed (fs_gen, inode->i_sb) && item_moved (&tmp_ih, &path)) { + reiserfs_restore_prepared_buffer(inode->i_sb, bh) ; + goto research; + } + + memcpy( B_I_PITEM(bh, ih) + pos_in_item, p + bytes_copied, copy_size) ; + + journal_mark_dirty(&th, inode->i_sb, bh) ; + bytes_copied += copy_size ; + set_block_dev_mapped(bh_result, 0, inode); + mark_buffer_uptodate(bh_result, 1); + + /* are there still bytes left? */ + if (bytes_copied < bh_result->b_size && + (byte_offset + bytes_copied) < inode->i_size) { + set_cpu_key_k_offset(&key, cpu_key_k_offset(&key) + copy_size) ; + goto research ; + } + } else { + reiserfs_warning("clm-6003: bad item inode %lu, device %s\n", inode->i_ino, kdevname(inode->i_sb->s_dev)) ; + retval = -EIO ; + goto out ; + } + retval = 0 ; + +out: + pathrelse(&path) ; + journal_end(&th, inode->i_sb, jbegin_count) ; + unlock_kernel() ; + + /* this is where we fill in holes in the file. */ + if (use_get_block) { + kmap(bh_result->b_page) ; + retval = reiserfs_get_block(inode, block, bh_result, + GET_BLOCK_CREATE | GET_BLOCK_NO_ISEM) ; + kunmap(bh_result->b_page) ; + if (!retval) { + if (!buffer_mapped(bh_result) || bh_result->b_blocknr == 0) { + /* get_block failed to find a mapped unformatted node. */ + use_get_block = 0 ; + goto start_over ; + } + } + } + return retval ; +} + +/* helper func to get a buffer head ready for writepage to send to +** ll_rw_block +*/ +static inline void submit_bh_for_writepage(struct buffer_head **bhp, int nr) { + struct buffer_head *bh ; + int i; + for(i = 0 ; i < nr ; i++) { + bh = bhp[i] ; + lock_buffer(bh) ; + set_buffer_async_io(bh) ; + /* submit_bh doesn't care if the buffer is dirty, but nobody + ** later on in the call chain will be cleaning it. So, we + ** clean the buffer here, it still gets written either way. + */ + clear_bit(BH_Dirty, &bh->b_state) ; + set_bit(BH_Uptodate, &bh->b_state) ; + submit_bh(WRITE, bh) ; + } +} + +static int reiserfs_write_full_page(struct page *page) { + struct inode *inode = page->mapping->host ; + unsigned long end_index = inode->i_size >> PAGE_CACHE_SHIFT ; + unsigned last_offset = PAGE_CACHE_SIZE; + int error = 0; + unsigned long block ; + unsigned cur_offset = 0 ; + struct buffer_head *head, *bh ; + int partial = 0 ; + struct buffer_head *arr[PAGE_CACHE_SIZE/512] ; + int nr = 0 ; + + if (!page->buffers) { + block_prepare_write(page, 0, 0, NULL) ; + kunmap(page) ; + } + /* last page in the file, zero out any contents past the + ** last byte in the file + */ + if (page->index >= end_index) { + last_offset = inode->i_size & (PAGE_CACHE_SIZE - 1) ; + /* no file contents in this page */ + if (page->index >= end_index + 1 || !last_offset) { + error = -EIO ; + goto fail ; + } + memset((char *)kmap(page)+last_offset, 0, PAGE_CACHE_SIZE-last_offset) ; + flush_dcache_page(page) ; + kunmap(page) ; + } + head = page->buffers ; + bh = head ; + block = page->index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits) ; + do { + /* if this offset in the page is outside the file */ + if (cur_offset >= last_offset) { + if (!buffer_uptodate(bh)) + partial = 1 ; + } else { + /* fast path, buffer mapped to an unformatted node */ + if (buffer_mapped(bh) && bh->b_blocknr != 0) { + arr[nr++] = bh ; + } else { + /* buffer not mapped yet, or points to a direct item. + ** search and dirty or log + */ + if ((error = map_block_for_writepage(inode, bh, block))) { + goto fail ; + } + /* map_block_for_writepage either found an unformatted node + ** and mapped it for us, or it found a direct item + ** and logged the changes. + */ + if (buffer_mapped(bh) && bh->b_blocknr != 0) { + arr[nr++] = bh ; + } + } + } + bh = bh->b_this_page ; + cur_offset += bh->b_size ; + block++ ; + } while(bh != head) ; + + /* if this page only had a direct item, it is very possible for + ** nr == 0 without there being any kind of error. + */ + if (nr) { + submit_bh_for_writepage(arr, nr) ; + } else { + UnlockPage(page) ; + } + if (!partial) + SetPageUptodate(page) ; + + return 0 ; + +fail: + if (nr) { + submit_bh_for_writepage(arr, nr) ; + } else { + UnlockPage(page) ; + } + ClearPageUptodate(page) ; + return error ; +} + +// +// this is exactly what 2.3.99-pre9's ext2_readpage is +// +static int reiserfs_readpage (struct file *f, struct page * page) +{ + return block_read_full_page (page, reiserfs_get_block); +} + + +// +// modified from ext2_writepage is +// +static int reiserfs_writepage (struct page * page) +{ + struct inode *inode = page->mapping->host ; + reiserfs_wait_on_write_block(inode->i_sb) ; + return reiserfs_write_full_page(page) ; +} + + +// +// from ext2_prepare_write, but modified +// +int reiserfs_prepare_write(struct file *f, struct page *page, + unsigned from, unsigned to) { + struct inode *inode = page->mapping->host ; + reiserfs_wait_on_write_block(inode->i_sb) ; + fix_tail_page_for_writing(page) ; + return block_prepare_write(page, from, to, reiserfs_get_block) ; +} + + +// +// this is exactly what 2.3.99-pre9's ext2_bmap is +// +static int reiserfs_aop_bmap(struct address_space *as, long block) { + return generic_block_bmap(as, block, reiserfs_bmap) ; +} + +static int reiserfs_commit_write(struct file *f, struct page *page, + unsigned from, unsigned to) { + struct inode *inode = page->mapping->host ; + loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to; + int ret ; + + reiserfs_wait_on_write_block(inode->i_sb) ; + + /* generic_commit_write does this for us, but does not update the + ** transaction tracking stuff when the size changes. So, we have + ** to do the i_size updates here. + */ + if (pos > inode->i_size) { + struct reiserfs_transaction_handle th ; + lock_kernel() ; + journal_begin(&th, inode->i_sb, 1) ; + reiserfs_update_inode_transaction(inode) ; + inode->i_size = pos ; + reiserfs_update_sd(&th, inode) ; + journal_end(&th, inode->i_sb, 1) ; + unlock_kernel() ; + } + + ret = generic_commit_write(f, page, from, to) ; + + /* we test for O_SYNC here so we can commit the transaction + ** for any packed tails the file might have had + */ + if (f && f->f_flags & O_SYNC) { + lock_kernel() ; + reiserfs_commit_for_inode(inode) ; + unlock_kernel(); + } + return ret ; +} + +void sd_attrs_to_i_attrs( __u16 sd_attrs, struct inode *inode ) +{ + if( !reiserfs_noattrs( inode -> i_sb ) ) { + if( sd_attrs & REISERFS_SYNC_FL ) + inode -> i_flags |= S_SYNC; + else + inode -> i_flags &= ~S_SYNC; + if( sd_attrs & REISERFS_IMMUTABLE_FL ) + inode -> i_flags |= S_IMMUTABLE; + else + inode -> i_flags &= ~S_IMMUTABLE; + if( sd_attrs & REISERFS_NOATIME_FL ) + inode -> i_flags |= S_NOATIME; + else + inode -> i_flags &= ~S_NOATIME; + if( sd_attrs & REISERFS_NOTAIL_FL ) + inode->u.reiserfs_i.i_flags |= i_nopack_mask; + else + inode->u.reiserfs_i.i_flags &= ~i_nopack_mask; + } +} + +void i_attrs_to_sd_attrs( struct inode *inode, __u16 *sd_attrs ) +{ + if( !reiserfs_noattrs( inode -> i_sb ) ) { + if( inode -> i_flags & S_IMMUTABLE ) + *sd_attrs |= REISERFS_IMMUTABLE_FL; + else + *sd_attrs &= ~REISERFS_IMMUTABLE_FL; + if( inode -> i_flags & S_SYNC ) + *sd_attrs |= REISERFS_SYNC_FL; + else + *sd_attrs &= ~REISERFS_SYNC_FL; + if( inode -> i_flags & S_NOATIME ) + *sd_attrs |= REISERFS_NOATIME_FL; + else + *sd_attrs &= ~REISERFS_NOATIME_FL; + if( inode->u.reiserfs_i.i_flags & i_nopack_mask ) + *sd_attrs |= REISERFS_NOTAIL_FL; + else + *sd_attrs &= ~REISERFS_NOTAIL_FL; + } +} + +struct address_space_operations reiserfs_address_space_operations = { + writepage: reiserfs_writepage, + readpage: reiserfs_readpage, + sync_page: block_sync_page, + prepare_write: reiserfs_prepare_write, + commit_write: reiserfs_commit_write, + bmap: reiserfs_aop_bmap +} ; diff -urN linux-2.4.16-reiserfspatches/fs/reiserfs/ioctl.c linux-2.4.16-reiserfspatches-immutable/fs/reiserfs/ioctl.c --- linux-2.4.16-reiserfspatches/fs/reiserfs/ioctl.c Mon Dec 10 13:57:11 2001 +++ linux-2.4.16-reiserfspatches-immutable/fs/reiserfs/ioctl.c Mon Dec 10 14:26:07 2001 @@ -47,7 +47,7 @@ if (get_user(flags, (int *) arg)) return -EFAULT; - if ( ( flags & REISERFS_IMMUTABLE_FL ) && + if ( ( flags & REISERFS_IMMUTABLE_FILE_FL ) && !capable( CAP_LINUX_IMMUTABLE ) ) return -EPERM; diff -urN linux-2.4.16-reiserfspatches/fs/reiserfs/ioctl.c~ linux-2.4.16-reiserfspatches-immutable/fs/reiserfs/ioctl.c~ --- linux-2.4.16-reiserfspatches/fs/reiserfs/ioctl.c~ Thu Jan 1 01:00:00 1970 +++ linux-2.4.16-reiserfspatches-immutable/fs/reiserfs/ioctl.c~ Mon Dec 10 13:57:11 2001 @@ -0,0 +1,147 @@ +/* + * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README + */ + +#include +#include +#include +#include +#include +#include + +/* +** reiserfs_ioctl - handler for ioctl for inode +** supported commands: +** 1) REISERFS_IOC_UNPACK - try to unpack tail from direct item into indirect +** and prevent packing file (argument arg has to be non-zero) +** 2) REISERFS_IOC_[GS]ETFLAGS, REISERFS_IOC_[GS]ETVERSION +** 3) That's all for a while ... +*/ +int reiserfs_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, + unsigned long arg) +{ + unsigned int flags; + + switch (cmd) { + case REISERFS_IOC_UNPACK: + if( S_ISREG( inode -> i_mode ) ) { + if (arg) + return reiserfs_unpack (inode, filp); + else + return 0; + } else + return -ENOTTY; + /* following two cases are taken from fs/ext2/ioctl.c by Remy + Card (card@masi.ibp.fr) */ + case REISERFS_IOC_GETFLAGS: + flags = inode -> u.reiserfs_i.i_attrs; + i_attrs_to_sd_attrs( inode, ( __u16 * ) &flags ); + return put_user(flags, (int *) arg); + case REISERFS_IOC_SETFLAGS: { + if (IS_RDONLY(inode)) + return -EROFS; + + if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + return -EPERM; + + if (get_user(flags, (int *) arg)) + return -EFAULT; + + if ( ( flags & REISERFS_IMMUTABLE_FL ) && + !capable( CAP_LINUX_IMMUTABLE ) ) + return -EPERM; + + if( ( flags & REISERFS_NOTAIL_FL ) && + S_ISREG( inode -> i_mode ) ) { + int result; + + result = reiserfs_unpack( inode, filp ); + if( result ) + return result; + } + sd_attrs_to_i_attrs( flags, inode ); + inode -> u.reiserfs_i.i_attrs = flags; + inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + return 0; + } + case REISERFS_IOC_GETVERSION: + return put_user(inode->i_generation, (int *) arg); + case REISERFS_IOC_SETVERSION: + if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + return -EPERM; + if (IS_RDONLY(inode)) + return -EROFS; + if (get_user(inode->i_generation, (int *) arg)) + return -EFAULT; + inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + return 0; + default: + return -ENOTTY; + } +} + +/* +** reiserfs_unpack +** Function try to convert tail from direct item into indirect. +** It set up nopack attribute in the inode.u.reiserfs_i.nopack +*/ +int reiserfs_unpack (struct inode * inode, struct file * filp) +{ + int retval = 0; + int index ; + struct page *page ; + unsigned long write_from ; + unsigned long blocksize = inode->i_sb->s_blocksize ; + + if (inode->i_size == 0) { + inode->u.reiserfs_i.i_flags |= i_nopack_mask; + return 0 ; + } + /* ioctl already done */ + if (inode->u.reiserfs_i.i_flags & i_nopack_mask) { + return 0 ; + } + lock_kernel(); + + /* we need to make sure nobody is changing the file size beneath + ** us + */ + down(&inode->i_sem) ; + + write_from = inode->i_size & (blocksize - 1) ; + /* if we are on a block boundary, we are already unpacked. */ + if ( write_from == 0) { + inode->u.reiserfs_i.i_flags |= i_nopack_mask; + goto out ; + } + + /* we unpack by finding the page with the tail, and calling + ** reiserfs_prepare_write on that page. This will force a + ** reiserfs_get_block to unpack the tail for us. + */ + index = inode->i_size >> PAGE_CACHE_SHIFT ; + page = grab_cache_page(inode->i_mapping, index) ; + retval = -ENOMEM; + if (!page) { + goto out ; + } + retval = reiserfs_prepare_write(NULL, page, write_from, blocksize) ; + if (retval) + goto out_unlock ; + + /* conversion can change page contents, must flush */ + flush_dcache_page(page) ; + inode->u.reiserfs_i.i_flags |= i_nopack_mask; + kunmap(page) ; /* mapped by prepare_write */ + +out_unlock: + UnlockPage(page) ; + page_cache_release(page) ; + +out: + up(&inode->i_sem) ; + unlock_kernel(); + return retval; +} diff -urN linux-2.4.16-reiserfspatches/fs/udf/inode.c linux-2.4.16-reiserfspatches-immutable/fs/udf/inode.c --- linux-2.4.16-reiserfspatches/fs/udf/inode.c Mon Dec 10 13:12:23 2001 +++ linux-2.4.16-reiserfspatches-immutable/fs/udf/inode.c Mon Dec 10 14:28:03 2001 @@ -863,7 +863,7 @@ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) return; - if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + if (IS_APPEND(inode) || IS_IMMUTABLE_FILE(inode)) return; if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) diff -urN linux-2.4.16-reiserfspatches/fs/ufs/truncate.c linux-2.4.16-reiserfspatches-immutable/fs/ufs/truncate.c --- linux-2.4.16-reiserfspatches/fs/ufs/truncate.c Mon Dec 10 13:12:48 2001 +++ linux-2.4.16-reiserfspatches-immutable/fs/ufs/truncate.c Mon Dec 10 14:28:03 2001 @@ -434,7 +434,7 @@ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) return; - if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + if (IS_APPEND(inode) || IS_IMMUTABLE_FILE(inode)) return; while (1) { retry = ufs_trunc_direct(inode); diff -urN linux-2.4.16-reiserfspatches/include/linux/ext2_fs.h linux-2.4.16-reiserfspatches-immutable/include/linux/ext2_fs.h --- linux-2.4.16-reiserfspatches/include/linux/ext2_fs.h Mon Dec 10 13:12:27 2001 +++ linux-2.4.16-reiserfspatches-immutable/include/linux/ext2_fs.h Mon Dec 10 14:28:03 2001 @@ -187,7 +187,7 @@ #define EXT2_UNRM_FL 0x00000002 /* Undelete */ #define EXT2_COMPR_FL 0x00000004 /* Compress file */ #define EXT2_SYNC_FL 0x00000008 /* Synchronous updates */ -#define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */ +#define EXT2_IMMUTABLE_FILE_FL 0x00000010 /* Immutable file */ #define EXT2_APPEND_FL 0x00000020 /* writes to file may only append */ #define EXT2_NODUMP_FL 0x00000040 /* do not dump file */ #define EXT2_NOATIME_FL 0x00000080 /* do not update atime */ @@ -198,10 +198,11 @@ #define EXT2_ECOMPR_FL 0x00000800 /* Compression error */ /* End compression flags --- maybe not all used */ #define EXT2_BTREE_FL 0x00001000 /* btree format dir */ +#define EXT2_IMMUTABLE_LINK_FL 0x00008000 /* Immutable link */ #define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */ -#define EXT2_FL_USER_VISIBLE 0x00001FFF /* User visible flags */ -#define EXT2_FL_USER_MODIFIABLE 0x000000FF /* User modifiable flags */ +#define EXT2_FL_USER_VISIBLE 0x00009FFF /* User visible flags */ +#define EXT2_FL_USER_MODIFIABLE 0x000080FF /* User modifiable flags */ /* * ioctl commands diff -urN linux-2.4.16-reiserfspatches/include/linux/ext3_fs.h linux-2.4.16-reiserfspatches-immutable/include/linux/ext3_fs.h --- linux-2.4.16-reiserfspatches/include/linux/ext3_fs.h Mon Dec 10 13:12:49 2001 +++ linux-2.4.16-reiserfspatches-immutable/include/linux/ext3_fs.h Mon Dec 10 14:28:03 2001 @@ -190,7 +190,7 @@ #define EXT3_UNRM_FL 0x00000002 /* Undelete */ #define EXT3_COMPR_FL 0x00000004 /* Compress file */ #define EXT3_SYNC_FL 0x00000008 /* Synchronous updates */ -#define EXT3_IMMUTABLE_FL 0x00000010 /* Immutable file */ +#define EXT3_IMMUTABLE_FILE_FL 0x00000010 /* Immutable file */ #define EXT3_APPEND_FL 0x00000020 /* writes to file may only append */ #define EXT3_NODUMP_FL 0x00000040 /* do not dump file */ #define EXT3_NOATIME_FL 0x00000080 /* do not update atime */ @@ -203,10 +203,11 @@ #define EXT3_INDEX_FL 0x00001000 /* hash-indexed directory */ #define EXT3_IMAGIC_FL 0x00002000 /* AFS directory */ #define EXT3_JOURNAL_DATA_FL 0x00004000 /* file data should be journaled */ +#define EXT3_IMMUTABLE_LINK_FL 0x00008000 /* Immutable link */ #define EXT3_RESERVED_FL 0x80000000 /* reserved for ext3 lib */ -#define EXT3_FL_USER_VISIBLE 0x00005FFF /* User visible flags */ -#define EXT3_FL_USER_MODIFIABLE 0x000000FF /* User modifiable flags */ +#define EXT3_FL_USER_VISIBLE 0x0000DFFF /* User visible flags */ +#define EXT3_FL_USER_MODIFIABLE 0x000080FF /* User modifiable flags */ /* * Inode dynamic state flags diff -urN linux-2.4.16-reiserfspatches/include/linux/fs.h linux-2.4.16-reiserfspatches-immutable/include/linux/fs.h --- linux-2.4.16-reiserfspatches/include/linux/fs.h Mon Dec 10 13:57:11 2001 +++ linux-2.4.16-reiserfspatches-immutable/include/linux/fs.h Mon Dec 10 14:28:03 2001 @@ -127,13 +127,14 @@ /* Inode flags - they have nothing to superblock flags now */ -#define S_SYNC 1 /* Writes are synced at once */ -#define S_NOATIME 2 /* Do not update access times */ -#define S_QUOTA 4 /* Quota initialized for file */ -#define S_APPEND 8 /* Append-only file */ -#define S_IMMUTABLE 16 /* Immutable file */ -#define S_DEAD 32 /* removed, but still open directory */ -#define S_NOQUOTA 64 /* Inode is not counted to quota */ +#define S_SYNC 1 /* Writes are synced at once */ +#define S_NOATIME 2 /* Do not update access times */ +#define S_QUOTA 4 /* Quota initialized for file */ +#define S_APPEND 8 /* Append-only file */ +#define S_IMMUTABLE_FILE 16 /* Immutable file */ +#define S_DEAD 32 /* removed, but still open directory */ +#define S_NOQUOTA 64 /* Inode is not counted to quota */ +#define S_IMMUTABLE_LINK 128 /* Immutable links */ /* * Note that nosuid etc flags are inode-specific: setting some file-system @@ -157,7 +158,8 @@ #define IS_QUOTAINIT(inode) ((inode)->i_flags & S_QUOTA) #define IS_NOQUOTA(inode) ((inode)->i_flags & S_NOQUOTA) #define IS_APPEND(inode) ((inode)->i_flags & S_APPEND) -#define IS_IMMUTABLE(inode) ((inode)->i_flags & S_IMMUTABLE) +#define IS_IMMUTABLE_FILE(inode) ((inode)->i_flags & S_IMMUTABLE_FILE) +#define IS_IMMUTABLE_LINK(inode) ((((inode)->i_flags & S_IMMUTABLE_FILE) << 3) ^ ((inode)->i_flags & S_IMMUTABLE_LINK) ) #define IS_NOATIME(inode) (__IS_FLG(inode, MS_NOATIME) || ((inode)->i_flags & S_NOATIME)) #define IS_NODIRATIME(inode) __IS_FLG(inode, MS_NODIRATIME) @@ -356,11 +358,12 @@ /* * This is the inode attributes flag definitions */ -#define ATTR_FLAG_SYNCRONOUS 1 /* Syncronous write */ -#define ATTR_FLAG_NOATIME 2 /* Don't update atime */ -#define ATTR_FLAG_APPEND 4 /* Append-only file */ -#define ATTR_FLAG_IMMUTABLE 8 /* Immutable file */ -#define ATTR_FLAG_NODIRATIME 16 /* Don't update atime for directory */ +#define ATTR_FLAG_SYNCRONOUS 1 /* Syncronous write */ +#define ATTR_FLAG_NOATIME 2 /* Don't update atime */ +#define ATTR_FLAG_APPEND 4 /* Append-only file */ +#define ATTR_FLAG_IMMUTABLE_FILE 8 /* Immutable file */ +#define ATTR_FLAG_NODIRATIME 16 /* Don't update atime for directory */ +#define ATTR_FLAG_IMMUTABLE_LINK 32 /* Immutable file */ /* * Includes for diskquotas and mount structures. diff -urN linux-2.4.16-reiserfspatches/include/linux/reiserfs_fs.h linux-2.4.16-reiserfspatches-immutable/include/linux/reiserfs_fs.h --- linux-2.4.16-reiserfspatches/include/linux/reiserfs_fs.h Mon Dec 10 13:57:11 2001 +++ linux-2.4.16-reiserfspatches-immutable/include/linux/reiserfs_fs.h Mon Dec 10 14:28:53 2001 @@ -724,7 +724,8 @@ /* we want common flags to have the same values as in ext2, so chattr(1) will work without problems */ -#define REISERFS_IMMUTABLE_FL EXT2_IMMUTABLE_FL +#define REISERFS_IMMUTABLE_LINK_FL EXT2_IMMUTABLE_LINK_FL +#define REISERFS_IMMUTABLE_FILE_FL EXT2_IMMUTABLE_FILE_FL #define REISERFS_SYNC_FL EXT2_SYNC_FL #define REISERFS_NOATIME_FL EXT2_NOATIME_FL #define REISERFS_NODUMP_FL EXT2_NODUMP_FL @@ -740,7 +741,8 @@ #define REISERFS_NOTAIL_FL (0x00008000) /* EXT2_NOTAIL_FL */ /* persistent flags that file inherits from the parent directory */ -#define REISERFS_INHERIT_MASK ( REISERFS_IMMUTABLE_FL | \ +#define REISERFS_INHERIT_MASK ( REISERFS_IMMUTABLE_FILE_FL | \ + REISERFS_IMMUTABLE_LINK_FL | \ REISERFS_SYNC_FL | \ REISERFS_NOATIME_FL | \ REISERFS_NODUMP_FL | \ diff -urN linux-2.4.16-reiserfspatches/include/linux/reiserfs_fs.h~ linux-2.4.16-reiserfspatches-immutable/include/linux/reiserfs_fs.h~ --- linux-2.4.16-reiserfspatches/include/linux/reiserfs_fs.h~ Thu Jan 1 01:00:00 1970 +++ linux-2.4.16-reiserfspatches-immutable/include/linux/reiserfs_fs.h~ Mon Dec 10 13:57:11 2001 @@ -0,0 +1,1965 @@ +/* + * Copyright 1996, 1997, 1998 Hans Reiser, see reiserfs/README for licensing and copyright details + */ + + /* this file has an amazingly stupid + name, yura please fix it to be + reiserfs.h, and merge all the rest + of our .h files that are in this + directory into it. */ + + +#ifndef _LINUX_REISER_FS_H +#define _LINUX_REISER_FS_H + +#include +#ifdef __KERNEL__ +#include +#include +#include +#include +#include +#include +#endif + +/* + * include/linux/reiser_fs.h + * + * Reiser File System constants and structures + * + */ + +/* in reading the #defines, it may help to understand that they employ + the following abbreviations: + + B = Buffer + I = Item header + H = Height within the tree (should be changed to LEV) + N = Number of the item in the node + STAT = stat data + DEH = Directory Entry Header + EC = Entry Count + E = Entry number + UL = Unsigned Long + BLKH = BLocK Header + UNFM = UNForMatted node + DC = Disk Child + P = Path + + These #defines are named by concatenating these abbreviations, + where first comes the arguments, and last comes the return value, + of the macro. + +*/ + +#define USE_INODE_GENERATION_COUNTER + +#define REISERFS_PREALLOCATE +#define PREALLOCATION_SIZE 8 + +/* n must be power of 2 */ +#define _ROUND_UP(x,n) (((x)+(n)-1u) & ~((n)-1u)) + +// to be ok for alpha and others we have to align structures to 8 byte +// boundary. +// FIXME: do not change 4 by anything else: there is code which relies on that +#define ROUND_UP(x) _ROUND_UP(x,8LL) + +/* debug levels. Right now, CONFIG_REISERFS_CHECK means print all debug +** messages. +*/ +#define REISERFS_DEBUG_CODE 5 /* extra messages to help find/debug errors */ + +/* assertions handling */ + +/** always check a condition and panic if it's false. */ +#define RASSERT( cond, format, args... ) \ +if( !( cond ) ) \ + reiserfs_panic( 0, "reiserfs[%i]: assertion " #cond " failed at " \ + __FILE__ ":%i:" __FUNCTION__ ": " format "\n", \ + in_interrupt() ? -1 : current -> pid, __LINE__ , ##args ) + +#if defined( CONFIG_REISERFS_CHECK ) +#define RFALSE( cond, format, args... ) RASSERT( !( cond ), format, ##args ) +#else +#define RFALSE( cond, format, args... ) do {;} while( 0 ) +#endif + +#define CONSTF __attribute__( ( const ) ) +/* + * Disk Data Structures + */ + +/***************************************************************************/ +/* SUPER BLOCK */ +/***************************************************************************/ + +/* + * Structure of super block on disk, a version of which in RAM is often accessed as s->u.reiserfs_sb.s_rs + * the version in RAM is part of a larger structure containing fields never written to disk. + */ + + /* used by gcc */ +#define REISERFS_SUPER_MAGIC 0x52654973 + /* used by file system utilities that + look at the superblock, etc. */ +#define REISERFS_SUPER_MAGIC_STRING "ReIsErFs" +#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" + +extern char reiserfs_super_magic_string[]; +extern char reiser2fs_super_magic_string[]; + +static inline int is_reiserfs_magic_string (const struct reiserfs_super_block * rs) +{ + return (!strncmp (rs->s_magic, reiserfs_super_magic_string, + strlen ( reiserfs_super_magic_string)) || + !strncmp (rs->s_magic, reiser2fs_super_magic_string, + strlen ( reiser2fs_super_magic_string))); +} + +/* ReiserFS leaves the first 64k unused, so that partition labels have + enough space. If someone wants to write a fancy bootloader that + needs more than 64k, let us know, and this will be increased in size. + This number must be larger than than the largest block size on any + platform, or code will break. -Hans */ +#define REISERFS_DISK_OFFSET_IN_BYTES (64 * 1024) +#define REISERFS_FIRST_BLOCK unused_define + +/* the spot for the super in versions 3.5 - 3.5.10 (inclusive) */ +#define REISERFS_OLD_DISK_OFFSET_IN_BYTES (8 * 1024) + +// reiserfs internal error code (used by search_by_key adn fix_nodes)) +#define CARRY_ON 0 +#define REPEAT_SEARCH -1 +#define IO_ERROR -2 +#define NO_DISK_SPACE -3 +#define NO_BALANCING_NEEDED (-4) +#define NO_MORE_UNUSED_CONTIGUOUS_BLOCKS (-5) + +typedef unsigned long b_blocknr_t; +typedef __u32 unp_t; + +struct unfm_nodeinfo { + unp_t unfm_nodenum; + unsigned short unfm_freespace; +}; + + +/* there are two formats of keys: 3.5 and 3.6 + */ +#define KEY_FORMAT_3_5 0 +#define KEY_FORMAT_3_6 1 + +/* there are two stat datas */ +#define STAT_DATA_V1 0 +#define STAT_DATA_V2 1 + +/** this says about version of key of all items (but stat data) the + object consists of */ +#define get_inode_item_key_version( inode ) \ + (((inode)->u.reiserfs_i.i_flags & i_item_key_version_mask) ? KEY_FORMAT_3_6 : KEY_FORMAT_3_5) + +#define set_inode_item_key_version( inode, version ) \ + ({ if((version)==KEY_FORMAT_3_6) \ + (inode)->u.reiserfs_i.i_flags |= i_item_key_version_mask; \ + else \ + (inode)->u.reiserfs_i.i_flags &= ~i_item_key_version_mask; }) + +#define get_inode_sd_version(inode) \ + (((inode)->u.reiserfs_i.i_flags & i_stat_data_version_mask) ? STAT_DATA_V2 : STAT_DATA_V1) + +#define set_inode_sd_version(inode, version) \ + ({ if((version)==STAT_DATA_V2) \ + (inode)->u.reiserfs_i.i_flags |= i_stat_data_version_mask; \ + else \ + (inode)->u.reiserfs_i.i_flags &= ~i_stat_data_version_mask; }) + +/* This is an aggressive tail suppression policy, I am hoping it + improves our benchmarks. The principle behind it is that percentage + space saving is what matters, not absolute space saving. This is + non-intuitive, but it helps to understand it if you consider that the + cost to access 4 blocks is not much more than the cost to access 1 + block, if you have to do a seek and rotate. A tail risks a + non-linear disk access that is significant as a percentage of total + time cost for a 4 block file and saves an amount of space that is + less significant as a percentage of space, or so goes the hypothesis. + -Hans */ +#define STORE_TAIL_IN_UNFM(n_file_size,n_tail_size,n_block_size) \ +(\ + (!(n_tail_size)) || \ + (((n_tail_size) > MAX_DIRECT_ITEM_LEN(n_block_size)) || \ + ( (n_file_size) >= (n_block_size) * 4 ) || \ + ( ( (n_file_size) >= (n_block_size) * 3 ) && \ + ( (n_tail_size) >= (MAX_DIRECT_ITEM_LEN(n_block_size))/4) ) || \ + ( ( (n_file_size) >= (n_block_size) * 2 ) && \ + ( (n_tail_size) >= (MAX_DIRECT_ITEM_LEN(n_block_size))/2) ) || \ + ( ( (n_file_size) >= (n_block_size) ) && \ + ( (n_tail_size) >= (MAX_DIRECT_ITEM_LEN(n_block_size) * 3)/4) ) ) \ +) + + +/* + * values for s_state field + */ +#define REISERFS_VALID_FS 1 +#define REISERFS_ERROR_FS 2 + + + +/***************************************************************************/ +/* KEY & ITEM HEAD */ +/***************************************************************************/ + +// +// directories use this key as well as old files +// +struct offset_v1 { + __u32 k_offset; + __u32 k_uniqueness; +} __attribute__ ((__packed__)); + +struct offset_v2 { +#ifdef __LITTLE_ENDIAN + /* little endian version */ + __u64 k_offset:60; + __u64 k_type: 4; +#else + /* big endian version */ + __u64 k_type: 4; + __u64 k_offset:60; +#endif +} __attribute__ ((__packed__)); + +#ifndef __LITTLE_ENDIAN +typedef union { + struct offset_v2 offset_v2; + __u64 linear; +} __attribute__ ((__packed__)) offset_v2_esafe_overlay; + +static inline __u16 offset_v2_k_type( const struct offset_v2 *v2 ) +{ + offset_v2_esafe_overlay tmp = *(const constoffset_v2_esafe_overlay *)v2; + tmp.linear = le64_to_cpu( tmp.linear ); + return tmp.offset_v2.k_type; +} + +static inline void set_offset_v2_k_type( struct offset_v2 *v2, int type ) +{ + offset_v2_esafe_overlay *tmp = (offset_v2_esafe_overlay *)v2; + tmp->linear = le64_to_cpu(tmp->linear); + tmp->offset_v2.k_type = type; + tmp->linear = le64_to_cpu(tmp->linear); +} + +static inline loff_t offset_v2_k_offset( const struct offset_v2 *v2 ) +{ + offset_v2_esafe_overlay tmp = *(const offset_v2_esafe_overlay *)v2; + tmp.linear = le64_to_cpu( tmp.linear ); + return tmp.offset_v2.k_offset; +} + +static inline void set_offset_v2_k_offset( struct offset_v2 *v2, loff_t offset ){ + offset_v2_esafe_overlay *tmp = (offset_v2_esafe_overlay *)v2; + tmp->linear = le64_to_cpu(tmp->linear); + tmp->offset_v2.k_offset = offset; + tmp->linear = le64_to_cpu(tmp->linear); +} +#else +# define offset_v2_k_type(v2) ((v2)->k_type) +# define set_offset_v2_k_type(v2,val) (offset_v2_k_type(v2) = (val)) +# define offset_v2_k_offset(v2) ((v2)->k_offset) +# define set_offset_v2_k_offset(v2,val) (offset_v2_k_offset(v2) = (val)) +#endif + +/* Key of an item determines its location in the S+tree, and + is composed of 4 components */ +struct key { + __u32 k_dir_id; /* packing locality: by default parent + directory object id */ + __u32 k_objectid; /* object identifier */ + union { + struct offset_v1 k_offset_v1; + struct offset_v2 k_offset_v2; + } __attribute__ ((__packed__)) u; +} __attribute__ ((__packed__)); + + +struct cpu_key { + struct key on_disk_key; + int version; + int key_length; /* 3 in all cases but direct2indirect and + indirect2direct conversion */ +}; + +/* Our function for comparing keys can compare keys of different + lengths. It takes as a parameter the length of the keys it is to + compare. These defines are used in determining what is to be passed + to it as that parameter. */ +#define REISERFS_FULL_KEY_LEN 4 +#define REISERFS_SHORT_KEY_LEN 2 + +/* The result of the key compare */ +#define FIRST_GREATER 1 +#define SECOND_GREATER -1 +#define KEYS_IDENTICAL 0 +#define KEY_FOUND 1 +#define KEY_NOT_FOUND 0 + +#define KEY_SIZE (sizeof(struct key)) +#define SHORT_KEY_SIZE (sizeof (__u32) + sizeof (__u32)) + +/* return values for search_by_key and clones */ +#define ITEM_FOUND 1 +#define ITEM_NOT_FOUND 0 +#define ENTRY_FOUND 1 +#define ENTRY_NOT_FOUND 0 +#define DIRECTORY_NOT_FOUND -1 +#define REGULAR_FILE_FOUND -2 +#define DIRECTORY_FOUND -3 +#define BYTE_FOUND 1 +#define BYTE_NOT_FOUND 0 +#define FILE_NOT_FOUND -1 + +#define POSITION_FOUND 1 +#define POSITION_NOT_FOUND 0 + +// return values for reiserfs_find_entry and search_by_entry_key +#define NAME_FOUND 1 +#define NAME_NOT_FOUND 0 +#define GOTO_PREVIOUS_ITEM 2 +#define NAME_FOUND_INVISIBLE 3 + +/* Everything in the filesystem is stored as a set of items. The + item head contains the key of the item, its free space (for + indirect items) and specifies the location of the item itself + within the block. */ + +struct item_head +{ + /* Everything in the tree is found by searching for it based on + * its key.*/ + struct key ih_key; + union { + /* The free space in the last unformatted node of an + indirect item if this is an indirect item. This + equals 0xFFFF iff this is a direct item or stat data + item. Note that the key, not this field, is used to + determine the item type, and thus which field this + union contains. */ + __u16 ih_free_space_reserved; + /* Iff this is a directory item, this field equals the + number of directory entries in the directory item. */ + __u16 ih_entry_count; + } __attribute__ ((__packed__)) u; + __u16 ih_item_len; /* total size of the item body */ + __u16 ih_item_location; /* an offset to the item body + * within the block */ + __u16 ih_version; /* 0 for all old items, 2 for new + ones. Highest bit is set by fsck + temporary, cleaned after all + done */ +} __attribute__ ((__packed__)); +/* size of item header */ +#define IH_SIZE (sizeof(struct item_head)) + +#define ih_free_space(ih) le16_to_cpu((ih)->u.ih_free_space_reserved) +#define ih_version(ih) le16_to_cpu((ih)->ih_version) +#define ih_entry_count(ih) le16_to_cpu((ih)->u.ih_entry_count) +#define ih_location(ih) le16_to_cpu((ih)->ih_item_location) +#define ih_item_len(ih) le16_to_cpu((ih)->ih_item_len) + +#define put_ih_free_space(ih, val) do { (ih)->u.ih_free_space_reserved = cpu_to_le16(val); } while(0) +#define put_ih_version(ih, val) do { (ih)->ih_version = cpu_to_le16(val); } while (0) +#define put_ih_entry_count(ih, val) do { (ih)->u.ih_entry_count = cpu_to_le16(val); } while (0) +#define put_ih_location(ih, val) do { (ih)->ih_item_location = cpu_to_le16(val); } while (0) +#define put_ih_item_len(ih, val) do { (ih)->ih_item_len = cpu_to_le16(val); } while (0) + + +#define unreachable_item(ih) (ih_version(ih) & (1 << 15)) + +#define get_ih_free_space(ih) (ih_version (ih) == KEY_FORMAT_3_6 ? 0 : ih_free_space (ih)) +#define set_ih_free_space(ih,val) put_ih_free_space((ih), ((ih_version(ih) == KEY_FORMAT_3_6) ? 0 : (val))) + +/* these operate on indirect items, where you've got an array of ints +** at a possibly unaligned location. These are a noop on ia32 +** +** p is the array of __u32, i is the index into the array, v is the value +** to store there. +*/ +#define get_block_num(p, i) le32_to_cpu(get_unaligned((p) + (i))) +#define put_block_num(p, i, v) put_unaligned(cpu_to_le32(v), (p) + (i)) + +// +// there are 5 item types currently +// +#define TYPE_STAT_DATA 0 +#define TYPE_INDIRECT 1 +#define TYPE_DIRECT 2 +#define TYPE_DIRENTRY 3 +#define TYPE_ANY 15 // FIXME: comment is required + +// +// in old version uniqueness field shows key type +// +#define V1_SD_UNIQUENESS 0 +#define V1_INDIRECT_UNIQUENESS 0xfffffffe +#define V1_DIRECT_UNIQUENESS 0xffffffff +#define V1_DIRENTRY_UNIQUENESS 500 +#define V1_ANY_UNIQUENESS 555 // FIXME: comment is required + +extern void reiserfs_warning (const char * fmt, ...); +/* __attribute__( ( format ( printf, 1, 2 ) ) ); */ + +// +// here are conversion routines +// +static inline int uniqueness2type (__u32 uniqueness) CONSTF; +static inline int uniqueness2type (__u32 uniqueness) +{ + switch (uniqueness) { + case V1_SD_UNIQUENESS: return TYPE_STAT_DATA; + case V1_INDIRECT_UNIQUENESS: return TYPE_INDIRECT; + case V1_DIRECT_UNIQUENESS: return TYPE_DIRECT; + case V1_DIRENTRY_UNIQUENESS: return TYPE_DIRENTRY; + default: + reiserfs_warning( "vs-500: unknown uniqueness %d\n", uniqueness); + case V1_ANY_UNIQUENESS: + return TYPE_ANY; + } +} + +static inline __u32 type2uniqueness (int type) CONSTF; +static inline __u32 type2uniqueness (int type) +{ + switch (type) { + case TYPE_STAT_DATA: return V1_SD_UNIQUENESS; + case TYPE_INDIRECT: return V1_INDIRECT_UNIQUENESS; + case TYPE_DIRECT: return V1_DIRECT_UNIQUENESS; + case TYPE_DIRENTRY: return V1_DIRENTRY_UNIQUENESS; + default: + reiserfs_warning( "vs-501: unknown type %d\n", type); + case TYPE_ANY: + return V1_ANY_UNIQUENESS; + } +} + +// +// key is pointer to on disk key which is stored in le, result is cpu, +// there is no way to get version of object from key, so, provide +// version to these defines +// +static inline loff_t le_key_k_offset (int version, const struct key * key) +{ + return (version == KEY_FORMAT_3_5) ? + le32_to_cpu( key->u.k_offset_v1.k_offset ) : + offset_v2_k_offset( &(key->u.k_offset_v2) ); +} + +static inline loff_t le_ih_k_offset (const struct item_head * ih) +{ + return le_key_k_offset (ih_version (ih), &(ih->ih_key)); +} + +static inline loff_t le_key_k_type (int version, const struct key * key) +{ + return (version == KEY_FORMAT_3_5) ? + uniqueness2type( le32_to_cpu( key->u.k_offset_v1.k_uniqueness)) : + offset_v2_k_type( &(key->u.k_offset_v2) ); +} + +static inline loff_t le_ih_k_type (const struct item_head * ih) +{ + return le_key_k_type (ih_version (ih), &(ih->ih_key)); +} + + +static inline void set_le_key_k_offset (int version, struct key * key, loff_t offset) +{ + (version == KEY_FORMAT_3_5) ? + (key->u.k_offset_v1.k_offset = cpu_to_le32 (offset)) : /* jdm check */ + (set_offset_v2_k_offset( &(key->u.k_offset_v2), offset )); +} + + +static inline void set_le_ih_k_offset (struct item_head * ih, loff_t offset) +{ + set_le_key_k_offset (ih_version (ih), &(ih->ih_key), offset); +} + + +static inline void set_le_key_k_type (int version, struct key * key, int type) +{ + (version == KEY_FORMAT_3_5) ? + (key->u.k_offset_v1.k_uniqueness = cpu_to_le32(type2uniqueness(type))): + (set_offset_v2_k_type( &(key->u.k_offset_v2), type )); +} +static inline void set_le_ih_k_type (struct item_head * ih, int type) +{ + set_le_key_k_type (ih_version (ih), &(ih->ih_key), type); +} + + +#define is_direntry_le_key(version,key) (le_key_k_type (version, key) == TYPE_DIRENTRY) +#define is_direct_le_key(version,key) (le_key_k_type (version, key) == TYPE_DIRECT) +#define is_indirect_le_key(version,key) (le_key_k_type (version, key) == TYPE_INDIRECT) +#define is_statdata_le_key(version,key) (le_key_k_type (version, key) == TYPE_STAT_DATA) + +// +// item header has version. +// +#define is_direntry_le_ih(ih) is_direntry_le_key (ih_version (ih), &((ih)->ih_key)) +#define is_direct_le_ih(ih) is_direct_le_key (ih_version (ih), &((ih)->ih_key)) +#define is_indirect_le_ih(ih) is_indirect_le_key (ih_version(ih), &((ih)->ih_key)) +#define is_statdata_le_ih(ih) is_statdata_le_key (ih_version (ih), &((ih)->ih_key)) + + + +// +// key is pointer to cpu key, result is cpu +// +static inline loff_t cpu_key_k_offset (const struct cpu_key * key) +{ + return (key->version == KEY_FORMAT_3_5) ? + key->on_disk_key.u.k_offset_v1.k_offset : + key->on_disk_key.u.k_offset_v2.k_offset; +} + +static inline loff_t cpu_key_k_type (const struct cpu_key * key) +{ + return (key->version == KEY_FORMAT_3_5) ? + uniqueness2type (key->on_disk_key.u.k_offset_v1.k_uniqueness) : + key->on_disk_key.u.k_offset_v2.k_type; +} + +static inline void set_cpu_key_k_offset (struct cpu_key * key, loff_t offset) +{ + (key->version == KEY_FORMAT_3_5) ? + (key->on_disk_key.u.k_offset_v1.k_offset = offset) : + (key->on_disk_key.u.k_offset_v2.k_offset = offset); +} + + +static inline void set_cpu_key_k_type (struct cpu_key * key, int type) +{ + (key->version == KEY_FORMAT_3_5) ? + (key->on_disk_key.u.k_offset_v1.k_uniqueness = type2uniqueness (type)): + (key->on_disk_key.u.k_offset_v2.k_type = type); +} + + +static inline void cpu_key_k_offset_dec (struct cpu_key * key) +{ + if (key->version == KEY_FORMAT_3_5) + key->on_disk_key.u.k_offset_v1.k_offset --; + else + key->on_disk_key.u.k_offset_v2.k_offset --; +} + + +#define is_direntry_cpu_key(key) (cpu_key_k_type (key) == TYPE_DIRENTRY) +#define is_direct_cpu_key(key) (cpu_key_k_type (key) == TYPE_DIRECT) +#define is_indirect_cpu_key(key) (cpu_key_k_type (key) == TYPE_INDIRECT) +#define is_statdata_cpu_key(key) (cpu_key_k_type (key) == TYPE_STAT_DATA) + + +/* are these used ? */ +#define is_direntry_cpu_ih(ih) (is_direntry_cpu_key (&((ih)->ih_key))) +#define is_direct_cpu_ih(ih) (is_direct_cpu_key (&((ih)->ih_key))) +#define is_indirect_cpu_ih(ih) (is_indirect_cpu_key (&((ih)->ih_key))) +#define is_statdata_cpu_ih(ih) (is_statdata_cpu_key (&((ih)->ih_key))) + + + + + +#define I_K_KEY_IN_ITEM(p_s_ih, p_s_key, n_blocksize) \ + ( ! COMP_SHORT_KEYS(p_s_ih, p_s_key) && \ + I_OFF_BYTE_IN_ITEM(p_s_ih, k_offset (p_s_key), n_blocksize) ) + +/* maximal length of item */ +#define MAX_ITEM_LEN(block_size) (block_size - BLKH_SIZE - IH_SIZE) +#define MIN_ITEM_LEN 1 + + +/* object identifier for root dir */ +#define REISERFS_ROOT_OBJECTID 2 +#define REISERFS_ROOT_PARENT_OBJECTID 1 +extern struct key root_key; + + + + +/* + * Picture represents a leaf of the S+tree + * ______________________________________________________ + * | | Array of | | | + * |Block | Object-Item | F r e e | Objects- | + * | head | Headers | S p a c e | Items | + * |______|_______________|___________________|___________| + */ + +/* Header of a disk block. More precisely, header of a formatted leaf + or internal node, and not the header of an unformatted node. */ +struct block_head { + __u16 blk_level; /* Level of a block in the tree. */ + __u16 blk_nr_item; /* Number of keys/items in a block. */ + __u16 blk_free_space; /* Block free space in bytes. */ + __u16 blk_reserved; + /* dump this in v4/planA */ + struct key blk_right_delim_key; /* kept only for compatibility */ +}; + +#define BLKH_SIZE (sizeof(struct block_head)) +#define blkh_level(p_blkh) (le16_to_cpu((p_blkh)->blk_level)) +#define blkh_nr_item(p_blkh) (le16_to_cpu((p_blkh)->blk_nr_item)) +#define blkh_free_space(p_blkh) (le16_to_cpu((p_blkh)->blk_free_space)) +#define blkh_reserved(p_blkh) (le16_to_cpu((p_blkh)->blk_reserved)) +#define set_blkh_level(p_blkh,val) ((p_blkh)->blk_level = cpu_to_le16(val)) +#define set_blkh_nr_item(p_blkh,val) ((p_blkh)->blk_nr_item = cpu_to_le16(val)) +#define set_blkh_free_space(p_blkh,val) ((p_blkh)->blk_free_space = cpu_to_le16(val)) +#define set_blkh_reserved(p_blkh,val) ((p_blkh)->blk_reserved = cpu_to_le16(val)) +#define blkh_right_delim_key(p_blkh) ((p_blkh)->blk_right_delim_key) +#define set_blkh_right_delim_key(p_blkh,val) ((p_blkh)->blk_right_delim_key = val) + +/* + * values for blk_level field of the struct block_head + */ + +#define FREE_LEVEL 0 /* when node gets removed from the tree its + blk_level is set to FREE_LEVEL. It is then + used to see whether the node is still in the + tree */ + +#define DISK_LEAF_NODE_LEVEL 1 /* Leaf node level.*/ + +/* Given the buffer head of a formatted node, resolve to the block head of that node. */ +#define B_BLK_HEAD(p_s_bh) ((struct block_head *)((p_s_bh)->b_data)) +/* Number of items that are in buffer. */ +#define B_NR_ITEMS(p_s_bh) (blkh_nr_item(B_BLK_HEAD(p_s_bh))) +#define B_LEVEL(p_s_bh) (blkh_level(B_BLK_HEAD(p_s_bh))) +#define B_FREE_SPACE(p_s_bh) (blkh_free_space(B_BLK_HEAD(p_s_bh))) + +#define PUT_B_NR_ITEMS(p_s_bh,val) do { set_blkh_nr_item(B_BLK_HEAD(p_s_bh),val); } while (0) +#define PUT_B_LEVEL(p_s_bh,val) do { set_blkh_level(B_BLK_HEAD(p_s_bh),val); } while (0) +#define PUT_B_FREE_SPACE(p_s_bh,val) do { set_blkh_free_space(B_BLK_HEAD(p_s_bh),val); } while (0) + + +/* Get right delimiting key. -- little endian */ +#define B_PRIGHT_DELIM_KEY(p_s_bh) (&(blk_right_delim_key(B_BLK_HEAD(p_s_bh)) + +/* Does the buffer contain a disk leaf. */ +#define B_IS_ITEMS_LEVEL(p_s_bh) (B_LEVEL(p_s_bh) == DISK_LEAF_NODE_LEVEL) + +/* Does the buffer contain a disk internal node */ +#define B_IS_KEYS_LEVEL(p_s_bh) (B_LEVEL(p_s_bh) > DISK_LEAF_NODE_LEVEL \ + && B_LEVEL(p_s_bh) <= MAX_HEIGHT) + + + + +/***************************************************************************/ +/* STAT DATA */ +/***************************************************************************/ + + +// +// old stat data is 32 bytes long. We are going to distinguish new one by +// different size +// +struct stat_data_v1 +{ + __u16 sd_mode; /* file type, permissions */ + __u16 sd_nlink; /* number of hard links */ + __u16 sd_uid; /* owner */ + __u16 sd_gid; /* group */ + __u32 sd_size; /* file size */ + __u32 sd_atime; /* time of last access */ + __u32 sd_mtime; /* time file was last modified */ + __u32 sd_ctime; /* time inode (stat data) was last changed (except changes to sd_atime and sd_mtime) */ + union { + __u32 sd_rdev; + __u32 sd_blocks; /* number of blocks file uses */ + } __attribute__ ((__packed__)) u; + __u32 sd_first_direct_byte; /* first byte of file which is stored + in a direct item: except that if it + equals 1 it is a symlink and if it + equals ~(__u32)0 there is no + direct item. The existence of this + field really grates on me. Let's + replace it with a macro based on + sd_size and our tail suppression + policy. Someday. -Hans */ +} __attribute__ ((__packed__)); + +#define SD_V1_SIZE (sizeof(struct stat_data_v1)) +#define stat_data_v1(ih) (ih_version (ih) == KEY_FORMAT_3_5) +#define sd_v1_mode(sdp) (le16_to_cpu((sdp)->sd_mode)) +#define set_sd_v1_mode(sdp,v) ((sdp)->sd_mode = cpu_to_le16(v)) +#define sd_v1_nlink(sdp) (le16_to_cpu((sdp)->sd_nlink)) +#define set_sd_v1_nlink(sdp,v) ((sdp)->sd_nlink = cpu_to_le16(v)) +#define sd_v1_uid(sdp) (le16_to_cpu((sdp)->sd_uid)) +#define set_sd_v1_uid(sdp,v) ((sdp)->sd_uid = cpu_to_le16(v)) +#define sd_v1_gid(sdp) (le16_to_cpu((sdp)->sd_gid)) +#define set_sd_v1_gid(sdp,v) ((sdp)->sd_gid = cpu_to_le16(v)) +#define sd_v1_size(sdp) (le32_to_cpu((sdp)->sd_size)) +#define set_sd_v1_size(sdp,v) ((sdp)->sd_size = cpu_to_le32(v)) +#define sd_v1_atime(sdp) (le32_to_cpu((sdp)->sd_atime)) +#define set_sd_v1_atime(sdp,v) ((sdp)->sd_atime = cpu_to_le32(v)) +#define sd_v1_mtime(sdp) (le32_to_cpu((sdp)->sd_mtime)) +#define set_sd_v1_mtime(sdp,v) ((sdp)->sd_mtime = cpu_to_le32(v)) +#define sd_v1_ctime(sdp) (le32_to_cpu((sdp)->sd_ctime)) +#define set_sd_v1_ctime(sdp,v) ((sdp)->sd_ctime = cpu_to_le32(v)) +#define sd_v1_rdev(sdp) (le32_to_cpu((sdp)->u.sd_rdev)) +#define set_sd_v1_rdev(sdp,v) ((sdp)->u.sd_rdev = cpu_to_le32(v)) +#define sd_v1_blocks(sdp) (le32_to_cpu((sdp)->u.sd_blocks)) +#define set_sd_v1_blocks(sdp,v) ((sdp)->u.sd_blocks = cpu_to_le32(v)) +#define sd_v1_first_direct_byte(sdp) \ + (le32_to_cpu((sdp)->sd_first_direct_byte)) +#define set_sd_v1_first_direct_byte(sdp,v) \ + ((sdp)->sd_first_direct_byte = cpu_to_le32(v)) + +#include + +/* inode flags stored in sd_attrs (nee sd_reserved) */ + +/* we want common flags to have the same values as in ext2, + so chattr(1) will work without problems */ +#define REISERFS_IMMUTABLE_FL EXT2_IMMUTABLE_FL +#define REISERFS_SYNC_FL EXT2_SYNC_FL +#define REISERFS_NOATIME_FL EXT2_NOATIME_FL +#define REISERFS_NODUMP_FL EXT2_NODUMP_FL +#define REISERFS_SECRM_FL EXT2_SECRM_FL +#define REISERFS_UNRM_FL EXT2_UNRM_FL +#define REISERFS_COMPR_FL EXT2_COMPR_FL +/* persistent flag to disable tails on per-file basic. + Note, that is inheritable: mark directory with this and + all new files inside will not have tails. + + Teodore Tso allocated EXT2_NODUMP_FL (0x00008000) for this. Change + numeric constant to ext2 macro when available. */ +#define REISERFS_NOTAIL_FL (0x00008000) /* EXT2_NOTAIL_FL */ + +/* persistent flags that file inherits from the parent directory */ +#define REISERFS_INHERIT_MASK ( REISERFS_IMMUTABLE_FL | \ + REISERFS_SYNC_FL | \ + REISERFS_NOATIME_FL | \ + REISERFS_NODUMP_FL | \ + REISERFS_SECRM_FL | \ + REISERFS_COMPR_FL | \ + REISERFS_NOTAIL_FL ) + +/* Stat Data on disk (reiserfs version of UFS disk inode minus the + address blocks) */ +struct stat_data { + __u16 sd_mode; /* file type, permissions */ + __u16 sd_attrs; /* persistent inode flags */ + __u32 sd_nlink; /* number of hard links */ + __u64 sd_size; /* file size */ + __u32 sd_uid; /* owner */ + __u32 sd_gid; /* group */ + __u32 sd_atime; /* time of last access */ + __u32 sd_mtime; /* time file was last modified */ + __u32 sd_ctime; /* time inode (stat data) was last changed (except changes to sd_atime and sd_mtime) */ + __u32 sd_blocks; + union { + __u32 sd_rdev; + __u32 sd_generation; + //__u32 sd_first_direct_byte; + /* first byte of file which is stored in a + direct item: except that if it equals 1 + it is a symlink and if it equals + ~(__u32)0 there is no direct item. The + existence of this field really grates + on me. Let's replace it with a macro + based on sd_size and our tail + suppression policy? */ + } __attribute__ ((__packed__)) u; +} __attribute__ ((__packed__)); +// +// this is 44 bytes long +// +#define SD_SIZE (sizeof(struct stat_data)) +#define SD_V2_SIZE SD_SIZE +#define stat_data_v2(ih) (ih_version (ih) == KEY_FORMAT_3_6) +#define sd_v2_mode(sdp) (le16_to_cpu((sdp)->sd_mode)) +#define set_sd_v2_mode(sdp,v) ((sdp)->sd_mode = cpu_to_le16(v)) +/* sd_reserved */ +/* set_sd_reserved */ +#define sd_v2_nlink(sdp) (le32_to_cpu((sdp)->sd_nlink)) +#define set_sd_v2_nlink(sdp,v) ((sdp)->sd_nlink = cpu_to_le32(v)) +#define sd_v2_size(sdp) (le64_to_cpu((sdp)->sd_size)) +#define set_sd_v2_size(sdp,v) ((sdp)->sd_size = cpu_to_le64(v)) +#define sd_v2_uid(sdp) (le32_to_cpu((sdp)->sd_uid)) +#define set_sd_v2_uid(sdp,v) ((sdp)->sd_uid = cpu_to_le32(v)) +#define sd_v2_gid(sdp) (le32_to_cpu((sdp)->sd_gid)) +#define set_sd_v2_gid(sdp,v) ((sdp)->sd_gid = cpu_to_le32(v)) +#define sd_v2_atime(sdp) (le32_to_cpu((sdp)->sd_atime)) +#define set_sd_v2_atime(sdp,v) ((sdp)->sd_atime = cpu_to_le32(v)) +#define sd_v2_mtime(sdp) (le32_to_cpu((sdp)->sd_mtime)) +#define set_sd_v2_mtime(sdp,v) ((sdp)->sd_mtime = cpu_to_le32(v)) +#define sd_v2_ctime(sdp) (le32_to_cpu((sdp)->sd_ctime)) +#define set_sd_v2_ctime(sdp,v) ((sdp)->sd_ctime = cpu_to_le32(v)) +#define sd_v2_blocks(sdp) (le32_to_cpu((sdp)->sd_blocks)) +#define set_sd_v2_blocks(sdp,v) ((sdp)->sd_blocks = cpu_to_le32(v)) +#define sd_v2_rdev(sdp) (le32_to_cpu((sdp)->u.sd_rdev)) +#define set_sd_v2_rdev(sdp,v) ((sdp)->u.sd_rdev = cpu_to_le32(v)) +#define sd_v2_generation(sdp) (le32_to_cpu((sdp)->u.sd_generation)) +#define set_sd_v2_generation(sdp,v) ((sdp)->u.sd_generation = cpu_to_le32(v)) +#define sd_v2_attrs(sdp) (le16_to_cpu((sdp)->sd_attrs)) +#define set_sd_v2_attrs(sdp,v) ((sdp)->sd_attrs = cpu_to_le16(v)) + + +/***************************************************************************/ +/* DIRECTORY STRUCTURE */ +/***************************************************************************/ +/* + Picture represents the structure of directory items + ________________________________________________ + | Array of | | | | | | + | directory |N-1| N-2 | .... | 1st |0th| + | entry headers | | | | | | + |_______________|___|_____|________|_______|___| + <---- directory entries ------> + + First directory item has k_offset component 1. We store "." and ".." + in one item, always, we never split "." and ".." into differing + items. This makes, among other things, the code for removing + directories simpler. */ +#define SD_OFFSET 0 +#define SD_UNIQUENESS 0 +#define DOT_OFFSET 1 +#define DOT_DOT_OFFSET 2 +#define DIRENTRY_UNIQUENESS 500 + +/* */ +#define FIRST_ITEM_OFFSET 1 + +/* + Q: How to get key of object pointed to by entry from entry? + + A: Each directory entry has its header. This header has deh_dir_id and deh_objectid fields, those are key + of object, entry points to */ + +/* NOT IMPLEMENTED: + Directory will someday contain stat data of object */ + + + +struct reiserfs_de_head +{ + __u32 deh_offset; /* third component of the directory entry key */ + __u32 deh_dir_id; /* objectid of the parent directory of the object, that is referenced + by directory entry */ + __u32 deh_objectid; /* objectid of the object, that is referenced by directory entry */ + __u16 deh_location; /* offset of name in the whole item */ + __u16 deh_state; /* whether 1) entry contains stat data (for future), and 2) whether + entry is hidden (unlinked) */ +} __attribute__ ((__packed__)); +#define DEH_SIZE sizeof(struct reiserfs_de_head) +#define deh_offset(p_deh) (le32_to_cpu((p_deh)->deh_offset)) +#define deh_dir_id(p_deh) (le32_to_cpu((p_deh)->deh_dir_id)) +#define deh_objectid(p_deh) (le32_to_cpu((p_deh)->deh_objectid)) +#define deh_location(p_deh) (le16_to_cpu((p_deh)->deh_location)) +#define deh_state(p_deh) (le16_to_cpu((p_deh)->deh_state)) + +#define put_deh_offset(p_deh,v) ((p_deh)->deh_offset = cpu_to_le32((v))) +#define put_deh_dir_id(p_deh,v) ((p_deh)->deh_dir_id = cpu_to_le32((v))) +#define put_deh_objectid(p_deh,v) ((p_deh)->deh_objectid = cpu_to_le32((v))) +#define put_deh_location(p_deh,v) ((p_deh)->deh_location = cpu_to_le16((v))) +#define put_deh_state(p_deh,v) ((p_deh)->deh_state = cpu_to_le16((v))) + +/* empty directory contains two entries "." and ".." and their headers */ +#define EMPTY_DIR_SIZE \ +(DEH_SIZE * 2 + ROUND_UP (strlen (".")) + ROUND_UP (strlen (".."))) + +/* old format directories have this size when empty */ +#define EMPTY_DIR_SIZE_V1 (DEH_SIZE * 2 + 3) + +#define DEH_Statdata 0 /* not used now */ +#define DEH_Visible 2 + +/* 64 bit systems (and the S/390) need to be aligned explicitly -jdm */ +#if BITS_PER_LONG == 64 || defined(__s390__) || defined(__hppa__) +# define ADDR_UNALIGNED_BITS (3) +#endif + +/* These are only used to manipulate deh_state. + * Because of this, we'll use the ext2_ bit routines, + * since they are little endian */ +#ifdef ADDR_UNALIGNED_BITS + +# define aligned_address(addr) ((void *)((long)(addr) & ~((1UL << ADDR_UNALIGNED_BITS) - 1))) +# define unaligned_offset(addr) (((int)((long)(addr) & ((1 << ADDR_UNALIGNED_BITS) - 1))) << 3) + +# define set_bit_unaligned(nr, addr) ext2_set_bit((nr) + unaligned_offset(addr), aligned_address(addr)) +# define clear_bit_unaligned(nr, addr) ext2_clear_bit((nr) + unaligned_offset(addr), aligned_address(addr)) +# define test_bit_unaligned(nr, addr) ext2_test_bit((nr) + unaligned_offset(addr), aligned_address(addr)) + +#else + +# define set_bit_unaligned(nr, addr) ext2_set_bit(nr, addr) +# define clear_bit_unaligned(nr, addr) ext2_clear_bit(nr, addr) +# define test_bit_unaligned(nr, addr) ext2_test_bit(nr, addr) + +#endif + +#define mark_de_with_sd(deh) set_bit_unaligned (DEH_Statdata, &((deh)->deh_state)) +#define mark_de_without_sd(deh) clear_bit_unaligned (DEH_Statdata, &((deh)->deh_state)) +#define mark_de_visible(deh) set_bit_unaligned (DEH_Visible, &((deh)->deh_state)) +#define mark_de_hidden(deh) clear_bit_unaligned (DEH_Visible, &((deh)->deh_state)) + +#define de_with_sd(deh) test_bit_unaligned (DEH_Statdata, &((deh)->deh_state)) +#define de_visible(deh) test_bit_unaligned (DEH_Visible, &((deh)->deh_state)) +#define de_hidden(deh) !test_bit_unaligned (DEH_Visible, &((deh)->deh_state)) + +extern void make_empty_dir_item_v1 (char * body, __u32 dirid, __u32 objid, + __u32 par_dirid, __u32 par_objid); +extern void make_empty_dir_item (char * body, __u32 dirid, __u32 objid, + __u32 par_dirid, __u32 par_objid); + +/* array of the entry headers */ + /* get item body */ +#define B_I_PITEM(bh,ih) ( (bh)->b_data + ih_location(ih) ) +#define B_I_DEH(bh,ih) ((struct reiserfs_de_head *)(B_I_PITEM(bh,ih))) + +/* length of the directory entry in directory item. This define + calculates length of i-th directory entry using directory entry + locations from dir entry head. When it calculates length of 0-th + directory entry, it uses length of whole item in place of entry + location of the non-existent following entry in the calculation. + See picture above.*/ +/* +#define I_DEH_N_ENTRY_LENGTH(ih,deh,i) \ +((i) ? (deh_location((deh)-1) - deh_location((deh))) : (ih_item_len((ih)) - deh_location((deh)))) +*/ +static inline int entry_length (const struct buffer_head * bh, + const struct item_head * ih, int pos_in_item) +{ + struct reiserfs_de_head * deh; + + deh = B_I_DEH (bh, ih) + pos_in_item; + if (pos_in_item) + return deh_location(deh-1) - deh_location(deh); + + return ih_item_len(ih) - deh_location(deh); +} + + + +/* number of entries in the directory item, depends on ENTRY_COUNT being at the start of directory dynamic data. */ +#define I_ENTRY_COUNT(ih) (ih_entry_count((ih))) + + +/* name by bh, ih and entry_num */ +#define B_I_E_NAME(bh,ih,entry_num) ((char *)(bh->b_data + ih_location(ih) + deh_location(B_I_DEH(bh,ih)+(entry_num)))) + +// two entries per block (at least) +//#define REISERFS_MAX_NAME_LEN(block_size) +//((block_size - BLKH_SIZE - IH_SIZE - DEH_SIZE * 2) / 2) +#define REISERFS_MAX_NAME_LEN(block_size) 255 + + +/* this structure is used for operations on directory entries. It is + not a disk structure. */ +/* When reiserfs_find_entry or search_by_entry_key find directory + entry, they return filled reiserfs_dir_entry structure */ +struct reiserfs_dir_entry +{ + struct buffer_head * de_bh; + int de_item_num; + struct item_head * de_ih; + int de_entry_num; + struct reiserfs_de_head * de_deh; + int de_entrylen; + int de_namelen; + char * de_name; + char * de_gen_number_bit_string; + + __u32 de_dir_id; + __u32 de_objectid; + + struct cpu_key de_entry_key; +}; + +/* these defines are useful when a particular member of a reiserfs_dir_entry is needed */ + +/* pointer to file name, stored in entry */ +#define B_I_DEH_ENTRY_FILE_NAME(bh,ih,deh) (B_I_PITEM (bh, ih) + deh_location(deh)) + +/* length of name */ +#define I_DEH_N_ENTRY_FILE_NAME_LENGTH(ih,deh,entry_num) \ +(I_DEH_N_ENTRY_LENGTH (ih, deh, entry_num) - (de_with_sd (deh) ? SD_SIZE : 0)) + + + +/* hash value occupies bits from 7 up to 30 */ +#define GET_HASH_VALUE(offset) ((offset) & 0x7fffff80LL) +/* generation number occupies 7 bits starting from 0 up to 6 */ +#define GET_GENERATION_NUMBER(offset) ((offset) & 0x7fLL) +#define MAX_GENERATION_NUMBER 127 + +#define SET_GENERATION_NUMBER(offset,gen_number) (GET_HASH_VALUE(offset)|(gen_number)) + + +/* + * Picture represents an internal node of the reiserfs tree + * ______________________________________________________ + * | | Array of | Array of | Free | + * |block | keys | pointers | space | + * | head | N | N+1 | | + * |______|_______________|___________________|___________| + */ + +/***************************************************************************/ +/* DISK CHILD */ +/***************************************************************************/ +/* Disk child pointer: The pointer from an internal node of the tree + to a node that is on disk. */ +struct disk_child { + __u32 dc_block_number; /* Disk child's block number. */ + __u16 dc_size; /* Disk child's used space. */ + __u16 dc_reserved; +}; + +#define DC_SIZE (sizeof(struct disk_child)) +#define dc_block_number(dc_p) (le32_to_cpu((dc_p)->dc_block_number)) +#define dc_size(dc_p) (le16_to_cpu((dc_p)->dc_size)) +#define put_dc_block_number(dc_p, val) do { (dc_p)->dc_block_number = cpu_to_le32(val); } while(0) +#define put_dc_size(dc_p, val) do { (dc_p)->dc_size = cpu_to_le16(val); } while(0) + +/* Get disk child by buffer header and position in the tree node. */ +#define B_N_CHILD(p_s_bh,n_pos) ((struct disk_child *)\ +((p_s_bh)->b_data+BLKH_SIZE+B_NR_ITEMS(p_s_bh)*KEY_SIZE+DC_SIZE*(n_pos))) + +/* Get disk child number by buffer header and position in the tree node. */ +#define B_N_CHILD_NUM(p_s_bh,n_pos) (dc_block_number(B_N_CHILD(p_s_bh,n_pos))) +#define PUT_B_N_CHILD_NUM(p_s_bh,n_pos, val) (put_dc_block_number(B_N_CHILD(p_s_bh,n_pos), val )) + + /* maximal value of field child_size in structure disk_child */ + /* child size is the combined size of all items and their headers */ +#define MAX_CHILD_SIZE(bh) ((int)( (bh)->b_size - BLKH_SIZE )) + +/* amount of used space in buffer (not including block head) */ +#define B_CHILD_SIZE(cur) (MAX_CHILD_SIZE(cur)-(B_FREE_SPACE(cur))) + +/* max and min number of keys in internal node */ +#define MAX_NR_KEY(bh) ( (MAX_CHILD_SIZE(bh)-DC_SIZE)/(KEY_SIZE+DC_SIZE) ) +#define MIN_NR_KEY(bh) (MAX_NR_KEY(bh)/2) + +/***************************************************************************/ +/* PATH STRUCTURES AND DEFINES */ +/***************************************************************************/ + + +/* Search_by_key fills up the path from the root to the leaf as it descends the tree looking for the + key. It uses reiserfs_bread to try to find buffers in the cache given their block number. If it + does not find them in the cache it reads them from disk. For each node search_by_key finds using + reiserfs_bread it then uses bin_search to look through that node. bin_search will find the + position of the block_number of the next node if it is looking through an internal node. If it + is looking through a leaf node bin_search will find the position of the item which has key either + equal to given key, or which is the maximal key less than the given key. */ + +struct path_element { + struct buffer_head * pe_buffer; /* Pointer to the buffer at the path in the tree. */ + int pe_position; /* Position in the tree node which is placed in the */ + /* buffer above. */ +}; + +#define MAX_HEIGHT 5 /* maximal height of a tree. don't change this without changing JOURNAL_PER_BALANCE_CNT */ +#define EXTENDED_MAX_HEIGHT 7 /* Must be equals MAX_HEIGHT + FIRST_PATH_ELEMENT_OFFSET */ +#define FIRST_PATH_ELEMENT_OFFSET 2 /* Must be equal to at least 2. */ + +#define ILLEGAL_PATH_ELEMENT_OFFSET 1 /* Must be equal to FIRST_PATH_ELEMENT_OFFSET - 1 */ +#define MAX_FEB_SIZE 6 /* this MUST be MAX_HEIGHT + 1. See about FEB below */ + + + +/* We need to keep track of who the ancestors of nodes are. When we + perform a search we record which nodes were visited while + descending the tree looking for the node we searched for. This list + of nodes is called the path. This information is used while + performing balancing. Note that this path information may become + invalid, and this means we must check it when using it to see if it + is still valid. You'll need to read search_by_key and the comments + in it, especially about decrement_counters_in_path(), to understand + this structure. + +Paths make the code so much harder to work with and debug.... An +enormous number of bugs are due to them, and trying to write or modify +code that uses them just makes my head hurt. They are based on an +excessive effort to avoid disturbing the precious VFS code.:-( The +gods only know how we are going to SMP the code that uses them. +znodes are the way! */ + + +struct path { + int path_length; /* Length of the array above. */ + struct path_element path_elements[EXTENDED_MAX_HEIGHT]; /* Array of the path elements. */ + int pos_in_item; +}; + +#define pos_in_item(path) ((path)->pos_in_item) + +#define INITIALIZE_PATH(var) \ +struct path var = {ILLEGAL_PATH_ELEMENT_OFFSET, } + +/* Get path element by path and path position. */ +#define PATH_OFFSET_PELEMENT(p_s_path,n_offset) ((p_s_path)->path_elements +(n_offset)) + +/* Get buffer header at the path by path and path position. */ +#define PATH_OFFSET_PBUFFER(p_s_path,n_offset) (PATH_OFFSET_PELEMENT(p_s_path,n_offset)->pe_buffer) + +/* Get position in the element at the path by path and path position. */ +#define PATH_OFFSET_POSITION(p_s_path,n_offset) (PATH_OFFSET_PELEMENT(p_s_path,n_offset)->pe_position) + + +#define PATH_PLAST_BUFFER(p_s_path) (PATH_OFFSET_PBUFFER((p_s_path), (p_s_path)->path_length)) + /* you know, to the person who didn't + write this the macro name does not + at first suggest what it does. + Maybe POSITION_FROM_PATH_END? Or + maybe we should just focus on + dumping paths... -Hans */ +#define PATH_LAST_POSITION(p_s_path) (PATH_OFFSET_POSITION((p_s_path), (p_s_path)->path_length)) + + +#define PATH_PITEM_HEAD(p_s_path) B_N_PITEM_HEAD(PATH_PLAST_BUFFER(p_s_path),PATH_LAST_POSITION(p_s_path)) + +/* in do_balance leaf has h == 0 in contrast with path structure, + where root has level == 0. That is why we need these defines */ +#define PATH_H_PBUFFER(p_s_path, h) PATH_OFFSET_PBUFFER (p_s_path, p_s_path->path_length - (h)) /* tb->S[h] */ +#define PATH_H_PPARENT(path, h) PATH_H_PBUFFER (path, (h) + 1) /* tb->F[h] or tb->S[0]->b_parent */ +#define PATH_H_POSITION(path, h) PATH_OFFSET_POSITION (path, path->path_length - (h)) +#define PATH_H_B_ITEM_ORDER(path, h) PATH_H_POSITION(path, h + 1) /* tb->S[h]->b_item_order */ + +#define PATH_H_PATH_OFFSET(p_s_path, n_h) ((p_s_path)->path_length - (n_h)) + +#define get_last_bh(path) PATH_PLAST_BUFFER(path) +#define get_ih(path) PATH_PITEM_HEAD(path) +#define get_item_pos(path) PATH_LAST_POSITION(path) +#define get_item(path) ((void *)B_N_PITEM(PATH_PLAST_BUFFER(path), PATH_LAST_POSITION (path))) +#define item_moved(ih,path) comp_items(ih, path) +#define path_changed(ih,path) comp_items (ih, path) + + +/***************************************************************************/ +/* MISC */ +/***************************************************************************/ + +/* Size of pointer to the unformatted node. */ +#define UNFM_P_SIZE (sizeof(unp_t)) + +// in in-core inode key is stored on le form +#define INODE_PKEY(inode) ((struct key *)((inode)->u.reiserfs_i.i_key)) + +#define MAX_UL_INT 0xffffffff +#define MAX_INT 0x7ffffff +#define MAX_US_INT 0xffff + +// reiserfs version 2 has max offset 60 bits. Version 1 - 32 bit offset +#define U32_MAX (~(__u32)0) + +static inline loff_t max_reiserfs_offset (const struct inode * inode) +{ + if (get_inode_item_key_version(inode) == KEY_FORMAT_3_5) + return (loff_t)U32_MAX; + + return (loff_t)((~(__u64)0) >> 4); +} + + +/*#define MAX_KEY_UNIQUENESS MAX_UL_INT*/ +#define MAX_KEY_OBJECTID MAX_UL_INT + + +#define MAX_B_NUM MAX_UL_INT +#define MAX_FC_NUM MAX_US_INT + + +/* the purpose is to detect overflow of an unsigned short */ +#define REISERFS_LINK_MAX (MAX_US_INT - 1000) + + +/* The following defines are used in reiserfs_insert_item and reiserfs_append_item */ +#define REISERFS_KERNEL_MEM 0 /* reiserfs kernel memory mode */ +#define REISERFS_USER_MEM 1 /* reiserfs user memory mode */ + +#define fs_generation(s) ((s)->u.reiserfs_sb.s_generation_counter) +#define get_generation(s) atomic_read (&fs_generation(s)) +#define FILESYSTEM_CHANGED_TB(tb) (get_generation((tb)->tb_sb) != (tb)->fs_gen) +#define fs_changed(gen,s) (gen != get_generation (s)) + + +/***************************************************************************/ +/* FIXATE NODES */ +/***************************************************************************/ + +#define VI_TYPE_LEFT_MERGEABLE 1 +#define VI_TYPE_RIGHT_MERGEABLE 2 + +/* To make any changes in the tree we always first find node, that + contains item to be changed/deleted or place to insert a new + item. We call this node S. To do balancing we need to decide what + we will shift to left/right neighbor, or to a new node, where new + item will be etc. To make this analysis simpler we build virtual + node. Virtual node is an array of items, that will replace items of + node S. (For instance if we are going to delete an item, virtual + node does not contain it). Virtual node keeps information about + item sizes and types, mergeability of first and last items, sizes + of all entries in directory item. We use this array of items when + calculating what we can shift to neighbors and how many nodes we + have to have if we do not any shiftings, if we shift to left/right + neighbor or to both. */ +struct virtual_item +{ + int vi_index; // index in the array of item operations + unsigned short vi_type; // left/right mergeability + unsigned short vi_item_len; /* length of item that it will have after balancing */ + struct item_head * vi_ih; + const char * vi_item; // body of item (old or new) + const void * vi_new_data; // 0 always but paste mode + void * vi_uarea; // item specific area +}; + + +struct virtual_node +{ + char * vn_free_ptr; /* this is a pointer to the free space in the buffer */ + unsigned short vn_nr_item; /* number of items in virtual node */ + short vn_size; /* size of node , that node would have if it has unlimited size and no balancing is performed */ + short vn_mode; /* mode of balancing (paste, insert, delete, cut) */ + short vn_affected_item_num; + short vn_pos_in_item; + struct item_head * vn_ins_ih; /* item header of inserted item, 0 for other modes */ + const void * vn_data; + struct virtual_item * vn_vi; /* array of items (including a new one, excluding item to be deleted) */ +}; + + +/***************************************************************************/ +/* TREE BALANCE */ +/***************************************************************************/ + +/* This temporary structure is used in tree balance algorithms, and + constructed as we go to the extent that its various parts are + needed. It contains arrays of nodes that can potentially be + involved in the balancing of node S, and parameters that define how + each of the nodes must be balanced. Note that in these algorithms + for balancing the worst case is to need to balance the current node + S and the left and right neighbors and all of their parents plus + create a new node. We implement S1 balancing for the leaf nodes + and S0 balancing for the internal nodes (S1 and S0 are defined in + our papers.)*/ + +#define MAX_FREE_BLOCK 7 /* size of the array of buffers to free at end of do_balance */ + +/* maximum number of FEB blocknrs on a single level */ +#define MAX_AMOUNT_NEEDED 2 + +/* someday somebody will prefix every field in this struct with tb_ */ +struct tree_balance +{ + int tb_mode; + int need_balance_dirty; + struct super_block * tb_sb; + struct reiserfs_transaction_handle *transaction_handle ; + struct path * tb_path; + struct buffer_head * L[MAX_HEIGHT]; /* array of left neighbors of nodes in the path */ + struct buffer_head * R[MAX_HEIGHT]; /* array of right neighbors of nodes in the path*/ + struct buffer_head * FL[MAX_HEIGHT]; /* array of fathers of the left neighbors */ + struct buffer_head * FR[MAX_HEIGHT]; /* array of fathers of the right neighbors */ + struct buffer_head * CFL[MAX_HEIGHT]; /* array of common parents of center node and its left neighbor */ + struct buffer_head * CFR[MAX_HEIGHT]; /* array of common parents of center node and its right neighbor */ + + struct buffer_head * FEB[MAX_FEB_SIZE]; /* array of empty buffers. Number of buffers in array equals + cur_blknum. */ + struct buffer_head * used[MAX_FEB_SIZE]; + struct buffer_head * thrown[MAX_FEB_SIZE]; + int lnum[MAX_HEIGHT]; /* array of number of items which must be + shifted to the left in order to balance the + current node; for leaves includes item that + will be partially shifted; for internal + nodes, it is the number of child pointers + rather than items. It includes the new item + being created. The code sometimes subtracts + one to get the number of wholly shifted + items for other purposes. */ + int rnum[MAX_HEIGHT]; /* substitute right for left in comment above */ + int lkey[MAX_HEIGHT]; /* array indexed by height h mapping the key delimiting L[h] and + S[h] to its item number within the node CFL[h] */ + int rkey[MAX_HEIGHT]; /* substitute r for l in comment above */ + int insert_size[MAX_HEIGHT]; /* the number of bytes by we are trying to add or remove from + S[h]. A negative value means removing. */ + int blknum[MAX_HEIGHT]; /* number of nodes that will replace node S[h] after + balancing on the level h of the tree. If 0 then S is + being deleted, if 1 then S is remaining and no new nodes + are being created, if 2 or 3 then 1 or 2 new nodes is + being created */ + + /* fields that are used only for balancing leaves of the tree */ + int cur_blknum; /* number of empty blocks having been already allocated */ + int s0num; /* number of items that fall into left most node when S[0] splits */ + int s1num; /* number of items that fall into first new node when S[0] splits */ + int s2num; /* number of items that fall into second new node when S[0] splits */ + int lbytes; /* number of bytes which can flow to the left neighbor from the left */ + /* most liquid item that cannot be shifted from S[0] entirely */ + /* if -1 then nothing will be partially shifted */ + int rbytes; /* number of bytes which will flow to the right neighbor from the right */ + /* most liquid item that cannot be shifted from S[0] entirely */ + /* if -1 then nothing will be partially shifted */ + int s1bytes; /* number of bytes which flow to the first new node when S[0] splits */ + /* note: if S[0] splits into 3 nodes, then items do not need to be cut */ + int s2bytes; + struct buffer_head * buf_to_free[MAX_FREE_BLOCK]; /* buffers which are to be freed after do_balance finishes by unfix_nodes */ + char * vn_buf; /* kmalloced memory. Used to create + virtual node and keep map of + dirtied bitmap blocks */ + int vn_buf_size; /* size of the vn_buf */ + struct virtual_node * tb_vn; /* VN starts after bitmap of bitmap blocks */ + + int fs_gen; /* saved value of `reiserfs_generation' counter + see FILESYSTEM_CHANGED() macro in reiserfs_fs.h */ +} ; + +/* These are modes of balancing */ + +/* When inserting an item. */ +#define M_INSERT 'i' +/* When inserting into (directories only) or appending onto an already + existant item. */ +#define M_PASTE 'p' +/* When deleting an item. */ +#define M_DELETE 'd' +/* When truncating an item or removing an entry from a (directory) item. */ +#define M_CUT 'c' + +/* used when balancing on leaf level skipped (in reiserfsck) */ +#define M_INTERNAL 'n' + +/* When further balancing is not needed, then do_balance does not need + to be called. */ +#define M_SKIP_BALANCING 's' +#define M_CONVERT 'v' + +/* modes of leaf_move_items */ +#define LEAF_FROM_S_TO_L 0 +#define LEAF_FROM_S_TO_R 1 +#define LEAF_FROM_R_TO_L 2 +#define LEAF_FROM_L_TO_R 3 +#define LEAF_FROM_S_TO_SNEW 4 + +#define FIRST_TO_LAST 0 +#define LAST_TO_FIRST 1 + +/* used in do_balance for passing parent of node information that has + been gotten from tb struct */ +struct buffer_info { + struct tree_balance * tb; + struct buffer_head * bi_bh; + struct buffer_head * bi_parent; + int bi_position; +}; + + +/* there are 4 types of items: stat data, directory item, indirect, direct. ++-------------------+------------+--------------+------------+ +| | k_offset | k_uniqueness | mergeable? | ++-------------------+------------+--------------+------------+ +| stat data | 0 | 0 | no | ++-------------------+------------+--------------+------------+ +| 1st directory item| DOT_OFFSET |DIRENTRY_UNIQUENESS| no | +| non 1st directory | hash value | | yes | +| item | | | | ++-------------------+------------+--------------+------------+ +| indirect item | offset + 1 |TYPE_INDIRECT | if this is not the first indirect item of the object ++-------------------+------------+--------------+------------+ +| direct item | offset + 1 |TYPE_DIRECT | if not this is not the first direct item of the object ++-------------------+------------+--------------+------------+ +*/ + +struct item_operations { + int (*bytes_number) (struct item_head * ih, int block_size); + void (*decrement_key) (struct cpu_key *); + int (*is_left_mergeable) (struct key * ih, unsigned long bsize); + void (*print_item) (struct item_head *, char * item); + void (*check_item) (struct item_head *, char * item); + + int (*create_vi) (struct virtual_node * vn, struct virtual_item * vi, + int is_affected, int insert_size); + int (*check_left) (struct virtual_item * vi, int free, + int start_skip, int end_skip); + int (*check_right) (struct virtual_item * vi, int free); + int (*part_size) (struct virtual_item * vi, int from, int to); + int (*unit_num) (struct virtual_item * vi); + void (*print_vi) (struct virtual_item * vi); +}; + + +extern struct item_operations stat_data_ops, indirect_ops, direct_ops, + direntry_ops; +extern struct item_operations * item_ops [4]; + +#define op_bytes_number(ih,bsize) item_ops[le_ih_k_type (ih)]->bytes_number (ih, bsize) +#define op_is_left_mergeable(key,bsize) item_ops[le_key_k_type (le_key_version (key), key)]->is_left_mergeable (key, bsize) +#define op_print_item(ih,item) item_ops[le_ih_k_type (ih)]->print_item (ih, item) +#define op_check_item(ih,item) item_ops[le_ih_k_type (ih)]->check_item (ih, item) +#define op_create_vi(vn,vi,is_affected,insert_size) item_ops[le_ih_k_type ((vi)->vi_ih)]->create_vi (vn,vi,is_affected,insert_size) +#define op_check_left(vi,free,start_skip,end_skip) item_ops[(vi)->vi_index]->check_left (vi, free, start_skip, end_skip) +#define op_check_right(vi,free) item_ops[(vi)->vi_index]->check_right (vi, free) +#define op_part_size(vi,from,to) item_ops[(vi)->vi_index]->part_size (vi, from, to) +#define op_unit_num(vi) item_ops[(vi)->vi_index]->unit_num (vi) +#define op_print_vi(vi) item_ops[(vi)->vi_index]->print_vi (vi) + + + + + +#define COMP_KEYS comp_keys +#define COMP_SHORT_KEYS comp_short_keys +/*#define keys_of_same_object comp_short_keys*/ + +/* number of blocks pointed to by the indirect item */ +#define I_UNFM_NUM(p_s_ih) ( ih_item_len(p_s_ih) / UNFM_P_SIZE ) + +/* the used space within the unformatted node corresponding to pos within the item pointed to by ih */ +#define I_POS_UNFM_SIZE(ih,pos,size) (((pos) == I_UNFM_NUM(ih) - 1 ) ? (size) - ih_free_space(ih) : (size)) + +/* number of bytes contained by the direct item or the unformatted nodes the indirect item points to */ + + +/* get the item header */ +#define B_N_PITEM_HEAD(bh,item_num) ( (struct item_head * )((bh)->b_data + BLKH_SIZE) + (item_num) ) + +/* get key */ +#define B_N_PDELIM_KEY(bh,item_num) ( (struct key * )((bh)->b_data + BLKH_SIZE) + (item_num) ) + +/* get the key */ +#define B_N_PKEY(bh,item_num) ( &(B_N_PITEM_HEAD(bh,item_num)->ih_key) ) + +/* get item body */ +#define B_N_PITEM(bh,item_num) ( (bh)->b_data + ih_location(B_N_PITEM_HEAD((bh),(item_num)))) + +/* get the stat data by the buffer header and the item order */ +#define B_N_STAT_DATA(bh,nr) \ +( (struct stat_data *)((bh)->b_data + ih_location(B_N_PITEM_HEAD((bh),(nr))) ) ) + + /* following defines use reiserfs buffer header and item header */ + +/* get stat-data */ +#define B_I_STAT_DATA(bh, ih) ( (struct stat_data * )((bh)->b_data + ih_location(ih)) ) + +// this is 3976 for size==4096 +#define MAX_DIRECT_ITEM_LEN(size) ((size) - BLKH_SIZE - 2*IH_SIZE - SD_SIZE - UNFM_P_SIZE) + +/* indirect items consist of entries which contain blocknrs, pos + indicates which entry, and B_I_POS_UNFM_POINTER resolves to the + blocknr contained by the entry pos points to */ +#define B_I_POS_UNFM_POINTER(bh,ih,pos) le32_to_cpu(*(((unp_t *)B_I_PITEM(bh,ih)) + (pos))) +#define PUT_B_I_POS_UNFM_POINTER(bh,ih,pos, val) do {*(((unp_t *)B_I_PITEM(bh,ih)) + (pos)) = cpu_to_le32(val); } while (0) + +struct reiserfs_iget4_args { + __u32 objectid ; +} ; + +/***************************************************************************/ +/* FUNCTION DECLARATIONS */ +/***************************************************************************/ + +/*#ifdef __KERNEL__*/ + +/* journal.c see journal.c for all the comments here */ + +#define JOURNAL_TRANS_HALF 1018 /* must be correct to keep the desc and commit structs at 4k */ + + +/* first block written in a commit. */ +struct reiserfs_journal_desc { + __u32 j_trans_id ; /* id of commit */ + __u32 j_len ; /* length of commit. len +1 is the commit block */ + __u32 j_mount_id ; /* mount id of this trans*/ + __u32 j_realblock[JOURNAL_TRANS_HALF] ; /* real locations for each block */ + char j_magic[12] ; +} ; + +/* last block written in a commit */ +struct reiserfs_journal_commit { + __u32 j_trans_id ; /* must match j_trans_id from the desc block */ + __u32 j_len ; /* ditto */ + __u32 j_realblock[JOURNAL_TRANS_HALF] ; /* real locations for each block */ + char j_digest[16] ; /* md5 sum of all the blocks involved, including desc and commit. not used, kill it */ +} ; + +/* this header block gets written whenever a transaction is considered fully flushed, and is more recent than the +** last fully flushed transaction. fully flushed means all the log blocks and all the real blocks are on disk, +** and this transaction does not need to be replayed. +*/ +struct reiserfs_journal_header { + __u32 j_last_flush_trans_id ; /* id of last fully flushed transaction */ + __u32 j_first_unflushed_offset ; /* offset in the log of where to start replay after a crash */ + __u32 j_mount_id ; +} ; + +extern task_queue reiserfs_commit_thread_tq ; +extern wait_queue_head_t reiserfs_commit_thread_wait ; + +/* biggest tunable defines are right here */ +#define JOURNAL_BLOCK_COUNT 8192 /* number of blocks in the journal */ +#define JOURNAL_MAX_BATCH 900 /* max blocks to batch into one transaction, don't make this any bigger than 900 */ +#define JOURNAL_MAX_COMMIT_AGE 30 +#define JOURNAL_MAX_TRANS_AGE 30 +#define JOURNAL_PER_BALANCE_CNT (3 * (MAX_HEIGHT-2) + 9) + +/* both of these can be as low as 1, or as high as you want. The min is the +** number of 4k bitmap nodes preallocated on mount. New nodes are allocated +** as needed, and released when transactions are committed. On release, if +** the current number of nodes is > max, the node is freed, otherwise, +** it is put on a free list for faster use later. +*/ +#define REISERFS_MIN_BITMAP_NODES 10 +#define REISERFS_MAX_BITMAP_NODES 100 + +#define JBH_HASH_SHIFT 13 /* these are based on journal hash size of 8192 */ +#define JBH_HASH_MASK 8191 + +/* After several hours of tedious analysis, the following hash + * function won. Do not mess with it... -DaveM + */ +#define _jhashfn(dev,block) \ + ((((dev)<<(JBH_HASH_SHIFT - 6)) ^ ((dev)<<(JBH_HASH_SHIFT - 9))) ^ \ + (((block)<<(JBH_HASH_SHIFT - 6)) ^ ((block) >> 13) ^ ((block) << (JBH_HASH_SHIFT - 12)))) +#define journal_hash(t,dev,block) ((t)[_jhashfn((dev),(block)) & JBH_HASH_MASK]) + +/* finds n'th buffer with 0 being the start of this commit. Needs to go away, j_ap_blocks has changed +** since I created this. One chunk of code in journal.c needs changing before deleting it +*/ +#define JOURNAL_BUFFER(j,n) ((j)->j_ap_blocks[((j)->j_start + (n)) % JOURNAL_BLOCK_COUNT]) + +void reiserfs_commit_for_inode(struct inode *) ; +void reiserfs_update_inode_transaction(struct inode *) ; +void reiserfs_wait_on_write_block(struct super_block *s) ; +void reiserfs_block_writes(struct reiserfs_transaction_handle *th) ; +void reiserfs_allow_writes(struct super_block *s) ; +void reiserfs_check_lock_depth(char *caller) ; +void reiserfs_prepare_for_journal(struct super_block *, struct buffer_head *bh, int wait) ; +void reiserfs_restore_prepared_buffer(struct super_block *, struct buffer_head *bh) ; +int journal_init(struct super_block *) ; +int journal_release(struct reiserfs_transaction_handle*, struct super_block *) ; +int journal_release_error(struct reiserfs_transaction_handle*, struct super_block *) ; +int journal_end(struct reiserfs_transaction_handle *, struct super_block *, unsigned long) ; +int journal_end_sync(struct reiserfs_transaction_handle *, struct super_block *, unsigned long) ; +int journal_mark_dirty_nolog(struct reiserfs_transaction_handle *, struct super_block *, struct buffer_head *bh) ; +int journal_mark_freed(struct reiserfs_transaction_handle *, struct super_block *, unsigned long blocknr) ; +int push_journal_writer(char *w) ; +int pop_journal_writer(int windex) ; +int journal_transaction_should_end(struct reiserfs_transaction_handle *, int) ; +int reiserfs_in_journal(struct super_block *p_s_sb, kdev_t dev, unsigned long bl, int size, int searchall, unsigned long *next) ; +int journal_begin(struct reiserfs_transaction_handle *, struct super_block *p_s_sb, unsigned long) ; +struct super_block *reiserfs_get_super(kdev_t dev) ; +void flush_async_commits(struct super_block *p_s_sb) ; + +int buffer_journaled(const struct buffer_head *bh) ; +int mark_buffer_journal_new(struct buffer_head *bh) ; +int reiserfs_sync_all_buffers(kdev_t dev, int wait) ; +int reiserfs_sync_buffers(kdev_t dev, int wait) ; +int reiserfs_add_page_to_flush_list(struct reiserfs_transaction_handle *, + struct inode *, struct buffer_head *) ; +int reiserfs_remove_page_from_flush_list(struct reiserfs_transaction_handle *, + struct inode *) ; + +int reiserfs_allocate_list_bitmaps(struct super_block *s, struct reiserfs_list_bitmap *, int) ; + + /* why is this kerplunked right here? */ +static inline int reiserfs_buffer_prepared(const struct buffer_head *bh) { + if (bh && test_bit(BH_JPrepared, ( struct buffer_head * ) &bh->b_state)) + return 1 ; + else + return 0 ; +} + +/* buffer was journaled, waiting to get to disk */ +static inline int buffer_journal_dirty(const struct buffer_head *bh) { + if (bh) + return test_bit(BH_JDirty_wait, ( struct buffer_head * ) &bh->b_state) ; + else + return 0 ; +} +static inline int mark_buffer_notjournal_dirty(struct buffer_head *bh) { + if (bh) + clear_bit(BH_JDirty_wait, &bh->b_state) ; + return 0 ; +} +static inline int mark_buffer_notjournal_new(struct buffer_head *bh) { + if (bh) { + clear_bit(BH_JNew, &bh->b_state) ; + } + return 0 ; +} + +void add_save_link (struct reiserfs_transaction_handle * th, + struct inode * inode, int truncate); +void remove_save_link (struct inode * inode, int truncate); + +/* objectid.c */ +__u32 reiserfs_get_unused_objectid (struct reiserfs_transaction_handle *th); +void reiserfs_release_objectid (struct reiserfs_transaction_handle *th, __u32 objectid_to_release); +int reiserfs_convert_objectid_map_v1(struct super_block *) ; + +/* stree.c */ +int B_IS_IN_TREE(const struct buffer_head *); +extern inline void copy_short_key (void * to, const void * from); +extern inline void copy_item_head(struct item_head * p_v_to, + const struct item_head * p_v_from); + +// first key is in cpu form, second - le +extern inline int comp_keys (const struct key * le_key, + const struct cpu_key * cpu_key); +extern inline int comp_short_keys (const struct key * le_key, + const struct cpu_key * cpu_key); +extern inline void le_key2cpu_key (struct cpu_key * to, const struct key * from); + +// both are cpu keys +extern inline int comp_cpu_keys (const struct cpu_key *, const struct cpu_key *); +extern inline int comp_short_cpu_keys (const struct cpu_key *, + const struct cpu_key *); +extern inline void cpu_key2cpu_key (struct cpu_key *, const struct cpu_key *); + +// both are in le form +extern inline int comp_le_keys (const struct key *, const struct key *); +extern inline int comp_short_le_keys (const struct key *, const struct key *); + +// +// get key version from on disk key - kludge +// +static inline int le_key_version (const struct key * key) +{ + int type; + + type = offset_v2_k_type( &(key->u.k_offset_v2)); + if (type != TYPE_DIRECT && type != TYPE_INDIRECT && type != TYPE_DIRENTRY) + return KEY_FORMAT_3_5; + + return KEY_FORMAT_3_6; + +} + + +static inline void copy_key (struct key *to, const struct key *from) +{ + memcpy (to, from, KEY_SIZE); +} + + +int comp_items (const struct item_head * stored_ih, const struct path * p_s_path); +const struct key * get_rkey (const struct path * p_s_chk_path, + const struct super_block * p_s_sb); +inline int bin_search (const void * p_v_key, const void * p_v_base, + int p_n_num, int p_n_width, int * p_n_pos); +int search_by_key (struct super_block *, const struct cpu_key *, + struct path *, int); +#define search_item(s,key,path) search_by_key (s, key, path, DISK_LEAF_NODE_LEVEL) +int search_for_position_by_key (struct super_block * p_s_sb, + const struct cpu_key * p_s_cpu_key, + struct path * p_s_search_path); +extern inline void decrement_bcount (struct buffer_head * p_s_bh); +void decrement_counters_in_path (struct path * p_s_search_path); +void pathrelse (struct path * p_s_search_path); +int reiserfs_check_path(struct path *p) ; +void pathrelse_and_restore (struct super_block *s, struct path * p_s_search_path); + +int reiserfs_insert_item (struct reiserfs_transaction_handle *th, + struct path * path, + const struct cpu_key * key, + struct item_head * ih, const char * body); + +int reiserfs_paste_into_item (struct reiserfs_transaction_handle *th, + struct path * path, + const struct cpu_key * key, + const char * body, int paste_size); + +int reiserfs_cut_from_item (struct reiserfs_transaction_handle *th, + struct path * path, + struct cpu_key * key, + struct inode * inode, + struct page *page, + loff_t new_file_size); + +int reiserfs_delete_item (struct reiserfs_transaction_handle *th, + struct path * path, + const struct cpu_key * key, + struct inode * inode, + struct buffer_head * p_s_un_bh); + +void reiserfs_delete_solid_item (struct reiserfs_transaction_handle *th, + struct key * key); +void reiserfs_delete_object (struct reiserfs_transaction_handle *th, struct inode * p_s_inode); +void reiserfs_do_truncate (struct reiserfs_transaction_handle *th, + struct inode * p_s_inode, struct page *, + int update_timestamps); + +#define block_size(inode) ((inode)->i_sb->s_blocksize) +#define file_size(inode) ((inode)->i_size) +#define tail_size(inode) (file_size (inode) & (block_size (inode) - 1)) + +#define tail_has_to_be_packed(inode) (!dont_have_tails ((inode)->i_sb) &&\ +!STORE_TAIL_IN_UNFM(file_size (inode), tail_size(inode), block_size (inode))) + +void padd_item (char * item, int total_length, int length); + +/* inode.c */ + +void reiserfs_read_inode (struct inode * inode) ; +void reiserfs_read_inode2(struct inode * inode, void *p) ; +void reiserfs_delete_inode (struct inode * inode); +void reiserfs_write_inode (struct inode * inode, int) ; +struct dentry *reiserfs_fh_to_dentry(struct super_block *sb, __u32 *data, + int len, int fhtype, int parent); +int reiserfs_dentry_to_fh(struct dentry *dentry, __u32 *data, int *lenp, int need_parent); + +int reiserfs_prepare_write(struct file *, struct page *, unsigned, unsigned) ; +void reiserfs_truncate_file(struct inode *, int update_timestamps) ; +void make_cpu_key (struct cpu_key * cpu_key, const struct inode * inode, loff_t offset, + int type, int key_length); +void make_le_item_head (struct item_head * ih, const struct cpu_key * key, + int version, + loff_t offset, int type, int length, int entry_count); +struct inode * reiserfs_iget (struct super_block * s, + const struct cpu_key * key); + + +struct inode * reiserfs_new_inode (struct reiserfs_transaction_handle *th, + const struct inode * dir, int mode, + const char * symname, int item_len, + struct dentry *dentry, struct inode *inode, int * err); +int reiserfs_sync_inode (struct reiserfs_transaction_handle *th, struct inode * inode); +void reiserfs_update_sd (struct reiserfs_transaction_handle *th, struct inode * inode); + +void sd_attrs_to_i_attrs( __u16 sd_attrs, struct inode *inode ); +void i_attrs_to_sd_attrs( struct inode *inode, __u16 *sd_attrs ); + +/* namei.c */ +inline void set_de_name_and_namelen (struct reiserfs_dir_entry * de); +int search_by_entry_key (struct super_block * sb, const struct cpu_key * key, + struct path * path, + struct reiserfs_dir_entry * de); +/* procfs.c */ + +#if defined( CONFIG_PROC_FS ) && defined( CONFIG_REISERFS_PROC_INFO ) +#define REISERFS_PROC_INFO +#else +#undef REISERFS_PROC_INFO +#endif + +int reiserfs_proc_info_init( struct super_block *sb ); +int reiserfs_proc_info_done( struct super_block *sb ); +struct proc_dir_entry *reiserfs_proc_register( struct super_block *sb, + char *name, read_proc_t *func ); +void reiserfs_proc_unregister( struct super_block *sb, const char *name ); +struct proc_dir_entry *reiserfs_proc_register_global( char *name, + read_proc_t *func ); +void reiserfs_proc_unregister_global( const char *name ); +int reiserfs_proc_info_global_init( void ); +int reiserfs_proc_info_global_done( void ); +int reiserfs_proc_tail( int len, char *buffer, char **start, + off_t offset, int count, int *eof ); +int reiserfs_global_version_in_proc( char *buffer, char **start, off_t offset, + int count, int *eof, void *data ); +int reiserfs_version_in_proc( char *buffer, char **start, off_t offset, + int count, int *eof, void *data ); +int reiserfs_super_in_proc( char *buffer, char **start, off_t offset, + int count, int *eof, void *data ); +int reiserfs_per_level_in_proc( char *buffer, char **start, off_t offset, + int count, int *eof, void *data ); +int reiserfs_bitmap_in_proc( char *buffer, char **start, off_t offset, + int count, int *eof, void *data ); +int reiserfs_on_disk_super_in_proc( char *buffer, char **start, off_t offset, + int count, int *eof, void *data ); +int reiserfs_oidmap_in_proc( char *buffer, char **start, off_t offset, + int count, int *eof, void *data ); +int reiserfs_journal_in_proc( char *buffer, char **start, off_t offset, + int count, int *eof, void *data ); + +#if defined( REISERFS_PROC_INFO ) + +#define PROC_EXP( e ) e + +#define MAX( a, b ) ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) ) +#define __PINFO( sb ) ( sb ) -> u.reiserfs_sb.s_proc_info_data +#define PROC_INFO_MAX( sb, field, value ) \ + __PINFO( sb ).field = \ + MAX( ( sb ) -> u.reiserfs_sb.s_proc_info_data.field, value ) +#define PROC_INFO_INC( sb, field ) ( ++ ( __PINFO( sb ).field ) ) +#define PROC_INFO_ADD( sb, field, val ) ( __PINFO( sb ).field += ( val ) ) +#define PROC_INFO_BH_STAT( sb, bh, level ) \ + PROC_INFO_INC( sb, sbk_read_at[ ( level ) ] ); \ + PROC_INFO_ADD( sb, free_at[ ( level ) ], B_FREE_SPACE( bh ) ); \ + PROC_INFO_ADD( sb, items_at[ ( level ) ], B_NR_ITEMS( bh ) ) +#else +#define PROC_EXP( e ) +#define VOID_V ( ( void ) 0 ) +#define PROC_INFO_MAX( sb, field, value ) VOID_V +#define PROC_INFO_INC( sb, field ) VOID_V +#define PROC_INFO_ADD( sb, field, val ) VOID_V +#define PROC_INFO_BH_STAT( p_s_sb, p_s_bh, n_node_level ) VOID_V +#endif + +/* dir.c */ +extern struct inode_operations reiserfs_dir_inode_operations; +extern struct file_operations reiserfs_dir_operations; + +/* tail_conversion.c */ +int direct2indirect (struct reiserfs_transaction_handle *, struct inode *, struct path *, struct buffer_head *, loff_t); +int indirect2direct (struct reiserfs_transaction_handle *, struct inode *, struct page *, struct path *, const struct cpu_key *, loff_t, char *); +void reiserfs_unmap_buffer(struct buffer_head *) ; + + +/* file.c */ +extern struct inode_operations reiserfs_file_inode_operations; +extern struct file_operations reiserfs_file_operations; +extern struct address_space_operations reiserfs_address_space_operations ; +int get_new_buffer (struct reiserfs_transaction_handle *th, struct buffer_head *, + struct buffer_head **, struct path *); + + +/* buffer2.c */ +struct buffer_head * reiserfs_getblk (kdev_t n_dev, int n_block, int n_size); +void wait_buffer_until_released (const struct buffer_head * bh); +struct buffer_head * reiserfs_bread (struct super_block *super, int n_block, + int n_size); + +/* fix_nodes.c */ +void * reiserfs_kmalloc (size_t size, int flags, struct super_block * s); +void reiserfs_kfree (const void * vp, size_t size, struct super_block * s); +int fix_nodes (int n_op_mode, struct tree_balance * p_s_tb, + struct item_head * p_s_ins_ih, const void *); +void unfix_nodes (struct tree_balance *); +void free_buffers_in_tb (struct tree_balance * p_s_tb); + + +/* prints.c */ +void reiserfs_panic (struct super_block * s, const char * fmt, ...) +__attribute__ ( ( noreturn ) );/* __attribute__( ( format ( printf, 2, 3 ) ) ) */ +void reiserfs_debug (struct super_block *s, int level, const char * fmt, ...); +/* __attribute__( ( format ( printf, 3, 4 ) ) ); */ +void print_virtual_node (struct virtual_node * vn); +void print_indirect_item (struct buffer_head * bh, int item_num); +void store_print_tb (struct tree_balance * tb); +void print_cur_tb (char * mes); +void print_de (struct reiserfs_dir_entry * de); +void print_bi (struct buffer_info * bi, char * mes); +#define PRINT_LEAF_ITEMS 1 /* print all items */ +#define PRINT_DIRECTORY_ITEMS 2 /* print directory items */ +#define PRINT_DIRECT_ITEMS 4 /* print contents of direct items */ +void print_block (struct buffer_head * bh, ...); +void print_path (struct tree_balance * tb, struct path * path); +void print_bmap (struct super_block * s, int silent); +void print_bmap_block (int i, char * data, int size, int silent); +/*void print_super_block (struct super_block * s, char * mes);*/ +void print_objectid_map (struct super_block * s); +void print_block_head (struct buffer_head * bh, char * mes); +void check_leaf (struct buffer_head * bh); +void check_internal (struct buffer_head * bh); +void print_statistics (struct super_block * s); + +/* lbalance.c */ +int leaf_move_items (int shift_mode, struct tree_balance * tb, int mov_num, int mov_bytes, struct buffer_head * Snew); +int leaf_shift_left (struct tree_balance * tb, int shift_num, int shift_bytes); +int leaf_shift_right (struct tree_balance * tb, int shift_num, int shift_bytes); +void leaf_delete_items (struct buffer_info * cur_bi, int last_first, int first, int del_num, int del_bytes); +void leaf_insert_into_buf (struct buffer_info * bi, int before, + struct item_head * inserted_item_ih, const char * inserted_item_body, int zeros_number); +void leaf_paste_in_buffer (struct buffer_info * bi, int pasted_item_num, + int pos_in_item, int paste_size, const char * body, int zeros_number); +void leaf_cut_from_buffer (struct buffer_info * bi, int cut_item_num, int pos_in_item, + int cut_size); +void leaf_paste_entries (struct buffer_head * bh, int item_num, int before, + int new_entry_count, struct reiserfs_de_head * new_dehs, const char * records, int paste_size); +/* ibalance.c */ +int balance_internal (struct tree_balance * , int, int, struct item_head * , + struct buffer_head **); + +/* do_balance.c */ +inline void do_balance_mark_leaf_dirty (struct tree_balance * tb, + struct buffer_head * bh, int flag); +#define do_balance_mark_internal_dirty do_balance_mark_leaf_dirty +#define do_balance_mark_sb_dirty do_balance_mark_leaf_dirty + +void do_balance (struct tree_balance * tb, struct item_head * ih, + const char * body, int flag); +void reiserfs_invalidate_buffer (struct tree_balance * tb, struct buffer_head * bh); + +int get_left_neighbor_position (struct tree_balance * tb, int h); +int get_right_neighbor_position (struct tree_balance * tb, int h); +void replace_key (struct tree_balance * tb, struct buffer_head *, int, struct buffer_head *, int); +void replace_lkey (struct tree_balance *, int, struct item_head *); +void replace_rkey (struct tree_balance *, int, struct item_head *); +void make_empty_node (struct buffer_info *); +struct buffer_head * get_FEB (struct tree_balance *); + +/* bitmap.c */ +int is_reusable (struct super_block * s, unsigned long block, int bit_value); +void reiserfs_free_block (struct reiserfs_transaction_handle *th, unsigned long); +int reiserfs_new_blocknrs (struct reiserfs_transaction_handle *th, + unsigned long * pblocknrs, unsigned long start_from, int amount_needed); +int reiserfs_new_unf_blocknrs (struct reiserfs_transaction_handle *th, + unsigned long * pblocknr, unsigned long start_from); +#ifdef REISERFS_PREALLOCATE +int reiserfs_new_unf_blocknrs2 (struct reiserfs_transaction_handle *th, + struct inode * inode, + unsigned long * pblocknr, + unsigned long start_from); + +void reiserfs_discard_prealloc (struct reiserfs_transaction_handle *th, + struct inode * inode); +void reiserfs_discard_all_prealloc (struct reiserfs_transaction_handle *th); +#endif + +/* hashes.c */ +__u32 keyed_hash (const signed char *msg, int len); +__u32 yura_hash (const signed char *msg, int len); +__u32 r5_hash (const signed char *msg, int len); + +/* version.c */ +const char *reiserfs_get_version_string(void) CONSTF; + +/* the ext2 bit routines adjust for big or little endian as +** appropriate for the arch, so in our laziness we use them rather +** than using the bit routines they call more directly. These +** routines must be used when changing on disk bitmaps. */ +#define reiserfs_test_and_set_le_bit ext2_set_bit +#define reiserfs_test_and_clear_le_bit ext2_clear_bit +#define reiserfs_test_le_bit ext2_test_bit +#define reiserfs_find_next_zero_le_bit ext2_find_next_zero_bit + +/* sometimes reiserfs_truncate may require to allocate few new blocks + to perform indirect2direct conversion. People probably used to + think, that truncate should work without problems on a filesystem + without free disk space. They may complain that they can not + truncate due to lack of free disk space. This spare space allows us + to not worry about it. 500 is probably too much, but it should be + absolutely safe */ +#define SPARE_SPACE 500 + +static inline unsigned long reiserfs_get_journal_block(const struct super_block *s) { + return le32_to_cpu(SB_DISK_SUPER_BLOCK(s)->s_journal_block) ; +} +static inline unsigned long reiserfs_get_journal_orig_size(const struct super_block *s) { + return le32_to_cpu(SB_DISK_SUPER_BLOCK(s)->s_orig_journal_size) ; +} + +/* prototypes from ioctl.c */ +int reiserfs_ioctl (struct inode * inode, struct file * filp, + unsigned int cmd, unsigned long arg); +int reiserfs_unpack (struct inode * inode, struct file * filp); + +/* ioctl's command */ +#define REISERFS_IOC_UNPACK _IOW(0xCD,1,long) +/* define following flags to be the same as in ext2, so that chattr(1), + lsattr(1) will work with us. */ +#define REISERFS_IOC_GETFLAGS EXT2_IOC_GETFLAGS +#define REISERFS_IOC_SETFLAGS EXT2_IOC_SETFLAGS +#define REISERFS_IOC_GETVERSION EXT2_IOC_GETVERSION +#define REISERFS_IOC_SETVERSION EXT2_IOC_SETVERSION + +#endif /* _LINUX_REISER_FS_H */ + +