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

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

Revision 1.84, Wed Feb 26 17:15:24 2003 UTC (14 years, 8 months ago) by lord
Branch: MAIN
Changes since 1.83: +6 -5 lines

rework readdir to be closer to the irix model internally, do all
the filldir fixup at the linvfs layer. This is the V2 directory
component, the V1 code still needs fixing up. We now return the
same directory offsets as Irix does.
Merge of 2.4.x-xfs:slinx:134646a by lord.

  Merge of 2.4.x-xfs-dev:slinx:134646a by lord.
  do directory offset fixup in linvfs_readdir

/*
 * Copyright (c) 2000-2002 Silicon Graphics, Inc.  All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it would be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * Further, this software is distributed without any warranty that it is
 * free of the rightful claim of any third person regarding infringement
 * or the like.	 Any license provided herein, whether implied or
 * otherwise, applies only to this software file.  Patent licenses, if
 * any, provided herein do not apply to combinations of this program with
 * other software, or any other product whatsoever.
 *
 * 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.
 *
 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
 * Mountain View, CA  94043, or:
 *
 * http://www.sgi.com
 *
 * For further information regarding this notice, see:
 *
 * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
 */

#include <xfs.h>
#include <linux/dcache.h>
#include <linux/pagemap.h>
#include <linux/slab.h>
#include <linux/mman.h> /* for PROT_WRITE */

static struct vm_operations_struct linvfs_file_vm_ops;


STATIC ssize_t
linvfs_readv(
	struct file		*filp,
	const struct iovec	*iovp,
	unsigned long		nr_segs,
	loff_t			*ppos)
{
	vnode_t			*vp = LINVFS_GET_VP(filp->f_dentry->d_inode);
	int			error;

	VOP_READ(vp, filp, iovp, nr_segs, ppos, NULL, error);

	return error;
}


STATIC ssize_t
linvfs_writev(
	struct file		*filp,
	const struct iovec	*iovp,
	unsigned long		nr_segs,
	loff_t			*ppos)
{
	struct inode		*inode = filp->f_dentry->d_inode;
	vnode_t			*vp = LINVFS_GET_VP(inode);
	int			error = filp->f_error;

	if (unlikely(error)) {
		filp->f_error = 0;
		return error;
	}

	/*
	 * We allow multiple direct writers in, there is no
	 * potential call to vmtruncate in that path.
	 */
	if (filp->f_flags & O_DIRECT) {
		VOP_WRITE(vp, filp, iovp, nr_segs, ppos, NULL, error);
	} else {
		down(&inode->i_sem);
		VOP_WRITE(vp, filp, iovp, nr_segs, ppos, NULL, error);
		up(&inode->i_sem);
	}

	return error;
}


STATIC ssize_t
linvfs_read(
	struct file		*filp,
	char			*buf,
	size_t			count,
	loff_t			*ppos)
{
	struct iovec		iov = {buf, count};

	return linvfs_readv(filp, &iov, 1, ppos);
}


STATIC ssize_t
linvfs_write(
	struct file		*file,
	const char		*buf,
	size_t			count,
	loff_t			*ppos)
{
	struct iovec		iov = {(void *)buf, count};

	return linvfs_writev(file, &iov, 1, ppos);
}


STATIC ssize_t
linvfs_aio_read(
	struct kiocb		*iocb,
	char			*buf,
	size_t			count,
	loff_t			pos)
{
	struct iovec		iov = {buf, count};

	return linvfs_readv(iocb->ki_filp, &iov, 1, &iocb->ki_pos);
}


STATIC ssize_t
linvfs_aio_write(
	struct kiocb		*iocb,
	const char		*buf,
	size_t			count,
	loff_t			pos)
{
	struct iovec		iov = {(void *)buf, count};

	return linvfs_writev(iocb->ki_filp, &iov, 1, &iocb->ki_pos);
}

STATIC ssize_t
linvfs_sendfile(
	struct file		*filp,
	loff_t			*ppos,
	size_t			count,
	read_actor_t		actor,
	void			*target)
{
	vnode_t			*vp = LINVFS_GET_VP(filp->f_dentry->d_inode);
	int			error;

	VOP_SENDFILE(vp, filp, ppos, count, actor, target, NULL, error);

	return error;
}


STATIC int
linvfs_open(
	struct inode	*inode,
	struct file	*filp)
{
	vnode_t		*vp = LINVFS_GET_VP(inode);
	int		error;

	if (!(filp->f_flags & O_LARGEFILE) && inode->i_size > MAX_NON_LFS)
		return -EFBIG;

	ASSERT(vp);
	VOP_OPEN(vp, NULL, error);
	return -error;
}


STATIC int
linvfs_release(
	struct inode	*inode,
	struct file	*filp)
{
	vnode_t		*vp = LINVFS_GET_VP(inode);
	int		error = 0;

	if (vp)
		VOP_RELEASE(vp, error);
	return -error;
}


STATIC int
linvfs_fsync(
	struct file	*filp,
	struct dentry	*dentry,
	int		datasync)
{
	struct inode	*inode = dentry->d_inode;
	vnode_t		*vp = LINVFS_GET_VP(inode);
	int		error;
	int		flags = FSYNC_WAIT;

	if (datasync)
		flags |= FSYNC_DATA;

	ASSERT(vp);

	VOP_FSYNC(vp, flags, NULL, (xfs_off_t)0, (xfs_off_t)-1, error);

	return -error;
}

/*
 * linvfs_readdir maps to VOP_READDIR().
 * We need to build a uio, cred, ...
 */

