c6e0355cee
received granular locking) but the comment present in UFS has been copied all over other filesystems code incorrectly for several times. Removes comments that makes no sense now. Reviewed by: kib MFC after: 3 days
913 lines
24 KiB
C
913 lines
24 KiB
C
/*-
|
|
* 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 reiserfs_node *ip;
|
|
|
|
error = 0;
|
|
vp = ap->a_vp;
|
|
ip = VTOI(vp);
|
|
|
|
reiserfs_log(LOG_DEBUG, "deactivating inode used %d times\n",
|
|
vp->v_usecount);
|
|
|
|
#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);
|
|
}
|
|
|
|
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);
|
|
ip = VTOI(vp);
|
|
|
|
/* XXX Update this node (write to the disk) */
|
|
|
|
/* Remove the inode from its hash chain. */
|
|
vfs_hash_remove(vp);
|
|
|
|
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;
|
|
|
|
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) != 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);
|
|
#endif
|
|
|
|
lockmgr(vp->v_vnlock, LK_EXCLUSIVE, NULL);
|
|
error = insmntque(vp, mp);
|
|
if (error != 0) {
|
|
free(ip, M_REISERFSNODE);
|
|
*vpp = NULL;
|
|
reiserfs_log(LOG_DEBUG, "insmntque FAILED\n");
|
|
return (error);
|
|
}
|
|
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;
|
|
|
|
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
|
|
}
|
|
}
|