1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 #include <fs/fs_subr.h>
  30 
  31 #include <sys/errno.h>
  32 #include <sys/file.h>
  33 #include <sys/kmem.h>
  34 #include <sys/kobj.h>
  35 #include <sys/cmn_err.h>
  36 #include <sys/stat.h>
  37 #include <sys/systm.h>
  38 #include <sys/sysmacros.h>
  39 #include <sys/atomic.h>
  40 #include <sys/vfs.h>
  41 #include <sys/vfs_opreg.h>
  42 
  43 #include <sharefs/sharefs.h>
  44 
  45 /*
  46  * sharefs_snap_create: create a large character buffer with
  47  * the shares enumerated.
  48  */
  49 static int
  50 sharefs_snap_create(shnode_t *sft)
  51 {
  52         sharetab_t              *sht;
  53         share_t                 *sh;
  54         size_t                  sWritten = 0;
  55         int                     iCount = 0;
  56         char                    *buf;
  57 
  58         rw_enter(&sharefs_lock, RW_WRITER);
  59         rw_enter(&sharetab_lock, RW_READER);
  60 
  61         if (sft->sharefs_snap) {
  62                 /*
  63                  * Nothing has changed, so no need to grab a new copy!
  64                  */
  65                 if (sft->sharefs_generation == sharetab_generation) {
  66                         rw_exit(&sharetab_lock);
  67                         rw_exit(&sharefs_lock);
  68                         return (0);
  69                 }
  70 
  71                 ASSERT(sft->sharefs_size != 0);
  72                 kmem_free(sft->sharefs_snap, sft->sharefs_size + 1);
  73                 sft->sharefs_snap = NULL;
  74         }
  75 
  76         sft->sharefs_size = sharetab_size;
  77         sft->sharefs_count = sharetab_count;
  78 
  79         if (sft->sharefs_size == 0) {
  80                 rw_exit(&sharetab_lock);
  81                 rw_exit(&sharefs_lock);
  82                 return (0);
  83         }
  84 
  85         sft->sharefs_snap = kmem_zalloc(sft->sharefs_size + 1, KM_SLEEP);
  86 
  87         buf = sft->sharefs_snap;
  88 
  89         /*
  90          * Walk the Sharetab, dumping each entry.
  91          */
  92         for (sht = sharefs_sharetab; sht != NULL; sht = sht->s_next) {
  93                 int     i;
  94 
  95                 for (i = 0; i < SHARETAB_HASHES; i++) {
  96                         for (sh = sht->s_buckets[i].ssh_sh;
  97                             sh != NULL;
  98                             sh = sh->sh_next) {
  99                                 int     n;
 100 
 101                                 if ((sWritten + sh->sh_size) >
 102                                     sft->sharefs_size) {
 103                                         goto error_fault;
 104                                 }
 105 
 106                                 /*
 107                                  * Note that sh->sh_size accounts
 108                                  * for the field seperators.
 109                                  * We need to add one for the EOL
 110                                  * marker. And we should note that
 111                                  * the space is accounted for in
 112                                  * each share by the EOS marker.
 113                                  */
 114                                 n = snprintf(&buf[sWritten],
 115                                     sh->sh_size + 1,
 116                                     "%s\t%s\t%s\t%s\t%s\n",
 117                                     sh->sh_path,
 118                                     sh->sh_res,
 119                                     sh->sh_fstype,
 120                                     sh->sh_opts,
 121                                     sh->sh_descr);
 122 
 123                                 if (n != sh->sh_size) {
 124                                         goto error_fault;
 125                                 }
 126 
 127                                 sWritten += n;
 128                                 iCount++;
 129                         }
 130                 }
 131         }
 132 
 133         /*
 134          * We want to record the generation number and
 135          * mtime inside this snapshot.
 136          */
 137         gethrestime(&sharetab_snap_time);
 138         sft->sharefs_snap_time = sharetab_snap_time;
 139         sft->sharefs_generation = sharetab_generation;
 140 
 141         ASSERT(iCount == sft->sharefs_count);
 142 
 143         rw_exit(&sharetab_lock);
 144         rw_exit(&sharefs_lock);
 145         return (0);
 146 
 147 error_fault:
 148 
 149         kmem_free(sft->sharefs_snap, sft->sharefs_size + 1);
 150         sft->sharefs_size = 0;
 151         sft->sharefs_count = 0;
 152         sft->sharefs_snap = NULL;
 153         rw_exit(&sharetab_lock);
 154         rw_exit(&sharefs_lock);
 155 
 156         return (EFAULT);
 157 }
 158 
 159 /* ARGSUSED */
 160 static int
 161 sharefs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
 162     caller_context_t *ct)
 163 {
 164         timestruc_t     now;
 165         shnode_t        *sft = VTOSH(vp);
 166 
 167         vap->va_type = VREG;
 168         vap->va_mode = S_IRUSR | S_IRGRP | S_IROTH;
 169         vap->va_nodeid = SHAREFS_INO_FILE;
 170         vap->va_nlink = 1;
 171 
 172         rw_enter(&sharefs_lock, RW_READER);
 173 
 174         /*
 175          * If we get asked about a snapped vnode, then
 176          * we must report the data in that vnode.
 177          *
 178          * Else we report what is currently in the
 179          * sharetab.
 180          */
 181         if (sft->sharefs_real_vp) {
 182                 rw_enter(&sharetab_lock, RW_READER);
 183                 vap->va_size = sharetab_size;
 184                 vap->va_mtime = sharetab_mtime;
 185                 rw_exit(&sharetab_lock);
 186         } else {
 187                 vap->va_size = sft->sharefs_size;
 188                 vap->va_mtime = sft->sharefs_snap_time;
 189         }
 190         rw_exit(&sharefs_lock);
 191 
 192         gethrestime(&now);
 193         vap->va_atime = vap->va_ctime = now;
 194 
 195         vap->va_uid = 0;
 196         vap->va_gid = 0;
 197         vap->va_rdev = 0;
 198         vap->va_blksize = DEV_BSIZE;
 199         vap->va_nblocks = howmany(vap->va_size, vap->va_blksize);
 200         vap->va_seq = 0;
 201         vap->va_fsid = vp->v_vfsp->vfs_dev;
 202 
 203         return (0);
 204 }
 205 
 206 /* ARGSUSED */
 207 static int
 208 sharefs_access(vnode_t *vp, int mode, int flags, cred_t *cr,
 209     caller_context_t *ct)
 210 {
 211         if (mode & (VWRITE|VEXEC))
 212                 return (EROFS);
 213 
 214         return (0);
 215 }
 216 
 217 /* ARGSUSED */
 218 int
 219 sharefs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
 220 {
 221         vnode_t         *vp;
 222         vnode_t         *ovp = *vpp;
 223         shnode_t        *sft;
 224         int             error = 0;
 225 
 226         if (flag & FWRITE)
 227                 return (EINVAL);
 228 
 229         /*
 230          * Create a new sharefs vnode for each operation. In order to
 231          * avoid locks, we create a snapshot which can not change during
 232          * reads.
 233          */
 234         vp = gfs_file_create(sizeof (shnode_t), NULL, sharefs_ops_data);
 235 
 236         ((gfs_file_t *)vp->v_data)->gfs_ino = SHAREFS_INO_FILE;
 237 
 238         /*
 239          * Hold the parent!
 240          */
 241         VFS_HOLD(ovp->v_vfsp);
 242 
 243         VN_SET_VFS_TYPE_DEV(vp, ovp->v_vfsp, VREG, 0);
 244 
 245         vp->v_flag |= VROOT | VNOCACHE | VNOMAP | VNOSWAP | VNOMOUNT;
 246 
 247         *vpp = vp;
 248         VN_RELE(ovp);
 249 
 250         sft = VTOSH(vp);
 251 
 252         /*
 253          * No need for the lock, no other thread can be accessing
 254          * this data structure.
 255          */
 256         atomic_add_32(&sft->sharefs_refs, 1);
 257         sft->sharefs_real_vp = 0;
 258 
 259         /*
 260          * Since the sharetab could easily change on us whilst we
 261          * are dumping an extremely huge sharetab, we make a copy
 262          * of it here and use it to dump instead.
 263          */
 264         error = sharefs_snap_create(sft);
 265 
 266         return (error);
 267 }
 268 
 269 /* ARGSUSED */
 270 int
 271 sharefs_close(vnode_t *vp, int flag, int count,
 272     offset_t off, cred_t *cr, caller_context_t *ct)
 273 {
 274         shnode_t        *sft = VTOSH(vp);
 275 
 276         if (count > 1)
 277                 return (0);
 278 
 279         rw_enter(&sharefs_lock, RW_WRITER);
 280         if (vp->v_count == 1) {
 281                 if (sft->sharefs_snap != NULL) {
 282                         kmem_free(sft->sharefs_snap, sft->sharefs_size + 1);
 283                         sft->sharefs_size = 0;
 284                         sft->sharefs_snap = NULL;
 285                         sft->sharefs_generation = 0;
 286                 }
 287         }
 288         atomic_add_32(&sft->sharefs_refs, -1);
 289         rw_exit(&sharefs_lock);
 290 
 291         return (0);
 292 }
 293 
 294 /* ARGSUSED */
 295 static int
 296 sharefs_read(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr,
 297                         caller_context_t *ct)
 298 {
 299         shnode_t        *sft = VTOSH(vp);
 300         off_t           off = uio->uio_offset;
 301         size_t          len = uio->uio_resid;
 302         int             error = 0;
 303 
 304         rw_enter(&sharefs_lock, RW_READER);
 305 
 306         /*
 307          * First check to see if we need to grab a new snapshot.
 308          */
 309         if (off == (off_t)0) {
 310                 rw_exit(&sharefs_lock);
 311                 error = sharefs_snap_create(sft);
 312                 if (error) {
 313                         return (EFAULT);
 314                 }
 315                 rw_enter(&sharefs_lock, RW_READER);
 316         }
 317 
 318         /* LINTED */
 319         if (len <= 0 || off >= sft->sharefs_size) {
 320                 rw_exit(&sharefs_lock);
 321                 return (error);
 322         }
 323 
 324         if ((size_t)(off + len) > sft->sharefs_size)
 325                 len = sft->sharefs_size - off;
 326 
 327         if (off < 0 || len > sft->sharefs_size) {
 328                 rw_exit(&sharefs_lock);
 329                 return (EFAULT);
 330         }
 331 
 332         if (len != 0) {
 333                 error = uiomove(sft->sharefs_snap + off,
 334                     len, UIO_READ, uio);
 335         }
 336 
 337         rw_exit(&sharefs_lock);
 338         return (error);
 339 }
 340 
 341 /* ARGSUSED */
 342 static void
 343 sharefs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *tx)
 344 {
 345         gfs_file_t      *fp = vp->v_data;
 346         shnode_t        *sft;
 347 
 348         sft = (shnode_t *)gfs_file_inactive(vp);
 349         if (sft) {
 350                 rw_enter(&sharefs_lock, RW_WRITER);
 351                 if (sft->sharefs_snap != NULL) {
 352                         kmem_free(sft->sharefs_snap, sft->sharefs_size + 1);
 353                 }
 354 
 355                 kmem_free(sft, fp->gfs_size);
 356                 rw_exit(&sharefs_lock);
 357         }
 358 }
 359 
 360 vnode_t *
 361 sharefs_create_root_file(vfs_t *vfsp)
 362 {
 363         vnode_t         *vp;
 364         shnode_t        *sft;
 365 
 366         vp = gfs_root_create_file(sizeof (shnode_t),
 367             vfsp, sharefs_ops_data, SHAREFS_INO_FILE);
 368 
 369         sft = VTOSH(vp);
 370 
 371         sft->sharefs_real_vp = 1;
 372 
 373         return (vp);
 374 }
 375 
 376 const fs_operation_def_t sharefs_tops_data[] = {
 377         { VOPNAME_OPEN,         { .vop_open = sharefs_open } },
 378         { VOPNAME_CLOSE,        { .vop_close = sharefs_close } },
 379         { VOPNAME_IOCTL,        { .error = fs_inval } },
 380         { VOPNAME_GETATTR,      { .vop_getattr = sharefs_getattr } },
 381         { VOPNAME_ACCESS,       { .vop_access = sharefs_access } },
 382         { VOPNAME_INACTIVE,     { .vop_inactive = sharefs_inactive } },
 383         { VOPNAME_READ,         { .vop_read = sharefs_read } },
 384         { VOPNAME_SEEK,         { .vop_seek = fs_seek } },
 385         { NULL }
 386 };