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

File: [Development] / xfs-linux-nodel / Attic / xfs_handle.c (download)

Revision 1.20, Sat Feb 3 00:28:04 1996 UTC (21 years, 8 months ago) by huy
Branch: MAIN
Changes since 1.19: +4 -2 lines

variable used to hold the open vnode needs to be different from
the one used to hold the return vnode. This needs to be enforced so
that the locking on the open vnode interposition chain doesn't get messed up
when the return vnode is different from the original opened vnode.

/**************************************************************************
 *                                                                        *
 *               Copyright (C) 1994, Silicon Graphics, Inc.               *
 *                                                                        *
 *  These coded instructions, statements, and computer programs  contain  *
 *  unpublished  proprietary  information of Silicon Graphics, Inc., and  *
 *  are protected by Federal copyright law.  They  may  not be disclosed  *
 *  to  third  parties  or copied or duplicated in any form, in whole or  *
 *  in part, without the prior written consent of Silicon Graphics, Inc.  *
 *                                                                        *
 **************************************************************************/

#ident "$Revision: 1.20 $"

#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/cred.h>
#include <sys/file.h>
#include <sys/kabi.h>
#include <sys/kmem.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/sat.h>
#include "xfs_handle.h"
#include <sys/debug.h>


/*
 *  Ok.  This file contains stuff that defines a general handle
 *  mechanism, except that it's only used by xfs at the moment.
 *  Imagine that it's in some generic place.  The same goes for
 *  xfs_handle.h; it's would-be generic but xfs-only right now.
 */


int	vp_open ( vnode_t *vp, int filemode, struct cred * );


STATIC int
copyout_handle (
	handle_t	*handle,	/* handle data to copy out */
	size_t		hsize,		/* amount of data in handle */
	void		*hbuf,		/* user's data buffer */
	size_t		*hlen)		/* user's data buffer's size */
{
	if (copyout (handle, hbuf, (int) hsize))
		return EFAULT;
#ifdef _K64U64
	if (ABI_IS_64BIT(get_current_abi())) {
		if (copyout(&hsize, hlen, sizeof *hlen))
			return EFAULT;
		return 0;
	}
#endif
	if (suword(hlen, (int) hsize))
		return EFAULT;
	return 0;
}

/*
 *  Return the handle for the object named by path.
 */

int
path_to_handle (
	char	*path,		/* any path name */
	void	*hbuf,		/* user's data buffer */
	size_t	*hlen)		/* set to size of data copied to hbuf */
{
	int		error;
	vnode_t		*vp;
	handle_t	handle;

	error = lookupname (path, UIO_USERSPACE, NO_FOLLOW, NULLVPP, &vp);
	if (error)
		return error;
	error = vp_to_handle (vp, &handle);
	VN_RELE (vp);
	if (error)
		return error;
	return copyout_handle (&handle, HSIZE (handle), hbuf, hlen);
}


/*
 *  Return the handle for the file system containing the object named by path.
 */

int
path_to_fshandle (
	char	*path,		/* any path name */
	void	*hbuf,		/* user's data buffer */
	size_t	*hlen)		/* set to size of data copied to hbuf */
{
	int		error;
	vnode_t		*vp;
	handle_t	handle;

	error = lookupname (path, UIO_USERSPACE, NO_FOLLOW, NULLVPP, &vp);
	if (error)
		return error;
	error = vp_to_handle (vp, &handle);
	VN_RELE (vp);
	if (error)
		return error;
	return copyout_handle (&handle, sizeof (fsid_t), hbuf, hlen);
}


int
fd_to_handle (
	int	fd,		/* any file descriptor */
	void	*hbuf,		/* user's data buffer */
	size_t	*hlen)		/* set to size of data copied to hbuf */
{
	int		error;
	vnode_t		*vp;
	file_t		*fp;
	handle_t	handle;

	error = getf (fd, &fp);
	if (error)
		return error;
	vp = fp->f_vnode;
	error = vp_to_handle (vp, &handle);
	if (error)
		return error;
	return copyout_handle (&handle, HSIZE (handle), hbuf, hlen);
}


/*
 *  Just like readlink, except that it uses a handle instead of a path
 *  to locate the vnode.
 */

