#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>
static int
sharefs_snap_create(sharetab_globals_t *sg, shnode_t *sft)
{
sharetab_t *sht;
share_t *sh;
size_t sWritten = 0;
int iCount = 0;
char *buf;
rw_enter(&sg->sharefs_lock, RW_WRITER);
rw_enter(&sg->sharetab_lock, RW_READER);
if (sft->sharefs_snap) {
if (sft->sharefs_generation == sg->sharetab_generation) {
rw_exit(&sg->sharetab_lock);
rw_exit(&sg->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 = sg->sharetab_size;
sft->sharefs_count = sg->sharetab_count;
if (sft->sharefs_size == 0) {
rw_exit(&sg->sharetab_lock);
rw_exit(&sg->sharefs_lock);
return (0);
}
sft->sharefs_snap = kmem_zalloc(sft->sharefs_size + 1, KM_SLEEP);
buf = sft->sharefs_snap;
for (sht = sg->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;
}
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++;
}
}
}
gethrestime(&sg->sharetab_snap_time);
sft->sharefs_snap_time = sg->sharetab_snap_time;
sft->sharefs_generation = sg->sharetab_generation;
ASSERT(iCount == sft->sharefs_count);
rw_exit(&sg->sharetab_lock);
rw_exit(&sg->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(&sg->sharetab_lock);
rw_exit(&sg->sharefs_lock);
return (EFAULT);
}
static int
sharefs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
caller_context_t *ct)
{
timestruc_t now;
shnode_t *sft = VTOSH(vp);
sharetab_globals_t *sg = sharetab_get_globals(vp->v_vfsp->vfs_zone);
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(&sg->sharefs_lock, RW_READER);
if (sft->sharefs_real_vp) {
rw_enter(&sg->sharetab_lock, RW_READER);
vap->va_size = sg->sharetab_size;
vap->va_mtime = sg->sharetab_mtime;
rw_exit(&sg->sharetab_lock);
} else {
vap->va_size = sft->sharefs_size;
vap->va_mtime = sft->sharefs_snap_time;
}
rw_exit(&sg->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);
}
static int
sharefs_access(vnode_t *vp, int mode, int flags, cred_t *cr,
caller_context_t *ct)
{
if (mode & (VWRITE|VEXEC))
return (EROFS);
return (0);
}
int
sharefs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
{
vnode_t *vp;
vnode_t *ovp = *vpp;
shnode_t *sft;
int error = 0;
if (flag & FWRITE)
return (EINVAL);
vp = gfs_file_create(sizeof (shnode_t), NULL, sharefs_ops_data);
((gfs_file_t *)vp->v_data)->gfs_ino = SHAREFS_INO_FILE;
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);
atomic_inc_32(&sft->sharefs_refs);
sft->sharefs_real_vp = 0;
error = sharefs_snap_create(sharetab_get_globals(vp->v_vfsp->vfs_zone),
sft);
return (error);
}
int
sharefs_close(vnode_t *vp, int flag, int count,
offset_t off, cred_t *cr, caller_context_t *ct)
{
shnode_t *sft = VTOSH(vp);
sharetab_globals_t *sg = sharetab_get_globals(vp->v_vfsp->vfs_zone);
if (count > 1)
return (0);
rw_enter(&sg->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_dec_32(&sft->sharefs_refs);
rw_exit(&sg->sharefs_lock);
return (0);
}
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;
sharetab_globals_t *sg = sharetab_get_globals(vp->v_vfsp->vfs_zone);
rw_enter(&sg->sharefs_lock, RW_READER);
if (off == (off_t)0) {
rw_exit(&sg->sharefs_lock);
error = sharefs_snap_create(sg, sft);
if (error) {
return (EFAULT);
}
rw_enter(&sg->sharefs_lock, RW_READER);
}
if (len <= 0 || off >= sft->sharefs_size) {
rw_exit(&sg->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(&sg->sharefs_lock);
return (EFAULT);
}
if (len != 0) {
error = uiomove(sft->sharefs_snap + off,
len, UIO_READ, uio);
}
rw_exit(&sg->sharefs_lock);
return (error);
}
static void
sharefs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *tx)
{
gfs_file_t *fp = vp->v_data;
shnode_t *sft;
sharetab_globals_t *sg = sharetab_get_globals(vp->v_vfsp->vfs_zone);
sft = (shnode_t *)gfs_file_inactive(vp);
if (sft) {
rw_enter(&sg->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(&sg->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 }
};