4.4BSD/usr/src/contrib/sun.sharedlib/lang/rtld/cache_common.c

Compare this file to the similar file:
Show the results in this format:

/*
 * This source code is a product of Sun Microsystems, Inc. and is provided
 * for unrestricted use provided that this legend is included on all tape
 * media and as a part of the software program in whole or part.  Users
 * may copy or modify this source code without charge, but are not authorized
 * to license or distribute it to anyone else except as part of a product or
 * program developed by the user.
 *
 * THIS PROGRAM CONTAINS SOURCE CODE COPYRIGHTED BY SUN MICROSYSTEMS, INC.
 * SUN MICROSYSTEMS, INC., MAKES NO REPRESENTATIONS ABOUT THE SUITABLITY
 * OF SUCH SOURCE CODE FOR ANY PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT
 * EXPRESS OR IMPLIED WARRANTY OF ANY KIND.  SUN MICROSYSTEMS, INC. DISCLAIMS
 * ALL WARRANTIES WITH REGARD TO SUCH SOURCE CODE, INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  IN
 * NO EVENT SHALL SUN MICROSYSTEMS, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT,
 * INCIDENTAL, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
 * FROM USE OF SUCH SOURCE CODE, REGARDLESS OF THE THEORY OF LIABILITY.
 * 
 * This source code is provided with no support and without any obligation on
 * the part of Sun Microsystems, Inc. to assist in its use, correction, 
 * modification or enhancement.
 *
 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS
 * SOURCE CODE OR ANY PART THEREOF.
 *
 * Sun Microsystems, Inc.
 * 2550 Garcia Avenue
 * Mountain View, California 94043
 */

/* @(#)cache_common.c 1.10 69/12/31 SMI */

/*
 * ld.so directory caching: common code
 */

/*
 * Copyright (c) 1989, 1991 by Sun Microsystems, Inc.
 */

#include <sys/param.h>
#include <sys/file.h>
#include <sys/dirent.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/exec.h>
#include "link.h"
#include "cache.h"
#include <string.h>

extern char *getenv();
extern int stol();
extern int rest_ok();
static struct link_object *get_lo();
static	struct dbd *new_dbd();
static struct db *find_so();
static void fix_lo();
static void get_cache_file();

#define	LIB 	"lib"			/* library name prefix string */
#define SO	".so."			/* extension for shared object */
#define	LIBLEN	(sizeof (LIB) - 1)	/* lengths of same */
#define	SOLEN	(sizeof (SO) - 1)

caddr_t db_base;			/* base address from mmap() */
static	struct dbd *dbd_head = NULL;	/* head of data bases */

/* 
 * Given a directory name - give back a data base. The data base may have
 * orginated from the mmapped file or temporarily created
 */
struct db *
lo_cache(ds)
	char *ds;			/* directory to get cache for */
{
	struct db *dbp;			/* database pointer */
	struct dbd *dbdp;		/* working database descriptor */
	struct dbd **dbdpp;		/* insertion pointer */
	static int once = 1;		/* once-only flag */

	if (once) {
		get_cache_file();
		once = 0;
	}
	dbdpp = &dbd_head;
	for (dbdp = dbd_head; dbdp; dbdp = dbdp->dbd_next) {
		if (!strcmp(ds, &AP(dbdp->dbd_db)[dbdp->dbd_db->db_name]))
			return (dbdp->dbd_db);
		dbdpp = &dbdp->dbd_next;
	}
	if (dbp = find_so(ds)){
		(void) new_dbd(dbdpp, dbp);
	}
	return (dbp);
}

/* 
 * Delete from the db list those dbs that came from the mmapped file
 */
void
dbd_flush()
{
	struct dbd *dbdp;		/* working dbd ptr. */

	for (dbdp = dbd_head; dbdp; dbdp = dbdp->dbd_next) {
		if (dbdp->dbd_db >= (struct db *)db_base) {
			if (dbdp == dbd_head)
				dbd_head = dbdp->dbd_next;
			else if (dbdp->dbd_db >= (struct db *)db_base) 
				dbdp = dbdp->dbd_next;
		}
		if (dbdp->dbd_next == NULL)
			break;
	}
};