int
readlink_by_handle (
	void		*hanp,			/* handle data */
	size_t		hlen,			/* size of handle data */
	void		*buf,			/* return buffer */
	size_t		bufsiz,			/* size provided */
	rval_t		*rvp)
{
	int		error;
	handle_t	handle;
	vnode_t		*vp;
	struct	iovec	aiov;
	struct	uio	auio;

	error = gethandle (hanp, hlen, &handle);
	if (error)
		return error;
	vp = handle_to_vp (&handle);
	if (vp == NULL)
		return EINVAL;
	if (vp->v_type != VLNK) {
		error = EINVAL;
		goto out;
        }

	aiov.iov_base	= buf;
	aiov.iov_len	= bufsiz;
	auio.uio_iov	= &aiov;
	auio.uio_iovcnt	= 1;

	auio.uio_pio	= 0;
	auio.uio_pbuf	= 0;

	auio.uio_fmode	= FINVIS;		/* not used otherwise */
	auio.uio_offset	= 0;
	auio.uio_segflg	= UIO_USERSPACE;
	auio.uio_resid	= bufsiz;
	VOP_READLINK (vp, &auio, get_current_cred(), error);
out:
	VN_RELE (vp);
	rvp->r_val1 = bufsiz - auio.uio_resid;
	return error;
}


/*
 *  Open a file specified by a file handle.  Follow the same basic
 *  algorithm as copen, cutting out some things not needed bacause
 *  of self-imposed restrictions.  The restrictions are that only
 *  directories and regular files are allowed and none of the funky
 *  open flags are used.
 *
 *  Always sets the FINVIS flag.
 */

int
open_by_handle (
	void		*hanp,			/* handle data */
	size_t		hlen,			/* size of handle data */
	int		filemode,		/* open mode */
	rval_t		*rvp)
{
	vnode_t		*vp;
	file_t		*fp;
	int		fd;
	int		error;
	handle_t	handle;
	int		sat_flags = filemode;	/* what was requested */

	filemode -= FOPEN;
	filemode &= (FREAD|FWRITE);
	if ((filemode & (FREAD|FWRITE)) == 0)
		return EINVAL;
	filemode |= FINVIS;

	error = gethandle (hanp, hlen, &handle);
	if (error)
		return error;
	vp = handle_to_vp (&handle);
	if (vp == NULL)
		return EINVAL;

	switch (vp->v_type) {
		case VDIR:
		case VREG:
			break;
		default:
			error = EINVAL;
			goto out;
	}

	error = falloc ((vnode_t *) NULL, filemode, &fp, &fd);
	if (error)
		goto out;

	/*
	 * a kludge for the usema device - it really needs to know
	 * the 'fp' for it opening file - we use u_openfp to mark
	 * this.
	 */
	curprocp->p_user->u_openfp = fp;

	if ((filemode & FWRITE) == 0)
		_SAT_PNALLOC(SAT_OPEN_RO);
	else
		_SAT_PNALLOC(SAT_OPEN);

	error = vp_open (vp, filemode, get_current_cred());

	curprocp->p_user->u_openfp = NULL;
	if (error) {
		setf (fd, NULLFP);
		unfalloc (fp);
	} else {
		fp->f_vnode = vp;
		rvp->r_val1 = fd;
		fready (fp);
	}

out:
	if (error)
		VN_RELE (vp);
	_SAT_OPEN (rvp->r_val1, (filemode&FCREAT), sat_flags, error);
	return error;
}


/*
 *  vp_open - based on vn_open.  Differences are
 *	+ gets a vp passed to it, instead of using lookupname
 *	+ restricted to FREAD and FWRITE, always has FINVIS
 *	+ restricted to directories and regular files.
 */

int
vp_open (
	vnode_t		*vp,
	int		filemode,
	cred_t		*crp)
{
	struct vnode *tvp;
	register int mode;
	register int error;
	struct vnode *openvp;

	tvp = (struct vnode *) NULL;
	mode = 0;
	filemode &= (FREAD | FWRITE | FINVIS);
	if (filemode & FREAD)
		mode |= VREAD;
	if (filemode & FWRITE)
		mode |= VWRITE;
 
	tvp = vp;
	VN_HOLD (tvp);

	VOP_SETFL (vp, 0, filemode, crp, error);
	if (error)
		goto out;

	/*
	 * Can't write directories, active texts, swap files,
	 * or read-only filesystems.  
	 */
	if (filemode & FWRITE) {
		if (vp->v_type == VDIR) {
			error = EISDIR;
			goto out;
		}
		if (vp->v_vfsp->vfs_flag & VFS_RDONLY) {
			error = EROFS;
			goto out;
		}
		if ((vp->v_flag & VISSWAP) && vp->v_type == VREG) {
			error = EBUSY;
			goto out;
		}
	}
	/* Check discretionary permissions. */
	VOP_ACCESS (vp, mode, 0, crp, error);
	if (error)
		goto out;

	/*
	 * Do opening protocol.
	 */
	openvp = vp;
	VOP_OPEN (openvp, &vp, filemode, crp, error);
	if (!error) {
		if (tvp)
			VN_RELE (tvp);
	}
out:
	if (error) {
		if (tvp)
			VN_RELE (tvp);
	}
	return error;
}


