xref: /illumos-gate/usr/src/lib/libadm/common/devtab.c (revision 8b80e8cb6855118d46f605e91b5ed4ce83417395)
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 (c) 1996-1998 by Sun Microsystems, Inc.
24  * All Rights reserved.
25  */
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 
30 #pragma	ident	"%Z%%M%	%I%	%E% SMI"	/* SVr4.0 1.2 */
31 /*LINTLIBRARY*/
32 
33 /*
34  * devtab.c
35  *
36  *  Contains functions that deal with the device table and are not for
37  *  consumption by the general user population.
38  *
39  *  Functions defined:
40  *	_opendevtab()		Opens the device table for commands
41  *	_setdevtab()		Rewinds the open device table
42  *	_enddevtab()		Closes the open device table
43  *	_getdevtabent()		Gets the next entry in the device table
44  *	_freedevtabent()	Frees memory allocated to a device-table entry
45  *	_getdevrec()		Gets a specific record from the device table
46  *	_devtabpath()		Get the pathname of the device table file
47  *	_validalias()		Is a value a valid alias?
48  */
49 
50 /*
51  *  Header files
52  *
53  *	<sys/sysmacros.h>	System macro definitions
54  *	<sys/types.h>		System data types
55  *	<sys/mkdev.h>		Device Macros
56  *	<unistd.h>		System Symbolic Constants
57  *	<stdio.h>		Standard I/O definitions
58  *	<string.h>		String handling definitions
59  *	<ctype.h>		Character types and macros
60  *	<errno.h>		Error codes
61  *	<sys/stat.h>		File status information
62  *	<devmgmt.h>		Global Device Management definitions
63  *	"devtab.h"		Local Device Management definitions
64  */
65 
66 #include	<sys/sysmacros.h>
67 #include	<sys/types.h>
68 #ifndef SUNOS41
69 #include	<sys/mkdev.h>
70 #endif
71 #include	<unistd.h>
72 #include	<stdio.h>
73 #include	<string.h>
74 #include	<ctype.h>
75 #include	<errno.h>
76 #include	<sys/stat.h>
77 #include	<devmgmt.h>
78 #include	"devtab.h"
79 #include	<stdlib.h>
80 
81 /*
82  *  Static data definitions:
83  *	dtabrecnum	Record number of the current record (0 to n-1)
84  *	leftoff		Addr of char to begin next parse using
85  *			getfld(), getattrval(), getquoted()
86  *	recbufsz	The size of the buffer used for reading records
87  *	recbuf		Addr of malloc() buffer for reading records
88  *	xtndcnt		Number of malloc()/realloc() calls on record buffer
89  */
90 
91 static	int		xtndcnt = 0;
92 static	char		*recbuf = NULL;
93 static	int		recbufsz = 0;
94 
95 static	char		*leftoff = NULL;
96 static	int		dtabrecnum = 0;
97 
98 /*
99  * int samedev(x, y)
100  *	struct stat	x, y
101  *
102  *	Compares pertinent information in a stat() structure
103  *	to see if the two structures describe the same device.
104  *	If the file modes are the same and they have the same
105  *	file system and i-node (i.e. they're links) or they
106  *	are block or character devices and have the same major
107  *	and minor device numbers (i.e. "mknod"s for the same
108  *	device), it's the same device.
109  *
110  *   Returns:  int
111  *	TRUE if the two structures describe the same device
112  *	FALSE otherwise
113  */
114 
115 static int
116 samedev(struct stat64 x, struct stat64 y)
117 {
118 	int	same;
119 
120 
121 	/* If the devices are of the same type ... */
122 	if ((x.st_mode & 0170000) == (y.st_mode & 0170000)) {
123 
124 		/*
125 		 * If they are described by the same inode on the same device,
126 		 * the two devices are the same.  Otherwise, if the devices are
127 		 * character-special or block-special devices, try to match by
128 		 * device type and major and minor device numbers.
129 		 */
130 
131 	    if ((x.st_dev == y.st_dev) && (x.st_ino == y.st_ino)) same = TRUE;
132 	    else
133 		if (((x.st_mode & 0170000) == 0020000) ||
134 		    ((x.st_mode & 0170000) == 0060000)) {
135 		    if ((major(x.st_rdev) == major(y.st_rdev)) &&
136 			(minor(x.st_rdev) == minor(y.st_rdev))) same = TRUE;
137 		    else same = FALSE;
138 		} else same = FALSE;
139 
140 	} else same = FALSE;
141 
142 	return (same);
143 }
144 
145 /*
146  *  void _setdevtab()
147  *
148  *	This function rewinds the open device table so that the next
149  *	_getdevtabent() returns the first record in the device table.
150  *
151  *  Arguments:  None
152  *
153  *  Returns:  Void
154  */
155 
156 void
157 _setdevtab(void)
158 {
159 	/*  If the device table file is open, rewind the file  */
160 	if (oam_devtab != NULL) {
161 	    rewind(oam_devtab);
162 	    dtabrecnum = 0;
163 	}
164 }
165 
166 /*
167  *  void _enddevtab()
168  *
169  *	This function closes the open device table.  It resets the
170  *	open device table external variable to NULL.
171  *
172  *  Arguments:  None
173  *
174  *  Returns:  Void
175  */
176 
177 void
178 _enddevtab(void)
179 {
180 	/*  If the device table file is open, close it  */
181 	if (oam_devtab != NULL) {
182 	    (void) fclose(oam_devtab);
183 	    oam_devtab = NULL;
184 	    dtabrecnum = 0;
185 	}
186 }
187 
188 /*
189  *  char *getfld(ptr, delims)
190  *	char   *ptr
191  *	char   *delims
192  *
193  *  Notes:
194  *    -	Can't use "strtok()" because of its use of static data.  The caller
195  *	may be using strtok() and we'll really mess them up.
196  *    - The function returns NULL if it didn't find any token -- '\0' can't
197  *	be a delimiter using this algorithm.
198  */
199 
200 static char *
201 getfld(
202 	char   *ptr,		/* String to parse */
203 	char   *delims)		/* List of delimiters */
204 {
205 	int	done;		/* TRUE if we're finished */
206 	char   *p, *q;		/* Temp pointers */
207 
208 	/*
209 	 *  Figure out where to start.
210 	 *  If given a pointer, use that.
211 	 *  Otherwise, use where we left off.
212 	 */
213 
214 	p = ptr ? ptr : leftoff;
215 
216 
217 	/*
218 	 *  If there's anything to parse, search the string for the first
219 	 *  occurrence of any of the delimiters.  If one is found, change it
220 	 *  to '\0' and remember the place to start for next time.  If not
221 	 *  found, forget the restart address and prepare to return NULL.
222 	 *  Don't terminate on "escaped" characters.
223 	 */
224 
225 	if (p) {				    /* Anything to do ?? */
226 	    q = p;				    /* Where to begin */
227 	    done = FALSE;			    /* We're not done yet */
228 	    while (*q && !done) {		    /* Any more chars */
229 		if (*q == '\\') {		    /* Escaped ? */
230 		    if (*(++q)) q++;		    /* Skip escaped char */
231 		} else				    /* Not escaped */
232 		    if (!strchr(delims, *q)) q++;   /* Skip non-delim */
233 		    else done = TRUE;		    /* Otherwise, done */
234 	    }
235 	    if (*q) {				    /* Terminator found? */
236 		*q++ = '\0';			    /* Null-terminate token */
237 		leftoff = q;			    /* Remember restart pt. */
238 	    } else
239 		leftoff = p = NULL;		    /* Nothin found or left */
240 	}
241 
242 	/*  Finished  */
243 	return (p);				    /* Return ptr to token */
244 }
245 
246 /*
247  *  char *getquoted(ptr)
248  *	char   *ptr;
249  *
250  *	This function extracts a quoted string from the string pointed
251  *	to by <ptr>, or, if <ptr> is NULL, wherever we left off
252  *	last time.
253  *
254  *  Arguments:
255  *	char *ptr	Pointer to the character-string to parse, or
256  *			(char *) NULL if we're to pick up where we
257  *			[getquoted(), getfld(), and getattrval()] left off.
258  *
259  *  Returns:  char *
260  *	The address of malloc()ed space that contains the possibly quoted
261  *	string.
262  *
263  *  Notes:
264  *    -	This code only works if it can assume that the last character in
265  *	the string it's parsing is a '\n', something that is guarenteed
266  *	by the getnextrec() function.
267  */
268 
269 static char *
270 getquoted(char *ptr)
271 {
272 	/*  Automatic data  */
273 	char   *rtn;		/* Value to return */
274 	char   *p, *q;		/* Temps */
275 
276 	/* Figure out where to start off */
277 	p = ptr ? ptr : leftoff;
278 
279 	/* If there's anything to parse and it's a quoted string ... */
280 	if ((p) && (*p == '"') && (p = getfld(p+1, "\""))) {
281 
282 	    /* Copy string for the caller */
283 	    if (rtn = malloc(strlen(p)+1)) {	/* Malloc() space */
284 		q = rtn;			/* Set up temp ptr */
285 		do {
286 		    if (*p == '\\') p++;	/* Skip escape */
287 		    *q++ = *p;			/* Copy char */
288 		} while (*p++); 		/* While there's chars */
289 	    } else leftoff = rtn = NULL;
290 	} else leftoff = rtn = NULL;
291 
292 	/* Fini */
293 	return (rtn);
294 }
295 
296 /*
297  *  struct attrval *getattrval(ptr)
298  *	char   *ptr
299  *
300  *	This function extracts the next attr=val pair from <ptr> or wherever
301  *	getfld() left off...
302  *
303  *  Arguments:
304  *	char *ptr	The string to parse, or (char *) NULL if we're to
305  *			begin wherever we left off last time.
306  *
307  *  Returns:  struct attrval *
308  *	The address of a malloc()ed structure containing the attribute and the
309  *	value of the attr=val pair extracted.
310  */
311 
312 static struct attrval *
313 getattrval(char *ptr)
314 {
315 	/*  Automatic data  */
316 	struct attrval *rtn;		/* Ptr to struct to return */
317 	char		*p, *q;		/* Temp pointers */
318 
319 
320 	/*  Use what's given to us or wherever we left off  */
321 	p = ptr ? ptr : leftoff;
322 
323 	/*  If there's anything to parse, extract the next attr=val pair  */
324 	if (p) {
325 
326 	    /*  Eat white space  */
327 	    while (*p && isspace((unsigned char)*p)) p++;
328 
329 	    /*  Extract the attribute name, if any  */
330 	    if (*p && getfld(p, "=")) {
331 
332 		/*  Allocate space for the structure we're building  */
333 		if (rtn = malloc(sizeof (struct attrval))) {
334 
335 		    /*  Allocate space for the attribute name  */
336 		    if (rtn->attr = malloc(strlen(p)+1)) {
337 
338 			/*  Copy the attribute name into alloc'd space  */
339 			q = rtn->attr;			/* Set up temp ptr */
340 			do {
341 			    if (*p == '\\') p++;	/* Skip escape */
342 			    *q++ = *p;			/* Copy char */
343 			} while (*p++); 		/* While more */
344 
345 			/*  Extract the value  */
346 			if (!(rtn->val = getquoted(NULL))) {
347 			    /* Error getting value, free resources */
348 			    free(rtn->attr);
349 			    free(rtn);
350 			    leftoff = NULL;
351 			    rtn = NULL;
352 			}
353 		    } else {
354 			/* Error getting space for attribute, free resources */
355 			free(rtn);
356 			leftoff = NULL;
357 			rtn = NULL;
358 		    }
359 
360 		} else {
361 		    /* No space for attr struct */
362 		    leftoff = NULL;
363 		    rtn = NULL;
364 		}
365 
366 	    } else {
367 		/* No attribute name */
368 		leftoff = NULL;
369 		rtn = NULL;
370 	    }
371 
372 	} else {
373 	    /* Nothing to parse */
374 	    leftoff = NULL;
375 	    rtn = NULL;
376 	}
377 
378 	/* Done */
379 	return (rtn);
380 }
381 
382 /*
383  *  char *getnextrec()
384  *
385  *	This function gets the next record from the input stream "oam_devtab"
386  *	and puts it in the device-table record buffer (whose address is in
387  *	"recbuf").  If the buffer is not allocated or is too small to
388  *	accommodate the record, the function allocates more space to the
389  *	buffer.
390  *
391  *  Arguments:  None
392  *
393  *  Returns:  char *
394  *	The address of the buffer containing the record.
395  *
396  *  Static Data Referenced:
397  *	recbuf		Address of the buffer containing records read from the
398  *			device table file
399  *	recbufsz	Current size of the record buffer
400  *	xtndcnt		Number of times the record buffer has been extended
401  *	oam_devtab	Device table stream, expected to be open for (at
402  *			least) reading
403  *
404  *  Notes:
405  *    - The string returned in the buffer <buf> ALWAYS end in a '\n' (newline)
406  *	character followed by a '\0' (null).
407  */
408 
409 static char *
410 getnextrec(void)
411 {
412 	/* Automatic data */
413 	char		*recp;		/* Value to return */
414 	char		*p;		/* Temp pointer */
415 	int		done;		/* TRUE if we're finished */
416 	int		reclen;		/* Number of chars in record */
417 
418 
419 	/* If there's no buffer for records, try to get one */
420 	if (!recbuf) {
421 	    if (recbuf = malloc(DTAB_BUFSIZ)) {
422 		recbufsz = DTAB_BUFSIZ;
423 		xtndcnt = 0;
424 	    } else return (NULL);
425 	}
426 
427 
428 	/* Get the next record */
429 	recp = fgets(recbuf, recbufsz, oam_devtab);
430 	done = FALSE;
431 
432 	/* While we've something to return and we're not finished ... */
433 	while (recp && !done) {
434 
435 	    /* If our return string isn't a null-string ... */
436 	    if ((reclen = (int)strlen(recp)) != 0) {
437 
438 		/* If we have a complete record, we're finished */
439 		if ((*(recp+reclen-1) == '\n') &&
440 		    ((reclen == 1) || (*(recp+reclen-2) != '\\'))) done = TRUE;
441 		else while (!done) {
442 
443 			/*
444 			 * Need to complete the record.  A complete record is
445 			 * one which is terminated by an unescaped new-line
446 			 * character.
447 			 */
448 
449 		    /* If the buffer is full, expand it and continue reading */
450 		    if (reclen == recbufsz-1) {
451 
452 			/* Have we reached our maximum extension count? */
453 			if (xtndcnt < XTND_MAXCNT) {
454 
455 			    /* Expand the record buffer */
456 			    if (p = realloc(recbuf,
457 				(size_t)recbufsz+DTAB_BUFINC)) {
458 
459 				/* Update buffer information */
460 				xtndcnt++;
461 				recbuf = p;
462 				recbufsz += DTAB_BUFINC;
463 
464 			    } else {
465 
466 				/* Expansion failed */
467 				recp = NULL;
468 				done = TRUE;
469 			    }
470 
471 			} else {
472 
473 			    /* Maximum extend count exceeded.  Insane table */
474 			    recp = NULL;
475 			    done = TRUE;
476 			}
477 
478 		    }
479 
480 		    /* Complete the record */
481 		    if (!done) {
482 
483 			/* Read stuff into the expanded space */
484 			if (fgets(recbuf+reclen, recbufsz-reclen, oam_devtab)) {
485 			    reclen = (int)strlen(recbuf);
486 			    recp = recbuf;
487 			    if ((*(recp+reclen-1) == '\n') &&
488 				((reclen == 1) || (*(recp+reclen-2) != '\\')))
489 				    done = TRUE;
490 			} else {
491 			    /* Read failed, corrupt record? */
492 			    recp = NULL;
493 			    done = TRUE;
494 			}
495 		    }
496 
497 		}   /* End incomplete record handling */
498 
499 	    } else {
500 
501 		/* Read a null string?  (corrupt table) */
502 		recp = NULL;
503 		done = TRUE;
504 	    }
505 
506 	}   /* while (recp && !done) */
507 
508 	/* Return what we've got (if anything) */
509 	return (recp);
510 }
511 
512 /*
513  *  char *_devtabpath()
514  *
515  *	Get the pathname of the device table
516  *
517  *  Arguments:  None
518  *
519  *  Returns:  char *
520  *	Returns the pathname to the device table of NULL if
521  *	there was a problem getting the memory needed to contain the
522  *	pathname.
523  *
524  *  Algorithm:
525  *	1.  If OAM_DEVTAB is defined in the environment and is not
526  *	    defined as "", it returns the value of that environment
527  *	    variable.
528  *	2.  Otherwise, use the value of the environment variable DTAB_PATH.
529  */
530 
531 
532 char *
533 _devtabpath(void)
534 {
535 
536 	/* Automatic data */
537 #ifdef	DEBUG
538 	char		*path;		/* Ptr to path in environment */
539 #endif
540 	char		*rtnval;		/* Ptr to value to return */
541 
542 
543 	/*
544 	 * If compiled with -DDEBUG=1,
545 	 * look for the pathname in the environment
546 	 */
547 
548 #ifdef	DEBUG
549 	if (((path = getenv(OAM_DEVTAB)) != NULL) && (*path)) {
550 	    if (rtnval = malloc(strlen(path)+1))
551 		(void) strcpy(rtnval, path);
552 	} else {
553 #endif
554 		/*
555 		 * Use the standard device table.
556 		 */
557 
558 	    if (rtnval = malloc(strlen(DTAB_PATH)+1))
559 		(void) strcpy(rtnval, DTAB_PATH);
560 
561 #ifdef	DEBUG
562 	}
563 #endif
564 
565 	/* Finished */
566 	return (rtnval);
567 }
568 
569 /*
570  *  int _opendevtab(mode)
571  *	char   *mode
572  *
573  *	The _opendevtab() function opens a device table for a command.
574  *
575  *  Arguments:
576  *	mode	The open mode to use to open the file.  (i.e. "r" for
577  *		reading, "w" for writing.  See FOPEN(BA_OS) in SVID.)
578  *
579  *  Returns:  int
580  *	TRUE if it successfully opens the device table file, FALSE otherwise
581  */
582 
583 int
584 _opendevtab(char *mode)
585 {
586 	/*
587 	 *  Automatic data
588 	 */
589 
590 	char   *devtabname;		/* Ptr to the device table name */
591 	int	rtnval;			/* Value to return */
592 
593 
594 	rtnval = TRUE;
595 	if (devtabname = _devtabpath()) {
596 	    if (oam_devtab) (void) fclose(oam_devtab);
597 	    if (oam_devtab = fopen(devtabname, mode))
598 		dtabrecnum = 0;  /* :-) */
599 	    else rtnval = FALSE; /* :-( */
600 	} else rtnval = FALSE;   /* :-( */
601 	return (rtnval);
602 }
603 
604 /*
605  *  int _validalias(alias)
606  *	char   *alias
607  *
608  *	Determine if <alias> is a valid alias.  Returns TRUE if it is
609  *	a valid alias, FALSE otherwise.
610  *
611  *  Arguments:
612  *	alias		Value to check out
613  *
614  *  Returns:  int
615  *	TRUE if <alias> is a valid alias, FALSE otherwise.
616  */
617 
618 int
619 _validalias(char   *alias)			/* Alias to validate */
620 {
621 	/* Automatic data */
622 	char		*p;		/* Temp pointer */
623 	size_t		len;		/* Length of <alias> */
624 	int		rtn;		/* Value to return */
625 
626 
627 	/* Assume the worst */
628 	rtn = FALSE;
629 
630 	/*
631 	 * A valid alias contains 0 < i <= 14 characters.  The first
632 	 * must be alphanumeric or "@$_." and the rest must be alphanumeric
633 	 * or "@#$_+-."
634 	 */
635 
636 	/* Check length */
637 	if ((alias != NULL) && ((len = strlen(alias)) > 0) && (len <= 14)) {
638 
639 	    /* Check the first character */
640 	    p = alias;
641 	    if (isalnum((unsigned char)*p) || strchr("@$_.", *p)) {
642 
643 		/* Check the rest of the characters */
644 		for (p++; *p && (isalnum((unsigned char)*p) ||
645 			strchr("@#$_-+.", *p)); p++)
646 			;
647 		if (!(*p)) rtn = TRUE;
648 	    }
649 	}
650 
651 	/* Return indicator... */
652 	return (rtn);
653 
654 }   /* int _validalias() */
655 
656 /*
657  *  struct devtabent *_getdevtabent()
658  *
659  *  	This function returns the next entry in the device table.
660  *	If no device table is open, it opens the standard device table
661  *	and returns the first record in the table.
662  *
663  *  Arguments:  None.
664  *
665  *  Returns:  struct devtabent *
666  *	Pointer to the next record in the device table, or
667  *	(struct devtabent *) NULL if it was unable to open the file or there
668  *	are no more records to read.  "errno" reflects the situation.  If
669  *	errno is not changed and the function returns NULL, there are no more
670  *	records to read.  If errno is set, it indicates the error.
671  *
672  *  Notes:
673  *    - The caller should set "errno" to 0 before calling this function.
674  */
675 
676 struct devtabent *
677 _getdevtabent(void)
678 {
679 	/*  Automatic data  */
680 	struct devtabent	*ent;	/* Ptr to dev table entry structure */
681 	struct attrval		*attr;	/* Ptr to struct for attr/val pair */
682 	struct attrval		*t;	/* Tmp ptr to attr/val struct */
683 	char			*record; /* Ptr to the record just read */
684 	char			*p, *q;	/* Tmp char ptrs */
685 	int			done;	/* TRUE if we've built an entry */
686 
687 
688 	/*  Open the device table if it's not already open */
689 	if (oam_devtab == NULL) {
690 	    if (!_opendevtab("r"))
691 		return (NULL);
692 	}
693 
694 	/*  Get space for the structure we're returning  */
695 	if (!(ent = malloc(sizeof (struct devtabent)))) {
696 	    return (NULL);
697 	}
698 
699 	done = FALSE;
700 	while (!done && (record = getnextrec())) {
701 
702 	    /* Save record number in structure */
703 	    ent->entryno = dtabrecnum++;
704 
705 	    /* Comment record?  If so, just save the value and we're through */
706 	    if (strchr("#\n", *record) || isspace((unsigned char)*record)) {
707 		ent->comment = TRUE;
708 		done = TRUE;
709 		if (ent->attrstr = malloc(strlen(record)+1)) {
710 		    q = ent->attrstr;
711 		    p = record;
712 		    do {
713 			if (*p == '\\') p++;
714 			*q++ = *p;
715 		    } while (*p++);
716 		} else {
717 		    free(ent);
718 		    ent = NULL;
719 		}
720 	    }
721 
722 	    else {
723 
724 		/* Record is a data record.   Parse it. */
725 		ent->comment = FALSE;
726 		ent->attrstr = NULL;  /* For now */
727 
728 		/* Extract the device alias */
729 		if (p = getfld(record, ":")) {
730 		    if (*p) {
731 			if (ent->alias = malloc(strlen(p)+1)) {
732 			    q = ent->alias;
733 			    do {
734 				if (*p == '\\') p++;
735 				*q++ = *p;
736 			    } while (*p++);
737 			}
738 		    } else ent->alias = NULL;
739 
740 		    /* Extract the character-device name */
741 		    if ((p = getfld(NULL, ":")) == NULL) {
742 			if (ent->alias)
743 			    free(ent->alias);
744 		    } else {
745 			if (*p) {
746 			    if (ent->cdevice = malloc(strlen(p)+1)) {
747 				q = ent->cdevice;
748 				do {
749 				    if (*p == '\\') p++;
750 				    *q++ = *p;
751 				} while (*p++);
752 			    }
753 			} else ent->cdevice = NULL;
754 
755 			/* Extract the block-device name */
756 			if (!(p = getfld(NULL, ":"))) {
757 			    if (ent->alias) free(ent->alias);
758 			    if (ent->cdevice) free(ent->cdevice);
759 			} else {
760 			    if (*p) {
761 				if (ent->bdevice = malloc(strlen(p)+1)) {
762 				    q = ent->bdevice;
763 				    do {
764 					if (*p == '\\') p++;
765 					*q++ = *p;
766 				    } while (*p++);
767 				}
768 			    } else
769 				ent->bdevice = NULL;
770 
771 			    /* Extract the pathname */
772 			    if ((p = getfld(NULL, ":\n")) == NULL) {
773 				if (ent->alias) free(ent->alias);
774 				if (ent->cdevice) free(ent->cdevice);
775 				if (ent->bdevice) free(ent->bdevice);
776 			    } else {
777 				if (*p) {
778 				    if (ent->pathname = malloc(strlen(p)+1)) {
779 					q = ent->pathname;
780 					do {
781 					    if (*p == '\\') p++;
782 					    *q++ = *p;
783 					} while (*p++);
784 				    }
785 				} else
786 				    ent->pathname = NULL;
787 
788 				/* Found a valid record */
789 				done = TRUE;
790 
791 				/*
792 				 * Extract attributes, build a linked list of
793 				 * 'em (may be none)
794 				 */
795 				if (attr = getattrval(NULL)) {
796 				    ent->attrlist = attr;
797 				    t = attr;
798 				    while (attr = getattrval(NULL)) {
799 					t->next = attr;
800 					t = attr;
801 				    }
802 				    t->next = NULL;
803 				} else
804 				    ent->attrlist = NULL;
805 
806 			    } /* pathname extracted */
807 			} /* bdevice extracted */
808 		    } /* cdevice extracted */
809 		} /* alias extracted */
810 	    }
811 	} /* !done && record read */
812 
813 	/*  If no entry was read, free space allocated to the structure  */
814 	if (!done) {
815 	    free(ent);
816 	    ent = NULL;
817 	}
818 
819 	return (ent);
820 }
821 
822 /*
823  *  void _freedevtabent(devtabent)
824  *	struct devtabent       *devtabent;
825  *
826  *	This function frees space allocated to a device table entry.
827  *
828  *  Arguments:
829  *	struct devtabent *devtabent	The structure whose space is to be
830  *					freed.
831  *
832  *  Returns:  void
833  */
834 
835 void
836 _freedevtabent(struct devtabent *ent)
837 {
838 	/*
839 	 * Automatic data
840 	 */
841 
842 	struct attrval *p;		/* Structure being freed */
843 	struct attrval *q;		/* Next structure to free */
844 
845 	if (!ent->comment) {
846 
847 		/*
848 		 *  Free the attribute list.  For each item in the attribute
849 		 *  list,
850 		 *    1.  Free the attribute name (always defined),
851 		 *    2.  Free the value (if any -- it's not defined if we're
852 		 *		changing an existing attribute),
853 		 *    3.  Free the space allocated to the structure.
854 		 */
855 
856 	    q = ent->attrlist;
857 	    if (q)
858 		do {
859 		    p = q;
860 		    q = p->next;
861 		    free(p->attr);
862 		    if (p->val) free(p->val);
863 		    free(p);
864 		} while (q);
865 
866 	    /* Free the standard fields (alias, cdevice, bdevice, pathname) */
867 	    if (ent->alias) free(ent->alias);
868 	    if (ent->cdevice) free(ent->cdevice);
869 	    if (ent->bdevice) free(ent->bdevice);
870 	    if (ent->pathname) free(ent->pathname);
871 	}
872 
873 	/* Free the attribute string */
874 	if (ent->attrstr) free(ent->attrstr);
875 
876 	/* Free the space allocated to the structure */
877 	free(ent);
878 }
879 
880 /*
881  *  struct devtabent *_getdevrec(device)
882  *	char *device
883  *
884  *	Thie _getdevrec() function returns a pointer to a structure that
885  *	contains the information in the device-table entry that describes
886  *	the device <device>.
887  *
888  *	The device <device> can be a device alias, a pathname contained in
889  *	the entry as the "cdevice", "bdevice", or "pathname" attribute,
890  *	or a pathname to a device described using the "cdevice", "bdevice",
891  *	or "pathname" attribute (depending on whether the pathname references
892  *	a character-special file, block-special file, or something else,
893  *	respectively.
894  *
895  *  Arguments:
896  *	char *device	A character-string describing the device whose record
897  *			is to be retrieved from the device table.
898  *
899  *  Returns:  struct devtabent *
900  *	A pointer to a structure describing the device.
901  *
902  *  Notes:
903  *    -	Someday, add a cache so that repeated requests for the same record
904  *	don't require going to the filesystem.  (Maybe -- this might belong
905  *	in devattr()...)
906  */
907 
908 struct devtabent *
909 _getdevrec(char	*device)			/* The device to search for */
910 {
911 	/*
912 	 *  Automatic data
913 	 */
914 
915 	struct stat64		devstatbuf;	/* Stat struct, <device> */
916 	struct stat64		tblstatbuf;	/* Stat struct, tbl entry */
917 	struct devtabent	*devrec;	/* Pointer to current record */
918 	int			found;		/* TRUE if record found */
919 	int			olderrno;	/* Old value of errno */
920 
921 
922 	/*
923 	 *  Search the device table looking for the requested device
924 	 */
925 
926 	_setdevtab();
927 	olderrno = errno;
928 	found = FALSE;
929 	if ((device != NULL) && !_validalias(device)) {
930 	    while (!found && (devrec = _getdevtabent())) {
931 		if (!devrec->comment) {
932 		    if (devrec->cdevice)
933 			if (strcmp(device, devrec->cdevice) == 0) found = TRUE;
934 		    if (devrec->bdevice)
935 			if (strcmp(device, devrec->bdevice) == 0) found = TRUE;
936 		    if (devrec->pathname)
937 			if (strcmp(device, devrec->pathname) == 0) found = TRUE;
938 		} else _freedevtabent(devrec);
939 	    }
940 
941 		/*
942 		 *  If the device <device> wasn't named explicitly in the device
943 		 *  table, compare it against like entries by comparing file-
944 		 *  system, major device number, and minor device number
945 		 */
946 
947 	    if (!found) {
948 		_setdevtab();
949 
950 		/*  Status the file <device>.  If fails, invalid device */
951 		if (stat64(device, &devstatbuf) != 0) errno = ENODEV;
952 		else {
953 
954 			/*
955 			 *  If <device> is a block-special device.  See if it is
956 			 *  in the table by matching its file-system indicator
957 			 * and major/minor device numbers against the
958 			 * file-system and major/minor device numbers of the
959 			 * "bdevice" entries.
960 			 */
961 
962 		    if ((devstatbuf.st_mode & 0170000) == 0020000) {
963 			while (!found && (devrec = _getdevtabent())) {
964 			    if (!devrec->comment &&
965 				(devrec->cdevice != NULL))
966 				if (stat64(devrec->cdevice, &tblstatbuf) == 0) {
967 				    if (samedev(tblstatbuf, devstatbuf))
968 					found = TRUE;
969 				} else {
970 					/* Ignore stat() errs */
971 					errno = olderrno;
972 				}
973 			    if (!found) _freedevtabent(devrec);
974 			}
975 		    }
976 
977 			/*
978 			 * If <device> is a block-special device.  See if it is
979 			 * in the table by matching its file-system indicator
980 			 * and major/minor device numbers against the
981 			 * file-system and major/minor device numbers of the
982 			 * "bdevice" entries.
983 			 */
984 
985 		    else if ((devstatbuf.st_mode & 0170000) == 0060000) {
986 			while (!found && (devrec = _getdevtabent())) {
987 			    if (!devrec->comment &&
988 				(devrec->bdevice != NULL))
989 				if (stat64(devrec->bdevice, &tblstatbuf) == 0) {
990 				    if (samedev(tblstatbuf, devstatbuf))
991 					found = TRUE;
992 				} else {
993 					/* Ignore stat() errs */
994 					errno = olderrno;
995 				}
996 			    if (!found) _freedevtabent(devrec);
997 			}
998 		    }
999 
1000 			/*
1001 			 * If <device> is neither a block-special or character-
1002 			 * special device.  See if it is in the table by
1003 			 * matching its file-system indicator and major/minor
1004 			 * device numbers against the file-system and
1005 			 * major/minor device numbers of the "pathname" entries.
1006 			 */
1007 
1008 		    else {
1009 			while (!found && (devrec = _getdevtabent())) {
1010 			    if (!devrec->comment &&
1011 				(devrec->pathname != NULL))
1012 				if (stat64(devrec->pathname,
1013 				    &tblstatbuf) == 0) {
1014 				    if (samedev(tblstatbuf, devstatbuf))
1015 					found = TRUE;
1016 				} else {
1017 					/* Ignore stat() errs */
1018 					errno = olderrno;
1019 				}
1020 			    if (!found) _freedevtabent(devrec);
1021 			}
1022 		    }
1023 
1024 		    if (!found) {
1025 			devrec = NULL;
1026 			errno = ENODEV;
1027 		    }
1028 
1029 		} /* End case where stat() on the <device> succeeded */
1030 
1031 	    } /* End case handling pathname not explicitly in device table */
1032 
1033 	} /* End case handling <device> as a fully-qualified pathname */
1034 
1035 
1036 	/*
1037 	 *  Otherwise the device <device> is an alias.
1038 	 *  Search the table for a record that has as the "alias" attribute
1039 	 *  the value <device>.
1040 	 */
1041 
1042 	else {
1043 	    while (!found && (devrec = _getdevtabent())) {
1044 		if (!devrec->comment && (device != NULL) &&
1045 		    strcmp(device, devrec->alias) == 0)
1046 		    found = TRUE;
1047 		else _freedevtabent(devrec);
1048 	    }
1049 	    if (!found) {
1050 		devrec = NULL;
1051 		errno = ENODEV;
1052 	    }
1053 	}
1054 
1055 	/* Fini */
1056 	return (devrec);
1057 }
1058