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 {
163 	timestruc_t	now;
164 	shnode_t	*sft = VTOSH(vp);
165 
166 	vap->va_type = VREG;
167 	vap->va_mode = S_IRUSR | S_IRGRP | S_IROTH;
168 	vap->va_nodeid = SHAREFS_INO_FILE;
169 	vap->va_nlink = 1;
170 
171 	rw_enter(&sharefs_lock, RW_READER);
172 
173 	/*
174 	 * If we get asked about a snapped vnode, then
175 	 * we must report the data in that vnode.
176 	 *
177 	 * Else we report what is currently in the
178 	 * sharetab.
179 	 */
180 	if (sft->sharefs_real_vp) {
181 		rw_enter(&sharetab_lock, RW_READER);
182 		vap->va_size = sharetab_size;
183 		vap->va_mtime = sharetab_mtime;
184 		rw_exit(&sharetab_lock);
185 	} else {
186 		vap->va_size = sft->sharefs_size;
187 		vap->va_mtime = sft->sharefs_snap_time;
188 	}
189 	rw_exit(&sharefs_lock);
190 
191 	gethrestime(&now);
192 	vap->va_atime = vap->va_ctime = now;
193 
194 	vap->va_uid = 0;
195 	vap->va_gid = 0;
196 	vap->va_rdev = 0;
197 	vap->va_blksize = DEV_BSIZE;
198 	vap->va_nblocks = howmany(vap->va_size, vap->va_blksize);
199 	vap->va_seq = 0;
200 	vap->va_fsid = vp->v_vfsp->vfs_dev;
201 
202 	return (0);
203 }
204 
205 /* ARGSUSED */
206 static int
207 sharefs_access(vnode_t *vp, int mode, int flags, cred_t *cr)
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)
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_add_32(&sft->sharefs_refs, 1);
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)
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_add_32(&sft->sharefs_refs, -1);
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)
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