/*
 *  Make a handle from a vnode.
 */

int
vp_to_handle (
	vnode_t		*vp,
	handle_t	*handlep)
{
	int		error;
	struct	fid	*fidp = NULL;
	struct	fid	fid;
	int		hsize;

	if (vp->v_vfsp->vfs_altfsid == NULL)
		return EINVAL;
	switch (vp->v_type) {
		case VREG:
		case VDIR:
		case VLNK:
			break;
		default:
			return EBADF;
	}
#ifdef SMALLFIDS
	VOP_FID (vp, &fidp, error);
#else
	VOP_FID2 (vp, &fid, error);
	fidp = &fid;
#endif /* SMALLFIDS */
	if (error)
		return error;
	if (fidp == NULL)
		return EIO;		/* FIX: what real errno? */
	bcopy (vp->v_vfsp->vfs_altfsid, &handlep->ha_fsid, sizeof (fsid_t));
	/* Copy only the currently used part of the fid struct */
	bcopy (fidp, &handlep->ha_fid, fidp->fid_len + sizeof fidp->fid_len);
	hsize = HSIZE (*handlep);
	bzero ((char *) handlep + hsize, sizeof *handlep - hsize);
#ifdef SMALLFIDS
	freefid (fidp);
#endif
	return 0;
}


/*
 *  Get a vnode for the object referenced by handle.
 */

struct vnode *
handle_to_vp (
	handle_t	*handlep)
{
	struct	vfs	*vfsp;
	struct	vnode	*vp;
		int	error;

	vfsp = altgetvfs (&handlep->ha_fsid);
	if (vfsp == NULL)
		return NULL;
	error = VFS_VGET (vfsp, &vp, &handlep->ha_fid);
	if (error)
		return NULL;
	return vp;
}


/*
 *  Get a handle of the form (void *, size_t) from user space and
 *  convert it to a handle_t handle.  Do as much validation of the
 *  result as possible; the content of each fid still has to be
 *  checked by its consumer.
 */

int
gethandle (
	void		*hanp,		/* input,  handle data */
	size_t		hlen,		/* input,  size of handle data */
	handle_t	*hp)		/* output, copy of data */
{
	if (hlen < sizeof hp->ha_fsid || hlen > sizeof *hp)
		return EINVAL;
	if (copyin (hanp, hp, hlen))
		return EFAULT;
	if (hlen < sizeof *hp)
		bzero (((char *) hp) + hlen, sizeof *hp - hlen);
	if (hlen == sizeof hp->ha_fsid)	
		return 0;	/* FS handle, nothing more to check */
	if (hp->ha_fid.fid_len != (hlen - sizeof hp->ha_fsid -
	    sizeof hp->ha_fid.fid_len) || *((short *) hp->ha_fid.fid_data))
	{
		return EINVAL;
	}
	return 0;
}


/*
 *  An alternate version of getvfs.  Needed because handles require
 *  a file system ID that remains constant for the life of the file
 *  system.  Cloned from getvfs in os/vfs.c, where this might arguably
 *  belong.  It's here because it's only used by things related to
 *  these handles.
 */

struct vfs *
altgetvfs (fsid_t *fsid)
{
	register struct vfs *vfsp;
	int s;

	s = vfs_spinlock();
	for (vfsp = rootvfs; vfsp != NULL; vfsp = vfsp->vfs_next) {
		if (vfsp->vfs_altfsid &&
		    vfsp->vfs_altfsid->val[0] == fsid->val[0] &&
		    vfsp->vfs_altfsid->val[1] == fsid->val[1] &&
		    !(vfsp->vfs_flag & VFS_OFFLINE)) {
			break;
		}
	}
	vfs_spinunlock(s);
	return vfsp;
}