diff options
author | dumbbell <dumbbell@FreeBSD.org> | 2005-06-18 17:10:50 +0000 |
---|---|---|
committer | dumbbell <dumbbell@FreeBSD.org> | 2005-06-18 17:10:50 +0000 |
commit | 21b06667dd613612697e2117f4d8e7a301dacd72 (patch) | |
tree | c098f3ed0a96017bac6283248e8dcc6e9dd9f895 /sys/gnu/fs/reiserfs/reiserfs_inode.c | |
parent | c99c5450e2d73a20a1b2386daced6928b617a5d8 (diff) | |
download | FreeBSD-src-21b06667dd613612697e2117f4d8e7a301dacd72.zip FreeBSD-src-21b06667dd613612697e2117f4d8e7a301dacd72.tar.gz |
Moving reiserfs from sys/gnu to sys/gnu/fs. This was discussed on arch@.
Reviewed by: mux (mentor)
Approved by: re (scottl)
Diffstat (limited to 'sys/gnu/fs/reiserfs/reiserfs_inode.c')
-rw-r--r-- | sys/gnu/fs/reiserfs/reiserfs_inode.c | 926 |
1 files changed, 926 insertions, 0 deletions
diff --git a/sys/gnu/fs/reiserfs/reiserfs_inode.c b/sys/gnu/fs/reiserfs/reiserfs_inode.c new file mode 100644 index 0000000..13f88da --- /dev/null +++ b/sys/gnu/fs/reiserfs/reiserfs_inode.c @@ -0,0 +1,926 @@ +/*- + * Copyright 2000 Hans Reiser + * See README for licensing and copyright details + * + * Ported to FreeBSD by Jean-Sébastien Pédron <dumbbell@FreeBSD.org> + * + * $FreeBSD$ + */ + +#include <gnu/fs/reiserfs/reiserfs_fs.h> + +static b_strategy_t reiserfs_bufstrategy; + +/* + * Buffer operations for ReiserFS vnodes. + * We punt on VOP_BMAP, so we need to do strategy on the file's vnode + * rather than the underlying device's. + */ +static struct buf_ops reiserfs_vnbufops = { + .bop_name = "ReiserFS", + .bop_strategy = reiserfs_bufstrategy, +}; + +/* Default io size devuned in super.c */ +extern int reiserfs_default_io_size; +void inode_set_bytes(struct reiserfs_node *ip, off_t bytes); + +/* 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 */ +#define GET_BLOCK_NO_DANGLE 16 /* Don't leave any transactions running */ + +/* ------------------------------------------------------------------- + * vnode operations + * -------------------------------------------------------------------*/ + +int +reiserfs_read(struct vop_read_args *ap) +{ + struct uio *uio; + struct vnode *vp; + struct reiserfs_node *ip; + struct reiserfs_sb_info *sbi; + + int error; + long size; + daddr_t lbn; + off_t bytesinfile, offset; + + uio = ap->a_uio; + vp = ap->a_vp; + ip = VTOI(vp); + sbi = ip->i_reiserfs; + + size = sbi->s_blocksize; + + for (error = 0; uio->uio_resid > 0;) { + if ((bytesinfile = ip->i_size - uio->uio_offset) <= 0) + break; + + /* Compute the logical block number and its offset */ + lbn = uio->uio_offset / size; + offset = uio->uio_offset % size; + reiserfs_log(LOG_DEBUG, "logical block number: %ju\n", + (intmax_t)lbn); + reiserfs_log(LOG_DEBUG, "block offset: %ju\n", + (intmax_t)offset); + + /* Read file blocks */ + reiserfs_log(LOG_DEBUG, "reiserfs_get_block(%ju)\n", + (intmax_t)lbn); + if ((error = reiserfs_get_block(ip, lbn, offset, uio)) != 0) { + reiserfs_log(LOG_DEBUG, + "reiserfs_get_block returned the error %d\n", + error); + break; + } + } + + return (error); +} + +static void +reiserfs_bufstrategy(struct bufobj *bo, struct buf *bp) +{ + struct vnode *vp; + int rc; + + vp = bo->bo_private; + KASSERT(bo == &vp->v_bufobj, ("BO/VP mismatch: vp %p bo %p != %p", + vp, &vp->v_bufobj, bo)); + rc = VOP_STRATEGY(vp, bp); + KASSERT(rc == 0, ("ReiserFS VOP_STRATEGY failed: bp=%p, " + "vp=%p, rc=%d", bp, vp, rc)); +} + +int +reiserfs_inactive(struct vop_inactive_args *ap) +{ + int error; + struct vnode *vp; + struct thread *td; + struct reiserfs_node *ip; + + error = 0; + vp = ap->a_vp; + td = ap->a_td; + ip = VTOI(vp); + + reiserfs_log(LOG_DEBUG, "deactivating inode used %d times\n", + vp->v_usecount); + if (prtactive && vrefcnt(vp) != 0) + vprint("ReiserFS/reclaim: pushing active", vp); + +#if 0 + /* Ignore inodes related to stale file handles. */ + if (ip->i_mode == 0) + goto out; + +out: +#endif + + /* + * If we are done with the inode, reclaim it so that it can be reused + * immediately. + */ + if (ip->i_mode == 0) { + reiserfs_log(LOG_DEBUG, "recyling\n"); + vrecycle(vp, td); + } + + return (error); +} + +int +reiserfs_reclaim(struct vop_reclaim_args *ap) +{ + struct reiserfs_node *ip; + struct vnode *vp; + + vp = ap->a_vp; + + reiserfs_log(LOG_DEBUG, "reclaiming inode used %d times\n", + vp->v_usecount); + if (prtactive && vrefcnt(vp) != 0) + vprint("ReiserFS/reclaim: pushing active", vp); + ip = VTOI(vp); + + /* XXX Update this node (write to the disk) */ + + /* Remove the inode from its hash chain. */ + vfs_hash_remove(vp); + + /* Purge old data structures associated with the inode. */ + if (ip->i_devvp) { + reiserfs_log(LOG_DEBUG, "releasing device (0x%p)\n", + ip->i_devvp); + vrele(ip->i_devvp); + ip->i_devvp = NULL; + } + + reiserfs_log(LOG_DEBUG, "free private data\n"); + FREE(vp->v_data, M_REISERFSNODE); + vp->v_data = NULL; + vnode_destroy_vobject(vp); + + return (0); +} + +/* ------------------------------------------------------------------- + * Functions from linux/fs/reiserfs/inode.c + * -------------------------------------------------------------------*/ + +static void +_make_cpu_key(struct cpu_key *key, int version, + uint32_t dirid, uint32_t objectid, off_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, struct reiserfs_node *ip, off_t offset, + int type, int length) +{ + + _make_cpu_key(key, get_inode_item_key_version(ip), + le32toh(INODE_PKEY(ip)->k_dir_id), + le32toh(INODE_PKEY(ip)->k_objectid), + offset, type, length); +} + +int +reiserfs_get_block(struct reiserfs_node *ip, long block, off_t offset, + struct uio *uio) +{ + caddr_t blk = NULL, p; + struct cpu_key key; + /* unsigned long offset; */ + INITIALIZE_PATH(path); + struct buf *bp, *blk_bp; + struct item_head *ih; + struct reiserfs_sb_info *sbi; + int blocknr, chars, done = 0, ret = 0, args = 0; + + sbi = ip->i_reiserfs; + + /* Prepare the key to look for the 'block'-th block of file */ + reiserfs_log(LOG_DEBUG, "prepare cpu key\n"); + make_cpu_key(&key, ip, (off_t)block * sbi->s_blocksize + 1, TYPE_ANY, 3); + + /* research: */ + reiserfs_log(LOG_DEBUG, "search for position\n"); + if (search_for_position_by_key(sbi, &key, &path) != POSITION_FOUND) { + reiserfs_log(LOG_DEBUG, "position not found\n"); + pathrelse(&path); +#if 0 + if (blk) + kunmap(bh_result->b_page); +#endif + /* + * We do not return ENOENT if there is a hole but page is + * uptodate, because it means that there is some MMAPED data + * associated with it that is yet to be written to disk. + */ + if ((args & GET_BLOCK_NO_HOLE)/* && + !PageUptodate(bh_result->b_page)*/) + return (ENOENT); + return (0); + } + reiserfs_log(LOG_DEBUG, "position found\n"); + + bp = get_last_bp(&path); + ih = get_ih(&path); + + if (is_indirect_le_ih(ih)) { + off_t xfersize; + uint32_t *ind_item = (uint32_t *)B_I_PITEM(bp, ih); + + reiserfs_log(LOG_DEBUG, "item is INDIRECT\n"); + + blocknr = get_block_num(ind_item, path.pos_in_item); + reiserfs_log(LOG_DEBUG, "block number: %d " + "(ind_item=%p, pos_in_item=%u)\n", + blocknr, ind_item, path.pos_in_item); + + xfersize = MIN(sbi->s_blocksize - offset, + ip->i_size - uio->uio_offset); + xfersize = MIN(xfersize, uio->uio_resid); + + if (blocknr) { + ret = bread(sbi->s_devvp, + blocknr * btodb(sbi->s_blocksize), + sbi->s_blocksize, NOCRED, &blk_bp); + reiserfs_log(LOG_DEBUG, "xfersize: %ju\n", + (intmax_t)xfersize); + ret = uiomove(blk_bp->b_data + offset, xfersize, uio); + brelse(blk_bp); + } else { + /* + * We do not return ENOENT if there is a hole but + * page is uptodate, because it means That there + * is some MMAPED data associated with it that + * is yet to be written to disk. + */ + if ((args & GET_BLOCK_NO_HOLE)/* && + !PageUptodate(bh_result->b_page)*/) + ret = (ENOENT); + + /* Skip this hole */ + uio->uio_resid -= xfersize; + uio->uio_offset += xfersize; + } + + pathrelse(&path); + return (ret); + } + + reiserfs_log(LOG_DEBUG, "item should be DIRECT\n"); + +#if 0 + /* 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 0 + if (blk) + kunmap(bh_result->b_page); +#endif + return (ENOENT); + } +#endif + +#if 0 + /* + * If we've got a direct item, and the buffer or page 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; + } else + /* + * grab_tail_page can trigger calls to reiserfs_get_block + * on up to date pages without any buffers. If the page + * is up to date, we don't want read old data off disk. + * Set the up to date bit on the buffer instead and jump + * to the end + */ + if (!bh_result->b_page || PageUptodate(bh_result->b_page)) { + set_buffer_uptodate(bh_result); + goto finished; + } +#endif + +#if 0 + /* Read file tail into part of page */ + offset = (cpu_key_k_offset(&key) - 1) & (PAGE_CACHE_SIZE - 1); + fs_gen = get_generation(ip->i_reiserfs); + copy_item_head(&tmp_ih, ih); +#endif + +#if 0 + /* + * 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 (!blk) { + blk = (char *)kmap(bh_result->b_page); + if (fs_changed (fs_gen, sbi) && item_moved(&tmp_ih, &path)) + goto research; + } + blk += offset; + memset(blk, 0, sbi->s_blocksize); +#endif + if (!blk) { + reiserfs_log(LOG_DEBUG, "allocating buffer\n"); + blk = malloc(ip->i_size, M_REISERFSNODE, M_WAITOK | M_ZERO); + if (!blk) + return (ENOMEM); + } + /* p += offset; */ + + p = blk; + do { + if (!is_direct_le_ih(ih)) { + reiserfs_log(LOG_ERR, "BUG\n"); + return (ENOENT); /* XXX Wrong error code */ + } + + /* + * 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) > ip->i_size) + break; + + if ((le_ih_k_offset(ih) - 1 + ih_item_len(ih)) > ip->i_size) { + chars = ip->i_size - (le_ih_k_offset(ih) - 1) - + path.pos_in_item; + done = 1; + } else { + chars = ih_item_len(ih) - path.pos_in_item; + } + reiserfs_log(LOG_DEBUG, "copying %d bytes\n", chars); + memcpy(p, B_I_PITEM(bp, ih) + path.pos_in_item, chars); + if (done) { + reiserfs_log(LOG_DEBUG, "copy done\n"); + break; + } + + p += chars; + + if (PATH_LAST_POSITION(&path) != (B_NR_ITEMS(bp) - 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(sbi, &key, &path) != + POSITION_FOUND) + /* + * We read something from tail, even if now we got + * IO_ERROR + */ + break; + + bp = get_last_bp(&path); + ih = get_ih(&path); + } while (1); + + /* finished: */ + pathrelse(&path); + /* + * This buffer has valid data, but isn't valid for io. mapping it to + * block #0 tells the rest of reiserfs it just has a tail in it + */ + ret = uiomove(blk, ip->i_size, uio); + free(blk, M_REISERFSNODE); + return (ret); +} + +/* + * Compute real number of used bytes by file + * Following three functions can go away when we'll have enough space in + * stat item + */ +static int +real_space_diff(struct reiserfs_node *ip, int sd_size) +{ + int bytes; + off_t blocksize = ip->i_reiserfs->s_blocksize; + + if (S_ISLNK(ip->i_mode) || S_ISDIR(ip->i_mode)) + return (sd_size); + + /* End of file is also in full block with indirect reference, so round + * up to the next block. + * + * There is just no way to know if the tail is actually packed on the + * file, so we have to assume it isn't. When we pack the tail, we add + * 4 bytes to pretend there really is an unformatted node pointer. */ + bytes = ((ip->i_size + (blocksize - 1)) >> + ip->i_reiserfs->s_blocksize_bits) * UNFM_P_SIZE + sd_size; + + return (bytes); +} + +static inline off_t +to_real_used_space(struct reiserfs_node *ip, unsigned long blocks, int sd_size) +{ + + if (S_ISLNK(ip->i_mode) || S_ISDIR(ip->i_mode)) { + return ip->i_size + (off_t)(real_space_diff(ip, sd_size)); + } + + return ((off_t)real_space_diff(ip, sd_size)) + (((off_t)blocks) << 9); +} + +void +inode_set_bytes(struct reiserfs_node *ip, off_t bytes) +{ + + ip->i_blocks = bytes >> 9; + ip->i_bytes = bytes & 511; +} + +/* Called by read_locked_inode */ +static void +init_inode(struct reiserfs_node *ip, struct path *path) +{ + struct buf *bp; + struct item_head *ih; + uint32_t rdev; + + bp = PATH_PLAST_BUFFER(path); + ih = PATH_PITEM_HEAD(path); + + reiserfs_log(LOG_DEBUG, "copy the key (objectid=%d, dirid=%d)\n", + ih->ih_key.k_objectid, ih->ih_key.k_dir_id); + copy_key(INODE_PKEY(ip), &(ih->ih_key)); + /* ip->i_blksize = reiserfs_default_io_size; */ + + reiserfs_log(LOG_DEBUG, "reset some inode structure members\n"); + REISERFS_I(ip)->i_flags = 0; +#if 0 + REISERFS_I(ip)->i_prealloc_block = 0; + REISERFS_I(ip)->i_prealloc_count = 0; + REISERFS_I(ip)->i_trans_id = 0; + REISERFS_I(ip)->i_jl = NULL; + REISERFS_I(ip)->i_acl_access = NULL; + REISERFS_I(ip)->i_acl_default = NULL; +#endif + + if (stat_data_v1(ih)) { + reiserfs_log(LOG_DEBUG, "reiserfs/init_inode: stat data v1\n"); + struct stat_data_v1 *sd; + unsigned long blocks; + + sd = (struct stat_data_v1 *)B_I_PITEM(bp, ih); + + reiserfs_log(LOG_DEBUG, + "reiserfs/init_inode: filling more members\n"); + set_inode_item_key_version(ip, KEY_FORMAT_3_5); + set_inode_sd_version(ip, STAT_DATA_V1); + ip->i_mode = sd_v1_mode(sd); + ip->i_nlink = sd_v1_nlink(sd); + ip->i_uid = sd_v1_uid(sd); + ip->i_gid = sd_v1_gid(sd); + ip->i_size = sd_v1_size(sd); + ip->i_atime.tv_sec = sd_v1_atime(sd); + ip->i_mtime.tv_sec = sd_v1_mtime(sd); + ip->i_ctime.tv_sec = sd_v1_ctime(sd); + ip->i_atime.tv_nsec = 0; + ip->i_ctime.tv_nsec = 0; + ip->i_mtime.tv_nsec = 0; + + reiserfs_log(LOG_DEBUG, " mode = %08x\n", ip->i_mode); + reiserfs_log(LOG_DEBUG, " nlink = %d\n", ip->i_nlink); + reiserfs_log(LOG_DEBUG, " owner = %d:%d\n", ip->i_uid, + ip->i_gid); + reiserfs_log(LOG_DEBUG, " size = %ju\n", + (intmax_t)ip->i_size); + reiserfs_log(LOG_DEBUG, " atime = %jd\n", + (intmax_t)ip->i_atime.tv_sec); + reiserfs_log(LOG_DEBUG, " mtime = %jd\n", + (intmax_t)ip->i_mtime.tv_sec); + reiserfs_log(LOG_DEBUG, " ctime = %jd\n", + (intmax_t)ip->i_ctime.tv_sec); + + ip->i_blocks = sd_v1_blocks(sd); + ip->i_generation = le32toh(INODE_PKEY(ip)->k_dir_id); + blocks = (ip->i_size + 511) >> 9; + blocks = _ROUND_UP(blocks, ip->i_reiserfs->s_blocksize >> 9); + if (ip->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. + */ + ip->i_blocks = blocks; + } + + rdev = sd_v1_rdev(sd); + REISERFS_I(ip)->i_first_direct_byte = + sd_v1_first_direct_byte(sd); + + /* + * An early bug in the quota code can give us an odd number + * for the block count. This is incorrect, fix it here. + */ + if (ip->i_blocks & 1) { + ip->i_blocks++ ; + } + inode_set_bytes(ip, to_real_used_space(ip, ip->i_blocks, + SD_V1_SIZE)); + + /* + * nopack is initially zero for v1 objects. For v2 objects, + * nopack is initialised from sd_attrs + */ + REISERFS_I(ip)->i_flags &= ~i_nopack_mask; + reiserfs_log(LOG_DEBUG, "...done\n"); + } else { + reiserfs_log(LOG_DEBUG, "stat data v2\n"); + /* + * New stat data found, but object may have old items + * (directories and symlinks) + */ + struct stat_data *sd = (struct stat_data *)B_I_PITEM(bp, ih); + + reiserfs_log(LOG_DEBUG, "filling more members\n"); + ip->i_mode = sd_v2_mode(sd); + ip->i_nlink = sd_v2_nlink(sd); + ip->i_uid = sd_v2_uid(sd); + ip->i_size = sd_v2_size(sd); + ip->i_gid = sd_v2_gid(sd); + ip->i_mtime.tv_sec = sd_v2_mtime(sd); + ip->i_atime.tv_sec = sd_v2_atime(sd); + ip->i_ctime.tv_sec = sd_v2_ctime(sd); + ip->i_ctime.tv_nsec = 0; + ip->i_mtime.tv_nsec = 0; + ip->i_atime.tv_nsec = 0; + + reiserfs_log(LOG_DEBUG, " mode = %08x\n", ip->i_mode); + reiserfs_log(LOG_DEBUG, " nlink = %d\n", ip->i_nlink); + reiserfs_log(LOG_DEBUG, " owner = %d:%d\n", ip->i_uid, + ip->i_gid); + reiserfs_log(LOG_DEBUG, " size = %ju\n", + (intmax_t)ip->i_size); + reiserfs_log(LOG_DEBUG, " atime = %jd\n", + (intmax_t)ip->i_atime.tv_sec); + reiserfs_log(LOG_DEBUG, " mtime = %jd\n", + (intmax_t)ip->i_mtime.tv_sec); + reiserfs_log(LOG_DEBUG, " ctime = %jd\n", + (intmax_t)ip->i_ctime.tv_sec); + + ip->i_blocks = sd_v2_blocks(sd); + rdev = sd_v2_rdev(sd); + reiserfs_log(LOG_DEBUG, " blocks = %u\n", ip->i_blocks); + + if (S_ISCHR(ip->i_mode) || S_ISBLK(ip->i_mode)) + ip->i_generation = le32toh(INODE_PKEY(ip)->k_dir_id); + else + ip->i_generation = sd_v2_generation(sd); + + if (S_ISDIR(ip->i_mode) || S_ISLNK(ip->i_mode)) + set_inode_item_key_version(ip, KEY_FORMAT_3_5); + else + set_inode_item_key_version(ip, KEY_FORMAT_3_6); + + REISERFS_I(ip)->i_first_direct_byte = 0; + set_inode_sd_version(ip, STAT_DATA_V2); + inode_set_bytes(ip, to_real_used_space(ip, ip->i_blocks, + SD_V2_SIZE)); + + /* + * Read persistent inode attributes from sd and initalise + * generic inode flags from them + */ + REISERFS_I(ip)->i_attrs = sd_v2_attrs(sd); + sd_attrs_to_i_attrs(sd_v2_attrs(sd), ip); + reiserfs_log(LOG_DEBUG, "...done\n"); + } + + pathrelse(path); + if (S_ISREG(ip->i_mode)) { + reiserfs_log(LOG_DEBUG, "this inode is a regular file\n"); + //ip->i_op = &reiserfs_file_ip_operations; + //ip->i_fop = &reiserfs_file_operations; + //ip->i_mapping->a_ops = &reiserfs_address_space_operations ; + } else if (S_ISDIR(ip->i_mode)) { + reiserfs_log(LOG_DEBUG, "this inode is a directory\n"); + //ip->i_op = &reiserfs_dir_ip_operations; + //ip->i_fop = &reiserfs_dir_operations; + } else if (S_ISLNK(ip->i_mode)) { + reiserfs_log(LOG_DEBUG, "this inode is a symlink\n"); + //ip->i_op = &reiserfs_symlink_ip_operations; + //ip->i_mapping->a_ops = &reiserfs_address_space_operations; + } else { + reiserfs_log(LOG_DEBUG, "this inode is something unknown in " + "this universe\n"); + ip->i_blocks = 0; + //ip->i_op = &reiserfs_special_ip_operations; + //init_special_ip(ip, ip->i_mode, new_decode_dev(rdev)); + } +} + +/* + * reiserfs_read_locked_inode is called to read the inode off disk, and + * it does a make_bad_inode when things go wrong. But, we need to make + * sure and clear the key in the private portion of the inode, otherwise + * a corresponding iput might try to delete whatever object the inode + * last represented. + */ +static void +reiserfs_make_bad_inode(struct reiserfs_node *ip) { + + memset(INODE_PKEY(ip), 0, KEY_SIZE); + //make_bad_inode(inode); +} + +void +reiserfs_read_locked_inode(struct reiserfs_node *ip, + struct reiserfs_iget_args *args) +{ + INITIALIZE_PATH(path_to_sd); + struct cpu_key key; + unsigned long dirino; + int retval; + + dirino = args->dirid; + + /* + * 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 = ip->i_number; + 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(ip->i_reiserfs, &key, &path_to_sd); + if (retval == IO_ERROR) { + reiserfs_log(LOG_ERR, + "I/O failure occured trying to find stat" + "data %u/%u\n", + key.on_disk_key.k_dir_id, key.on_disk_key.k_objectid); + reiserfs_make_bad_inode(ip); + return; + } + if (retval != ITEM_FOUND) { + /* + * A stale NFS handle can trigger this without it being + * an error + */ + reiserfs_log(LOG_ERR, + "item not found (objectid=%u, dirid=%u)\n", + key.on_disk_key.k_objectid, key.on_disk_key.k_dir_id); + pathrelse(&path_to_sd); + reiserfs_make_bad_inode(ip); + ip->i_nlink = 0; + return; + } + + init_inode(ip, &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 <philippe.gramoulle@mmania.com> 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. + */ + /* + * Currently there is one place where it's ok to meet inode with + * nlink == 0: processing of open-unlinked and half-truncated files + * during mount (fs/reiserfs/super.c:finish_unfinished()). + */ + if((ip->i_nlink == 0) && + !REISERFS_SB(ip->i_reiserfs)->s_is_unlinked_ok ) { + reiserfs_log(LOG_WARNING, "dead inode read from disk. This is " + "likely to be race with knfsd. Ignore"); + reiserfs_make_bad_inode(ip); + } + + /* Init inode should be relsing */ + reiserfs_check_path(&path_to_sd); +} + +int +reiserfs_iget( + struct mount *mp, const struct cpu_key *key, + struct vnode **vpp, struct thread *td) +{ + int error, flags; + struct cdev *dev; + struct vnode *vp; + struct reiserfs_node *ip; + struct reiserfs_mount *rmp; + + struct reiserfs_iget_args args; + + //restart: + /* Check if the inode cache contains it */ + // XXX LK_EXCLUSIVE ? + flags = LK_EXCLUSIVE; + error = vfs_hash_get(mp, key->on_disk_key.k_objectid, flags, + td, vpp, NULL, NULL); + if (error || *vpp != NULL) + return (error); + + rmp = VFSTOREISERFS(mp); + dev = rmp->rm_dev; + + /* + * If this MALLOC() is performed after the getnewvnode() it might + * block, leaving a vnode with a NULL v_data to be found by + * reiserfs_sync() if a sync happens to fire right then, which + * will cause a panic because reiserfs_sync() blindly dereferences + * vp->v_data (as well it should). + */ + reiserfs_log(LOG_DEBUG, "malloc(struct reiserfs_node)\n"); + ip = malloc(sizeof(struct reiserfs_node), M_REISERFSNODE, + M_WAITOK | M_ZERO); + + /* Allocate a new vnode/inode. */ + reiserfs_log(LOG_DEBUG, "getnewvnode\n"); + if ((error = + getnewvnode("reiserfs", mp, &reiserfs_vnodeops, &vp)) != 0) { + *vpp = NULL; + free(ip, M_REISERFSNODE); + reiserfs_log(LOG_DEBUG, "getnewvnode FAILED\n"); + return (error); + } + + args.dirid = key->on_disk_key.k_dir_id; + args.objectid = key->on_disk_key.k_objectid; + + reiserfs_log(LOG_DEBUG, "filling *ip\n"); + vp->v_data = ip; + ip->i_vnode = vp; + ip->i_dev = dev; + ip->i_number = args.objectid; + ip->i_ino = args.dirid; + ip->i_reiserfs = rmp->rm_reiserfs; + + vp->v_bufobj.bo_ops = &reiserfs_vnbufops; + vp->v_bufobj.bo_private = vp; + + /* If this is the root node, set the VV_ROOT flag */ + if (ip->i_number == REISERFS_ROOT_OBJECTID && + ip->i_ino == REISERFS_ROOT_PARENT_OBJECTID) + vp->v_vflag |= VV_ROOT; + +#if 0 + if (VOP_LOCK(vp, LK_EXCLUSIVE, td) != 0) + panic("reiserfs/iget: unexpected lock failure"); + + /* + * Exclusively lock the vnode before adding to hash. Note, that we + * must not release nor downgrade the lock (despite flags argument + * says) till it is fully initialized. + */ + lockmgr(vp->v_vnlock, LK_EXCLUSIVE, (struct mtx *)0, td); +#endif + + error = vfs_hash_insert(vp, key->on_disk_key.k_objectid, flags, + td, vpp, NULL, NULL); + if (error || *vpp != NULL) + return (error); + + /* Read the inode */ + reiserfs_log(LOG_DEBUG, "call reiserfs_read_locked_inode (" + "objectid=%d,dirid=%d)\n", args.objectid, args.dirid); + reiserfs_read_locked_inode(ip, &args); + + ip->i_devvp = rmp->rm_devvp; + VREF(ip->i_devvp); + + switch(vp->v_type = IFTOVT(ip->i_mode)) { + case VBLK: + reiserfs_log(LOG_DEBUG, "vnode type VBLK\n"); + vp->v_op = &reiserfs_specops; + break; +#if 0 + case VCHR: + reiserfs_log(LOG_DEBUG, "vnode type VCHR\n"); + vp->v_op = &reiserfs_specops; + vp = addaliasu(vp, ip->i_rdev); + ip->i_vnode = vp; + break; + case VFIFO: + reiserfs_log(LOG_DEBUG, "vnode type VFIFO\n"); + vp->v_op = reiserfs_fifoop_p; + break; +#endif + default: + break; + } + + *vpp = vp; + return (0); +} + +void +sd_attrs_to_i_attrs(uint16_t sd_attrs, struct reiserfs_node *ip) +{ + + if (reiserfs_attrs(ip->i_reiserfs)) { +#if 0 + if (sd_attrs & REISERFS_SYNC_FL) + ip->i_flags |= S_SYNC; + else + ip->i_flags &= ~S_SYNC; +#endif + if (sd_attrs & REISERFS_IMMUTABLE_FL) + ip->i_flags |= IMMUTABLE; + else + ip->i_flags &= ~IMMUTABLE; + if (sd_attrs & REISERFS_APPEND_FL) + ip->i_flags |= APPEND; + else + ip->i_flags &= ~APPEND; +#if 0 + if (sd_attrs & REISERFS_NOATIME_FL) + ip->i_flags |= S_NOATIME; + else + ip->i_flags &= ~S_NOATIME; + if (sd_attrs & REISERFS_NOTAIL_FL) + REISERFS_I(ip)->i_flags |= i_nopack_mask; + else + REISERFS_I(ip)->i_flags &= ~i_nopack_mask; +#endif + } +} + +void +i_attrs_to_sd_attrs(struct reiserfs_node *ip, uint16_t *sd_attrs) +{ + + if (reiserfs_attrs(ip->i_reiserfs)) { +#if 0 + if (ip->i_flags & S_SYNC) + *sd_attrs |= REISERFS_SYNC_FL; + else + *sd_attrs &= ~REISERFS_SYNC_FL; +#endif + if (ip->i_flags & IMMUTABLE) + *sd_attrs |= REISERFS_IMMUTABLE_FL; + else + *sd_attrs &= ~REISERFS_IMMUTABLE_FL; + if (ip->i_flags & APPEND) + *sd_attrs |= REISERFS_APPEND_FL; + else + *sd_attrs &= ~REISERFS_APPEND_FL; +#if 0 + if (ip->i_flags & S_NOATIME) + *sd_attrs |= REISERFS_NOATIME_FL; + else + *sd_attrs &= ~REISERFS_NOATIME_FL; + if (REISERFS_I(ip)->i_flags & i_nopack_mask) + *sd_attrs |= REISERFS_NOTAIL_FL; + else + *sd_attrs &= ~REISERFS_NOTAIL_FL; +#endif + } +} |