[BACK]Return to xfs_iops.c CVS log [TXT][DIR] Up to [Development] / xfs-linux / linux-2.6

File: [Development] / xfs-linux / linux-2.6 / xfs_iops.c (download)

Revision 1.23, Tue Feb 8 04:47:02 2000 UTC (17 years, 8 months ago) by mostek
Branch: MAIN
Changes since 1.22: +5 -1 lines

Clear locked bit and set error when bmap fails.
The fun things that file system corruption finds.

/*
 * Copyright (C) 1999 Silicon Graphics, Inc.  All Rights Reserved.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
 */

/*
 *  fs/xfs/linux/xfs_iops.c
 *     This file would be called xfs_inode.c, but that is already taken
 *     in the standard XFS code.
 *
 */

#define FSID_T
#include <sys/types.h>
#include <linux/errno.h>
#include "xfs_coda_oops.h"

#include <linux/xfs_to_linux.h>

#undef  NODEV
#include <linux/version.h>
#include <linux/fs.h>
#include <linux/sched.h>	/* To get current */
#include <linux/locks.h>
#include <linux/slab.h>

#include <linux/linux_to_xfs.h>

#include <sys/sysmacros.h>
#include <sys/capability.h>
#include <sys/cred.h>
#include <sys/buf.h>
#include <sys/vfs.h>
#include <sys/pvfs.h>
#include <sys/vnode.h>
#include <ksys/behavior.h>
#include <sys/mode.h>
#include <xfs_linux.h>

#include <linux/xfs_file.h>

#include <linux/page_buf.h>

#include <asm/uaccess.h> /* For copy_from_user */

/*
 * Common code used to create/instantiate various things in a directory.
 */

int linvfs_common_cr(struct inode *dir, struct dentry *dentry, int mode,
				enum vtype tp, int rdev)
{
	int		error = 0;
	vnode_t		*dvp, *vp;
	struct inode	*ip;
	vattr_t		va;
	ino_t		ino;

	dvp = LINVFS_GET_VP(dir);
	ASSERT(dvp);

	vp = NULL;

	bzero(&va, sizeof(va));
	va.va_mask = AT_TYPE|AT_MODE;
	va.va_type = tp;
	va.va_mode = mode;	/* Do we need to pass this through? JIMJIM
				va.va_mode = 0777 & ~current->fs->umask; */
	va.va_size = 0;

	if (tp == VREG) {
		VOP_CREATE(dvp, (char *)dentry->d_name.name, &va, 0, 0, &vp,
				sys_cred, error);
	} else if ((tp == VBLK) || (tp == VCHR)) {
		/*
		 * Get the real type from the mode
		 */
		va.va_rdev = makedev(MAJOR(rdev), MINOR(rdev));
		va.va_mask |= AT_RDEV;

		va.va_type = IFTOVT(mode);
		if (va.va_type == VNON) {
			return -EINVAL;
		}
		VOP_CREATE(dvp, (char *)dentry->d_name.name, &va, 0, 0, &vp,
			sys_cred, error);
	} else if (tp == VDIR) {
		VOP_MKDIR(dvp, (char *)dentry->d_name.name, &va, &vp,
			sys_cred, error);
	}

	if (!error) {
		ASSERT(vp);
		bzero(&va, sizeof(va));
		va.va_mask = AT_NODEID;
		VOP_GETATTR(vp, &va, 0, sys_cred, error);
		if (error) {
			VN_RELE(vp);
			return -error;
		}
		ino = va.va_nodeid;
		ASSERT(ino);
		ip = iget(dir->i_sb, ino);
		if (!ip) {
			VN_RELE(vp);
			return -ENOMEM;
		}
		d_instantiate(dentry, ip);
	}
	return 0;
}


/*
 * Create a new file in dir using mode.
 */
int linvfs_create(struct inode *dir, struct dentry *dentry, int mode)
{
	return(linvfs_common_cr(dir, dentry, mode, VREG, 0));
}