/*
 * Get the cache file, if appropriate.
 */
static void
get_cache_file()
{
	int fd;				/* descriptor on cache file */
	struct stat sb;			/* used to find size */
	struct dbf *dbf_base;		/* working cache file pointer */
	struct db *dbp;			/* working data base pointer */
	struct dbd *dbdp;		/* working dbd */
	struct dbd **dbdpp;		/* insertion point for dbd list */

	/*
	 * If cache use is suppressed, simply skip all this.
	 */
	if (!use_cache)
		return;

	/*
	 * Open, map in the file.  Failures occur silently. (XXX)
	 */
	if ((fd = open(CACHE_FILE, O_RDONLY)) == -1) 
		return;
	if (fstat(fd, &sb) == -1) {
		(void) close(fd);
		return;
	}
	dbf_base = (struct dbf *)mmap(0, sb.st_size, PROT_READ, MAP_SHARED, 
	    fd, 0);
	(void) close(fd);
	if ((dbf_base == (struct dbf *)-1) ||
	   (dbf_base->dbf_magic != LD_CACHE_MAGIC))
		return;
	if ((dbf_base->dbf_version != LD_CACHE_VERSION) ||
#if TARGET==SUN2
	  (dbf_base->dbf_machtype != M_68010))
#endif
#if TARGET==SUN3 
	    (dbf_base->dbf_machtype != M_68010) && 
	    (dbf_base->dbf_machtype != M_68020))
#endif
#if TARGET==SUN4
	    (dbf_base->dbf_machtype != M_SPARC))
#endif
		return;
	db_base = &AP(dbf_base)[(int)dbf_base->dbf_db];
	/*
	 * For each data base in the file, build a dbd and link it
	 * on to the master list.
	 */
	for (dbdpp = &dbd_head, dbp = (struct db *)db_base; 
	    (dbp < (struct db *)(db_base + sb.st_size)) && (dbp->db_chain != 0); 
	    (char *)dbp += (int)dbp->db_chain) {
		dbdp = new_dbd(dbdpp, dbp);
		dbdpp = &dbdp->dbd_next;
	}
}

/*
 * Build a database for the directory "ds".
 */
