/*
 * ddev.c - SunOS (Solaris 1.x and 2.x) device support functions for lsof
 */


/*
 * Copyright 1994 Purdue Research Foundation, West Lafayette, Indiana
 * 47907.  All rights reserved.
 *
 * Written by Victor A. Abell
 *
 * This software is not subject to any license of the American Telephone
 * and Telegraph Company or the Regents of the University of California.
 *
 * Permission is granted to anyone to use this software for any purpose on
 * any computer system, and to alter it and redistribute it freely, subject
 * to the following restrictions:
 *
 * 1. Neither the authors nor Purdue University are responsible for any
 *    consequences of the use of this software.
 *
 * 2. The origin of this software must not be misrepresented, either by
 *    explicit claim or by omission.  Credit to the authors and Purdue
 *    University must appear in documentation and sources.
 *
 * 3. Altered versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 *
 * 4. This notice may not be removed or altered.
 */

#ifndef lint
static char copyright[] =
"@(#) Copyright 1994 Purdue Research Foundation.\nAll rights reserved.\n";
static char *rcsid = "$Id: ddev.c,v 1.21 95/09/17 08:45:19 abe Exp $";
#endif


#include "lsof.h"


/*
 * Local definitions
 */

#if	defined(solaris)
#define	DVCH_DEVPATH	"/devices"
#else	/* !defined(solaris) */
#define	DVCH_DEVPATH	"/dev"
#endif	/* defined(solaris) */


/*
 * Local static values
 */

static int Devx = 0;			/* current Devtp[] index */


_PROTOTYPE(static void make_devtp,(struct stat *s, char *p, MALLOC_S nl));

#if	defined(HASDCACHE)
_PROTOTYPE(static int rw_clone_sect,(int m));
_PROTOTYPE(static void clr_sect,(void));
# if	defined(solaris)
_PROTOTYPE(static int rw_pseudo_sect,(int m));
# endif	/* defined_solaris */
#endif	/* defined(HASDCACHE) */

_PROTOTYPE(static void stkdir,(char ***d, int *n, int *x, char *p));


/*
 * make_devtp() - make Devtp[] entry
 */

static void
make_devtp(s, p, nl)
	struct stat *s;			/* device stat() buffer */
	char *p;			/* device path name */
	MALLOC_S nl;			/* path name length (including '\0') */
{

/*
 * Make room for another Devtp[] entry.
 */
	if (Devx >= Ndev) {
		Ndev += DEVINCR;
		if (Devtp == NULL)
			Devtp = (struct l_dev *)malloc(
				(MALLOC_S)(sizeof(struct l_dev) * Ndev));
		else
			Devtp = (struct l_dev *)realloc((MALLOC_P *)Devtp,
				(MALLOC_S)(sizeof(struct l_dev) * Ndev));
		if (Devtp == NULL) {
			(void) fprintf(stderr, "%s: no space for devices\n",
				Pn);
			exit(1);
		}
	}
/*
 * Store the device number, inode number, and name in the Devtp[] entry.
 */
	Devtp[Devx].rdev = s->st_rdev;
	Devtp[Devx].inode = s->st_ino;
	if ((Devtp[Devx].name = (char *)malloc(nl)) == NULL) {
		(void) fprintf(stderr, "%s: no space for /dev/%s\n", Pn, p);
		exit(1);
	}
	(void) strcpy(Devtp[Devx].name, p);
	Devx++;
}


/*
 * printchdevname() - print character device name
 */

int
printchdevname(rdev, f)
	dev_t *rdev;			/* device */
	int f;				/* 1 = print trailing '\n' */
{
	struct clone *c;
	struct l_dev *dp;

#if	defined(solaris)
	struct pseudo *p;
#endif

	readdev();
/*
 * Search for clone parent.
 */
	if (Lf->is_stream && Clone) {
		for (c = Clone; c; c = c->next) {
			if (major(*rdev) == minor(c->rdev)) {
				if (f)
					(void) puts(c->nm);
				else
					(void) fputs(c->nm, stdout);
				return(1);
			}
		}
	}
/*
 * Search device table for match.
 */
	if ((dp = lkupdev(rdev, 1)) != (struct l_dev *)NULL) {
		if (f)
			(void) puts(dp->name);
		else
			(void) fputs(dp->name, stdout);
		return(1);
	}

#if	defined(solaris)
/*
 * Search for pseudo device match on major device only.
 */
	for (p = Pseudo; p; p = p->next) {
		if (major(*rdev) == major(p->rdev)) {
			if (f)
				(void) puts(p->nm);
			else
				(void) fputs(p->nm, stdout);
			return(1);
		}
	}
#endif	/* solaris */

	return(0);
}