struct dentry * linvfs_lookup(struct inode *dir, struct dentry *dentry)
{
	int		error;
	vnode_t		*vp, *cvp;
	pathname_t	pn;
	pathname_t      *pnp = &pn;
	struct inode	*ip = NULL;
	vattr_t		va;
	ino_t		ino;

	vp = LINVFS_GET_VP(dir);
	ASSERT(vp);

	/*
	 * Initialize a pathname_t to pass down.
	 */
	bzero(pnp, sizeof(pathname_t));
	pnp->pn_complen = dentry->d_name.len;
	pnp->pn_hash = dentry->d_name.hash;
	pnp->pn_path = (char *)dentry->d_name.name;

	cvp = NULL;

	VOP_LOOKUP(vp, (char *)dentry->d_name.name, &cvp, pnp, 0, NULL,
			sys_cred, error);
	if (!error) {
		ASSERT(cvp);
		bzero(&va, sizeof(va));
		va.va_mask = AT_NODEID;
		VOP_GETATTR(cvp, &va, 0, sys_cred, error);
		if (error) {
			VN_RELE(cvp);
			return ERR_PTR(-error);
		}
		ino = va.va_nodeid;
		ASSERT(ino);
		ip = iget(dir->i_sb, ino);
		if (!ip) {
			VN_RELE(cvp);
			return ERR_PTR(-EACCES);
		}
	}
	d_add(dentry, ip);	/* Negative entry goes in if ip is NULL */
	return NULL;
}


int linvfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
{
	int		error;
	vnode_t		*tdvp;	/* Target directory for new name/link */
	vnode_t		*vp;	/* vp of name being linked */
	struct inode	*ip;	/* inode of guy being linked to */

	tdvp = LINVFS_GET_VP(dir);
	ASSERT(tdvp);

	ip = old_dentry->d_inode;	/* inode being linked to */
	vp = LINVFS_GET_VP(ip);

	error = 0;
	VOP_LINK(tdvp, vp, (char *)dentry->d_name.name, sys_cred, error);
	if (!error) {
		ip->i_nlink++;
		ip->i_ctime = CURRENT_TIME;
		mark_inode_dirty(ip);
		ip->i_count++;
		VN_HOLD(vp);
		d_instantiate(dentry, ip);
	}
	return -error;
}


int linvfs_unlink(struct inode *dir, struct dentry *dentry)
{
	int		error;
	struct inode	*inode;
	vnode_t		*dvp;	/* directory containing name to remove */

	dvp = LINVFS_GET_VP(dir);
	inode = dentry->d_inode;
	ASSERT(dvp);

	/*
	 * Someday we could pass the dentry->d_inode into VOP_REMOVE so
	 * that it can skip the lookup.
	 */

	error = 0;
	VOP_REMOVE(dvp, (char *)dentry->d_name.name, sys_cred, error);
	if (!error) {
		dir->i_ctime = dir->i_mtime = CURRENT_TIME;
		dir->i_version = ++event;
		inode->i_nlink--;
		inode->i_ctime = dir->i_ctime;
		d_delete(dentry);       /* del and free inode */
	}
	return -error;
}


int linvfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
{
	int		error;
	vnode_t		*dvp;	/* directory containing name to remove */
	vnode_t		*cvp;	/* used to lookup symlink to put in dentry */
	vattr_t		va;
	pathname_t	pn;
	pathname_t      *pnp = &pn;
	struct inode	*ip = NULL;
	ino_t		ino;

	dvp = LINVFS_GET_VP(dir);
	ASSERT(dvp);

	bzero(&va, sizeof(va));
	va.va_type = VLNK;
	va.va_mode = 0777 & ~current->fs->umask;
	va.va_mask = AT_TYPE|AT_MODE; /* AT_PROJID? */
	error = 0;
	VOP_SYMLINK(dvp, (char *)dentry->d_name.name, &va, (char *)symname,
		sys_cred, error);
	if (!error) {
		/* Now need to lookup the name since we didn't get the vp back */
		/* Maybe modify VOP_SYMLINK to return *vpp? */
		/* But, lookup should hit DNLC, anyway */

		/*
		 * Initialize a pathname_t to pass down.
		 */
		bzero(pnp, sizeof(pathname_t));
		pnp->pn_complen = dentry->d_name.len;
		pnp->pn_hash = dentry->d_name.hash;
		pnp->pn_path = (char *)dentry->d_name.name;

		cvp = NULL;
		VOP_LOOKUP(dvp, (char *)dentry->d_name.name, &cvp, pnp, 0, NULL,
					sys_cred, error);
		if (!error) {
			ASSERT(cvp);
			bzero(&va, sizeof(va));
			va.va_mask = AT_NODEID;
			VOP_GETATTR(cvp, &va, 0, sys_cred, error);
			if (error) {
				VN_RELE(cvp);
				return error;
			}
			ino = va.va_nodeid;
			ASSERT(ino && (cvp->v_type == VLNK));
			ip = iget(dir->i_sb, ino);
			if (!ip) {
				error = -ENOMEM;
				VN_RELE(cvp);
			} else {
				d_instantiate(dentry, ip);
			}
		}
	}
	return -error;
}


int linvfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
	return(linvfs_common_cr(dir, dentry, mode, VDIR, 0));
}


int linvfs_rmdir(struct inode *dir, struct dentry *dentry)
{
	int		error;
	vnode_t		*dvp,	/* directory containing name to remove */
			*pwd_vp; /* current working directory, vnode */

	dvp = LINVFS_GET_VP(dir);
	ASSERT(dvp);
	ASSERT(current->fs->pwd->d_inode);

	pwd_vp = LINVFS_GET_VP(current->fs->pwd->d_inode);

	/*
	 * Someday we could pass the dentry->d_inode into VOP_REMOVE so
	 * that it can skip the lookup.
	 */

	error = 0;
	VOP_RMDIR(dvp, (char *)dentry->d_name.name, pwd_vp, sys_cred, error);
	if (!error) {
		ASSERT(dir->i_nlink == 2);
		dir->i_nlink = 0;
		dir->i_size = 0;
		dir->i_version = ++event;
		mark_inode_dirty(dir);
		dir->i_nlink--;
		dir->i_version = ++event;
		dir->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
		mark_inode_dirty(dir);
		d_delete(dentry);       /* del and free inode */
	}
	return -error;
}


int linvfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
{
	return(linvfs_common_cr(dir, dentry, mode, VBLK, rdev));
}


int linvfs_rename(struct inode *odir, struct dentry *odentry,
	       struct inode *ndir, struct dentry *ndentry)
{
	int		error;
	vnode_t		*fvp;	/* from directory */
	vnode_t		*tvp;	/* target directory */
	pathname_t	pn;
	pathname_t      *pnp = &pn;
	struct inode	*new_inode = NULL;

	bzero(pnp, sizeof(pathname_t));
	pnp->pn_complen = ndentry->d_name.len;
	pnp->pn_hash = ndentry->d_name.hash;
	pnp->pn_path = (char *)ndentry->d_name.name;

	fvp = LINVFS_GET_VP(odir);
	tvp = LINVFS_GET_VP(ndir);

	new_inode = ndentry->d_inode;

	VOP_RENAME(fvp, (char *)odentry->d_name.name, tvp,
		   (char *)ndentry->d_name.name, pnp, sys_cred, error);
	if (error)
		return -error;

	ndir->i_version = ++event;
	odir->i_version = ++event;
	if (new_inode) {
		new_inode->i_nlink--;
		new_inode->i_ctime = CURRENT_TIME;
		mark_inode_dirty(new_inode);
	}

	odir->i_ctime = odir->i_mtime = CURRENT_TIME;
	mark_inode_dirty(odir);
	return 0;
}


int linvfs_readlink(struct dentry *dentry, char *buf, int size)
{
	vnode_t	*vp;
	uio_t	uio;
	iovec_t	iov;
	int	error = 0;

	vp = LINVFS_GET_VP(dentry->d_inode);
	iov.iov_base = buf;
	iov.iov_len = size;

	uio.uio_iov = &iov;
	uio.uio_offset = 0;
	uio.uio_segflg = UIO_USERSPACE;
	uio.uio_resid = size;

	UPDATE_ATIME(dentry->d_inode);
	VOP_READLINK(vp, &uio, sys_cred, error);
	if (error)
		return -error;

	return (size - uio.uio_resid);
}


