xref: /titanic_44/usr/src/uts/common/fs/devfs/devfs_vfsops.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 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 /*
30  * This is the device filesystem.
31  *
32  * It is a combination of a namer to drive autoconfiguration,
33  * plus the access methods for the device drivers of the system.
34  *
35  * The prototype is fairly dependent on specfs for the latter part
36  * of its implementation, though a final version would integrate the two.
37  */
38 #include <sys/types.h>
39 #include <sys/param.h>
40 #include <sys/sysmacros.h>
41 #include <sys/systm.h>
42 #include <sys/kmem.h>
43 #include <sys/time.h>
44 #include <sys/pathname.h>
45 #include <sys/vfs.h>
46 #include <sys/vnode.h>
47 #include <sys/stat.h>
48 #include <sys/uio.h>
49 #include <sys/stat.h>
50 #include <sys/errno.h>
51 #include <sys/cmn_err.h>
52 #include <sys/cred.h>
53 #include <sys/statvfs.h>
54 #include <sys/mount.h>
55 #include <sys/debug.h>
56 #include <sys/modctl.h>
57 #include <fs/fs_subr.h>
58 #include <sys/fs/dv_node.h>
59 #include <sys/fs/snode.h>
60 #include <sys/sunndi.h>
61 #include <sys/policy.h>
62 
63 /*
64  * devfs vfs operations.
65  */
66 static int devfs_mount(struct vfs *, struct vnode *, struct mounta *,
67     struct cred *);
68 static int devfs_unmount(struct vfs *, int, struct cred *);
69 static int devfs_root(struct vfs *, struct vnode **);
70 static int devfs_statvfs(struct vfs *, struct statvfs64 *);
71 static int devfs_mountroot(struct vfs *, enum whymountroot);
72 
73 static int devfsinit(int, char *);
74 
75 static vfsdef_t devfs_vfssw = {
76 	VFSDEF_VERSION,
77 	"devfs",	/* type name string */
78 	devfsinit,	/* init routine */
79 	0,		/* flags */
80 	NULL		/* mount options table prototype */
81 };
82 
83 static kmutex_t devfs_lock;	/* protects global data */
84 static int devfstype;		/* fstype */
85 static dev_t devfsdev;		/* the fictious 'device' we live on */
86 static struct devfs_data *devfs_mntinfo;	/* linked list of instances */
87 
88 /*
89  * Module linkage information
90  */
91 static struct modlfs modlfs = {
92 	&mod_fsops, "devices filesystem %I%", &devfs_vfssw
93 };
94 
95 static struct modlinkage modlinkage = {
96 	MODREV_1, (void *)&modlfs, NULL
97 };
98 
99 int
100 _init(void)
101 {
102 	int e;
103 
104 	mutex_init(&devfs_lock, "devfs lock", MUTEX_DEFAULT, NULL);
105 	dv_node_cache_init();
106 	if ((e = mod_install(&modlinkage)) != 0) {
107 		dv_node_cache_fini();
108 		mutex_destroy(&devfs_lock);
109 		return (e);
110 	}
111 	dcmn_err(("devfs loaded\n"));
112 	return (0);
113 }
114 
115 int
116 _fini(void)
117 {
118 	return (EBUSY);
119 }
120 
121 int
122 _info(struct modinfo *modinfop)
123 {
124 	return (mod_info(&modlinkage, modinfop));
125 }
126 
127 /*ARGSUSED1*/
128 static int
129 devfsinit(int fstype, char *name)
130 {
131 	static const fs_operation_def_t devfs_vfsops_template[] = {
132 		VFSNAME_MOUNT, devfs_mount,
133 		VFSNAME_UNMOUNT, devfs_unmount,
134 		VFSNAME_ROOT, devfs_root,
135 		VFSNAME_STATVFS, devfs_statvfs,
136 		VFSNAME_SYNC, (fs_generic_func_p) fs_sync,
137 		VFSNAME_MOUNTROOT, devfs_mountroot,
138 		NULL, NULL
139 	};
140 	int error;
141 	int dev;
142 	extern major_t getudev(void);	/* gack - what a function */
143 
144 	devfstype = fstype;
145 	/*
146 	 * Associate VFS ops vector with this fstype
147 	 */
148 	error = vfs_setfsops(fstype, devfs_vfsops_template, NULL);
149 	if (error != 0) {
150 		cmn_err(CE_WARN, "devfsinit: bad vfs ops template");
151 		return (error);
152 	}
153 
154 	error = vn_make_ops("dev fs", dv_vnodeops_template, &dv_vnodeops);
155 	if (error != 0) {
156 		(void) vfs_freevfsops_by_type(fstype);
157 		cmn_err(CE_WARN, "devfsinit: bad vnode ops template");
158 		return (error);
159 	}
160 
161 	/*
162 	 * Invent a dev_t (sigh).
163 	 */
164 	if ((dev = getudev()) == (major_t)-1) {
165 		cmn_err(CE_NOTE, "%s: can't get unique dev", devfs_vfssw.name);
166 		dev = 0;
167 	}
168 	devfsdev = makedevice(dev, 0);
169 
170 	return (0);
171 }
172 
173 /*
174  * The name of the mount point and the name of the attribute
175  * filesystem are passed down from userland for now.
176  */
177 static int
178 devfs_mount(struct vfs *vfsp, struct vnode *mvp, struct mounta *uap,
179     struct cred *cr)
180 {
181 	struct devfs_data *devfs_data;
182 	struct vnode *avp;
183 	struct dv_node *dv;
184 	struct vattr va;
185 
186 	dcmn_err(("devfs_mount\n"));
187 
188 	if (secpolicy_fs_mount(cr, mvp, vfsp) != 0)
189 		return (EPERM);
190 
191 	/*
192 	 * check that the mount point is sane
193 	 */
194 	if (mvp->v_type != VDIR)
195 		return (ENOTDIR);
196 
197 	ASSERT(uap->flags & MS_SYSSPACE);
198 	/*
199 	 * Devfs can only be mounted from kernel during boot.
200 	 * avp is the existing /devices, the same as the mount point.
201 	 */
202 	avp = mvp;
203 
204 	/*
205 	 * Create and initialize the vfs-private data.
206 	 * This includes a hand-crafted root vnode (we build
207 	 * this here mostly so that traverse() doesn't sleep
208 	 * in VFS_ROOT()).
209 	 */
210 	mutex_enter(&devfs_lock);
211 	ASSERT(devfs_mntinfo == NULL);
212 	dv = dv_mkroot(vfsp, devfsdev);
213 	dv->dv_attrvp = avp;		/* attribute root vp */
214 
215 	ASSERT(dv == dv->dv_dotdot);
216 
217 	devfs_data = kmem_zalloc(sizeof (struct devfs_data), KM_SLEEP);
218 	devfs_data->devfs_vfsp = vfsp;
219 	devfs_data->devfs_root = dv;
220 
221 	vfsp->vfs_data = (caddr_t)devfs_data;
222 	vfsp->vfs_fstype = devfstype;
223 	vfsp->vfs_dev = devfsdev;
224 	vfsp->vfs_bsize = DEV_BSIZE;
225 	vfsp->vfs_mtime = ddi_get_time();
226 	vfs_make_fsid(&vfsp->vfs_fsid, vfsp->vfs_dev, devfstype);
227 
228 	/* We're there. */
229 	devfs_mntinfo = devfs_data;
230 	mutex_exit(&devfs_lock);
231 
232 	va.va_mask = AT_ATIME|AT_MTIME;
233 	gethrestime(&va.va_atime);
234 	gethrestime(&va.va_mtime);
235 	(void) VOP_SETATTR(DVTOV(dv), &va, 0, cr, NULL);
236 	return (0);
237 }
238 
239 
240 /*
241  * We never unmount devfs in a real production system.
242  */
243 /*ARGSUSED*/
244 static int
245 devfs_unmount(struct vfs *vfsp, int flag, struct cred *cr)
246 {
247 	return (EBUSY);
248 }
249 
250 /*
251  * return root vnode for given vfs
252  */
253 static int
254 devfs_root(struct vfs *vfsp, struct vnode **vpp)
255 {
256 	dcmn_err(("devfs_root\n"));
257 	*vpp = DVTOV(VFSTODVFS(vfsp)->devfs_root);
258 	VN_HOLD(*vpp);
259 	return (0);
260 }
261 
262 /*
263  * return 'generic superblock' information to userland.
264  *
265  * not much that we can usefully admit to here
266  */
267 static int
268 devfs_statvfs(struct vfs *vfsp, struct statvfs64 *sbp)
269 {
270 	extern kmem_cache_t *dv_node_cache;
271 
272 	dev32_t d32;
273 
274 	dcmn_err(("devfs_statvfs\n"));
275 	bzero(sbp, sizeof (*sbp));
276 	sbp->f_frsize = sbp->f_bsize = vfsp->vfs_bsize;
277 	/*
278 	 * We could compute the number of devfsnodes here .. but since
279 	 * it's dynamic anyway, it's not clear how useful this is.
280 	 */
281 	sbp->f_files = kmem_cache_stat(dv_node_cache, "alloc");
282 
283 	/* no illusions that free/avail files is relevant to devfs */
284 	sbp->f_ffree = 0;
285 	sbp->f_favail = 0;
286 
287 	/* no illusions that blocks are relevant to devfs */
288 	sbp->f_bfree = 0;
289 	sbp->f_bavail = 0;
290 	sbp->f_blocks = 0;
291 
292 	(void) cmpldev(&d32, vfsp->vfs_dev);
293 	sbp->f_fsid = d32;
294 	(void) strcpy(sbp->f_basetype, vfssw[devfstype].vsw_name);
295 	sbp->f_flag = vf_to_stf(vfsp->vfs_flag);
296 	sbp->f_namemax = MAXNAMELEN - 1;
297 	(void) strcpy(sbp->f_fstr, "devices");
298 
299 	return (0);
300 }
301 
302 /*
303  * devfs always mount after root is mounted, so this should never
304  * be invoked.
305  */
306 /*ARGSUSED*/
307 static int
308 devfs_mountroot(struct vfs *vfsp, enum whymountroot why)
309 {
310 	dcmn_err(("devfs_mountroot\n"));
311 
312 	return (EINVAL);
313 }
314 
315 struct dv_node *
316 devfs_dip_to_dvnode(dev_info_t *dip)
317 {
318 	char *dirpath;
319 	struct vnode *dirvp;
320 
321 	ASSERT(dip != NULL);
322 
323 	/* no-op if devfs not mounted yet */
324 	if (devfs_mntinfo == NULL)
325 		return (NULL);
326 
327 	/*
328 	 * The lookupname below only looks up cached dv_nodes
329 	 * because devfs_clean_key is set in thread specific data.
330 	 */
331 	dirpath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
332 	(void) ddi_pathname(dip, dirpath);
333 	if (devfs_lookupname(dirpath, NULLVPP, &dirvp)) {
334 		dcmn_err(("directory %s not found\n", dirpath));
335 		kmem_free(dirpath, MAXPATHLEN);
336 		return (NULL);
337 	}
338 
339 	kmem_free(dirpath, MAXPATHLEN);
340 	return (VTODV(dirvp));
341 }
342 
343 /*
344  * devfs_clean()
345  *
346  * Destroy unreferenced dv_node's and detach devices.
347  * Returns 0 on success, error if failed to unconfigure node.
348  *
349  * devfs caches unreferenced dv_node to speed by the performance
350  * of ls, find, etc. devfs_clean() is invoked to cleanup cached
351  * dv_nodes to reclaim memory as well as to facilitate device
352  * removal (dv_node reference devinfo nodes, which prevents driver
353  * detach).
354  *
355  * If a shell parks in a /devices directory, the dv_node will be
356  * held, preventing the corresponding device to be detached.
357  * This would be a denial of service against DR. To prevent this,
358  * DR code calls devfs_clean() with the DV_CLEAN_FORCE flag.
359  * The dv_cleandir() implementation does the right thing to ensure
360  * successful DR.
361  */
362 int
363 devfs_clean(dev_info_t *dip, char *devnm, uint_t flags)
364 {
365 	struct dv_node *dvp;
366 
367 	dcmn_err(("devfs_unconfigure: dip = 0x%p, flags = 0x%x",
368 		(void *)dip, flags));
369 
370 	/* avoid recursion back into the device tree */
371 	(void) tsd_set(devfs_clean_key, (void *)1);
372 	dvp = devfs_dip_to_dvnode(dip);
373 	(void) tsd_set(devfs_clean_key, NULL);
374 	if (dvp == NULL)
375 		return (0);
376 
377 	if (dv_cleandir(dvp, devnm, flags) != 0) {
378 		VN_RELE(DVTOV(dvp));
379 		return (EBUSY);
380 	}
381 	VN_RELE(DVTOV(dvp));
382 	return (0);
383 }
384 
385 /*
386  * lookup a devfs relative pathname, returning held vnodes for the final
387  * component and the containing directory (if requested).
388  *
389  * NOTE: We can't use lookupname because this would use the current
390  *	processes credentials (CRED) in the call lookuppnvp instead
391  *	of kcred.  It also does not give you the flexibility so
392  * 	specify the directory to start the resolution in (devicesdir).
393  */
394 int
395 devfs_lookupname(
396 	char	*pathname,		/* user pathname */
397 	vnode_t **dirvpp,		/* ret for ptr to parent dir vnode */
398 	vnode_t **compvpp)		/* ret for ptr to component vnode */
399 {
400 	struct pathname	pn;
401 	int		error;
402 
403 	ASSERT(devicesdir);		/* devfs must be initialized */
404 	ASSERT(pathname);		/* must have some path */
405 
406 	if (error = pn_get(pathname, UIO_SYSSPACE, &pn))
407 		return (error);
408 
409 	/* make the path relative to /devices. */
410 	pn_skipslash(&pn);
411 	if (pn_pathleft(&pn) == 0) {
412 		/* all we had was "\0" or "/" (which skipslash skiped) */
413 		if (dirvpp)
414 			*dirvpp = NULL;
415 		if (compvpp) {
416 			VN_HOLD(devicesdir);
417 			*compvpp = devicesdir;
418 		}
419 	} else {
420 		/*
421 		 * Use devfs lookup to resolve pathname to the vnode for
422 		 * the device via relative lookup in devfs. Extra holds for
423 		 * using devicesdir as directory we are searching and for
424 		 * being our root without being == rootdir.
425 		 */
426 		VN_HOLD(devicesdir);
427 		VN_HOLD(devicesdir);
428 		error = lookuppnvp(&pn, NULL, FOLLOW, dirvpp, compvpp,
429 		    devicesdir, devicesdir, kcred);
430 	}
431 	pn_free(&pn);
432 
433 	return (error);
434 }
435 
436 /*
437  * Given a devfs path (without the /devices prefix), walk
438  * the dv_node sub-tree rooted at the path.
439  */
440 int
441 devfs_walk(
442 	char		*path,
443 	void		(*callback)(struct dv_node *, void *),
444 	void		*arg)
445 {
446 	char *dirpath, *devnm;
447 	struct vnode	*dirvp;
448 
449 	ASSERT(path && callback);
450 
451 	if (*path != '/' || devfs_mntinfo == NULL)
452 		return (ENXIO);
453 
454 	dcmn_err(("devfs_walk: path = %s", path));
455 
456 	dirpath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
457 
458 	(void) snprintf(dirpath, MAXPATHLEN, "/devices%s", path);
459 
460 	devnm = strrchr(dirpath, '/');
461 
462 	ASSERT(devnm);
463 
464 	*devnm++ = '\0';
465 
466 	if (lookupname(dirpath, UIO_SYSSPACE, 0, NULL, &dirvp)) {
467 		dcmn_err(("directory %s not found\n", dirpath));
468 		kmem_free(dirpath, MAXPATHLEN);
469 		return (ENXIO);
470 	}
471 
472 	/*
473 	 * if path == "/", visit the root dv_node
474 	 */
475 	if (*devnm == '\0') {
476 		callback(VTODV(dirvp), arg);
477 		devnm = NULL;
478 	}
479 
480 	dv_walk(VTODV(dirvp), devnm, callback, arg);
481 
482 	VN_RELE(dirvp);
483 
484 	kmem_free(dirpath, MAXPATHLEN);
485 
486 	return (0);
487 }
488 
489 int
490 devfs_devpolicy(vnode_t *vp, devplcy_t **dpp)
491 {
492 	struct vnode *rvp;
493 	struct dv_node *dvp;
494 	int rval = -1;
495 
496 	/* fail if devfs not mounted yet */
497 	if (devfs_mntinfo == NULL)
498 		return (rval);
499 
500 	if (VOP_REALVP(vp, &rvp) == 0 && vn_matchops(rvp, dv_vnodeops)) {
501 		dvp = VTODV(rvp);
502 		rw_enter(&dvp->dv_contents, RW_READER);
503 		if (dvp->dv_priv) {
504 			dphold(dvp->dv_priv);
505 			*dpp = dvp->dv_priv;
506 			rval = 0;
507 		}
508 		rw_exit(&dvp->dv_contents);
509 	}
510 	return (rval);
511 }
512