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 }; 387