struct dentry * linvfs_follow_link(struct dentry *dentry,
				   struct dentry *base,
				   unsigned int follow)
{
	vnode_t	*vp;
	uio_t	uio;
	iovec_t	iov;
	int	error = 0;
	char	*link = kmalloc(MAXNAMELEN+1, GFP_KERNEL); 

	vp = LINVFS_GET_VP(dentry->d_inode);
	iov.iov_base = link;
	iov.iov_len = MAXNAMELEN;

	uio.uio_iov = &iov;
	uio.uio_offset = 0;
	uio.uio_segflg = UIO_SYSSPACE;
	uio.uio_resid = MAXNAMELEN;

	VOP_READLINK(vp, &uio, sys_cred, error);
	if (error) {
		kfree_s(link, MAXNAMELEN);
		return NULL;
	}

	link[MAXNAMELEN - uio.uio_resid] = '\0';

	UPDATE_ATIME(dentry->d_inode);
	base = lookup_dentry(link, base, follow);
	kfree_s(link, MAXNAMELEN);
	return base;
}

/*
 * The following is used to get a list of blocks of the given
 * size from a pagebuf_bmap_t returned by VOP_BMAP.
 */

static void
_linvfs_set_blocks(
	int		*blocks,	/* Array of blocknos to fill in */
	int		nblocks,	/* number of elms in blocks array */
	pb_bmap_t	*pbmap,		/* mappings of blocks (big for XFS) */
	int		npbmaps,	/* number of pbmap entries */
	int		block_bits,	/* bits (max size) of blocks e.g. 9 for 512 */
	ssize_t		blocksize)	/* size of block */
{
	int i, *p, bmaps, blockno;

	i = nblocks;
        p = blocks;

	/* Zero out all the blocks, first */
	while(i) {
		*p++ = 0;
		i--;
	}

        i = nblocks;
	p = blocks;

	/*
	 * We need to walk the bmaps to cover the case where this page
	 * spans extents.
	 */

	for(bmaps = 0; bmaps < npbmaps && i; bmaps++, pbmap++) {

		/*
		 * While we have room in this bmap, bump the block number if real.
		 * If the block number is a hole or unwritten, make the blockno 0.
		 * This will cause a clearing of the associated memory.
		 */
		while(pbmap->pbm_bsize && i) {
			blockno = (long)pbmap->pbm_bn;
			if (pbmap->pbm_flags & (PBMF_HOLE|PBMF_UNWRITTEN)) {
				printk("_linvfs_set_blocks have HOLE or UNWRITTEN\n");
				blockno = 0;
			} else if (pbmap->pbm_offset) {
				if ( pbmap->pbm_offset % blocksize) {
					printk("_linvfs_set_blocks: this shouldn't happen %lld %d!\n",
					   pbmap->pbm_offset, blocksize);
				}
				blockno += pbmap->pbm_offset >> block_bits;
				pbmap->pbm_offset = 0;
				pbmap->pbm_bn = blockno;
			}

			*p = blockno;
			p++;
			i--;
			if (pbmap->pbm_bn > 0) {
				pbmap->pbm_bn++;
			}
			pbmap->pbm_bsize -= blocksize;
		}
        }
	return;
}


int linvfs_readpage(struct file *filp, struct page *page)
{
	int rval;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1)
	struct dentry	*dentry = filp->f_dentry;
	struct inode	*inode = dentry->d_inode;
	vnode_t		*vp;
	int		nr[PAGE_SIZE/512];
