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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26#include <sys/atomic.h>
27#include <sys/cmn_err.h>
28#include <sys/errno.h>
29#include <sys/mount.h>
30#include <sharefs/sharefs.h>
31#include <sys/vfs_opreg.h>
32#include <sys/policy.h>
33#include <sys/sunddi.h>
34#include <sys/sysmacros.h>
35#include <sys/systm.h>
36
37#include <sys/mntent.h>
38#include <sys/vfs.h>
39
40/*
41 * Kernel sharetab filesystem.
42 *
43 * This is a pseudo filesystem which exports information about shares currently
44 * in kernel memory. The only element of the pseudo filesystem is a file.
45 *
46 * This file contains functions that interact with the VFS layer.
47 *
48 *	sharetab	sharefs_datanode_t	sharefs.c
49 *
50 */
51
52vnodeops_t			*sharefs_ops_data;
53
54static const fs_operation_def_t	sharefs_vfstops[];
55static gfs_opsvec_t		 sharefs_opsvec[];
56
57static int sharefs_init(int, char *);
58
59/*
60 * The sharefs system call.
61 */
62static struct sysent sharefs_sysent = {
63	3,
64	SE_32RVAL1 | SE_ARGC | SE_NOUNLOAD,
65	sharefs
66};
67
68static struct modlsys modlsys = {
69	&mod_syscallops,
70	"sharefs syscall",
71	&sharefs_sysent
72};
73
74#ifdef	_SYSCALL32_IMPL
75static struct modlsys modlsys32 = {
76	&mod_syscallops32,
77	"sharefs syscall (32-bit)",
78	&sharefs_sysent
79};
80#endif /* _SYSCALL32_IMPL */
81
82/*
83 * Module linkage
84 */
85static mntopts_t sharefs_mntopts = {
86	0,
87	NULL
88};
89
90static vfsdef_t vfw = {
91	VFSDEF_VERSION,
92	"sharefs",
93	sharefs_init,
94	VSW_HASPROTO | VSW_ZMOUNT,
95	&sharefs_mntopts,
96};
97
98extern struct mod_ops	mod_fsops;
99
100static struct modlfs modlfs = {
101	&mod_fsops,
102	"sharetab filesystem",
103	&vfw
104};
105
106static struct modlinkage modlinkage = {
107	MODREV_1,
108	&modlfs,
109	&modlsys,
110#ifdef	_SYSCALL32_IMPL
111	&modlsys32,
112#endif
113	NULL
114};
115
116int
117_init(void)
118{
119	return (mod_install(&modlinkage));
120}
121
122int
123_info(struct modinfo *modinfop)
124{
125	return (mod_info(&modlinkage, modinfop));
126}
127
128int
129_fini(void)
130{
131	/*
132	 * The sharetab filesystem cannot be unloaded.
133	 */
134	return (EBUSY);
135}
136
137/*
138 * Filesystem initialization.
139 */
140
141static int sharefs_fstype;
142static major_t sharefs_major;
143static minor_t sharefs_minor;
144
145static gfs_opsvec_t sharefs_opsvec[] = {
146	{ "sharefs sharetab file", sharefs_tops_data, &sharefs_ops_data },
147	{ NULL }
148};
149
150/* ARGSUSED */
151static int
152sharefs_init(int fstype, char *name)
153{
154	vfsops_t	*vfsops;
155	int		error;
156
157	sharefs_fstype = fstype;
158	if (error = vfs_setfsops(fstype, sharefs_vfstops, &vfsops)) {
159		cmn_err(CE_WARN, "sharefs_init: bad vfs ops template");
160		return (error);
161	}
162
163	if (error = gfs_make_opsvec(sharefs_opsvec)) {
164		(void) vfs_freevfsops(vfsops);
165		return (error);
166	}
167
168	if ((sharefs_major = getudev()) == (major_t)-1) {
169		cmn_err(CE_WARN,
170		    "sharefs_init: can't get unique device number");
171		sharefs_major = 0;
172	}
173
174	sharefs_sharetab_init();
175
176	return (0);
177}
178
179/*
180 * VFS entry points
181 */
182static int
183sharefs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
184{
185	sharefs_vfs_t	*data;
186	dev_t		dev;
187
188	if (secpolicy_fs_mount(cr, mvp, vfsp) != 0)
189		return (EPERM);
190
191	if ((uap->flags & MS_OVERLAY) == 0 &&
192	    (mvp->v_count > 1 || (mvp->v_flag & VROOT)))
193		return (EBUSY);
194
195	data = kmem_alloc(sizeof (sharefs_vfs_t), KM_SLEEP);
196
197	/*
198	 * Initialize vfs fields
199	 */
200	vfsp->vfs_bsize = DEV_BSIZE;
201	vfsp->vfs_fstype = sharefs_fstype;
202	do {
203		dev = makedevice(sharefs_major,
204		    atomic_add_32_nv(&sharefs_minor, 1) & L_MAXMIN32);
205	} while (vfs_devismounted(dev));
206	vfs_make_fsid(&vfsp->vfs_fsid, dev, sharefs_fstype);
207	vfsp->vfs_data = data;
208	vfsp->vfs_dev = dev;
209
210	/*
211	 * Create root
212	 */
213	data->sharefs_vfs_root = sharefs_create_root_file(vfsp);
214
215	return (0);
216}
217
218static int
219sharefs_unmount(vfs_t *vfsp, int flag, struct cred *cr)
220{
221	sharefs_vfs_t	*data;
222
223	if (secpolicy_fs_unmount(cr, vfsp) != 0)
224		return (EPERM);
225
226	/*
227	 * We do not currently support forced unmounts
228	 */
229	if (flag & MS_FORCE)
230		return (ENOTSUP);
231
232	/*
233	 * We should never have a reference count of less than 2: one for the
234	 * caller, one for the root vnode.
235	 */
236	ASSERT(vfsp->vfs_count >= 2);
237
238	/*
239	 * Any active vnodes will result in a hold on the root vnode
240	 */
241	data = vfsp->vfs_data;
242	if (data->sharefs_vfs_root->v_count > 1)
243		return (EBUSY);
244
245	/*
246	 * Only allow an unmount iff there are no entries in memory.
247	 */
248	rw_enter(&sharetab_lock, RW_READER);
249	if (sharetab_size != 0) {
250		rw_exit(&sharetab_lock);
251		return (EBUSY);
252	}
253	rw_exit(&sharetab_lock);
254
255	/*
256	 * Release the last hold on the root vnode
257	 */
258	VN_RELE(data->sharefs_vfs_root);
259
260	kmem_free(data, sizeof (sharefs_vfs_t));
261
262	return (0);
263}
264
265static int
266sharefs_root(vfs_t *vfsp, vnode_t **vpp)
267{
268	sharefs_vfs_t	*data = vfsp->vfs_data;
269
270	*vpp = data->sharefs_vfs_root;
271	VN_HOLD(*vpp);
272
273	return (0);
274}
275
276static int
277sharefs_statvfs(vfs_t *vfsp, statvfs64_t *sp)
278{
279	dev32_t	d32;
280	int	total = 1;
281
282	bzero(sp, sizeof (*sp));
283	sp->f_bsize = DEV_BSIZE;
284	sp->f_frsize = DEV_BSIZE;
285	sp->f_files = total;
286	sp->f_ffree = sp->f_favail = INT_MAX - total;
287	(void) cmpldev(&d32, vfsp->vfs_dev);
288	sp->f_fsid = d32;
289	(void) strlcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name,
290	    sizeof (sp->f_basetype));
291	sp->f_flag = vf_to_stf(vfsp->vfs_flag);
292	sp->f_namemax = SHAREFS_NAME_MAX;
293	(void) strlcpy(sp->f_fstr, "sharefs", sizeof (sp->f_fstr));
294
295	return (0);
296}
297
298static const fs_operation_def_t sharefs_vfstops[] = {
299	{ VFSNAME_MOUNT,	{ .vfs_mount = sharefs_mount } },
300	{ VFSNAME_UNMOUNT,	{ .vfs_unmount = sharefs_unmount } },
301	{ VFSNAME_ROOT,		{ .vfs_root = sharefs_root } },
302	{ VFSNAME_STATVFS,	{ .vfs_statvfs = sharefs_statvfs } },
303	{ NULL }
304};
305