xref: /illumos-gate/usr/src/uts/common/fs/dev/sdev_vfsops.c (revision 89b2a9fbeabf42fa54594df0e5927bcc50a07cc9)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * This is the /dev (hence, the sdev_ prefix) filesystem.
28  */
29 
30 #include <sys/types.h>
31 #include <sys/param.h>
32 #include <sys/sysmacros.h>
33 #include <sys/systm.h>
34 #include <sys/kmem.h>
35 #include <sys/time.h>
36 #include <sys/pathname.h>
37 #include <sys/vfs.h>
38 #include <sys/vfs_opreg.h>
39 #include <sys/vnode.h>
40 #include <sys/file.h>
41 #include <sys/stat.h>
42 #include <sys/uio.h>
43 #include <sys/stat.h>
44 #include <sys/errno.h>
45 #include <sys/cmn_err.h>
46 #include <sys/cred.h>
47 #include <sys/statvfs.h>
48 #include <sys/policy.h>
49 #include <sys/mount.h>
50 #include <sys/debug.h>
51 #include <sys/modctl.h>
52 #include <sys/mkdev.h>
53 #include <fs/fs_subr.h>
54 #include <sys/fs/sdev_impl.h>
55 #include <sys/fs/snode.h>
56 #include <sys/fs/dv_node.h>
57 #include <sys/sunndi.h>
58 #include <sys/mntent.h>
59 
60 /*
61  * /dev vfs operations.
62  */
63 
64 /*
65  * globals
66  */
67 struct sdev_data *sdev_origins; /* mount info for origins under /dev */
68 kmutex_t sdev_lock; /* used for mount/unmount/rename synchronization */
69 
70 /*
71  * static
72  */
73 static major_t devmajor;	/* the fictitious major we live on */
74 static major_t devminor;	/* the fictitious minor of this instance */
75 static struct sdev_data *sdev_mntinfo = NULL;	/* linked list of instances */
76 
77 /* LINTED E_STATIC_UNUSED */		/* useful for debugging */
78 static struct vnode *sdev_stale_attrvp; /* stale root attrvp after remount */
79 
80 static int sdev_mount(struct vfs *, struct vnode *, struct mounta *,
81     struct cred *);
82 static int sdev_unmount(struct vfs *, int, struct cred *);
83 static int sdev_root(struct vfs *, struct vnode **);
84 static int sdev_statvfs(struct vfs *, struct statvfs64 *);
85 static void sdev_insert_mntinfo(struct sdev_data *);
86 static int devinit(int, char *);
87 
88 static vfsdef_t sdev_vfssw = {
89 	VFSDEF_VERSION,
90 	"dev",		/* type name string */
91 	devinit,	/* init routine */
92 	VSW_CANREMOUNT,	/* flags */
93 	NULL		/* mount options table prototype */
94 };
95 
96 
97 /*
98  * Module linkage information
99  */
100 static struct modlfs modlfs = {
101 	&mod_fsops, "/dev filesystem", &sdev_vfssw
102 };
103 
104 static struct modlinkage modlinkage = {
105 	MODREV_1, (void *)&modlfs, NULL
106 };
107 
108 int
109 _init(void)
110 {
111 	int e;
112 
113 	mutex_init(&sdev_lock, NULL, MUTEX_DEFAULT, NULL);
114 	sdev_node_cache_init();
115 	sdev_devfsadm_lockinit();
116 	if ((e = mod_install(&modlinkage)) != 0) {
117 		sdev_devfsadm_lockdestroy();
118 		sdev_node_cache_fini();
119 		mutex_destroy(&sdev_lock);
120 		return (e);
121 	}
122 	return (0);
123 }
124 
125 /*
126  * dev module remained loaded for the global /dev instance
127  */
128 int
129 _fini(void)
130 {
131 	return (EBUSY);
132 }
133 
134 int
135 _info(struct modinfo *modinfop)
136 {
137 	return (mod_info(&modlinkage, modinfop));
138 }
139 
140 /*ARGSUSED*/
141 static int
142 devinit(int fstype, char *name)
143 {
144 	static const fs_operation_def_t dev_vfsops_tbl[] = {
145 		VFSNAME_MOUNT,		{ .vfs_mount = sdev_mount },
146 		VFSNAME_UNMOUNT,	{ .vfs_unmount = sdev_unmount },
147 		VFSNAME_ROOT, 		{ .vfs_root = sdev_root },
148 		VFSNAME_STATVFS,	{ .vfs_statvfs = sdev_statvfs },
149 		NULL,			NULL
150 	};
151 
152 	int	error;
153 	extern major_t getudev(void);
154 
155 	devtype = fstype;
156 
157 	error = vfs_setfsops(fstype, dev_vfsops_tbl, NULL);
158 	if (error != 0) {
159 		cmn_err(CE_WARN, "devinit: bad vfs ops tbl");
160 		return (error);
161 	}
162 
163 	error = vn_make_ops("dev", sdev_vnodeops_tbl, &sdev_vnodeops);
164 	if (error != 0) {
165 		(void) vfs_freevfsops_by_type(fstype);
166 		cmn_err(CE_WARN, "devinit: bad vnode ops tbl");
167 		return (error);
168 	}
169 
170 	if ((devmajor = getudev()) == (major_t)-1) {
171 		cmn_err(CE_WARN, "%s: can't get unique dev", sdev_vfssw.name);
172 		return (1);
173 	}
174 
175 	/* initialize negative cache */
176 	sdev_ncache_init();
177 
178 	return (0);
179 }
180 
181 /*
182  * Both mount point and backing store directory name are
183  * passed in from userland
184  */
185 static int
186 sdev_mount(struct vfs *vfsp, struct vnode *mvp, struct mounta *uap,
187     struct cred *cr)
188 {
189 	struct sdev_data *sdev_data;
190 	struct vnode *avp;
191 	struct sdev_node *dv;
192 	struct sdev_mountargs *args = NULL;
193 	int	error = 0;
194 	dev_t	devdev;
195 
196 	/*
197 	 * security check
198 	 */
199 	if ((secpolicy_fs_mount(cr, mvp, vfsp) != 0) ||
200 	    (secpolicy_sys_devices(cr) != 0))
201 		return (EPERM);
202 
203 	/*
204 	 * Sanity check the mount point
205 	 */
206 	if (mvp->v_type != VDIR)
207 		return (ENOTDIR);
208 
209 	/*
210 	 * Sanity Check for overlay mount.
211 	 */
212 	mutex_enter(&mvp->v_lock);
213 	if ((uap->flags & MS_OVERLAY) == 0 &&
214 	    (uap->flags & MS_REMOUNT) == 0 &&
215 	    (mvp->v_count > 1 || (mvp->v_flag & VROOT))) {
216 		mutex_exit(&mvp->v_lock);
217 		return (EBUSY);
218 	}
219 	mutex_exit(&mvp->v_lock);
220 
221 	args = kmem_zalloc(sizeof (*args), KM_SLEEP);
222 
223 	if ((uap->flags & MS_DATA) &&
224 	    (uap->datalen != 0 && uap->dataptr != NULL)) {
225 		/* copy in the arguments */
226 		if (error = sdev_copyin_mountargs(uap, args))
227 			goto cleanup;
228 	}
229 
230 	/*
231 	 * Sanity check the backing store
232 	 */
233 	if (args->sdev_attrdir) {
234 		/* user supplied an attribute store */
235 		if (error = lookupname((char *)(uintptr_t)args->sdev_attrdir,
236 		    UIO_USERSPACE, FOLLOW, NULLVPP, &avp)) {
237 			cmn_err(CE_NOTE, "/dev fs: lookup on attribute "
238 			    "directory %s failed",
239 			    (char *)(uintptr_t)args->sdev_attrdir);
240 			goto cleanup;
241 		}
242 
243 		if (avp->v_type != VDIR) {
244 			VN_RELE(avp);
245 			error = ENOTDIR;
246 			goto cleanup;
247 		}
248 	} else {
249 		/* use mountp as the attribute store */
250 		avp = mvp;
251 		VN_HOLD(avp);
252 	}
253 
254 	mutex_enter(&sdev_lock);
255 
256 	/*
257 	 * handling installation
258 	 */
259 	if (uap->flags & MS_REMOUNT) {
260 		sdev_data = (struct sdev_data *)vfsp->vfs_data;
261 		ASSERT(sdev_data);
262 
263 		dv = sdev_data->sdev_root;
264 		ASSERT(dv == dv->sdev_dotdot);
265 
266 		/*
267 		 * mark all existing sdev_nodes (except root node) stale
268 		 */
269 		sdev_stale(dv);
270 
271 		/* Reset previous mountargs */
272 		if (sdev_data->sdev_mountargs) {
273 			kmem_free(sdev_data->sdev_mountargs,
274 			    sizeof (struct sdev_mountargs));
275 		}
276 		sdev_data->sdev_mountargs = args;
277 		args = NULL;		/* so it won't be freed below */
278 
279 		sdev_stale_attrvp = dv->sdev_attrvp;
280 		dv->sdev_attrvp = avp;
281 		vfsp->vfs_mtime = ddi_get_time();
282 
283 		mutex_exit(&sdev_lock);
284 		goto cleanup;				/* we're done */
285 	}
286 
287 	/*
288 	 * Create and initialize the vfs-private data.
289 	 */
290 	devdev = makedevice(devmajor, devminor);
291 	while (vfs_devismounted(devdev)) {
292 		devminor = (devminor + 1) & MAXMIN32;
293 
294 		/*
295 		 * All the minor numbers are used up.
296 		 */
297 		if (devminor == 0) {
298 			mutex_exit(&sdev_lock);
299 			VN_RELE(avp);
300 			error = ENODEV;
301 			goto cleanup;
302 		}
303 
304 		devdev = makedevice(devmajor, devminor);
305 	}
306 
307 	dv = sdev_mkroot(vfsp, devdev, mvp, avp, cr);
308 	sdev_data = kmem_zalloc(sizeof (struct sdev_data), KM_SLEEP);
309 	vfsp->vfs_dev = devdev;
310 	vfsp->vfs_data = (caddr_t)sdev_data;
311 	vfsp->vfs_fstype = devtype;
312 	vfsp->vfs_bsize = DEV_BSIZE;
313 	vfsp->vfs_mtime = ddi_get_time();
314 	vfs_make_fsid(&vfsp->vfs_fsid, vfsp->vfs_dev, devtype);
315 
316 	ASSERT(dv == dv->sdev_dotdot);
317 
318 	sdev_data->sdev_vfsp = vfsp;
319 	sdev_data->sdev_root = dv;
320 	sdev_data->sdev_mountargs = args;
321 
322 	/* get acl flavor from attribute dir */
323 	if (VOP_PATHCONF(avp, _PC_ACL_ENABLED, &sdev_data->sdev_acl_flavor,
324 	    kcred, NULL) != 0 || sdev_data->sdev_acl_flavor == 0)
325 		sdev_data->sdev_acl_flavor = _ACL_ACLENT_ENABLED;
326 
327 	args = NULL;			/* so it won't be freed below */
328 	sdev_insert_mntinfo(sdev_data);
329 	mutex_exit(&sdev_lock);
330 
331 	if (!SDEV_IS_GLOBAL(dv)) {
332 		ASSERT(sdev_origins);
333 		dv->sdev_flags &= ~SDEV_GLOBAL;
334 		dv->sdev_origin = sdev_origins->sdev_root;
335 	} else {
336 		sdev_ncache_setup();
337 		rw_enter(&dv->sdev_contents, RW_WRITER);
338 		sdev_filldir_dynamic(dv);
339 		rw_exit(&dv->sdev_contents);
340 	}
341 
342 	sdev_update_timestamps(dv->sdev_attrvp,
343 	    cr, AT_CTIME|AT_MTIME|AT_ATIME);
344 
345 cleanup:
346 	if (args)
347 		kmem_free(args, sizeof (*args));
348 	return (error);
349 }
350 
351 /*
352  * unmounting the non-global /dev instances, e.g. when deleting a Kevlar zone.
353  */
354 static int
355 sdev_unmount(struct vfs *vfsp, int flag, struct cred *cr)
356 {
357 	struct sdev_node *dv;
358 	int error;
359 	struct sdev_data *sdev_data, *prev, *next;
360 
361 	/*
362 	 * enforce the security policies
363 	 */
364 	if ((secpolicy_fs_unmount(cr, vfsp) != 0) ||
365 	    (secpolicy_sys_devices(cr) != 0))
366 		return (EPERM);
367 
368 	if (flag & MS_FORCE)
369 		return (ENOTSUP);
370 
371 	mutex_enter(&sdev_lock);
372 	dv = VFSTOSDEVFS(vfsp)->sdev_root;
373 	ASSERT(dv == dv->sdev_dotdot);
374 	if (SDEVTOV(dv)->v_count > 1) {
375 		mutex_exit(&sdev_lock);
376 		return (EBUSY);
377 	}
378 
379 	/*
380 	 * global instance remains mounted
381 	 */
382 	if (SDEV_IS_GLOBAL(dv)) {
383 		mutex_exit(&sdev_lock);
384 		return (EBUSY);
385 	}
386 	mutex_exit(&sdev_lock);
387 
388 	/* verify the v_count */
389 	if ((error = sdev_cleandir(dv, NULL, 0)) != 0) {
390 		return (error);
391 	}
392 	ASSERT(SDEVTOV(dv)->v_count == 1);
393 
394 	/* release hold on root node and destroy it */
395 	SDEV_RELE(dv);
396 	dv->sdev_nlink -= 2;
397 	sdev_nodedestroy(dv, 0);
398 
399 	sdev_data = (struct sdev_data *)vfsp->vfs_data;
400 	vfsp->vfs_data = (caddr_t)0;
401 
402 	/*
403 	 * XXX separate it into sdev_delete_mntinfo() if useful
404 	 */
405 	mutex_enter(&sdev_lock);
406 	prev = sdev_data->sdev_prev;
407 	next = sdev_data->sdev_next;
408 	if (prev)
409 		prev->sdev_next = next;
410 	else
411 		sdev_mntinfo = next;
412 	if (next)
413 		next->sdev_prev = prev;
414 	mutex_exit(&sdev_lock);
415 
416 	if (sdev_data->sdev_mountargs) {
417 		kmem_free(sdev_data->sdev_mountargs,
418 		    sizeof (struct sdev_mountargs));
419 	}
420 	kmem_free(sdev_data, sizeof (struct sdev_data));
421 	return (0);
422 }
423 
424 /*
425  * return root vnode for given vfs
426  */
427 static int
428 sdev_root(struct vfs *vfsp, struct vnode **vpp)
429 {
430 	*vpp = SDEVTOV(VFSTOSDEVFS(vfsp)->sdev_root);
431 	VN_HOLD(*vpp);
432 	return (0);
433 }
434 
435 /*
436  * return 'generic superblock' information to userland.
437  *
438  * not much that we can usefully admit to here
439  */
440 static int
441 sdev_statvfs(struct vfs *vfsp, struct statvfs64 *sbp)
442 {
443 	dev32_t d32;
444 
445 	bzero(sbp, sizeof (*sbp));
446 	sbp->f_frsize = sbp->f_bsize = vfsp->vfs_bsize;
447 	sbp->f_files = kmem_cache_stat(sdev_node_cache, "alloc");
448 
449 	/* no illusions that free/avail files is relevant to dev */
450 	sbp->f_ffree = 0;
451 	sbp->f_favail = 0;
452 
453 	/* no illusions that blocks are relevant to devfs */
454 	sbp->f_bfree = 0;
455 	sbp->f_bavail = 0;
456 	sbp->f_blocks = 0;
457 
458 	(void) cmpldev(&d32, vfsp->vfs_dev);
459 	sbp->f_fsid = d32;
460 	(void) strcpy(sbp->f_basetype, vfssw[devtype].vsw_name);
461 	sbp->f_flag = vf_to_stf(vfsp->vfs_flag);
462 	sbp->f_namemax = MAXNAMELEN - 1;
463 	(void) strcpy(sbp->f_fstr, "dev");
464 
465 	return (0);
466 }
467 
468 static void
469 sdev_insert_mntinfo(struct sdev_data *data)
470 {
471 	ASSERT(mutex_owned(&sdev_lock));
472 	data->sdev_next = sdev_mntinfo;
473 	data->sdev_prev = NULL;
474 	if (sdev_mntinfo) {
475 		sdev_mntinfo->sdev_prev = data;
476 	} else {
477 		sdev_origins = data;
478 	}
479 	sdev_mntinfo = data;
480 }
481 
482 struct sdev_data *
483 sdev_find_mntinfo(char *mntpt)
484 {
485 	struct sdev_data *mntinfo;
486 
487 	mutex_enter(&sdev_lock);
488 	mntinfo = sdev_mntinfo;
489 	while (mntinfo) {
490 		if (strcmp(mntpt, mntinfo->sdev_root->sdev_name) == 0) {
491 			SDEVTOV(mntinfo->sdev_root)->v_count++;
492 			break;
493 		}
494 		mntinfo = mntinfo->sdev_next;
495 	}
496 	mutex_exit(&sdev_lock);
497 	return (mntinfo);
498 }
499 
500 void
501 sdev_mntinfo_rele(struct sdev_data *mntinfo)
502 {
503 	mutex_enter(&sdev_lock);
504 	SDEVTOV(mntinfo->sdev_root)->v_count--;
505 	mutex_exit(&sdev_lock);
506 }
507