#define LINBMAP_MAX 4
	pb_bmap_t	pbmap[LINBMAP_MAX], *pbmapp = &pbmap[0];
	int		npbmaps = LINBMAP_MAX, map, pgoff, sz;
	int		error;

	atomic_inc(&page->count);
	set_bit(PG_locked, &page->flags);
	set_bit(PG_free_after, &page->flags);
	
	vp = LINVFS_GET_VP(inode);

	VOP_RWLOCK(vp, VRWLOCK_READ);
	VOP_BMAP(vp, page->offset, PAGE_SIZE, B_READ, pbmapp, &npbmaps, error);
	VOP_RWUNLOCK(vp, VRWLOCK_READ);

	if (error) {
		set_bit(PG_error, &page->flags);
		clear_bit(PG_locked, &page->flags);
		atomic_dec(&page->count);
		return -EIO;
	}

	/*
	 * While the page isn't done, move through the mappings either zero'ing
	 * out or reading real blocks. This will usually just deal with one
	 * one mapping, but ...
	 */

	pgoff = 0;
	sz = PAGE_SIZE;
	map = 0;
	while(sz > 0 && map < npbmaps) {
		int msize;

		if (pgoff) {
			printk("linvfs_readpage: iterating?? JIMJIM remove me %d\n",
				pgoff);
		}
		pbmapp = &pbmap[map];
		msize = pbmapp->pbm_bsize - pbmapp->pbm_offset;
		if (msize > (PAGE_SIZE - pgoff)) {
			msize = PAGE_SIZE - pgoff;
		}

		if (pbmapp->pbm_flags & (PBMF_HOLE|PBMF_UNWRITTEN)) {
			memset((void *)(page_address(page) + pgoff), 0, msize);
			set_bit(PG_uptodate, &page->flags);
			clear_bit(PG_locked, &page->flags);
			wake_up(&page->wait);
			rval = 0;
		} else {
			_linvfs_set_blocks(nr, PAGE_SIZE/512, pbmapp, npbmaps - map,
				inode->i_sb->s_blocksize_bits, inode->i_sb->s_blocksize);
	
			/* IO start */
			rval = brw_page(READ, page, inode->i_dev, nr,
				inode->i_sb->s_blocksize, 1);
		}
		sz -= msize;
		pgoff += msize;
		if (msize == pbmapp->pbm_bsize - pbmapp->pbm_offset) {
			map++;
		}
	}
#else
	rval = block_read_full_page(filp, page);
#endif
	return(rval);
}

int linvfs_writepage(struct file *filp, struct page *page)
{
	int rval;
	rval = -ENOSYS;
	printk("linvfs_writepage: NOT IMPLEMENTED\n");
	return(rval);
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1)
int linvfs_bmap(struct inode *inode, int block)
#else
int linvfs_get_block(struct inode *inode, long block, struct buffer_head *bh_result, int create)
#endif
{
	vnode_t		*vp;
	int		block_shift = inode->i_sb->s_blocksize_bits;
	off_t		offset = block << block_shift;
	ssize_t		count = inode->i_sb->s_blocksize;
	pb_bmap_t	pbmap;
	int		npbmaps = 1;
	int		error;
	long		blockno;

	if (block < 0) {
		printk(__FUNCTION__": called with block of -1\n");
		return -EIO;
	}

	vp = LINVFS_GET_VP(inode);

	VOP_RWLOCK(vp, VRWLOCK_READ);
	VOP_BMAP(vp, offset, count, B_READ,(struct page_buf_bmap_s *) &pbmap, &npbmaps, error);
	VOP_RWUNLOCK(vp, VRWLOCK_READ);
	if (error)
		return -EIO;
	/*
	 * JIMJIM This interface needs to be fixed to support 64 bit
	 * block numbers.
	 */
	
	blockno = (long)pbmap.pbm_bn;
	if (blockno < 0) return 0;
	if (pbmap.pbm_offset) {
		if ( pbmap.pbm_offset % count) {
			printk("linvfs_bmap: this shouldn't happen %lld %d!\n",
				pbmap.pbm_offset,
				count);
		}
		blockno += pbmap.pbm_offset >> block_shift;
	}

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1)
	return(blockno >> (block_shift - 9));
#else
	if (!create) {
		bh_result->b_dev = inode->i_dev;
		bh_result->b_blocknr = blockno >> (block_shift - 9);
		bh_result->b_state |= (1UL << BH_Mapped);

		return 0;
	}

	return -EIO;
#endif
}