static struct db *
find_so(ds)
	char *ds;			/* directory to search */
{
	int fd;				/* descriptor on directory */
	int n;				/* bytes from getdents */
	char *cp;			/* working char * */
	struct stat sb;			/* buffer for stat'ing directory */
	struct db *dbp;			/* database */	
	static caddr_t buf = NULL;	/* buffer for doing getdents */
	static long bs;			/* cached blocksize for getdents */
	struct link_object *tlop;	/* working link object ptr. */
	struct dirent *dp;		/* directory entry ptr. */
	struct dbe *ep;			/* working db_entry ptr. */
	char *mnp;			/* where minor version begins */
	char *mjp;			/* where major version begins */
	int m;				/* the major number */
	int to_min;			/* index into string of minor */
	int cplen;			/* length of X */
	int index;			/* the hash value */

	/*
	 * Try to open directory.  Failing that, just return silently.
	 */
	if ((fd = open(ds, O_RDONLY)) == -1)
		return ((struct db *)NULL);

	/*
	 * If we have not yet gotten a buffer for reading directories,
	 * allocate it now.  Size it according to the most efficient size
	 * for the first directory we open successfully.
	 */
	if (!buf) {
		if (fstat(fd, &sb) == -1) {
			(void) close(fd);
			return ((struct db *)NULL);
		}
		buf = (*heap_malloc)(bs = sb.st_blksize);
	}

	/*
	 * Have a directory, have a buffer.  Allocate up a database
	 * and initialize it.
	 */
	dbp = (struct db *)(*db_malloc)(sizeof (struct db));
	dbp->db_name = RELPTR(dbp, (*db_malloc)(strlen(ds) + 1));
	(void) strcpy((char *)&AP(dbp)[dbp->db_name], ds);

	/*
	 * Scan the directory looking for shared libraries.  getdents()
	 * failures are silently ignored and terminate the scan.
	 */
	while ((n = getdents(fd, buf, bs)) > 0) 
		for (dp = (struct dirent *)buf; 
		    dp && (dp < (struct dirent *)(buf + n));
		    dp = (struct dirent *)((dp->d_reclen == 0) ? 
		    NULL : (char *)dp + dp->d_reclen)) {

			/*
			 * If file starts with a "lib", then extract the X
			 * from libX.
			 */
			cp = dp->d_name;
			if ((cplen = extract_name(&cp)) == -1) 
				continue;

			/* 
			 * Is the next component ".so."?
			 */
			if (strncmp(SO, cp + cplen, SOLEN))
				continue; 
	
			/*
			 * Check if next component is the major number and
			 * whether following components are legal.
			 */
			mnp = mjp = (dp->d_name + LIBLEN + cplen + SOLEN);
			if (!(stol(mjp, '.', &mnp, &m) && rest_ok(mnp + 1)))
				continue;
			to_min = mnp - dp->d_name + 1;

			/*
			 * Have libX.so.major.minor - attempt to add it to the
			 * cache. If there is another with the same major
			 * number then the chose the object with the highest
			 * minor number
			 */
			index = hash(cp, cplen, m);
			ep = &(dbp->db_hash[index]);
			if (ep->dbe_lop == NULL) {
				ep->dbe_lop = (long)get_lo(dbp, cp, 
				    cplen, m, to_min);
				tlop = (struct link_object *)
				    &AP(dbp)[ep->dbe_lop];
				strcpy(&AP(dbp)[tlop->lo_next], dp->d_name);
				continue;
			}
			for(ep = &(dbp->db_hash[index]); ep; 
			    ep = (struct dbe *) &AP(dbp)[ep->dbe_next]) {
				tlop = (struct link_object *)
				    &AP(dbp)[ep->dbe_lop];

				/* 
				 * Choose the highest minor version
				 */
				if ((tlop->lo_major == m) && 
				    (!strncmp(&AP(dbp)[tlop->lo_name],
				    cp, cplen)) &&
				    (*(&AP(dbp)[tlop->lo_name + cplen + 1]) ==
				     '\0')) {
				    	if (verscmp(dp->d_name + to_min, 
					    (char *)(&AP(dbp)[tlop->lo_next] + to_min))
					    > 0) 
						strcpy(&AP(dbp)[tlop->lo_next],
						    dp->d_name);
					break;
				}
				if (ep->dbe_next == NULL) {
					ep->dbe_next = RELPTR(dbp, 
					    (*db_malloc)(sizeof(struct dbe)));
					ep  = (struct dbe *)
					    &AP(dbp)[ep->dbe_next];
					ep->dbe_lop = (long)get_lo(dbp, 
					    cp, cplen, m, to_min);
					tlop = (struct link_object *)
					    &AP(dbp)[ep->dbe_lop];
					strcpy(&AP(dbp)[tlop->lo_next], dp->d_name);
					break;
				} 
			} 
		}
	fix_lo(dbp);
	(void) close(fd);
	return (dbp);
}

/*
 * Allocate and fill in the fields for a link_object
 */
