422 lines
11 KiB
C
422 lines
11 KiB
C
/* @(#)cache_common.c 1.1 94/10/31 SMI */
|
|
|
|
/*
|
|
* ld.so directory caching: common code
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 1989 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]) ==
|
|
'\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);
|
|
}
|