#if	defined(solaris)
/*
 * read_clone() - read Solaris clone device information
 */

void
read_clone()
{
	struct clone *c;
	char *cn;
	DIR *dfp;
	struct dirent *dp;
	static int first = 1;
	MALLOC_S nl;
	char path[MAXNAMLEN+1];
	struct pseudo *p;
	int pl;
	struct stat sb;
	STRNCPY_L dnamel;
	
	if (!first)
		return;
	first = 0;
/*
 * Open the /DVCH_DEVPATH/pseudo directory.
 */
	(void) sprintf(path, "%s/pseudo", DVCH_DEVPATH);
	if ((dfp = opendir(path)) == NULL) {

#if	defined(WARNDEVACCESS)
		if (!Fwarn)
			(void) fprintf(stderr, "%s: WARNING: can't open %s\n",
				Pn, path);
#endif	/* defined(WARNDEVACCESS) */

		return;
	}
	(void) strcat(path, "/");
	pl = strlen(path);
/*
 * Scan the directory.
 */
	for (dp = readdir(dfp); dp; dp = readdir(dfp)) {
		if (dp->d_ino == 0 || dp->d_name[0] == '.')
			continue;
	/*
	 * Form the full path name and lstat() it.
	 */
		dnamel = strlen(dp->d_name);
		if ((nl = pl + dnamel) >= sizeof(path)) {
		    if (!Fwarn)
			(void) fprintf(stderr,
				"%s: /%s/pseudo entry name too long: %s\n",
				Pn, DVCH_DEVPATH, dp->d_name);
		    continue;
		}
		(void) strncpy(&path[pl], dp->d_name, dnamel);
		path[nl++] = '\0';
		if (lstat(path, &sb) != 0) {
			if (!Fwarn)
				(void) fprintf(stderr,
					"%s: can't lstat %s: %s\n",
					Pn, path, strerror(errno));
			continue;
		}
	/*
	 * Skip subdirectories and all but character devices.
	 */
		if ((sb.st_mode & S_IFMT) == S_IFDIR
		||  (sb.st_mode & S_IFMT) != S_IFCHR)
			continue;
	/*
	 * Make Devtp[] entry.
	 */
		make_devtp(&sb, path, nl);
	 /*
	  * Create a clone structure entry for "clone*:" devices.
	  *
	  * Make special note of network clones -- icmp, ip, tcp, and udp.
	  */
		if (strncmp(&path[pl], "clone", 5) == 0) {
			if ((cn = strrchr(&path[pl], ':')) == NULL)
			    continue;
		/*
		 * Allocate a clone structure.
		 */
			if ((c = (struct clone *)malloc(sizeof(struct clone)))
			== NULL) {
			    (void) fprintf(stderr,
				"%s: no space for network clone device: %s\n",
				Pn, path);
			    exit(1);
			}
		/*
		 * Allocate space for the path name.
		 */
			if ((c->nm = (char *)malloc(nl)) == NULL) {
			    (void) fprintf(stderr,
				"%s: no space for network clone name: %s\n",
				Pn, path);
			    exit(1);
			}
		/*
		 * Save the path name and device number.
		 */
			(void) strcpy(c->nm, path);
			c->rdev = sb.st_rdev;
		/*
		 * Make special note of a network clone device.
		 */
			cn++;
			if (strcmp(cn, "icmp") == 0 || strcmp(cn, "ip")  == 0
			||  strcmp(cn, "tcp")  == 0 || strcmp(cn, "udp") == 0)
				c->n = cn - path;
			else
				c->n = 0;
		/*
		 * Link the new clone entry to the rest.
		 */
			c->next = Clone;
			Clone = c;
			continue;
		}
	/*
	 * Save pseudo device information.
	 */
		if (minor(sb.st_rdev) == 0) {

		/*
		 * Allocate space for the pseduo device entry.
		 */
		    if ((p = (struct pseudo *) malloc(sizeof(struct pseudo)))
		    == NULL) {
			(void) fprintf(stderr,
			    "%s: no space for pseudo device: %s\n",
			    Pn, path);
			exit(1);
		    }
		/*
		 * Allocate space for the path name.
		 */
		    if ((p->nm = (char *)malloc(nl)) == NULL) {
			(void) fprintf(stderr,
			    "%s: no space for pseudo name: %s\n", Pn, path);
			exit(1);
		    }
		/*
		 * Fill in the entry and link it to the rest.
		 */
		    (void) strcpy(p->nm, path);
		    p->rdev = sb.st_rdev;
		    p->next = Pseudo;
		    Pseudo = p;
		}
	}
	(void) closedir(dfp);
}
#endif	/* solaris */


