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