xref: /illumos-gate/usr/src/cmd/mdb/common/modules/genunix/vfs.c (revision cb6207858a9fcc2feaee22e626912fba281ac969)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <mdb/mdb_modapi.h>
29 #include <mdb/mdb_ks.h>
30 
31 #include <sys/types.h>
32 #include <sys/systm.h>
33 #include <sys/door.h>
34 #include <sys/file.h>
35 #include <sys/mount.h>
36 #include <sys/proc.h>
37 #include <sys/procfs.h>
38 #include <sys/proc/prdata.h>
39 #include <sys/stat.h>
40 #include <sys/vfs.h>
41 #include <sys/vnode.h>
42 #include <sys/fs/snode.h>
43 #include <sys/fs/fifonode.h>
44 #include <sys/fs/namenode.h>
45 #include <sys/socket.h>
46 #include <sys/stropts.h>
47 #include <sys/socketvar.h>
48 #include <sys/strsubr.h>
49 #include <sys/un.h>
50 
51 int
52 vfs_walk_init(mdb_walk_state_t *wsp)
53 {
54 	if (wsp->walk_addr == NULL &&
55 	    mdb_readvar(&wsp->walk_addr, "rootvfs") == -1) {
56 		mdb_warn("failed to read 'rootvfs'");
57 		return (WALK_ERR);
58 	}
59 
60 	wsp->walk_data = (void *)wsp->walk_addr;
61 	return (WALK_NEXT);
62 }
63 
64 int
65 vfs_walk_step(mdb_walk_state_t *wsp)
66 {
67 	vfs_t vfs;
68 	int status;
69 
70 	if (mdb_vread(&vfs, sizeof (vfs), wsp->walk_addr) == -1) {
71 		mdb_warn("failed to read vfs_t at %p", wsp->walk_addr);
72 		return (WALK_DONE);
73 	}
74 
75 	status = wsp->walk_callback(wsp->walk_addr, &vfs, wsp->walk_cbdata);
76 
77 	if (vfs.vfs_next == wsp->walk_data)
78 		return (WALK_DONE);
79 
80 	wsp->walk_addr = (uintptr_t)vfs.vfs_next;
81 
82 	return (status);
83 }
84 
85 /*
86  * Utility routine to read in a filesystem name given a vfs pointer.  If
87  * no vfssw entry for the vfs is available (as is the case with some pseudo-
88  * filesystems), we check against some known problem fs's: doorfs and
89  * portfs.  If that fails, we try to guess the filesystem name using
90  * symbol names.  fsname should be a buffer of size _ST_FSTYPSZ.
91  */
92 static int
93 read_fsname(uintptr_t vfsp, char *fsname)
94 {
95 	vfs_t vfs;
96 	struct vfssw vfssw_entry;
97 	GElf_Sym vfssw_sym, test_sym;
98 	char testname[MDB_SYM_NAMLEN];
99 
100 	if (mdb_vread(&vfs, sizeof (vfs), vfsp) == -1) {
101 		mdb_warn("failed to read vfs %p", vfsp);
102 		return (-1);
103 	}
104 
105 	if (mdb_lookup_by_name("vfssw", &vfssw_sym) == -1) {
106 		mdb_warn("failed to find vfssw");
107 		return (-1);
108 	}
109 
110 	/*
111 	 * vfssw is an array; we need vfssw[vfs.vfs_fstype].
112 	 */
113 	if (mdb_vread(&vfssw_entry, sizeof (vfssw_entry),
114 	    vfssw_sym.st_value + (sizeof (struct vfssw) * vfs.vfs_fstype))
115 	    == -1) {
116 		mdb_warn("failed to read vfssw index %d", vfs.vfs_fstype);
117 		return (-1);
118 	}
119 
120 	if (vfs.vfs_fstype != 0) {
121 		if (mdb_readstr(fsname, _ST_FSTYPSZ,
122 		    (uintptr_t)vfssw_entry.vsw_name) == -1) {
123 			mdb_warn("failed to find fs name %p",
124 			    vfssw_entry.vsw_name);
125 			return (-1);
126 		}
127 		return (0);
128 	}
129 
130 	/*
131 	 * Do precise detection for certain filesystem types that we
132 	 * know do not appear in vfssw[], and that we depend upon in other
133 	 * parts of the code: doorfs and portfs.
134 	 */
135 	if (mdb_lookup_by_name("door_vfs", &test_sym) != -1) {
136 		if (test_sym.st_value == vfsp) {
137 			strcpy(fsname, "doorfs");
138 			return (0);
139 		}
140 	}
141 	if (mdb_lookup_by_name("port_vfs", &test_sym) != -1) {
142 		if (test_sym.st_value == vfsp) {
143 			strcpy(fsname, "portfs");
144 			return (0);
145 		}
146 	}
147 
148 	/*
149 	 * Heuristic detection for other filesystems that don't have a
150 	 * vfssw[] entry.  These tend to be named <fsname>_vfs, so we do a
151 	 * lookup_by_addr and see if we find a symbol of that name.
152 	 */
153 	if (mdb_lookup_by_addr(vfsp, MDB_SYM_EXACT, testname, sizeof (testname),
154 	    &test_sym) != -1) {
155 		if ((strlen(testname) > 4) &&
156 		    (strcmp(testname + strlen(testname) - 4, "_vfs") == 0)) {
157 			testname[strlen(testname) - 4] = '\0';
158 			strncpy(fsname, testname, _ST_FSTYPSZ);
159 			return (0);
160 		}
161 	}
162 
163 	mdb_warn("unknown filesystem type for vfs %p", vfsp);
164 	return (-1);
165 }
166 
167 /*
168  * Column widths for mount point display in ::fsinfo output.
169  */
170 #ifdef _LP64
171 #define	FSINFO_MNTLEN	48
172 #else
173 #define	FSINFO_MNTLEN	56
174 #endif
175 
176 /*ARGSUSED*/
177 int
178 fsinfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
179 {
180 	vfs_t vfs;
181 	int len;
182 	int opt_v = 0;
183 	char buf[MAXPATHLEN];
184 	char fsname[_ST_FSTYPSZ];
185 	mntopt_t *mntopts;
186 	size_t size;
187 	int i;
188 	int first = 1;
189 	char opt[MAX_MNTOPT_STR];
190 	uintptr_t global_zone;
191 
192 	if (!(flags & DCMD_ADDRSPEC)) {
193 		if (mdb_walk_dcmd("vfs", "fsinfo", argc, argv) == -1) {
194 			mdb_warn("failed to walk file system list");
195 			return (DCMD_ERR);
196 		}
197 		return (DCMD_OK);
198 	}
199 
200 	if (mdb_getopts(argc, argv,
201 	    'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
202 		return (DCMD_USAGE);
203 
204 	if (DCMD_HDRSPEC(flags))
205 		mdb_printf("%<u>%?s %-15s %s%</u>\n",
206 		    "VFSP", "FS", "MOUNT");
207 
208 	if (mdb_vread(&vfs, sizeof (vfs), addr) == -1) {
209 		mdb_warn("failed to read vfs_t %p", addr);
210 		return (DCMD_ERR);
211 	}
212 
213 	if ((len = mdb_read_refstr((uintptr_t)vfs.vfs_mntpt, buf,
214 	    sizeof (buf))) <= 0)
215 		strcpy(buf, "??");
216 
217 	else if (!opt_v && (len >= FSINFO_MNTLEN))
218 		/*
219 		 * In normal mode, we truncate the path to keep the output
220 		 * clean.  In -v mode, we just print the full path.
221 		 */
222 		strcpy(&buf[FSINFO_MNTLEN - 4], "...");
223 
224 	if (read_fsname(addr, fsname) == -1)
225 		return (DCMD_ERR);
226 
227 	mdb_printf("%0?p %-15s %s\n", addr, fsname, buf);
228 
229 	if (!opt_v)
230 		return (DCMD_OK);
231 
232 	/*
233 	 * Print 'resource' string; this shows what we're mounted upon.
234 	 */
235 	if (mdb_read_refstr((uintptr_t)vfs.vfs_resource, buf,
236 	    MAXPATHLEN) <= 0)
237 		strcpy(buf, "??");
238 
239 	mdb_printf("%?s %s\n", "R:", buf);
240 
241 	/*
242 	 * Print mount options array; it sucks to be a mimic, but we copy
243 	 * the same logic as in mntvnops.c for adding zone= tags, and we
244 	 * don't bother with the obsolete dev= option.
245 	 */
246 	size = vfs.vfs_mntopts.mo_count * sizeof (mntopt_t);
247 	mntopts = mdb_alloc(size, UM_SLEEP | UM_GC);
248 
249 	if (mdb_vread(mntopts, size,
250 	    (uintptr_t)vfs.vfs_mntopts.mo_list) == -1) {
251 		mdb_warn("failed to read mntopts %p", vfs.vfs_mntopts.mo_list);
252 		return (DCMD_ERR);
253 	}
254 
255 	for (i = 0; i < vfs.vfs_mntopts.mo_count; i++) {
256 		if (mntopts[i].mo_flags & MO_SET) {
257 			if (mdb_readstr(opt, sizeof (opt),
258 			    (uintptr_t)mntopts[i].mo_name) == -1) {
259 				mdb_warn("failed to read mntopt name %p",
260 				    mntopts[i].mo_name);
261 				return (DCMD_ERR);
262 			}
263 			if (first) {
264 				mdb_printf("%?s ", "O:");
265 				first = 0;
266 			} else {
267 				mdb_printf(",");
268 			}
269 			mdb_printf("%s", opt);
270 			if (mntopts[i].mo_flags & MO_HASVALUE) {
271 				if (mdb_readstr(opt, sizeof (opt),
272 				    (uintptr_t)mntopts[i].mo_arg) == -1) {
273 					mdb_warn("failed to read mntopt "
274 					    "value %p", mntopts[i].mo_arg);
275 					return (DCMD_ERR);
276 				}
277 				mdb_printf("=%s", opt);
278 			}
279 		}
280 	}
281 
282 	if (mdb_readvar(&global_zone, "global_zone") == -1) {
283 		mdb_warn("failed to locate global_zone");
284 		return (DCMD_ERR);
285 	}
286 
287 	if ((vfs.vfs_zone != NULL) &&
288 	    ((uintptr_t)vfs.vfs_zone != global_zone)) {
289 		zone_t z;
290 
291 		if (mdb_vread(&z, sizeof (z), (uintptr_t)vfs.vfs_zone) == -1) {
292 			mdb_warn("failed to read zone");
293 			return (DCMD_ERR);
294 		}
295 		/*
296 		 * zone names are much shorter than MAX_MNTOPT_STR
297 		 */
298 		if (mdb_readstr(opt, sizeof (opt),
299 		    (uintptr_t)z.zone_name) == -1) {
300 			mdb_warn("failed to read zone name");
301 			return (DCMD_ERR);
302 		}
303 		if (first) {
304 			mdb_printf("%?s ", "O:");
305 		} else {
306 			mdb_printf(",");
307 		}
308 		mdb_printf("zone=%s", opt);
309 	}
310 	return (DCMD_OK);
311 }
312 
313 
314 #define	REALVP_DONE	0
315 #define	REALVP_ERR	1
316 #define	REALVP_CONTINUE	2
317 
318 static int
319 next_realvp(uintptr_t invp, struct vnode *outvn, uintptr_t *outvp)
320 {
321 	char fsname[_ST_FSTYPSZ];
322 
323 	*outvp = invp;
324 	if (mdb_vread(outvn, sizeof (struct vnode), invp) == -1) {
325 		mdb_warn("failed to read vnode at %p", invp);
326 		return (REALVP_ERR);
327 	}
328 
329 	if (read_fsname((uintptr_t)outvn->v_vfsp, fsname) == -1)
330 		return (REALVP_ERR);
331 
332 	/*
333 	 * We know how to do 'realvp' for as many filesystems as possible;
334 	 * for all other filesystems, we assume that the vp we are given
335 	 * is the realvp.  In the kernel, a realvp operation will sometimes
336 	 * dig through multiple layers.  Here, we only fetch the pointer
337 	 * to the next layer down.  This allows dcmds to print out the
338 	 * various layers.
339 	 */
340 	if (strcmp(fsname, "fifofs") == 0) {
341 		fifonode_t fn;
342 		if (mdb_vread(&fn, sizeof (fn),
343 		    (uintptr_t)outvn->v_data) == -1) {
344 			mdb_warn("failed to read fifonode");
345 			return (REALVP_ERR);
346 		}
347 		*outvp = (uintptr_t)fn.fn_realvp;
348 
349 	} else if (strcmp(fsname, "namefs") == 0) {
350 		struct namenode nn;
351 		if (mdb_vread(&nn, sizeof (nn),
352 		    (uintptr_t)outvn->v_data) == -1) {
353 			mdb_warn("failed to read namenode");
354 			return (REALVP_ERR);
355 		}
356 		*outvp = (uintptr_t)nn.nm_filevp;
357 
358 	} else if (outvn->v_type == VSOCK && outvn->v_stream != NULL) {
359 		struct stdata stream;
360 
361 		/*
362 		 * Sockets have a strange and different layering scheme; we
363 		 * hop over into the sockfs vnode (accessible via the stream
364 		 * head) if possible.
365 		 */
366 		if (mdb_vread(&stream, sizeof (stream),
367 		    (uintptr_t)outvn->v_stream) == -1) {
368 			mdb_warn("failed to read stream data");
369 			return (REALVP_ERR);
370 		}
371 		*outvp = (uintptr_t)stream.sd_vnode;
372 	}
373 
374 	if (*outvp == invp || *outvp == NULL)
375 		return (REALVP_DONE);
376 
377 	return (REALVP_CONTINUE);
378 }
379 
380 static void
381 pfiles_print_addr(struct sockaddr *addr)
382 {
383 	struct sockaddr_in *s_in;
384 	struct sockaddr_un *s_un;
385 	struct sockaddr_in6 *s_in6;
386 	in_port_t port;
387 
388 	switch (addr->sa_family) {
389 	case AF_INET:
390 		/*LINTED: alignment*/
391 		s_in = (struct sockaddr_in *)addr;
392 		mdb_nhconvert(&port, &s_in->sin_port, sizeof (port));
393 		mdb_printf("AF_INET %I %d ", s_in->sin_addr.s_addr, port);
394 		break;
395 
396 	case AF_INET6:
397 		/*LINTED: alignment*/
398 		s_in6 = (struct sockaddr_in6 *)addr;
399 		mdb_nhconvert(&port, &s_in6->sin6_port, sizeof (port));
400 		mdb_printf("AF_INET6 %N %d ", &(s_in6->sin6_addr), port);
401 		break;
402 
403 	case AF_UNIX:
404 		s_un = (struct sockaddr_un *)addr;
405 		mdb_printf("AF_UNIX %s ", s_un->sun_path);
406 		break;
407 	default:
408 		mdb_printf("AF_?? (%d) ", addr->sa_family);
409 		break;
410 	}
411 }
412 
413 
414 static int
415 pfiles_get_sonode(uintptr_t vp, struct sonode *sonode)
416 {
417 	vnode_t v;
418 	struct stdata stream;
419 
420 	if (mdb_vread(&v, sizeof (v), vp) == -1) {
421 		mdb_warn("failed to read socket vnode");
422 		return (-1);
423 	}
424 
425 	if (mdb_vread(&stream, sizeof (stream), (uintptr_t)v.v_stream) == -1) {
426 		mdb_warn("failed to read stream data");
427 		return (-1);
428 	}
429 
430 	if (mdb_vread(&v, sizeof (v), (uintptr_t)stream.sd_vnode) == -1) {
431 		mdb_warn("failed to read stream vnode");
432 		return (-1);
433 	}
434 
435 	if (mdb_vread(sonode, sizeof (struct sonode),
436 	    (uintptr_t)v.v_data) == -1) {
437 		mdb_warn("failed to read sonode");
438 		return (-1);
439 	}
440 
441 	return (0);
442 }
443 
444 /*
445  * Do some digging to get a reasonable pathname for this vnode. 'path'
446  * should point at a buffer of MAXPATHLEN in size.
447  */
448 static int
449 pfiles_dig_pathname(uintptr_t vp, char *path)
450 {
451 	vnode_t v;
452 
453 	bzero(path, MAXPATHLEN);
454 
455 	if (mdb_vread(&v, sizeof (v), vp) == -1) {
456 		mdb_warn("failed to read vnode");
457 		return (-1);
458 	}
459 
460 	if (v.v_path == NULL) {
461 		/*
462 		 * fifo's and doors are special.   Some have pathnames, and
463 		 * some do not.  And for these, it is pointless to go off to
464 		 * mdb_vnode2path, which is very slow.
465 		 *
466 		 * Event ports never have a pathname.
467 		 */
468 		if (v.v_type == VFIFO || v.v_type == VDOOR || v.v_type == VPORT)
469 			return (0);
470 
471 		/*
472 		 * For sockets, we won't find a path unless we print the path
473 		 * associated with the accessvp.
474 		 */
475 		if (v.v_type == VSOCK) {
476 			struct sonode sonode;
477 
478 			if (pfiles_get_sonode(vp, &sonode) == -1) {
479 				return (-1);
480 			}
481 
482 			vp = (uintptr_t)sonode.so_accessvp;
483 		}
484 	}
485 
486 
487 	/*
488 	 * mdb_vnode2path will print an error for us as needed, but not
489 	 * finding a pathname is not really an error, so we plow on.
490 	 */
491 	(void) mdb_vnode2path(vp, path, MAXPATHLEN);
492 
493 	/*
494 	 * A common problem is that device pathnames are prefixed with
495 	 * /dev/../devices/.  We just clean those up slightly:
496 	 * 	/dev/../devices/<mumble> --> /devices/<mumble>
497 	 * 	/dev/pts/../../devices/<mumble> --> /devices/<mumble>
498 	 */
499 	if (strncmp("/dev/../devices/", path, strlen("/dev/../devices/")) == 0)
500 		strcpy(path, path + 7);
501 
502 	if (strncmp("/dev/pts/../../devices/", path,
503 	    strlen("/dev/pts/../../devices/")) == 0)
504 		strcpy(path, path + 14);
505 
506 	return (0);
507 }
508 
509 const struct fs_type {
510 	int type;
511 	const char *name;
512 } fs_types[] = {
513 	{ VNON,   "NON" },
514 	{ VREG,   "REG" },
515 	{ VDIR,   "DIR" },
516 	{ VBLK,   "BLK" },
517 	{ VCHR,   "CHR" },
518 	{ VLNK,   "LNK" },
519 	{ VFIFO,  "FIFO" },
520 	{ VDOOR,  "DOOR" },
521 	{ VPROC,  "PROC" },
522 	{ VSOCK,  "SOCK" },
523 	{ VPORT,  "PORT" },
524 	{ VBAD,   "BAD" }
525 };
526 
527 #define	NUM_FS_TYPES (sizeof (fs_types) / sizeof (struct fs_type))
528 
529 struct pfiles_cbdata {
530 	int opt_p;
531 	int fd;
532 };
533 
534 static int
535 pfile_callback(uintptr_t addr, const struct file *f, struct pfiles_cbdata *cb)
536 {
537 	vnode_t v, layer_vn;
538 	int myfd = cb->fd;
539 	const char *type;
540 	char path[MAXPATHLEN];
541 	uintptr_t top_vnodep, realvpp;
542 	char fsname[_ST_FSTYPSZ];
543 	int err, i;
544 
545 	cb->fd++;
546 
547 	if (addr == NULL) {
548 		return (WALK_NEXT);
549 	}
550 
551 	top_vnodep = realvpp = (uintptr_t)f->f_vnode;
552 
553 	if (mdb_vread(&v, sizeof (v), realvpp) == -1) {
554 		mdb_warn("failed to read vnode");
555 		return (DCMD_ERR);
556 	}
557 
558 	type = "?";
559 	for (i = 0; i <= NUM_FS_TYPES; i++) {
560 		if (fs_types[i].type == v.v_type)
561 			type = fs_types[i].name;
562 	}
563 
564 	do {
565 		uintptr_t next_realvpp;
566 
567 		err = next_realvp(realvpp, &layer_vn, &next_realvpp);
568 		if (next_realvpp != NULL)
569 			realvpp = next_realvpp;
570 
571 	} while (err == REALVP_CONTINUE);
572 
573 	if (err == REALVP_ERR) {
574 		mdb_warn("failed to do realvp() for %p", realvpp);
575 		return (DCMD_ERR);
576 	}
577 
578 	if (read_fsname((uintptr_t)layer_vn.v_vfsp, fsname) == -1)
579 		return (DCMD_ERR);
580 
581 	mdb_printf("%4d %4s %?0p ", myfd, type, top_vnodep);
582 
583 	if (cb->opt_p) {
584 		if (pfiles_dig_pathname(top_vnodep, path) == -1)
585 			return (DCMD_ERR);
586 
587 		mdb_printf("%s\n", path);
588 		return (DCMD_OK);
589 	}
590 
591 	/*
592 	 * Sockets generally don't have interesting pathnames; we only
593 	 * show those in the '-p' view.
594 	 */
595 	path[0] = '\0';
596 	if (v.v_type != VSOCK) {
597 		if (pfiles_dig_pathname(top_vnodep, path) == -1)
598 			return (DCMD_ERR);
599 	}
600 	mdb_printf("%s%s", path, path[0] == '\0' ? "" : " ");
601 
602 	switch (v.v_type) {
603 	case VDOOR:
604 	{
605 		door_node_t doornode;
606 		proc_t pr;
607 
608 		if (mdb_vread(&doornode, sizeof (doornode),
609 		    (uintptr_t)layer_vn.v_data) == -1) {
610 			mdb_warn("failed to read door_node");
611 			return (DCMD_ERR);
612 		}
613 
614 		if (mdb_vread(&pr, sizeof (pr),
615 		    (uintptr_t)doornode.door_target) == -1) {
616 			mdb_warn("failed to read door server process %p",
617 			    doornode.door_target);
618 			return (DCMD_ERR);
619 		}
620 		mdb_printf("[door to '%s' (proc=%p)]", pr.p_user.u_comm,
621 		    doornode.door_target);
622 		break;
623 	}
624 
625 	case VSOCK:
626 	{
627 		struct sonode sonode;
628 
629 		if (pfiles_get_sonode(realvpp, &sonode) == -1)
630 			return (DCMD_ERR);
631 
632 		/*
633 		 * If the address is cached in the sonode, use it; otherwise,
634 		 * we print nothing.
635 		 */
636 		if (sonode.so_state & SS_LADDR_VALID) {
637 			struct sockaddr *laddr =
638 			    mdb_alloc(sonode.so_laddr_len, UM_SLEEP);
639 			if (mdb_vread(laddr, sonode.so_laddr_len,
640 			    (uintptr_t)sonode.so_laddr_sa) == -1) {
641 				mdb_warn("failed to read sonode socket addr");
642 				return (DCMD_ERR);
643 			}
644 
645 			mdb_printf("socket: ");
646 			pfiles_print_addr(laddr);
647 		}
648 
649 		if (sonode.so_state & SS_FADDR_VALID) {
650 			struct sockaddr *faddr =
651 			    mdb_alloc(sonode.so_faddr_len, UM_SLEEP);
652 			if (mdb_vread(faddr, sonode.so_faddr_len,
653 			    (uintptr_t)sonode.so_faddr_sa) == -1) {
654 				mdb_warn("failed to read sonode remote addr");
655 				return (DCMD_ERR);
656 			}
657 
658 			mdb_printf("remote: ");
659 			pfiles_print_addr(faddr);
660 		}
661 		break;
662 	}
663 
664 	case VPORT:
665 		mdb_printf("[event port (port=%p)]", v.v_data);
666 		break;
667 
668 	case VPROC:
669 	{
670 		prnode_t prnode;
671 		prcommon_t prcommon;
672 
673 		if (mdb_vread(&prnode, sizeof (prnode),
674 		    (uintptr_t)layer_vn.v_data) == -1) {
675 			mdb_warn("failed to read prnode");
676 			return (DCMD_ERR);
677 		}
678 
679 		if (mdb_vread(&prcommon, sizeof (prcommon),
680 		    (uintptr_t)prnode.pr_common) == -1) {
681 			mdb_warn("failed to read prcommon %p",
682 			    prnode.pr_common);
683 			return (DCMD_ERR);
684 		}
685 
686 		mdb_printf("(proc=%p)", prcommon.prc_proc);
687 		break;
688 	}
689 
690 	default:
691 		break;
692 	}
693 
694 
695 	mdb_printf("\n");
696 
697 	return (WALK_NEXT);
698 }
699 
700 static int
701 file_t_callback(uintptr_t addr, const struct file *f, struct pfiles_cbdata *cb)
702 {
703 	int myfd = cb->fd;
704 
705 	cb->fd++;
706 
707 	if (addr == NULL) {
708 		return (WALK_NEXT);
709 	}
710 
711 	/*
712 	 * We really need 20 digits to print a 64-bit offset_t, but this
713 	 * is exceedingly rare, so we cheat and assume a column width of 10
714 	 * digits, in order to fit everything cleanly into 80 columns.
715 	 */
716 	mdb_printf("%?0p %4d %8x %?0p %10lld %?0p %4d\n",
717 	    addr, myfd, f->f_flag, f->f_vnode, f->f_offset, f->f_cred,
718 	    f->f_count);
719 
720 	return (WALK_NEXT);
721 }
722 
723 int
724 pfiles(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
725 {
726 	int opt_f = 0;
727 
728 	struct pfiles_cbdata cb;
729 
730 	bzero(&cb, sizeof (cb));
731 
732 	if (!(flags & DCMD_ADDRSPEC))
733 		return (DCMD_USAGE);
734 
735 	if (mdb_getopts(argc, argv,
736 	    'p', MDB_OPT_SETBITS, TRUE, &cb.opt_p,
737 	    'f', MDB_OPT_SETBITS, TRUE, &opt_f, NULL) != argc)
738 		return (DCMD_USAGE);
739 
740 	if (opt_f) {
741 		mdb_printf("%<u>%?s %4s %8s %?s %10s %?s %4s%</u>\n", "FILE",
742 		    "FD", "FLAG", "VNODE", "OFFSET", "CRED", "CNT");
743 		if (mdb_pwalk("allfile", (mdb_walk_cb_t)file_t_callback, &cb,
744 		    addr) == -1) {
745 			mdb_warn("failed to walk 'allfile'");
746 			return (DCMD_ERR);
747 		}
748 	} else {
749 		mdb_printf("%<u>%-4s %4s %?s ", "FD", "TYPE", "VNODE");
750 		if (cb.opt_p)
751 			mdb_printf("PATH");
752 		else
753 			mdb_printf("INFO");
754 		mdb_printf("%</u>\n");
755 
756 		if (mdb_pwalk("allfile", (mdb_walk_cb_t)pfile_callback, &cb,
757 		    addr) == -1) {
758 			mdb_warn("failed to walk 'allfile'");
759 			return (DCMD_ERR);
760 		}
761 	}
762 
763 
764 	return (DCMD_OK);
765 }
766 
767 void
768 pfiles_help(void)
769 {
770 	mdb_printf(
771 	    "Given the address of a process, print information about files\n"
772 	    "which the process has open.  By default, this includes decoded\n"
773 	    "information about the file depending on file and filesystem type\n"
774 	    "\n"
775 	    "\t-p\tPathnames; omit decoded information.  Only display "
776 	    "pathnames\n"
777 	    "\t-f\tfile_t view; show the file_t structure corresponding to "
778 	    "the fd\n");
779 }
780