/*
 * readdev() - read names, modes and device types of everything in /dev
 *	       or /device (Solaris)
 */

void
readdev()
{
#if	defined(HASDCACHE)
	int dcrd;
#endif	/* defined(HASDCACHE) */

	struct clone *c;
	DIR *dfp;
	int dn = 0;
	struct dirent *dp;
	char **dstk = NULL;
	int dx = 0;
	int err = 0;
	static int first = 1;
	int i;
	MALLOC_S nl;
	char path[MAXNAMLEN+1];
	int pl;
	struct stat sb;

#if	defined(solaris)
	char *cn;
	STRNCPY_L dnamel;
	struct pseudo *p;
	char ppath[MAXPATHLEN+1];
#endif

	if (!first)
		return;
	first = 0;

#if	defined(HASDCACHE)
/*
 * Read device cache, as directed.
 */
	if (DCstate == 2 || DCstate == 3) {
		if ((dcrd = read_dcache()) == 0)
			return;
	}
#endif	/* defined(HASDCACHE) */

	Devx = 0;

#if	defined(solaris)
	(void) sprintf(ppath, "%s/pseudo", DVCH_DEVPATH);
	read_clone();
#endif	/* defined(solaris) */

	(void) stkdir(&dstk, &dn, &dx, DVCH_DEVPATH);
/*
 * Unstack the next directory.
 */
	while (--dx >= 0) {
		(void) strcpy(path, dstk[dx]);
		if ((dfp = opendir(path)) == NULL) {

#if	defined(WARNDEVACCESS)
			if (!Fwarn)
				(void) fprintf(stderr,
					"%s: WARNING: can't open %s\n",
					Pn, path);
#endif	/* defined(WARNDEVACCESS) */

			continue;
		}
		(void) strcat(path, "/");
		pl = strlen(path);
		(void) free((FREE_P *)dstk[dx]);
		dstk[dx] = NULL;
	/*
	 * Scan the directory.
	 */
		for (dp = readdir(dfp); dp; dp = readdir(dfp)) {
			if (dp->d_ino == 0 || dp->d_name[0] == '.')
				continue;
		/*
		 * Form the full path name and get its status.
		 */

#if	defined(solaris)
			dnamel = strlen(dp->d_name);
			if ((nl = pl + dnamel) >= sizeof(path))
#else
			if ((nl = pl + dp->d_namlen) >= sizeof(path))
#endif	/* solaris */

			{
				(void) fprintf(stderr,
					"%s: /dev entry name too long: %s\n",
					Pn, dp->d_name);
				exit(1);
			}
#if	defined(solaris)
			(void) strncpy(&path[pl], dp->d_name, dnamel);
#else
			(void) strncpy(&path[pl], dp->d_name,
				(STRNCPY_L)dp->d_namlen);
#endif	/* solaris */

			path[nl++] = '\0';
			if (lstat(path, &sb) != 0) {
				(void) fprintf(stderr,
					"%s: can't lstat %s: %s\n",
					Pn, path, strerror(errno));
				err = 1;
				continue;
			}
		/*
		 * If it's a subdirectory, stack its name for later
		 * processing.
		 */
			if ((sb.st_mode & S_IFMT) == S_IFDIR) {

#if	defined(solaris)
			/*
			 * Skip Solaris /DVCH_DEV_PATH/pseudo sub-directory;
			 * it has been examined in read_clone().
			 */
				if (strcmp(path, ppath) == 0)
					continue;
#endif	/* solaris */

				(void) stkdir(&dstk, &dn, &dx, path);
				continue;
			}
		/*
		 * Skip all but character devices.
		 */
			if ((sb.st_mode & S_IFMT) != S_IFCHR)
				continue;
		/*
		 * Make Devtp[] entry.
		 */
			make_devtp(&sb, path, nl);

#if	!defined(solaris)
		/*
		 * Save information on non-Solaris clone devices.
		 */
			if (major(sb.st_rdev) == CLONEMAJ) {

			/*
			 * Allocate space for a clone entry.
			 */
			    if ((c = (struct clone *)malloc(
				sizeof(struct clone)))
			    == NULL) {
				(void) fprintf(stderr,
				    "%s: no space for clone device: %s\n",
				    Pn, path);
				exit(1);
			    }
			/*
			 * Allocate space for the path name.
			 */
			    if ((c->nm = (char *)malloc(nl)) == NULL) {
				(void) fprintf(stderr,
				    "%s: no space for clone name: %s\n",
				    Pn, path);
				exit(1);
			    }
			/*
			 * Fill in the clone entry and link it to the rest.
			 */
			    (void) strcpy(c->nm, path);
			    c->n = 0;
			    c->rdev = sb.st_rdev;
			    c->next = Clone;
		  	    Clone = c;
			}
#endif	/* !solaris */

		}
		(void) closedir(dfp);
	}
/*
 * Free any directory stack space.
 */
	if (dstk != NULL)
		(void) free((FREE_P *)dstk);
/*
 * Reduce the Devtp[] table to its minimum size.
 */
	if (Ndev > Devx) {
		Ndev = Devx;
		Devtp = (struct l_dev *)realloc((MALLOC_P *)Devtp,
		      (MALLOC_S)(sizeof(struct l_dev) * Ndev));
	}
	if (err)
		exit(1);
/*
 * Allocate sorting pointers and sort them by Devtp[] device number.
 */
	if ((Sdev = (struct l_dev **)malloc((MALLOC_S)(sizeof(struct l_dev *)
	    * Ndev)))
	== NULL) {
		(void) fprintf(stderr, "%s: no space for device pointers\n",
			Pn);
		exit(1);
	}
	for (i = 0; i < Ndev; i++) {
		Sdev[i] = &Devtp[i];
	}
	(void) qsort((QSORT_P *)Sdev, (size_t)Ndev,
		(size_t)sizeof(struct l_dev *), compdev);

#if	defined(HASDCACHE)
/*
 * Write device cache file, as required.
 */
	if (DCstate == 1 || (DCstate == 3 && dcrd))
		write_dcache();
#endif	/* defined(HASDCACHE) */

}


