xref: /illumos-gate/usr/src/cmd/stat/common/dsr.c (revision c2e7b48d)
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 2005 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 #include <sys/stat.h>
30 #include <sys/types.h>
31 
32 /*
33  * Dependent on types.h, but not including it...
34  */
35 #include <stdio.h>
36 #include <sys/types.h>
37 #include <sys/dkio.h>
38 #include <sys/dktp/fdisk.h>
39 #include <sys/mnttab.h>
40 #include <sys/mntent.h>
41 #include <sys/sysmacros.h>
42 #include <sys/mkdev.h>
43 #include <sys/vfs.h>
44 #include <nfs/nfs.h>
45 #include <nfs/nfs_clnt.h>
46 #include <kstat.h>
47 #include <ctype.h>
48 #include <dirent.h>
49 #include <libdevinfo.h>
50 #include <limits.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
54 #include <errno.h>
55 #include <devid.h>
56 
57 #include "dsr.h"
58 #include "statcommon.h"
59 
60 static void rummage_dev(ldinfo_t *);
61 static void do_snm(char *, char *);
62 static int look_up_name(const char *, disk_list_t *);
63 static disk_list_t *make_an_entry(char *, char *,
64     char *, dir_info_t *, int, ldinfo_t *);
65 static char *trim(char *, char *, int);
66 static ldinfo_t	*rummage_devinfo(void);
67 static void pline(char *, int, char *, char *, ldinfo_t **);
68 static void insert_dlist_ent(disk_list_t *, disk_list_t **);
69 static int str_is_digit(char *);
70 static ldinfo_t *find_ldinfo_match(char *, ldinfo_t *);
71 
72 static void insert_into_dlist(dir_info_t *, disk_list_t *);
73 static void cleanup_dlist(dir_info_t *);
74 static void cleanup_ldinfo(ldinfo_t *);
75 static int devinfo_ident_disks(di_node_t, void *);
76 static int devinfo_ident_tapes(di_node_t, void *);
77 static void process_dir_ent(char *dent, int curr_type,
78     char *last_snm, dir_info_t *, ldinfo_t *);
79 
80 static char *get_nfs_by_minor(uint_t);
81 static char *cur_hostname(uint_t, kstat_ctl_t *);
82 static char *cur_special(char *, char *);
83 
84 extern kstat_ctl_t *kc;
85 extern mnt_t *nfs;
86 
87 /*
88  * To do: add VXVM support: /dev/vx/dsk and ap support: /dev/ap/
89  *
90  * Note: Adding support for VxVM is *not* as simple as adding another
91  * entry in the table and magically getting to see stuff related to
92  * VxVM. The structure is radically different *AND* they don't produce
93  * any IO kstats.
94  */
95 
96 #define	OSA_DISK	0
97 #define	DISK		1
98 #define	MD_DISK		2
99 #define	TAPE		3
100 
101 #define	MAX_TYPES	4
102 
103 #define	OSA_DISK_PATH	"/dev/osa/dev/dsk"
104 #define	MD_DISK_PATH	"/dev/md/dsk"
105 #define	DISK_PATH	"/dev/dsk"
106 #define	TAPE_PATH	"/dev/rmt"
107 
108 #define	BASE_TRIM	"../../devices"
109 #define	MD_TRIM		"../../../devices"
110 #define	COLON		':'
111 #define	COMMA		','
112 
113 #define	NAME_BUFLEN	256
114 
115 static dir_info_t dlist[MAX_TYPES] = {
116 	OSA_DISK_PATH, 0, 0, 0, 0, "sd", BASE_TRIM, COLON,
117 	DISK_PATH, 0, 0, 0, 0, "sd", BASE_TRIM, COLON,
118 	MD_DISK_PATH, 0, 0, 0, 1, "md", MD_TRIM, COMMA,
119 	TAPE_PATH, 0, 0, 0, 0, "st", BASE_TRIM, COLON,
120 };
121 
122 /*
123  * Build a list of disks attached to the system.
124  */
125 static void
126 build_disk_list(void)
127 {
128 	ldinfo_t *ptoi;
129 
130 	/*
131 	 * Build the list of devices connected to the system.
132 	 */
133 	ptoi = rummage_devinfo();
134 	rummage_dev(ptoi);
135 	cleanup_ldinfo(ptoi);
136 }
137 
138 /*
139  * Walk the /dev/dsk and /dev/rmt directories building a
140  * list of interesting devices. Interesting is everything in the
141  * /dev/dsk directory. We skip some of the stuff in the /dev/rmt
142  * directory.
143  *
144  * Note that not finding one or more of the directories is not an
145  * error.
146  */
147 static void
148 rummage_dev(ldinfo_t *ptoi)
149 {
150 	DIR		*dskp;
151 	int		i;
152 	struct stat 	buf;
153 
154 	for (i = 0; i < MAX_TYPES; i++) {
155 		if (stat(dlist[i].name, &buf) == 0) {
156 			if (dlist[i].mtime != buf.st_mtime) {
157 				/*
158 				 * We've found a change. We need to cleanup
159 				 * old information and then rebuild the list
160 				 * for this device type.
161 				 */
162 				cleanup_dlist(&dlist[i]);
163 				dlist[i].mtime = buf.st_mtime;
164 				if ((dskp = opendir(dlist[i].name))) {
165 					struct dirent 	*bpt;
166 					char	last_snm[NAME_BUFLEN];
167 
168 					last_snm[0] = NULL;
169 					while ((bpt = readdir(dskp)) != NULL) {
170 						if (bpt->d_name[0] != '.') {
171 							process_dir_ent(
172 							    bpt->d_name,
173 							    i, last_snm,
174 							    &dlist[i],
175 							    ptoi);
176 						}
177 					}
178 				}
179 				(void) closedir(dskp);
180 			}
181 		}
182 	}
183 }
184 
185 /*
186  * Walk the list of located devices and see if we've
187  * seen this device before. We look at the short name.
188  */
189 static int
190 look_up_name(const char *nm, disk_list_t *list)
191 {
192 	while (list) {
193 		if (strcmp(list->dsk, nm) != 0)
194 			list = list->next;
195 		else {
196 			return (1);
197 		}
198 	}
199 	return (0);
200 }
201 
202 /*
203  * Take a name of the form cNtNdNsN or cNtNdNpN
204  * or /dev/dsk/CNtNdNsN or /dev/dsk/cNtNdNpN
205  * remove the trailing sN or pN. Simply looking
206  * for the first 's' or 'p' doesn't cut it.
207  */
208 static void
209 do_snm(char *orig, char *shortnm)
210 {
211 	char *tmp;
212 	char *ptmp;
213 	int done = 0;
214 	char repl_char = 0;
215 
216 	tmp = strrchr(orig, 's');
217 	if (tmp) {
218 		ptmp = tmp;
219 		ptmp++;
220 		done = str_is_digit(ptmp);
221 	}
222 	if (done == 0) {
223 		/*
224 		 * The string either has no 's' in it
225 		 * or the stuff trailing the s has a
226 		 * non-numeric in it. Look to see if
227 		 * we have an ending 'p' followed by
228 		 * numerics.
229 		 */
230 		tmp = strrchr(orig, 'p');
231 		if (tmp) {
232 			ptmp = tmp;
233 			ptmp++;
234 			if (str_is_digit(ptmp))
235 				repl_char = 'p';
236 			else
237 				tmp = 0;
238 		}
239 	} else {
240 		repl_char = 's';
241 	}
242 	if (tmp)
243 		*tmp = '\0';
244 	(void) strcpy(shortnm, orig);
245 	if (repl_char)
246 		*tmp = repl_char;
247 }
248 
249 /*
250  * Create and insert an entry into the device list.
251  */
252 static disk_list_t *
253 make_an_entry(char *lname, char *shortnm, char *longnm,
254 	dir_info_t *drent, int devtype, ldinfo_t *ptoi)
255 {
256 	disk_list_t	*entry;
257 	char	*nlnm;
258 	char 	snm[NAME_BUFLEN];
259 	ldinfo_t *p;
260 
261 	entry = safe_alloc(sizeof (disk_list_t));
262 
263 	nlnm = trim(lname, drent->trimstr, drent->trimchr);
264 	entry->dsk = safe_strdup(shortnm);
265 	do_snm(longnm, snm);
266 	entry->dname = safe_strdup(snm);
267 	entry->devtype = devtype;
268 	entry->devidstr = NULL;
269 	if ((p = find_ldinfo_match(nlnm, ptoi))) {
270 		entry->dnum = p->dnum;
271 		entry->dtype = safe_strdup(p->dtype);
272 		if (p->devidstr)
273 			entry->devidstr = safe_strdup(p->devidstr);
274 	} else {
275 		entry->dtype = safe_strdup(drent->dtype);
276 		entry->dnum = -1;
277 		if (drent->dtype) {
278 			if (strcmp(drent->dtype, "md") == 0) {
279 				(void) sscanf(shortnm, "d%d", &entry->dnum);
280 			}
281 		}
282 	}
283 	entry->seen = 0;
284 	entry->next = 0;
285 	insert_dlist_ent(entry, &drent->list);
286 	return (entry);
287 }
288 
289 /*
290  * slice stuff off beginning and end of /devices directory names derived from
291  * device links.
292  */
293 static char *
294 trim(char *fnm, char *lname, int rchr)
295 {
296 	char	*ptr;
297 
298 	while (*lname == *fnm) {
299 		lname++;
300 		fnm++;
301 	}
302 	if ((ptr = strrchr(fnm, rchr)))
303 		*ptr = NULL;
304 	return (fnm);
305 }
306 
307 /*
308  * Find an entry matching the name passed in
309  */
310 static ldinfo_t *
311 find_ldinfo_match(char *name, ldinfo_t *ptoi)
312 {
313 	if (name) {
314 		while (ptoi) {
315 			if (strcmp(ptoi->name, name))
316 				ptoi = ptoi->next;
317 			else
318 				return (ptoi);
319 		}
320 	}
321 	return (NULL);
322 }
323 
324 /*
325  * Determine if a name is already in the list of disks. If not, insert the
326  * name in the list.
327  */
328 static void
329 insert_dlist_ent(disk_list_t *n, disk_list_t **hd)
330 {
331 	disk_list_t *tmp_ptr;
332 
333 	if (n->dtype != NULL) {
334 		tmp_ptr = *hd;
335 		while (tmp_ptr) {
336 			if (strcmp(n->dsk, tmp_ptr->dsk) != 0)
337 				tmp_ptr = tmp_ptr->next;
338 			else
339 				break;
340 		}
341 		if (tmp_ptr == NULL) {
342 			/*
343 			 * We don't do anything with MD_DISK types here
344 			 * since they don't have partitions.
345 			 */
346 			if (n->devtype == DISK || n->devtype == OSA_DISK) {
347 				n->flags = SLICES_OK;
348 #if defined(__i386)
349 				n->flags |= PARTITIONS_OK;
350 #endif
351 			} else {
352 				n->flags = 0;
353 			}
354 			/*
355 			 * Figure out where to insert the name. The list is
356 			 * ostensibly in sorted order.
357 			 */
358 			if (*hd) {
359 				disk_list_t *follw;
360 				int	mv;
361 
362 				tmp_ptr = *hd;
363 
364 				/*
365 				 * Look through the list. While the strcmp
366 				 * value is less than the current value,
367 				 */
368 				while (tmp_ptr) {
369 					if ((mv = strcmp(n->dtype,
370 					    tmp_ptr->dtype)) < 0) {
371 						follw = tmp_ptr;
372 						tmp_ptr = tmp_ptr->next;
373 					} else
374 						break;
375 				}
376 				if (mv == 0) {
377 					/*
378 					 * We're now in the area where the
379 					 * leading chars of the kstat name
380 					 * match. We need to insert in numeric
381 					 * order after that.
382 					 */
383 					while (tmp_ptr) {
384 						if (strcmp(n->dtype,
385 						    tmp_ptr->dtype) != 0)
386 							break;
387 						if (n->dnum > tmp_ptr->dnum) {
388 							follw = tmp_ptr;
389 							tmp_ptr = tmp_ptr->next;
390 						} else
391 							break;
392 					}
393 				}
394 				/*
395 				 * We should now be ready to insert an
396 				 * entry...
397 				 */
398 				if (mv >= 0) {
399 					if (tmp_ptr == *hd) {
400 						n->next = tmp_ptr;
401 						*hd = n;
402 					} else {
403 						n->next = follw->next;
404 						follw->next = n;
405 					}
406 				} else {
407 					/*
408 					 * insert at the end of the
409 					 * list
410 					 */
411 					follw->next = n;
412 					n->next = 0;
413 				}
414 			} else {
415 				*hd = n;
416 				n->next = 0;
417 			}
418 		}
419 	}
420 }
421 
422 /*
423  * find an entry matching the given kstat name in the list
424  * of disks, tapes and metadevices.
425  */
426 disk_list_t *
427 lookup_ks_name(char *dev_nm)
428 {
429 	int tried = 0;
430 	int	dv;
431 	int	len;
432 	char	cmpbuf[PATH_MAX + 1];
433 	struct	list_of_disks *list;
434 	char	*nm;
435 	dev_name_t *tmp;
436 	uint_t	i;
437 
438 	/*
439 	 * extract the device type from the kstat name. We expect the
440 	 * name to be one or more alphabetics followed by the device
441 	 * numeric id. We do this solely for speed purposes .
442 	 */
443 	len = 0;
444 	nm = dev_nm;
445 	while (*nm) {
446 		if (isalpha(*nm)) {
447 			nm++;
448 			len++;
449 		} else
450 			break;
451 	}
452 
453 	if (!*nm)
454 		return (NULL);
455 
456 	/*
457 	 * For each of the elements in the dlist array we keep
458 	 * an array of pointers to chains for each of the kstat
459 	 * prefixes found within that directory. This is typically
460 	 * 'sd' and 'ssd'. We walk the list in the directory and
461 	 * match on that type. Since the same prefixes can be
462 	 * in multiple places we keep checking if we don't find
463 	 * it in the first place.
464 	 */
465 
466 	(void) strncpy(cmpbuf, dev_nm, len);
467 	cmpbuf[len] = NULL;
468 	dv = atoi(nm);
469 
470 retry:
471 	for (i = 0; i < MAX_TYPES; i++) {
472 		tmp = dlist[i].nf;
473 		while (tmp) {
474 			if (strcmp(tmp->name, cmpbuf) == 0) {
475 				/*
476 				 * As an optimization we keep mins
477 				 * and maxes for the devices found.
478 				 * This helps chop the lists up and
479 				 * avoid some really long chains as
480 				 * we would get if we kept only prefix
481 				 * lists.
482 				 */
483 				if (dv >= tmp->min && dv <= tmp->max) {
484 					list = tmp->list_start;
485 					while (list) {
486 						if (list->dnum < dv)
487 							list = list->next;
488 						else
489 							break;
490 					}
491 					if (list && list->dnum == dv) {
492 						return (list);
493 					}
494 				}
495 			}
496 			tmp = tmp->next;
497 		}
498 	}
499 
500 	if (!tried) {
501 		tried = 1;
502 		build_disk_list();
503 		goto retry;
504 	}
505 
506 	return (0);
507 }
508 
509 static int
510 str_is_digit(char *str)
511 {
512 	while (*str) {
513 		if (isdigit(*str))
514 		    str++;
515 		else
516 		    return (0);
517 	}
518 	return (1);
519 }
520 
521 static void
522 insert_into_dlist(dir_info_t *d, disk_list_t *e)
523 {
524 	dev_name_t *tmp;
525 
526 	tmp = d->nf;
527 	while (tmp) {
528 		if (strcmp(e->dtype, tmp->name) != 0) {
529 			tmp = tmp->next;
530 		} else {
531 			if (e->dnum < tmp->min) {
532 				tmp->min = e->dnum;
533 				tmp->list_start = e;
534 			} else if (e->dnum > tmp->max) {
535 				tmp->max = e->dnum;
536 				tmp->list_end = e;
537 			}
538 			break;
539 		}
540 	}
541 	if (tmp == NULL) {
542 		tmp = safe_alloc(sizeof (dev_name_t));
543 		tmp->name = e->dtype;
544 		tmp->min = e->dnum;
545 		tmp->max = e->dnum;
546 		tmp->list_start = e;
547 		tmp->list_end = e;
548 		tmp->next = d->nf;
549 		d->nf = tmp;
550 	}
551 }
552 
553 /*
554  * devinfo_ident_disks() and devinfo_ident_tapes() are the callback functions we
555  * use while walking the device tree snapshot provided by devinfo.  If
556  * devinfo_ident_disks() identifies that the device being considered has one or
557  * more minor nodes _and_ is a block device, then it is a potential disk.
558  * Similarly for devinfo_ident_tapes(), except that the second criterion is that
559  * the minor_node be a character device.  (This is more inclusive than only
560  * tape devices, but will match any entries in /dev/rmt/.)
561  *
562  * Note: if a driver was previously loaded but is now unloaded, the kstat may
563  * still be around (e.g., st) but no information will be found in the
564  * libdevinfo tree.
565  */
566 
567 static int
568 devinfo_ident_disks(di_node_t node, void *arg)
569 {
570 	di_minor_t minor = DI_MINOR_NIL;
571 
572 	if ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
573 		int spectype = di_minor_spectype(minor);
574 
575 		if (S_ISBLK(spectype)) {
576 			char *physical_path = di_devfs_path(node);
577 			int instance = di_instance(node);
578 			char *driver_name = di_driver_name(node);
579 			char *devidstr;
580 
581 			/* lookup the devid, devt specific first */
582 			if ((di_prop_lookup_strings(di_minor_devt(minor), node,
583 			    DEVID_PROP_NAME, &devidstr) == -1) &&
584 			    (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
585 			    DEVID_PROP_NAME, &devidstr) == -1))
586 				devidstr = NULL;
587 
588 			if (driver_name == NULL)
589 				driver_name = "<nil>";
590 
591 			pline(physical_path, instance,
592 				    driver_name, devidstr, arg);
593 			di_devfs_path_free(physical_path);
594 		}
595 	}
596 	return (DI_WALK_CONTINUE);
597 }
598 
599 static int
600 devinfo_ident_tapes(di_node_t node, void *arg)
601 {
602 	di_minor_t minor = DI_MINOR_NIL;
603 
604 	if ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
605 		int spectype = di_minor_spectype(minor);
606 
607 		if (S_ISCHR(spectype)) {
608 			char *physical_path = di_devfs_path(node);
609 			int instance = di_instance(node);
610 			char *binding_name = di_binding_name(node);
611 
612 			pline(physical_path, instance,
613 			    binding_name, NULL, arg);
614 			di_devfs_path_free(physical_path);
615 		}
616 	}
617 	return (DI_WALK_CONTINUE);
618 }
619 
620 /*
621  * rummage_devinfo() is the driver routine that walks the devinfo snapshot.
622  */
623 static ldinfo_t *
624 rummage_devinfo(void)
625 {
626 	di_node_t root_node;
627 	ldinfo_t *rv = NULL;
628 
629 	if ((root_node = di_init("/", DINFOCPYALL)) != DI_NODE_NIL) {
630 		(void) di_walk_node(root_node, DI_WALK_CLDFIRST, (void *)&rv,
631 			devinfo_ident_disks);
632 		(void) di_walk_node(root_node, DI_WALK_CLDFIRST, (void *)&rv,
633 			devinfo_ident_tapes);
634 		di_fini(root_node);
635 	}
636 	return (rv);
637 }
638 
639 /*
640  * pline() performs the lookup of the device path in the current list of disks,
641  * and adds the appropriate information to the nms list in the case of a match.
642  */
643 static void
644 pline(char *devfs_path, int instance,
645 	char *driver_name, char *devidstr, ldinfo_t **list)
646 {
647 	ldinfo_t *entry;
648 
649 	entry = safe_alloc(sizeof (ldinfo_t));
650 	entry->dnum = instance;
651 	entry->name = safe_strdup(devfs_path);
652 	entry->dtype = safe_strdup(driver_name);
653 	entry->devidstr = safe_strdup(devidstr);
654 	entry->next = *list;
655 	*list = entry;
656 }
657 
658 /*
659  * Cleanup space allocated in dlist processing.
660  * We're only interested in cleaning up the list and nf
661  * fields in the structure. Everything else is static
662  * data.
663  */
664 static void
665 cleanup_dlist(dir_info_t *d)
666 {
667 	dev_name_t *tmp;
668 	dev_name_t *t1;
669 	disk_list_t *t2;
670 	disk_list_t *t3;
671 
672 	/*
673 	 * All of the entries in a dev_name_t use information
674 	 * from a disk_list_t structure that is freed later.
675 	 * All we need do here is free the dev_name_t
676 	 * structure itself.
677 	 */
678 	tmp = d->nf;
679 	while (tmp) {
680 		t1 = tmp->next;
681 		free(tmp);
682 		tmp = t1;
683 	}
684 	d->nf = 0;
685 	/*
686 	 * "Later". Free the disk_list_t structures and their
687 	 * data attached to this portion of the dir_info
688 	 * structure.
689 	 */
690 	t2 = d->list;
691 	while (t2) {
692 		if (t2->dtype) {
693 			free(t2->dtype);
694 			t2->dtype = NULL;
695 		}
696 		if (t2->dsk) {
697 			free(t2->dsk);
698 			t2->dsk = NULL;
699 		}
700 		if (t2->dname) {
701 			free(t2->dname);
702 			t2->dname = NULL;
703 		}
704 		t3 = t2->next;
705 		free(t2);
706 		t2 = t3;
707 	}
708 	d->list = 0;
709 }
710 
711 static void
712 process_dir_ent(char *dent, int curr_type, char *last_snm,
713     dir_info_t *dp, ldinfo_t *ptoi)
714 {
715 	struct stat	sbuf;
716 	char	dnmbuf[PATH_MAX + 1];
717 	char	lnm[NAME_BUFLEN];
718 	char	snm[NAME_BUFLEN];
719 	char	*npt;
720 
721 	snm[0] = NULL;
722 	if (curr_type == DISK || curr_type == OSA_DISK) {
723 		/*
724 		 * get the short name - omitting
725 		 * the trailing sN or PN
726 		 */
727 		(void) strcpy(lnm, dent);
728 		do_snm(dent, snm);
729 	} else if (curr_type == MD_DISK) {
730 		(void) strcpy(lnm, dent);
731 		(void) strcpy(snm, dent);
732 	} else {
733 		/*
734 		 * don't want all rewind/etc
735 		 * devices for a tape
736 		 */
737 		if (!str_is_digit(dent))
738 			return;
739 		(void) snprintf(snm, sizeof (snm), "rmt/%s", dent);
740 		(void) snprintf(lnm, sizeof (snm), "rmt/%s", dent);
741 	}
742 	/*
743 	 * See if we've already processed an entry for this device.
744 	 * If so, we're just another partition so we get another
745 	 * entry.
746 	 *
747 	 * last_snm is an optimization to avoid the function call
748 	 * and lookup since we'll often see partition records
749 	 * immediately after the disk record.
750 	 */
751 	if (dp->skip_lookup == 0) {
752 		if (strcmp(snm, last_snm) != 0) {
753 			/*
754 			 * a zero return means that
755 			 * no record was found. We'd
756 			 * return a pointer otherwise.
757 			 */
758 			if (look_up_name(snm,
759 				dp->list) == 0) {
760 				(void) strcpy(last_snm, snm);
761 			} else
762 				return;
763 		} else
764 			return;
765 	}
766 	/*
767 	 * Get the real device name for this beast
768 	 * by following the link into /devices.
769 	 */
770 	(void) snprintf(dnmbuf, sizeof (dnmbuf), "%s/%s", dp->name, dent);
771 	if (lstat(dnmbuf, &sbuf) != -1) {
772 		if ((sbuf.st_mode & S_IFMT) == S_IFLNK) {
773 			/*
774 			 * It's a link. Get the real name.
775 			 */
776 			char	nmbuf[PATH_MAX + 1];
777 			int	nbyr;
778 
779 			if ((nbyr = readlink(dnmbuf, nmbuf,
780 			    sizeof (nmbuf))) != 1) {
781 				npt = nmbuf;
782 				/*
783 				 * readlink does not terminate
784 				 * the string so we have to
785 				 * do it.
786 				 */
787 				nmbuf[nbyr] = NULL;
788 			} else
789 				npt = NULL;
790 		} else
791 			npt = lnm;
792 		/*
793 		 * make an entry in the device list
794 		 */
795 		if (npt) {
796 			disk_list_t *d;
797 
798 			d = make_an_entry(npt, snm,
799 			    dnmbuf, dp,
800 			    curr_type, ptoi);
801 			insert_into_dlist(dp, d);
802 		}
803 	}
804 }
805 static void
806 cleanup_ldinfo(ldinfo_t *list)
807 {
808 	ldinfo_t *tmp;
809 	while (list) {
810 		tmp = list;
811 		list = list->next;
812 		free(tmp->name);
813 		free(tmp->dtype);
814 		if (tmp->devidstr)
815 			free(tmp->devidstr);
816 		free(tmp);
817 	}
818 }
819 
820 char *
821 lookup_nfs_name(char *ks, kstat_ctl_t *kc)
822 {
823 	int tried = 0;
824 	uint_t minor;
825 	char *host, *path;
826 	char *cp;
827 	char *rstr = 0;
828 	size_t len;
829 
830 	if (sscanf(ks, "nfs%u", &minor) == 1) {
831 retry:
832 		cp = get_nfs_by_minor(minor);
833 		if (cp) {
834 			if (strchr(cp, ',') == NULL) {
835 				rstr = safe_strdup(cp);
836 				return (rstr);
837 			}
838 			host = cur_hostname(minor, kc);
839 			if (host) {
840 				if (*host) {
841 					path = cur_special(host, cp);
842 					if (path) {
843 						len = strlen(host);
844 						len += strlen(path);
845 						len += 2;
846 						rstr = safe_alloc(len);
847 						(void) snprintf(rstr, len,
848 						    "%s:%s", host, path);
849 					} else {
850 						rstr = safe_strdup(cp);
851 					}
852 				} else {
853 					rstr = safe_strdup(ks);
854 				}
855 				free(host);
856 			} else {
857 				rstr = safe_strdup(cp);
858 			}
859 		} else if (!tried) {
860 			tried = 1;
861 			do_mnttab();
862 			goto retry;
863 		}
864 	}
865 	return (rstr);
866 }
867 
868 static char *
869 get_nfs_by_minor(uint_t minor)
870 {
871 	mnt_t *localnfs;
872 
873 	localnfs = nfs;
874 	while (localnfs) {
875 		if (localnfs->minor == minor) {
876 			return (localnfs->device_name);
877 		}
878 		localnfs = localnfs->next;
879 	}
880 	return (0);
881 }
882 
883 /*
884  * Read the cur_hostname from the mntinfo kstat
885  */
886 static char *
887 cur_hostname(uint_t minor, kstat_ctl_t *kc)
888 {
889 	kstat_t *ksp;
890 	static struct mntinfo_kstat mik;
891 	char *rstr;
892 
893 	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
894 		if (ksp->ks_type != KSTAT_TYPE_RAW)
895 			continue;
896 		if (ksp->ks_instance != minor)
897 			continue;
898 		if (strcmp(ksp->ks_module, "nfs"))
899 			continue;
900 		if (strcmp(ksp->ks_name, "mntinfo"))
901 			continue;
902 		if (ksp->ks_flags & KSTAT_FLAG_INVALID)
903 			return (NULL);
904 		if (kstat_read(kc, ksp, &mik) == -1)
905 			return (NULL);
906 		rstr = safe_strdup(mik.mik_curserver);
907 		return (rstr);
908 	}
909 	return (NULL);
910 }
911 
912 /*
913  * Given the hostname of the mounted server, extract the server
914  * mount point from the mnttab string.
915  *
916  * Common forms:
917  *	server1,server2,server3:/path
918  *	server1:/path,server2:/path
919  * or a hybrid of the two
920  */
921 static char *
922 cur_special(char *hostname, char *special)
923 {
924 	char *cp;
925 	char *path;
926 	size_t hlen = strlen(hostname);
927 
928 	/*
929 	 * find hostname in string
930 	 */
931 again:
932 	if ((cp = strstr(special, hostname)) == NULL)
933 		return (NULL);
934 
935 	/*
936 	 * hostname must be followed by ',' or ':'
937 	 */
938 	if (cp[hlen] != ',' && cp[hlen] != ':') {
939 		special = &cp[hlen];
940 		goto again;
941 	}
942 
943 	/*
944 	 * If hostname is followed by a ',' eat all characters until a ':'
945 	 */
946 	cp = &cp[hlen];
947 	if (*cp == ',') {
948 		cp++;
949 		while (*cp != ':') {
950 			if (*cp == NULL)
951 				return (NULL);
952 			cp++;
953 		}
954 	}
955 	path = ++cp;			/* skip ':' */
956 
957 	/*
958 	 * path is terminated by either 0, or space or ','
959 	 */
960 	while (*cp) {
961 		if (isspace(*cp) || *cp == ',') {
962 			*cp = NULL;
963 			return (path);
964 		}
965 		cp++;
966 	}
967 	return (path);
968 }
969