view usr/src/uts/common/fs/sharefs/sharefs_vnops.c @ 3957:86c9dda5df37

PSARC/2007/052 In-kernel Sharetab 6371468 /etc/dfs/sharetab should be a mntfs style file
author th199096
date Mon, 02 Apr 2007 21:38:04 -0700
parents
children 3e3d6de427e8
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <fs/fs_subr.h>

#include <sys/errno.h>
#include <sys/file.h>
#include <sys/kmem.h>
#include <sys/kobj.h>
#include <sys/cmn_err.h>
#include <sys/stat.h>
#include <sys/systm.h>
#include <sys/sysmacros.h>
#include <sys/atomic.h>
#include <sys/vfs.h>
#include <sys/vfs_opreg.h>

#include <sharefs/sharefs.h>

/*
 * sharefs_snap_create: create a large character buffer with
 * the shares enumerated.
 */
static int
sharefs_snap_create(shnode_t *sft)
{
	sharetab_t		*sht;
	share_t			*sh;
	size_t			sWritten = 0;
	int			iCount = 0;
	char			*buf;

	rw_enter(&sharefs_lock, RW_WRITER);
	rw_enter(&sharetab_lock, RW_READER);

	if (sft->sharefs_snap) {
		/*
		 * Nothing has changed, so no need to grab a new copy!
		 */
		if (sft->sharefs_generation == sharetab_generation) {
			rw_exit(&sharetab_lock);
			rw_exit(&sharefs_lock);
			return (0);
		}

		ASSERT(sft->sharefs_size != 0);
		kmem_free(sft->sharefs_snap, sft->sharefs_size + 1);
		sft->sharefs_snap = NULL;
	}

	sft->sharefs_size = sharetab_size;
	sft->sharefs_count = sharetab_count;

	if (sft->sharefs_size == 0) {
		rw_exit(&sharetab_lock);
		rw_exit(&sharefs_lock);
		return (0);
	}

	sft->sharefs_snap = kmem_zalloc(sft->sharefs_size + 1, KM_SLEEP);

	buf = sft->sharefs_snap;

	/*
	 * Walk the Sharetab, dumping each entry.
	 */
	for (sht = sharefs_sharetab; sht != NULL; sht = sht->s_next) {
		int	i;

		for (i = 0; i < SHARETAB_HASHES; i++) {
			for (sh = sht->s_buckets[i].ssh_sh;
					sh != NULL;
					sh = sh->sh_next) {
				int	n;

				if ((sWritten + sh->sh_size) >
						sft->sharefs_size) {
					goto error_fault;
				}

				/*
				 * Note that sh->sh_size accounts
				 * for the field seperators.
				 * We need to add one for the EOL
				 * marker. And we should note that
				 * the space is accounted for in
				 * each share by the EOS marker.
				 */
				n = snprintf(&buf[sWritten],
					sh->sh_size + 1,
					"%s\t%s\t%s\t%s\t%s\n",
					sh->sh_path,
					sh->sh_res,
					sh->sh_fstype,
					sh->sh_opts,
					sh->sh_descr);

				if (n != sh->sh_size) {
					goto error_fault;
				}

				sWritten += n;
				iCount++;
			}
		}
	}

	/*
	 * We want to record the generation number and
	 * mtime inside this snapshot.
	 */
	gethrestime(&sharetab_snap_time);
	sft->sharefs_snap_time = sharetab_snap_time;
	sft->sharefs_generation = sharetab_generation;

	ASSERT(iCount == sft->sharefs_count);

	rw_exit(&sharetab_lock);
	rw_exit(&sharefs_lock);
	return (0);

error_fault:

	kmem_free(sft->sharefs_snap, sft->sharefs_size + 1);
	sft->sharefs_size = 0;
	sft->sharefs_count = 0;
	sft->sharefs_snap = NULL;
	rw_exit(&sharetab_lock);
	rw_exit(&sharefs_lock);

	return (EFAULT);
}

/* ARGSUSED */
static int
sharefs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr)
{
	timestruc_t	now;
	shnode_t	*sft = VTOSH(vp);

	vap->va_type = VREG;
	vap->va_mode = S_IRUSR | S_IRGRP | S_IROTH;
	vap->va_nodeid = SHAREFS_INO_FILE;
	vap->va_nlink = 1;

	rw_enter(&sharefs_lock, RW_READER);

	/*
	 * If we get asked about a snapped vnode, then
	 * we must report the data in that vnode.
	 *
	 * Else we report what is currently in the
	 * sharetab.
	 */
	if (sft->sharefs_real_vp) {
		rw_enter(&sharetab_lock, RW_READER);
		vap->va_size = sharetab_size;
		vap->va_mtime = sharetab_mtime;
		rw_exit(&sharetab_lock);
	} else {
		vap->va_size = sft->sharefs_size;
		vap->va_mtime = sft->sharefs_snap_time;
	}
	rw_exit(&sharefs_lock);

	gethrestime(&now);
	vap->va_atime = vap->va_ctime = now;

	vap->va_uid = 0;
	vap->va_gid = 0;
	vap->va_rdev = 0;
	vap->va_blksize = DEV_BSIZE;
	vap->va_nblocks = howmany(vap->va_size, vap->va_blksize);
	vap->va_seq = 0;
	vap->va_fsid = vp->v_vfsp->vfs_dev;

	return (0);
}