/*
 * JIMJIM have linvfs_updatepage use a new VOP if we are going to keep it
 * around. We might just want to use _pagebuf_file_write and the bmap, ...
 * instead of having this interface.
 */

int
linvfs_updatepage(struct file *filp, struct page *page, const char *buf,
	unsigned long offset, unsigned int bytes, int sync)
{
	vnode_t		*vp;
	struct dentry	*dentry = filp->f_dentry;
	struct inode	*inode = dentry->d_inode;
	unsigned	long block;
	int		*p, nr[PAGE_SIZE/512], error;
	int		wrote = 0, status, i, bmaps, block_bits;
	pb_bmap_t	pbmap[2];
	int		npbmaps = 2;
	long		blockno;

	/* printk("linvfs_updatepage(%s/%s %d@%ld, sync=%d)\n",
		dentry->d_parent->d_name.name, dentry->d_name.name,
		bytes, page->offset+offset, sync); */
	
	vp = LINVFS_GET_VP(inode);

	VOP_RWLOCK(vp, VRWLOCK_READ);
	VOP_BMAP(vp, page->offset, offset+bytes, B_WRITE,
		(struct page_buf_bmap_s *) &pbmap, &npbmaps, error);
	VOP_RWUNLOCK(vp, VRWLOCK_READ);
	if (error)
		return -EIO;

	atomic_inc(&page->count);

        set_bit(PG_locked, &page->flags);
        set_bit(PG_free_after, &page->flags);

	_linvfs_set_blocks(nr, PAGE_SIZE/512, &pbmap[0], npbmaps,
		inode->i_sb->s_blocksize_bits, inode->i_sb->s_blocksize);

	/* bytes can't be more than one page. */
	ASSERT(bytes <= PAGE_CACHE_SIZE);

	/*
	 * Clear the page up to starting offset in the page we
	 * are updating if that offset is beyond EOF.
	 * Otherwise, it has good data. We need to skip any good
	 * data within the page, too.
	 */

	if (offset && ((page->offset+offset) > inode->i_size)) {
		int good_bytes;

		/*
		 * Figure out how many bytes in the page are within
		 * the file and don't zero those.
		 */
		if (inode->i_size > page->offset) {
			good_bytes = inode->i_size - page->offset;
		} else {
			good_bytes = 0;
		}

		if (good_bytes >= PAGE_SIZE) {
			printk("linvfs_updatepage: NEW CODE no clear, good bytes %d\n",
				good_bytes);
		} else {
			memset((void *)(page_address(page)+good_bytes), 0, offset);
		}
	}

	/* Copy down the user's data */
	bytes -= copy_from_user((unsigned long *)(page_address(page) + offset),
						buf, bytes);
	

out_error:
	if (!bytes) {
		wrote = -EFAULT;
		clear_bit(PG_locked, &page->flags);
		clear_bit(PG_uptodate, &page->flags);
		goto out;
	}

	/* Now, write out the page to the right spot */
	status = brw_page(WRITE, page, inode->i_dev, nr, inode->i_sb->s_blocksize, 1);

	if (status < 0) /* Return error below if this failed */
		wrote = status;
	else
		wrote = bytes;

out:
	wake_up(&page->wait);
	return(wrote); /* The amount written */
}

/* Brute force approach for now - copy data into linux inode
 * from the results of a getattr. This gets called out of things
 * like stat.
 */
int linvfs_revalidate(struct dentry *dentry)
{
        struct inode *inode = dentry->d_inode;
        vnode_t *vp;
        vattr_t va;
        int     error;

        vp = LINVFS_GET_VP(inode);
        va.va_mask = AT_STAT;
        VOP_GETATTR(vp, &va, 0, sys_cred, error);

        inode->i_mode = VTTOIF(va.va_type) | va.va_mode;
        inode->i_nlink = va.va_nlink;
        inode->i_uid = va.va_uid;
        inode->i_gid = va.va_gid;
        inode->i_rdev = MKDEV(emajor(va.va_rdev), eminor(va.va_rdev));
        inode->i_size = va.va_size;
        inode->i_blocks = va.va_nblocks;
        inode->i_blksize = va.va_blksize;
        inode->i_atime = va.va_atime.tv_sec;
        inode->i_mtime = va.va_mtime.tv_sec;
        inode->i_ctime = va.va_ctime.tv_sec;

        return 0;
}

