freebsd-nq/sys/gnu/fs/reiserfs/reiserfs_vnops.c
Edward Tomasz Napierala 0da50f6ef8 According to phk@, VOP_STRATEGY should never, _ever_, return
anything other than 0.  Make it so.  This fixes
"panic: VOP_STRATEGY failed bp=0xc320dd90 vp=0xc3b9f648",
encountered when writing to an orphaned filesystem.  Reason
for the panic was the following assert:
KASSERT(i == 0, ("VOP_STRATEGY failed bp=%p vp=%p", bp, bp->b_vp));
at vfs_bio:bufstrategy().

Reviewed by:	scottl, phk
Approved by:	rwatson (mentor)
Sponsored by:	FreeBSD Foundation
2008-12-16 21:13:11 +00:00

388 lines
9.6 KiB
C

/*-
* Copyright 2000 Hans Reiser
* See README for licensing and copyright details
*
* Ported to FreeBSD by Jean-Sébastien Pédron <jspedron@club-internet.fr>
*
* $FreeBSD$
*/
#include <gnu/fs/reiserfs/reiserfs_fs.h>
static vop_access_t reiserfs_access;
static vop_bmap_t reiserfs_bmap;
static vop_getattr_t reiserfs_getattr;
static vop_open_t reiserfs_open;
static vop_pathconf_t reiserfs_pathconf;
static vop_readlink_t reiserfs_readlink;
static vop_strategy_t reiserfs_strategy;
static vop_vptofh_t reiserfs_vptofh;
/* Global vfs data structures for ReiserFS */
struct vop_vector reiserfs_vnodeops = {
.vop_default = &default_vnodeops,
.vop_access = reiserfs_access,
.vop_bmap = reiserfs_bmap,
.vop_cachedlookup = reiserfs_lookup,
.vop_getattr = reiserfs_getattr,
.vop_inactive = reiserfs_inactive,
.vop_lookup = vfs_cache_lookup,
.vop_open = reiserfs_open,
.vop_reclaim = reiserfs_reclaim,
.vop_read = reiserfs_read,
.vop_readdir = reiserfs_readdir,
.vop_readlink = reiserfs_readlink,
.vop_pathconf = reiserfs_pathconf,
.vop_strategy = reiserfs_strategy,
.vop_vptofh = reiserfs_vptofh,
};
struct vop_vector reiserfs_specops = {
.vop_default = &default_vnodeops,
.vop_access = reiserfs_access,
.vop_getattr = reiserfs_getattr,
.vop_inactive = reiserfs_inactive,
.vop_reclaim = reiserfs_reclaim,
};
/* -------------------------------------------------------------------
* vnode operations
* -------------------------------------------------------------------*/
static int
reiserfs_access(struct vop_access_args *ap)
{
int error;
struct vnode *vp = ap->a_vp;
struct reiserfs_node *ip = VTOI(vp);
accmode_t accmode = ap->a_accmode;
/*
* Disallow write attempts on read-only file systems; unless the file
* is a socket, fifo, or a block or character device resident on the
* file system.
*/
if (accmode & VWRITE) {
switch (vp->v_type) {
case VDIR:
case VLNK:
case VREG:
if (vp->v_mount->mnt_flag & MNT_RDONLY) {
reiserfs_log(LOG_DEBUG,
"no write access (read-only fs)\n");
return (EROFS);
}
break;
default:
break;
}
}
/* If immutable bit set, nobody gets to write it. */
if ((accmode & VWRITE) && (ip->i_flags & (IMMUTABLE | SF_SNAPSHOT))) {
reiserfs_log(LOG_DEBUG, "no write access (immutable)\n");
return (EPERM);
}
error = vaccess(vp->v_type, ip->i_mode, ip->i_uid, ip->i_gid,
ap->a_accmode, ap->a_cred, NULL);
return (error);
}
static int
reiserfs_getattr(struct vop_getattr_args *ap)
{
struct vnode *vp = ap->a_vp;
struct vattr *vap = ap->a_vap;
struct reiserfs_node *ip = VTOI(vp);
vap->va_fsid = dev2udev(ip->i_dev);
vap->va_fileid = ip->i_number;
vap->va_mode = ip->i_mode & ~S_IFMT;
vap->va_nlink = ip->i_nlink;
vap->va_uid = ip->i_uid;
vap->va_gid = ip->i_gid;
//XXX vap->va_rdev = ip->i_rdev;
vap->va_size = ip->i_size;
vap->va_atime = ip->i_atime;
vap->va_mtime = ip->i_mtime;
vap->va_ctime = ip->i_ctime;
vap->va_flags = ip->i_flags;
vap->va_gen = ip->i_generation;
vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize;
vap->va_bytes = dbtob((u_quad_t)ip->i_blocks);
vap->va_type = vp->v_type;
//XXX vap->va_filerev = ip->i_modrev;
return (0);
}
/* Return POSIX pathconf information applicable to ReiserFS filesystems */
static int
reiserfs_pathconf(struct vop_pathconf_args *ap)
{
switch (ap->a_name) {
case _PC_LINK_MAX:
*ap->a_retval = REISERFS_LINK_MAX;
return (0);
case _PC_NAME_MAX:
*ap->a_retval =
REISERFS_MAX_NAME(VTOI(ap->a_vp)->i_reiserfs->s_blocksize);
return (0);
case _PC_PATH_MAX:
*ap->a_retval = PATH_MAX;
return (0);
case _PC_PIPE_BUF:
*ap->a_retval = PIPE_BUF;
return (0);
case _PC_CHOWN_RESTRICTED:
*ap->a_retval = 1;
return (0);
case _PC_NO_TRUNC:
*ap->a_retval = 1;
return (0);
default:
return (EINVAL);
}
}
static int
reiserfs_open(struct vop_open_args *ap)
{
/* Files marked append-only must be opened for appending. */
if ((VTOI(ap->a_vp)->i_flags & APPEND) &&
(ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
return (EPERM);
vnode_create_vobject(ap->a_vp, VTOI(ap->a_vp)->i_size, ap->a_td);
return (0);
}
/* Return target name of a symbolic link */
static int
reiserfs_readlink(struct vop_readlink_args *ap)
{
struct vnode *vp = ap->a_vp;
reiserfs_log(LOG_DEBUG, "redirect to VOP_READ()\n");
return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred));
}
/* Bmap converts the logical block number of a file to its physical
* block number on the disk. */
static int
reiserfs_bmap(ap)
struct vop_bmap_args /* {
struct vnode *a_vp;
daddr_t a_bn;
struct bufobj **a_bop;
daddr_t *a_bnp;
int *a_runp;
int *a_runb;
} */ *ap;
{
daddr_t blkno;
struct buf *bp;
struct cpu_key key;
struct item_head *ih;
struct vnode *vp = ap->a_vp;
struct reiserfs_node *ip = VTOI(vp);
struct reiserfs_sb_info *sbi = ip->i_reiserfs;
INITIALIZE_PATH(path);
/* Prepare the key to look for the 'block'-th block of file
* (XXX we suppose that statfs.f_iosize == sbi->s_blocksize) */
make_cpu_key(&key, ip, (off_t)ap->a_bn * sbi->s_blocksize + 1,
TYPE_ANY, 3);
/* Search item */
if (search_for_position_by_key(sbi, &key, &path) != POSITION_FOUND) {
reiserfs_log(LOG_DEBUG, "position not found\n");
pathrelse(&path);
return (ENOENT);
}
bp = get_last_bp(&path);
ih = get_ih(&path);
if (is_indirect_le_ih(ih)) {
/* Indirect item can be read by the underlying layer, instead of
* VOP_STRATEGY. */
int i;
uint32_t *ind_item = (uint32_t *)B_I_PITEM(bp, ih);
reiserfs_log(LOG_DEBUG, "found an INDIRECT item\n");
blkno = get_block_num(ind_item, path.pos_in_item);
/* Read-ahead */
if (ap->a_runb) {
uint32_t count = 0;
for (i = path.pos_in_item - 1; i >= 0; --i) {
if ((blkno - get_block_num(ind_item, i)) !=
count + 1)
break;
++count;
}
/*
* This count isn't expressed in DEV_BSIZE base but
* in fs' own block base
* (see sys/vm/vnode_pager.c:vnode_pager_addr())
*/
*ap->a_runb = count;
reiserfs_log(LOG_DEBUG,
" read-ahead: %d blocks before\n", *ap->a_runb);
}
if (ap->a_runp) {
uint32_t count = 0;
/*
* ih is an uint32_t array, that's why we use
* its length (in bytes) divided by 4 to know
* the number of items
*/
for (i = path.pos_in_item + 1;
i < ih_item_len(ih) / 4; ++i) {
if ((get_block_num(ind_item, i) - blkno) !=
count + 1)
break;
++count;
}
/*
* This count isn't expressed in DEV_BSIZE base but
* in fs' own block base
* (see sys/vm/vnode_pager.c:vnode_pager_addr()) */
*ap->a_runp = count;
reiserfs_log(LOG_DEBUG,
" read-ahead: %d blocks after\n", *ap->a_runp);
}
/* Indirect items can be read using the device VOP_STRATEGY */
if (ap->a_bop)
*ap->a_bop = &VTOI(ap->a_vp)->i_devvp->v_bufobj;
/* Convert the block number into DEV_BSIZE base */
blkno *= btodb(sbi->s_blocksize);
} else {
/*
* Direct item are not DEV_BSIZE aligned, VOP_STRATEGY will
* have to handle this case specifically
*/
reiserfs_log(LOG_DEBUG, "found a DIRECT item\n");
blkno = ap->a_bn;
if (ap->a_runp)
*ap->a_runp = 0;
if (ap->a_runb)
*ap->a_runb = 0;
/* Direct item must be read by reiserfs_strategy */
if (ap->a_bop)
*ap->a_bop = &vp->v_bufobj;
}
if (ap->a_bnp)
*ap->a_bnp = blkno;
pathrelse(&path);
if (ap->a_bnp) {
reiserfs_log(LOG_DEBUG, "logical block: %ju (%ju),"
" physical block: %ju (%ju)\n",
(intmax_t)ap->a_bn,
(intmax_t)(ap->a_bn / btodb(sbi->s_blocksize)),
(intmax_t)*ap->a_bnp,
(intmax_t)(*ap->a_bnp / btodb(sbi->s_blocksize)));
}
return (0);
}
/* Does simply the same as reiserfs_read. It's called when reiserfs_bmap find
* an direct item. */
static int
reiserfs_strategy(struct vop_strategy_args /* {
struct vnode *a_vp;
struct buf *a_bp;
} */ *ap)
{
int error;
struct uio auio;
struct iovec aiov;
struct reiserfs_node *ip;
struct buf *bp = ap->a_bp;
struct vnode *vp = ap->a_vp;
reiserfs_log(LOG_DEBUG, "logical block: %ju,"
" physical block: %ju\n", (intmax_t)bp->b_lblkno,
(intmax_t)bp->b_blkno);
ip = VTOI(vp);
if (bp->b_iocmd == BIO_READ) {
/* Prepare the uio structure */
reiserfs_log(LOG_DEBUG, "prepare uio structure\n");
aiov.iov_base = bp->b_data;
aiov.iov_len = MIN(bp->b_bcount, ip->i_size);
reiserfs_log(LOG_DEBUG, " vector length: %ju\n",
(intmax_t)aiov.iov_len);
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_offset = 0;
auio.uio_rw = UIO_READ;
auio.uio_segflg = UIO_SYSSPACE;
auio.uio_td = curthread;
auio.uio_resid = bp->b_bcount;
reiserfs_log(LOG_DEBUG, " buffer length: %u\n",
auio.uio_resid);
reiserfs_log(LOG_DEBUG, "reading block #%ju\n",
(intmax_t)bp->b_blkno);
error = reiserfs_get_block(ip, bp->b_blkno, 0, &auio);
} else {
/* No write support yet */
error = (EOPNOTSUPP);
bp->b_error = error;
bp->b_ioflags |= BIO_ERROR;
}
if (error) {
bp->b_ioflags |= BIO_ERROR;
bp->b_error = error;
}
bufdone(bp);
return (0);
}
/*
* Vnode pointer to File handle
*/
static int
reiserfs_vptofh(struct vop_vptofh_args /* {
struct vnode *a_vp;
struct fid *a_fhp;
} */ *ap)
{
struct rfid *rfhp;
struct reiserfs_node *ip;
ip = VTOI(ap->a_vp);
reiserfs_log(LOG_DEBUG,
"fill *fhp with inode (dirid=%d, objectid=%d)\n",
ip->i_ino, ip->i_number);
rfhp = (struct rfid *)ap->a_fhp;
rfhp->rfid_len = sizeof(struct rfid);
rfhp->rfid_dirid = ip->i_ino;
rfhp->rfid_objectid = ip->i_number;
rfhp->rfid_gen = ip->i_generation;
reiserfs_log(LOG_DEBUG, "return it\n");
return (0);
}