#if	defined(HASDCACHE)


/*
 * clr_sect() - clear cached clone and pseudo sections
 */

static void
clr_sect()
{
	struct clone *c, *c1;

#if	defined(solaris)
	struct pseudo *p, *p1;
#endif	defined(solaris)

	if (Clone) {
		for (c = Clone; c; c = c1) {
			c1 = c->next;
			if (c->nm) {
				(void) free((FREE_P *)c->nm);
				c->nm = NULL;
			}
			(void) free((FREE_P *)c);
		}
		Clone = NULL;
	}

#if	defined(solaris)
	if (Pseudo) {
		for (p = Pseudo; p; p = p1) {
			p1 = p->next;
			if (p->nm) {
				(void) free((FREE_P *)p->nm);
				p->nm = NULL;
			}
			(void) free((FREE_P *)p);
		}
		Pseudo = NULL;
	}
#endif	defined(solaris)

}


/*
 * rw_clone_sect() - read/write the device cache file clone section
 */

static int
rw_clone_sect(m)
	int m;				/* mode: 1 = read; 2 = write */
{
	char buf[MAXPATHLEN*2], *cp;
	struct clone *c;
	int i, len, n;

	if (m == 1) {

	/*
	 * Read the clone section header and validate it.
	 */
	    if (fgets(buf, sizeof(buf), DCfs) == NULL) {

bad_clone_sect:

		if (!Fwarn)
		    (void) fprintf(stderr,
			"%s: bad clone section header in %s: %s",
			Pn, DCpath[DCpathX], buf);
		return(1);
	    }
	    (void) crc(buf, strlen(buf), &DCcksum);
	    len = strlen("clone section: ");
	    if (strncmp(buf, "clone section: ", len) != 0)
		goto bad_clone_sect;
	    if ((n = atoi(&buf[len])) < 0)
		goto bad_clone_sect;
	/*
	 * Read the clone section lines and create the Clone list.
	 */
	    for (i = 0; i < n; i++) {
		if (fgets(buf, sizeof(buf), DCfs) == NULL) {
		    if (!Fwarn)
			(void) fprintf(stderr,
			    "%s: bad clone line in %s: %s",
			    Pn, DCpath[DCpathX], buf);
		    return(1);
		}
		(void) crc(buf, strlen(buf), &DCcksum);
	    /*
	     * Allocate a clone structure.
	     */
		if ((c = (struct clone *)calloc(1, sizeof(struct clone)))
		== NULL) {
		    (void) fprintf(stderr,
			"%s: no space for cached clone: %s", Pn, buf);
		    exit(1);
		}
	    /*
	     * Enter the clone device number.
	     */
		if ((cp = x2dev(buf, &c->rdev)) == NULL || *cp++ != ' ') {
		    if (!Fwarn)
			(void) fprintf(stderr,
			    "%s: bad cached clone device: %s", Pn, buf);
		    return(1);
		}
	    /*
	     * Enter the clone network value.
	     */
		for (c->n = 0; *cp != ' '; cp++) {
		    if (*cp < '0' || *cp > '9') {
			if (!Fwarn)
			    (void) fprintf(stderr,
				"%s: bad cached clone network flag: %s",
				Pn, buf);
			return(1);
		    }
		    c->n = (c->n * 10) + (int)(*cp - '0');
		}
	    /*
	     * Enter the clone path name.
	     */
		if ((len = strlen(++cp)) < 2 || *(cp + len - 1) != '\n') {
		    if (!Fwarn)
			(void) fprintf(stderr,
			    "%s: bad cached clone path: %s", Pn, buf);
		    return(1);
		}
		if ((c->nm = (char *)malloc(len)) == NULL) {
		    (void) fprintf(stderr,
			"%s: no space for cached clone path: %s", Pn, buf);
		    exit(1);
		}
		*(cp + len - 1) = '\0';
		(void) strcpy(c->nm, cp);
		c->next = Clone;
		Clone = c;
	    }
	    return(0);
	} else if (m == 2) {

	/*
	 * Write the clone section header.
	 */
	    for (c = Clone, n = 0; c; c = c->next, n++)
		;
	    (void) sprintf(buf, "clone section: %d\n", n);
	    if (wr2DCfd(buf, &DCcksum))
		return(1);
	/*
	 * Write the clone section lines.
	 */
	    for (c = Clone; c; c = c->next) {
		(void) sprintf(buf, "%x %d %s\n", c->rdev, c->n, c->nm);
		if (wr2DCfd(buf, &DCcksum))
		    return(1);
	    }
	    return(0);
	}
/*
 * A shouldn't-happen case: mode neither 1 nor 2.
 */
	(void) fprintf(stderr, "%s: internal rw_clone_sect error: %d\n",
	    Pn, m);
	exit(1);
}


