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