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