#define nextdp(dp)	((struct xfs_dirent *)((char *)(dp) + (dp)->d_reclen))

STATIC int
linvfs_readdir(
	struct file	*filp,
	void		*dirent,
	filldir_t	filldir)
{
	int		error = 0;
	vnode_t		*vp;
	uio_t		uio;
	iovec_t		iov;
	int		eof = 0;
	caddr_t		read_buf;
	int		namelen, size = 0;
	size_t		rlen = PAGE_CACHE_SIZE;
	xfs_off_t	start_offset, curr_offset;
	xfs_dirent_t	*dbp = NULL;

	vp = LINVFS_GET_VP(filp->f_dentry->d_inode);
	ASSERT(vp);

	/* Try fairly hard to get memory */
	do {
		if ((read_buf = (caddr_t)kmalloc(rlen, GFP_KERNEL)))
			break;
		rlen >>= 1;
	} while (rlen >= 1024);

	if (read_buf == NULL)
		return -ENOMEM;

	uio.uio_iov = &iov;
	uio.uio_fmode = filp->f_mode;
	uio.uio_segflg = UIO_SYSSPACE;
	curr_offset = uio.uio_offset = filp->f_pos;

	while (!eof) {
		uio.uio_resid = iov.iov_len = rlen;
		iov.iov_base = read_buf;
		uio.uio_iovcnt = 1;

		start_offset = uio.uio_offset;

		VOP_READDIR(vp, &uio, NULL, &eof, error);
		if ((uio.uio_offset == start_offset) || error) {
			size = 0;
			break;
		}

		size = rlen - uio.uio_resid;
		dbp = (xfs_dirent_t *)read_buf;
		while (size > 0) {
			namelen = strlen(dbp->d_name);

			if (filldir(dirent, dbp->d_name, namelen,
					(loff_t) curr_offset,
					(ino_t) dbp->d_ino,
					DT_UNKNOWN)) {
				goto done;
			}
			size -= dbp->d_reclen;
			curr_offset = (loff_t)dbp->d_off;
			dbp = nextdp(dbp);
		}
	}
done:
	if (!error) {
		if (size == 0)
			filp->f_pos = uio.uio_offset;
		else if (dbp)
			filp->f_pos = curr_offset;
	}

	kfree(read_buf);
	return -error;
}

STATIC int
linvfs_file_mmap(
	struct file	*filp,
	struct vm_area_struct *vma)
{
	struct inode	*ip = filp->f_dentry->d_inode;
	vnode_t		*vp = LINVFS_GET_VP(ip);
	vattr_t		va = { .va_mask = XFS_AT_UPDATIME };
	int		error;

	if ((vp->v_type == VREG) && (vp->v_vfsp->vfs_flag & VFS_DMI)) {
		error = -xfs_dm_send_mmap_event(vma, 0);
		if (error)
			return error;
	}

	vma->vm_ops = &linvfs_file_vm_ops;

	VOP_SETATTR(vp, &va, XFS_AT_UPDATIME, NULL, error);
	UPDATE_ATIME(ip);
	return 0;
}


STATIC int
linvfs_ioctl(
	struct inode	*inode,
	struct file	*filp,
	unsigned int	cmd,
	unsigned long	arg)
{
	int		error;
	vnode_t		*vp = LINVFS_GET_VP(inode);

	ASSERT(vp);
	VOP_IOCTL(vp, inode, filp, cmd, arg, error);
	VMODIFY(vp);

	/* NOTE:  some of the ioctl's return positive #'s as a
	 *	  byte count indicating success, such as
	 *	  readlink_by_handle.  So we don't "sign flip"
	 *	  like most other routines.  This means true
	 *	  errors need to be returned as a negative value.
	 */
	return error;
}

#ifdef HAVE_VMOP_MPROTECT
STATIC int
linvfs_mprotect(
	struct vm_area_struct *vma,
	unsigned int	newflags)
{
	vnode_t		*vp = LINVFS_GET_VP(vma->vm_file->f_dentry->d_inode);
	int		error = 0;

	if ((vp->v_type == VREG) && (vp->v_vfsp->vfs_flag & VFS_DMI)) {
		if ((vma->vm_flags & VM_MAYSHARE) &&
		    (newflags & PROT_WRITE) && !(vma->vm_flags & PROT_WRITE)){
			error = xfs_dm_send_mmap_event(vma, VM_WRITE);
		    }
	}
	return error;
}
#endif /* HAVE_VMOP_MPROTECT */


struct file_operations linvfs_file_operations = {
	.llseek		= generic_file_llseek,
	.read		= linvfs_read,
	.write		= linvfs_write,
	.readv		= linvfs_readv,
	.writev		= linvfs_writev,
	.aio_read	= linvfs_aio_read,
	.aio_write	= linvfs_aio_write,
	.sendfile	= linvfs_sendfile,
	.ioctl		= linvfs_ioctl,
	.mmap		= linvfs_file_mmap,
	.open		= linvfs_open,
	.release	= linvfs_release,
	.fsync		= linvfs_fsync,
};

struct file_operations linvfs_dir_operations = {
	.read		= generic_read_dir,
	.readdir	= linvfs_readdir,
	.ioctl		= linvfs_ioctl,
	.fsync		= linvfs_fsync,
};

static struct vm_operations_struct linvfs_file_vm_ops = {
	.nopage		= filemap_nopage,
#ifdef HAVE_VMOP_MPROTECT
	.mprotect	= linvfs_mprotect,
#endif
};