# if	defined(solaris)
/*
 * rw_pseudo_sect() - read/write the device cache pseudo section
 */
static int
rw_pseudo_sect(m)
	int m;				/* mode: 1 = read; 2 = write */
{
	char buf[MAXPATHLEN*2], *cp;
	struct pseudo *p;
	int i, len, n;

	if (m == 1) {

	/*
	 * Read the pseudo section header and validate it.
	 */
	    if (fgets(buf, sizeof(buf), DCfs) == NULL) {

bad_pseudo_sect:

		if (!Fwarn)
		    (void) fprintf(stderr,
			"%s: bad pseudo section header in %s: %s",
			Pn, DCpath[DCpathX], buf);
		return(1);
	    }
	    (void) crc(buf, strlen(buf), &DCcksum);
	    len = strlen("pseudo section: ");
	    if (strncmp(buf, "pseudo section: ", len) != 0)
		goto bad_pseudo_sect;
	    if ((n = atoi(&buf[len])) < 0)
		goto bad_pseudo_sect;
	/*
	 * Read the pseudo section lines and create the Pseudo list.
	 */
	    for (i = 0; i < n; i++) {
		if (fgets(buf, sizeof(buf), DCfs) == NULL) {

bad_pseudo_line:
		    if (!Fwarn)
			(void) fprintf(stderr,
			    "%s: bad pseudo line in %s: %s",
			    Pn, DCpath[DCpathX], buf);
		    return(1);
		}
		(void) crc(buf, strlen(buf), &DCcksum);
	    /*
	     * Allocate a pseudo structure.
	     */
		if ((p = (struct pseudo *)calloc(1, sizeof(struct pseudo)))
		== NULL) {
		    (void) fprintf(stderr,
			"%s: no space for cached pseudo: %s", Pn, buf);
		    exit(1);
		}
	    /*
	     * Enter the pseudo device number.
	     */
		if ((cp = x2dev(buf, &p->rdev)) == NULL || *cp++ != ' ') {
		    if (!Fwarn)
			(void) fprintf(stderr,
			    "%s: bad cached pseudo device: %s", Pn, buf);
		    return(1);
		}
	    /*
	     * Enter the pseudo path name.
	     */
		if ((len = strlen(cp)) < 2 || *(cp + len - 1) != '\n') {
		    if (!Fwarn)
			(void) fprintf(stderr,
			    "%s: bad cached pseudo path: %s", Pn, buf);
		    return(1);
		}
		if ((p->nm = (char *)malloc(len)) == NULL) {
		    (void) fprintf(stderr,
			"%s: no space for cached pseudo path: %s", Pn, buf);
		    exit(1);
		}
		*(cp + len - 1) = '\0';
		(void) strcpy(p->nm, cp);
		p->next = Pseudo;
		Pseudo = p;
	    }
	    return(0);
	} else if (m == 2) {

	/*
	 * Write the pseudo section header.
	 */
	    for (p = Pseudo, n = 0; p; p = p->next, n++)
		;
	    (void) sprintf(buf, "pseudo section: %d\n", n);
	    if (wr2DCfd(buf, &DCcksum))
		return(1);
	/*
	 * Write the pseudo section lines.
	 */
	    for (p = Pseudo; p; p = p->next) {
		(void) sprintf(buf, "%x %s\n", p->rdev, p->nm);
		if (wr2DCfd(buf, &DCcksum))
		    return(1);
	    }
	    return(0);
	}
/*
 * A shouldn't-happen case: mode neither 1 nor 2.
 */
	(void) fprintf(stderr, "%s: internal rw_pseudo_sect error: %d\n",
	    Pn, m);
	return(1);
}
# endif	/* defined_solaris */
#endif	/* defined(HASDCACHE) */