static struct link_object *
get_lo(dbp, cp, cplen, m, n)
	struct db *dbp;			/* data base */
	char *cp;			/* ptr. to X of libX */
	int cplen;			/* length of X */
	int m;				/* major version */
	int n;				/* index to minor version */
{
	struct link_object *lop;	/* link_object to be returned */
	struct link_object *tlop;	/* working copy of the above */

	/*
	 * Allocate a link object prototype in the database heap.
	 * Store the numeric major (interface) number, but the minor
	 * number is stored in the database as an index to the string
	 * representing the minor version.  By keeping the minor version
	 * as a string, "subfields" (i.e., major.minor[.other.fields. etc.])
	 * are permitted.  Although not meaningful to the link editor, this
	 * permits run-time substitution of arbitrary customer revisions,
	 * although introducing the confusion of overloading the lo_minor
	 * field in the database (!)
	 */
	lop = (struct link_object *)RELPTR(dbp, 
	    (*db_malloc)(sizeof(struct link_object)));
	tlop = (struct link_object *)&AP(dbp)[(long)lop];
	tlop->lo_major = m;
	tlop->lo_minor = n;

	/*
	 * Allocate space for the complete path name on the host program's
	 * heap -- as we have to save it from the directory buffer which
	 * might otherwise get re-used on us.  Note that this space
	 * is wasted -- we can not assume that it can be reclaimed.
	 */
	tlop->lo_next = (long)RELPTR(dbp, (*heap_malloc)(MAXNAMLEN)); 

	/*
	 * Store the prototype name in the link object in the database.
	 */
	tlop->lo_name = (long)RELPTR(dbp, (*db_malloc)(cplen + 1)); 
	strncpy((char *)&AP(dbp)[tlop->lo_name], cp, cplen);
	return(lop);
}

/*
 * Pull the "X" from libX, set name to X and return the
 * length of X
 */
static int
extract_name(name)
	char **name;
{
	char *ls;			/* string after LIB root */
	char *dp;			/* string before first delimiter */
	
	if (strncmp(*name, LIB, LIBLEN) == 0) {
		ls = *name + LIBLEN;
		if ((dp = (char *)index(ls, '.')) != (char *)0) {
			*name = ls;
			return (dp - ls);
		}
	}
	return (-1);
}

/* 
 * Make a pass through the data base to set the dbe_name of a dbe.  This
 * is necessary because there may be several revisions of a library
 * but only one will be chosen. 
 */
static void 
fix_lo(dbp)
	struct db *dbp;
{
	int i;				/* loop temporary */
	int dirlen = strlen(&AP(dbp)[dbp->db_name]);
					/* length of directory pathname */
	char *cp;			/* working temporary */
	char *tp;			/* working temporary */
	struct dbe *ep;			/* working copy of dbe */
	struct link_object *lop;	/* working copy of link_object */

	for(i = 0; i < DB_HASH; i++) {
		for (ep = &(dbp->db_hash[i]); ep && ep->dbe_lop; 
		    (ep = ep->dbe_next == 0 ? NULL : 
		    (struct dbe *)&AP(dbp)[ep->dbe_next])) {
			lop = (struct link_object *)&AP(dbp)[ep->dbe_lop];
			tp = &AP(dbp)[lop->lo_next];
			ep->dbe_name = RELPTR(dbp, 
			    (*db_malloc)(dirlen + strlen(tp) + 2));
			lop->lo_minor += dirlen + 1;
			cp = strncpy(&AP(dbp)[ep->dbe_name], 
			    &AP(dbp)[dbp->db_name], dirlen);
			cp = strncpy(cp + dirlen, "/", 1);
			(void) strcpy(cp + 1, tp);
		}
	}
}

/*
 * Allocate a new dbd, append it after dbdpp and set the dbd_dbp to dbp.
 */
static struct dbd *
new_dbd(dbdpp, dbp)
	struct dbd **dbdpp;		/* insertion point */
	struct db *dbp;			/* db associated with this dbd */
{
	struct dbd *dbdp;		/* working dbd ptr. */

	dbdp = (struct dbd *)(*heap_malloc)(sizeof(struct dbd));
	dbdp->dbd_db = dbp;
	dbdp->dbd_next = NULL;
	*dbdpp = dbdp;
	return (dbdp);
}

/*
 * Calculate hash index for link object.
 * This is based on X.major from libX.so.major.minor.
 */
hash(np, nchrs, m) 
	char *np; 			/* X of libX */
	int nchrs;			/* no of chrs. to hash on */
	int m;				/* the major version */
{
	int h;				/* for loop counter */
	char *cp;			/* working (char *) ptr */

	for (h = 0, cp = np; h < nchrs; h++,*cp++)
		h = (h << 1) + *cp;
	h += (h << 1) + m;
	h = ((h & 0x7fffffff) % DB_HASH);
	return (h);
}