int
linvfs_pb_bmap(struct inode *inode, 
			   loff_t offset,
			   size_t count,
			   pb_bmap_t *pbmapp,
			   int maxpbbm, 
			   int *retpbbm, 
			   int flags/* page_buf_flags_t */)
{
	vnode_t		*vp;
	int		block_shift = inode->i_sb->s_blocksize_bits;
	pb_bmap_t	pbmap;
	int		npbmaps = 1;
	int		error, vop_flags;
	long		blockno;

	/*
	 * First map the flags into vop_flags.
	 */

	switch(flags) {
	case PBF_READ:
		vop_flags = B_READ;
		break;

	case PBF_WRITE:
		vop_flags = B_WRITE;
		break;
	
	default:
		printk("linvfs_pb_bmap: flag %x not implemented\n", flags);
		return -EINVAL;
	}

	vp = LINVFS_GET_VP(inode);

	*retpbbm = maxpbbm;

	VOP_RWLOCK(vp, VRWLOCK_READ);
	VOP_BMAP(vp, offset, count, vop_flags,
			(struct page_buf_bmap_s *) pbmapp, retpbbm, error);
	VOP_RWUNLOCK(vp, VRWLOCK_READ);

	return error;
}

struct inode_operations linvfs_file_inode_operations =
{
  &linvfs_file_operations,
  NULL,	 /*  create  */
  NULL,	 /*  lookup  */
  NULL,	 /*  link  */
  NULL,	 /*  unlink  */
  NULL,	 /*  symlink  */
  NULL,	 /*  mkdir  */
  NULL,	 /*  rmdir  */
  NULL,	 /*  mknod  */
  NULL,	 /*  rename  */
  NULL,	 /*  readlink  */
  NULL,	 /*  follow_link  */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1)
  linvfs_readpage,
  linvfs_writepage,
  linvfs_bmap,
#else
  linvfs_get_block,
  linvfs_readpage,
  linvfs_writepage,
  block_flushpage,
#endif
  NULL,	 /*  truncate  */
  NULL,  /*  permission  */
  NULL,	 /*  smap  */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1)
  linvfs_updatepage,  /*  updatepage  */
#endif
  linvfs_revalidate,
  linvfs_pb_bmap
};

struct inode_operations linvfs_dir_inode_operations =
{
  &linvfs_dir_operations,
  linvfs_create,
  linvfs_lookup,
  linvfs_link,	
  linvfs_unlink,	
  linvfs_symlink,	
  linvfs_mkdir,	
  linvfs_rmdir,	
  linvfs_mknod,	
  linvfs_rename,	
  NULL,	 /*  readlink  */
  NULL,	 /*  follow_link  */
  NULL,	 /*  readpage  */
  NULL,	 /*  writepage  */
  NULL,	 /*  bmap  */
  NULL,	 /*  truncate  */
  NULL,  /*  permission  */
  NULL,  /*  smap  */
  NULL,  /*  updatepage  */
  linvfs_revalidate
};

struct inode_operations linvfs_symlink_inode_operations =
{
  NULL,  /*  struct file_operations  */
  NULL,	 /*  create  */
  NULL,	 /*  lookup  */
  NULL,	 /*  link  */
  NULL,	 /*  unlink  */
  NULL,	 /*  symlink  */
  NULL,	 /*  mkdir  */
  NULL,	 /*  rmdir  */
  NULL,	 /*  mknod  */
  NULL,	 /*  rename  */
  linvfs_readlink,
  linvfs_follow_link,
  NULL,	 /*  readpage  */
  NULL,	 /*  writepage  */
  NULL,	 /*  bmap  */
  NULL,	 /*  truncate  */
  NULL,	 /*  permission  */
  NULL,	 /*  smap  */
  NULL,  /*  updatepage  */
  linvfs_revalidate
};