/*
 * stkdir() - stack directory name
 */

static void
stkdir(d, n, x, p)
	char ***d;		/* array of directory pointers */
	int *n;			/* number of pointers */
	int *x;			/* current index */
	char *p;		/* directory path */
{
	if (*d == NULL) {

	/*
	 * Allocate first entry.
	 */
		if ((*d = (char **)malloc(sizeof(char *))) == NULL) {

stkdir_nospace:

			(void) fprintf(stderr,
				"%s: no space for directory stack at %s\n",
				Pn, p);
			exit(1);
		}
		*n = 1;
		*x = 0;
	} else if (*x >= *n) {

	/*
	 * Allocate additional space as required.
	 */
		*n += 1;
		if ((*d = (char **)realloc((MALLOC_P *)*d,
		          (MALLOC_S)(*n * sizeof(char *))))
		== NULL)
			goto stkdir_nospace;
	}
/*
 * Allocate space for the name, copy it there and put its pointer on the stack.
 */
	if (((*d)[*x] = (char *)malloc((MALLOC_S)(strlen(p) + 1))) == NULL) {
		(void) fprintf(stderr, "%s: no space for %s\n", Pn, p);
		exit(1);
	}
	(void) strcpy((*d)[*x], p);
	*x += 1;
}


/*
 * find_ch_ino() comes from ../common/fchi.frag.
 * lkupdev() comes from ../common/lkud.frag.
 * read_dcache() and write_dcache() come from ../common/dvch.frag.
 */

#define	DCACHE_CLONE	rw_clone_sect	/* clone function for read_dcache */
#define	DCACHE_CLR	clr_sect	/* function to clear clone and
					 * pseudo caches when reading the
					 * device cache file fails */

# if	defined(solaris)
#define	DCACHE_PSEUDO	rw_pseudo_sect	/* pseudo function for read_dcache */
# endif	/* defined_solaris */