/* ARGSUSED */
static int
sharefs_access(vnode_t *vp, int mode, int flags, cred_t *cr)
{
	if (mode & (VWRITE|VEXEC))
		return (EROFS);

	return (0);
}

/* ARGSUSED */
int
sharefs_open(vnode_t **vpp, int flag, cred_t *cr)
{
	vnode_t		*vp;
	vnode_t		*ovp = *vpp;
	shnode_t	*sft;
	int		error = 0;

	if (flag & FWRITE)
		return (EINVAL);

	/*
	 * Create a new sharefs vnode for each operation. In order to
	 * avoid locks, we create a snapshot which can not change during
	 * reads.
	 */
	vp = gfs_file_create(sizeof (shnode_t), NULL, sharefs_ops_data);

	((gfs_file_t *)vp->v_data)->gfs_ino = SHAREFS_INO_FILE;

	/*
	 * Hold the parent!
	 */
	VFS_HOLD(ovp->v_vfsp);

	VN_SET_VFS_TYPE_DEV(vp, ovp->v_vfsp, VREG, 0);

	vp->v_flag |= VROOT | VNOCACHE | VNOMAP | VNOSWAP | VNOMOUNT;

	*vpp = vp;
	VN_RELE(ovp);

	sft = VTOSH(vp);

	/*
	 * No need for the lock, no other thread can be accessing
	 * this data structure.
	 */
	atomic_add_32(&sft->sharefs_refs, 1);
	sft->sharefs_real_vp = 0;

	/*
	 * Since the sharetab could easily change on us whilst we
	 * are dumping an extremely huge sharetab, we make a copy
	 * of it here and use it to dump instead.
	 */
	error = sharefs_snap_create(sft);

	return (error);
}

/* ARGSUSED */
int
sharefs_close(vnode_t *vp, int flag, int count,
			offset_t off, cred_t *cr)
{
	shnode_t	*sft = VTOSH(vp);

	if (count > 1)
		return (0);

	rw_enter(&sharefs_lock, RW_WRITER);
	if (vp->v_count == 1) {
		if (sft->sharefs_snap != NULL) {
			kmem_free(sft->sharefs_snap, sft->sharefs_size + 1);
			sft->sharefs_size = 0;
			sft->sharefs_snap = NULL;
			sft->sharefs_generation = 0;
		}
	}
	atomic_add_32(&sft->sharefs_refs, -1);
	rw_exit(&sharefs_lock);

	return (0);
}

/* ARGSUSED */
static int
sharefs_read(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr,
			caller_context_t *ct)
{
	shnode_t	*sft = VTOSH(vp);
	off_t		off = uio->uio_offset;
	size_t		len = uio->uio_resid;
	int		error = 0;

	rw_enter(&sharefs_lock, RW_READER);

	/*
	 * First check to see if we need to grab a new snapshot.
	 */
	if (off == (off_t)0) {
		rw_exit(&sharefs_lock);
		error = sharefs_snap_create(sft);
		if (error) {
			return (EFAULT);
		}
		rw_enter(&sharefs_lock, RW_READER);
	}

	/* LINTED */
	if (len <= 0 || off >= sft->sharefs_size) {
		rw_exit(&sharefs_lock);
		return (error);
	}

	if ((size_t)(off + len) > sft->sharefs_size)
		len = sft->sharefs_size - off;

	if (off < 0 || len > sft->sharefs_size) {
		rw_exit(&sharefs_lock);
		return (EFAULT);
	}

	if (len != 0) {
		error = uiomove(sft->sharefs_snap + off,
				len, UIO_READ, uio);
	}

	rw_exit(&sharefs_lock);
	return (error);
}

/* ARGSUSED */
static void
sharefs_inactive(vnode_t *vp, cred_t *cr)
{
	gfs_file_t	*fp = vp->v_data;
	shnode_t	*sft;

	sft = (shnode_t *)gfs_file_inactive(vp);
	if (sft) {
		rw_enter(&sharefs_lock, RW_WRITER);
		if (sft->sharefs_snap != NULL) {
			kmem_free(sft->sharefs_snap, sft->sharefs_size + 1);
		}

		kmem_free(sft, fp->gfs_size);
		rw_exit(&sharefs_lock);
	}
}

vnode_t *
sharefs_create_root_file(vfs_t *vfsp)
{
	vnode_t		*vp;
	shnode_t	*sft;

	vp = gfs_root_create_file(sizeof (shnode_t),
			vfsp, sharefs_ops_data, SHAREFS_INO_FILE);

	sft = VTOSH(vp);

	sft->sharefs_real_vp = 1;

	return (vp);
}

const fs_operation_def_t sharefs_tops_data[] = {
	{ VOPNAME_OPEN,		{ .vop_open = sharefs_open } },
	{ VOPNAME_CLOSE,	{ .vop_close = sharefs_close } },
	{ VOPNAME_IOCTL,	{ .error = fs_inval } },
	{ VOPNAME_GETATTR,	{ .vop_getattr = sharefs_getattr } },
	{ VOPNAME_ACCESS,	{ .vop_access = sharefs_access } },
	{ VOPNAME_INACTIVE,	{ .vop_inactive = sharefs_inactive } },
	{ VOPNAME_READ,		{ .vop_read = sharefs_read } },
	{ VOPNAME_SEEK,		{ .vop_seek = fs_seek } },
	{ NULL }
};