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  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 #include <sys/atomic.h>
26 #include <sys/cmn_err.h>
27 #include <sys/errno.h>
28 #include <sys/mount.h>
29 #include <sys/objfs.h>
30 #include <sys/objfs_impl.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 /*
38  * Kernel object filesystem.
39  *
40  * This is a pseudo filesystem which exports information about currently loaded
41  * kernel objects.  The root directory contains one directory for each loaded
42  * object, indexed by module name.  Within each object directory is an ELF file,
43  * 'object', that contains information about the currently loaded module.
44  *
45  * This file contains functions that interact with the VFS layer.  Each
46  * filesystem element is represented by a a different node.
47  *
48  * 	/		objfs_rootnode_t	objfs_root.c
49  *	/<obj>		objfs_odirnode_t	objfs_odir.c
50  *	/<obj>/object	objfs_datanode_t	objfs_data.c
51  *
52  * In addition, some common routines are found in the 'objfs_common.c' file.
53  */
54 
55 vnodeops_t *objfs_ops_root;
56 vnodeops_t *objfs_ops_odir;
57 vnodeops_t *objfs_ops_data;
58 
59 static const fs_operation_def_t objfs_vfstops[];
60 static gfs_opsvec_t objfs_opsvec[];
61 
62 static int objfs_init(int, char *);
63 
64 /*
65  * Module linkage
66  */
67 static mntopts_t objfs_mntopts = {
68 	0,
69 	NULL
70 };
71 
72 static vfsdef_t vfw = {
73 	VFSDEF_VERSION,
74 	"objfs",
75 	objfs_init,
76 	VSW_HASPROTO | VSW_ZMOUNT,
77 	&objfs_mntopts,
78 };
79 
80 extern struct mod_ops mod_fsops;
81 
82 static struct modlfs modlfs = {
83 	&mod_fsops, "kernel object filesystem", &vfw
84 };
85 
86 static struct modlinkage modlinkage = {
87 	MODREV_1, (void *)&modlfs, NULL
88 };
89 
90 int
_init(void)91 _init(void)
92 {
93 	return (mod_install(&modlinkage));
94 }
95 
96 int
_info(struct modinfo * modinfop)97 _info(struct modinfo *modinfop)
98 {
99 	return (mod_info(&modlinkage, modinfop));
100 }
101 
102 int
_fini(void)103 _fini(void)
104 {
105 	/*
106 	 * The object filesystem cannot be unloaded.
107 	 */
108 	return (EBUSY);
109 }
110 
111 /*
112  * Filesystem initialization.
113  */
114 
115 static int objfs_fstype;
116 static major_t objfs_major;
117 static minor_t objfs_minor;
118 
119 static gfs_opsvec_t objfs_opsvec[] = {
120 	{ "objfs root directory", objfs_tops_root, &objfs_ops_root },
121 	{ "objfs object directory", objfs_tops_odir, &objfs_ops_odir },
122 	{ "objfs data file", objfs_tops_data, &objfs_ops_data },
123 	{ NULL }
124 };
125 
126 /* ARGSUSED */
127 static int
objfs_init(int fstype,char * name)128 objfs_init(int fstype, char *name)
129 {
130 	vfsops_t *vfsops;
131 	int error;
132 
133 	objfs_fstype = fstype;
134 	if (error = vfs_setfsops(fstype, objfs_vfstops, &vfsops)) {
135 		cmn_err(CE_WARN, "objfs_init: bad vfs ops template");
136 		return (error);
137 	}
138 
139 	if (error = gfs_make_opsvec(objfs_opsvec)) {
140 		(void) vfs_freevfsops(vfsops);
141 		return (error);
142 	}
143 
144 	if ((objfs_major = getudev()) == (major_t)-1) {
145 		cmn_err(CE_WARN, "objfs_init: can't get unique device number");
146 		objfs_major = 0;
147 	}
148 
149 	objfs_data_init();
150 
151 	return (0);
152 }
153 
154 /*
155  * VFS entry points
156  */
157 static int
objfs_mount(vfs_t * vfsp,vnode_t * mvp,struct mounta * uap,cred_t * cr)158 objfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
159 {
160 	objfs_vfs_t *data;
161 	dev_t dev;
162 
163 	if (secpolicy_fs_mount(cr, mvp, vfsp) != 0)
164 		return (EPERM);
165 
166 	if (mvp->v_type != VDIR)
167 		return (ENOTDIR);
168 
169 	if ((uap->flags & MS_OVERLAY) == 0 &&
170 	    (mvp->v_count > 1 || (mvp->v_flag & VROOT)))
171 		return (EBUSY);
172 
173 	data = kmem_alloc(sizeof (objfs_vfs_t), KM_SLEEP);
174 
175 	/*
176 	 * Initialize vfs fields
177 	 */
178 	vfsp->vfs_bsize = DEV_BSIZE;
179 	vfsp->vfs_fstype = objfs_fstype;
180 	do {
181 		dev = makedevice(objfs_major,
182 		    atomic_inc_32_nv(&objfs_minor) & L_MAXMIN32);
183 	} while (vfs_devismounted(dev));
184 	vfs_make_fsid(&vfsp->vfs_fsid, dev, objfs_fstype);
185 	vfsp->vfs_data = data;
186 	vfsp->vfs_dev = dev;
187 
188 	/*
189 	 * Create root
190 	 */
191 	data->objfs_vfs_root = objfs_create_root(vfsp);
192 
193 	return (0);
194 }
195 
196 static int
objfs_unmount(vfs_t * vfsp,int flag,struct cred * cr)197 objfs_unmount(vfs_t *vfsp, int flag, struct cred *cr)
198 {
199 	objfs_vfs_t *data;
200 
201 	if (secpolicy_fs_unmount(cr, vfsp) != 0)
202 		return (EPERM);
203 
204 	/*
205 	 * We do not currently support forced unmounts
206 	 */
207 	if (flag & MS_FORCE)
208 		return (ENOTSUP);
209 
210 	/*
211 	 * We should never have a reference count of less than 2: one for the
212 	 * caller, one for the root vnode.
213 	 */
214 	ASSERT(vfsp->vfs_count >= 2);
215 
216 	/*
217 	 * Any active vnodes will result in a hold on the root vnode
218 	 */
219 	data = vfsp->vfs_data;
220 	if (data->objfs_vfs_root->v_count > 1)
221 		return (EBUSY);
222 
223 	/*
224 	 * Release the last hold on the root vnode
225 	 */
226 	VN_RELE(data->objfs_vfs_root);
227 
228 	kmem_free(data, sizeof (objfs_vfs_t));
229 
230 	return (0);
231 }
232 
233 static int
objfs_root(vfs_t * vfsp,vnode_t ** vpp)234 objfs_root(vfs_t *vfsp, vnode_t **vpp)
235 {
236 	objfs_vfs_t *data = vfsp->vfs_data;
237 
238 	*vpp = data->objfs_vfs_root;
239 	VN_HOLD(*vpp);
240 
241 	return (0);
242 }
243 
244 static int
objfs_statvfs(vfs_t * vfsp,statvfs64_t * sp)245 objfs_statvfs(vfs_t *vfsp, statvfs64_t *sp)
246 {
247 	dev32_t d32;
248 	int total = objfs_nobjs();
249 
250 	bzero(sp, sizeof (*sp));
251 	sp->f_bsize = DEV_BSIZE;
252 	sp->f_frsize = DEV_BSIZE;
253 	sp->f_files = total;
254 	sp->f_ffree = sp->f_favail = INT_MAX - total;
255 	(void) cmpldev(&d32, vfsp->vfs_dev);
256 	sp->f_fsid = d32;
257 	(void) strlcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name,
258 	    sizeof (sp->f_basetype));
259 	sp->f_flag = vf_to_stf(vfsp->vfs_flag);
260 	sp->f_namemax = OBJFS_NAME_MAX;
261 	(void) strlcpy(sp->f_fstr, "object", sizeof (sp->f_fstr));
262 
263 	return (0);
264 }
265 
266 static const fs_operation_def_t objfs_vfstops[] = {
267 	{ VFSNAME_MOUNT,	{ .vfs_mount = objfs_mount } },
268 	{ VFSNAME_UNMOUNT,	{ .vfs_unmount = objfs_unmount } },
269 	{ VFSNAME_ROOT,		{ .vfs_root = objfs_root } },
270 	{ VFSNAME_STATVFS,	{ .vfs_statvfs = objfs_statvfs } },
271 	{ NULL }
272 };
273