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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*
28 * 4.x ld.so directory caching: run-time link-editor specific functions.
29 */
30
31#include	<dirent.h>
32#include	<string.h>
33#include	<sys/stat.h>
34#include	<fcntl.h>
35#include	<unistd.h>
36#include	"_a.out.h"
37#include	"cache_a.out.h"
38#include	"_rtld.h"
39#include	"msg.h"
40
41static int	stol();
42static int	rest_ok();
43static int	verscmp();
44static void	fix_lo();
45static int	extract_name();
46static int	hash();
47
48static struct link_object *get_lo();
49static struct dbd *new_dbd();
50static struct db *find_so();
51
52#define	SKIP_DOT(str)	((*str == '.')  ? ++str : str)
53#define	EMPTY(str)	((str == NULL) || (*str == '\0'))
54#define	isdigit(c)	(((c) >= '0') && ((c) <= '9') ? 1:0)
55
56static struct dbd *dbd_head = NULL;	/* head of data bases */
57
58
59/*
60 * Given a db - find the highest shared versioned object. The
61 * highest versioned object is the .so  with a matching major number
62 * but the highest minor number
63 */
64char *
65ask_db(dbp, file)
66	struct db *dbp;
67	const char *file;
68{
69	char *libname, *n;
70	char *mnp;
71	char *mjp;
72	int liblen;
73	int major = 0;
74	int to_min;
75	struct dbe *ep;
76	struct link_object *tlop;
77	int index;
78
79	n = (char *)file;
80	if ((liblen = extract_name(&n)) == -1)
81		return (NULL);
82	if ((libname = malloc(liblen + 1)) == 0)
83		return (NULL);
84	(void) strncpy(libname, n, liblen);
85	libname[liblen] = NULL;
86
87	if (strncmp(MSG_ORIG(MSG_FIL_DOTSODOT), (n + liblen),
88	    MSG_FIL_DOTSODOT_SIZE))
89		return (NULL);
90
91	mnp = mjp = ((char *)file + MSG_FIL_LIB_SIZE + liblen +
92	    MSG_FIL_DOTSODOT_SIZE);
93	if (!(stol(mjp, '.', &mnp, &major) && (*mnp == '.') &&
94	    rest_ok(mnp + 1)))
95		return (NULL);
96	to_min = mnp - file + 1;
97
98	/*
99	 * Search appropriate hash bucket for a matching entry.
100	 */
101	index = hash(libname, liblen, major);
102	for (ep = (struct dbe *)&(dbp->db_hash[index]); (ep && ep->dbe_lop);
103	    ep = ep->dbe_next == 0 ? NULL :
104	    /* LINTED */
105	    (struct dbe *)&AP(dbp)[ep->dbe_next]) {
106		/* LINTED */
107		tlop = (struct link_object *)&AP(dbp)[ep->dbe_lop];
108		if (tlop->lo_major == major)
109			if (strcmp((char *)&AP(dbp)[tlop->lo_name],
110			    libname) == 0)
111				break;
112	}
113
114	/*
115	 * If no entry was found, we've lost.
116	 */
117	if (!(ep && ep->dbe_lop))
118		return (NULL);
119	if (verscmp(file + to_min,
120	    &AP(dbp)[ep->dbe_name] + tlop->lo_minor) > 0)
121		eprintf(&lml_main, ERR_WARNING, MSG_INTL(MSG_GEN_OLDREV),
122		    &AP(dbp)[ep->dbe_name], file + to_min);
123	return (&AP(dbp)[ep->dbe_name]);
124}
125
126/*
127 * Given a directory name - give back a data base. The data base may have
128 * orginated from the mmapped file or temporarily created
129 */
130struct db *
131lo_cache(const char *ds)
132{
133	struct db *dbp;			/* database pointer */
134	struct dbd *dbdp;		/* working database descriptor */
135	struct dbd **dbdpp;		/* insertion pointer */
136
137	dbdpp = &dbd_head;
138	for (dbdp = dbd_head; dbdp; dbdp = dbdp->dbd_next) {
139		if (strcmp(ds, &AP(dbdp->dbd_db)[dbdp->dbd_db->db_name]) == 0)
140			return (dbdp->dbd_db);
141		dbdpp = &dbdp->dbd_next;
142	}
143	if (dbp = find_so(ds)) {
144		(void) new_dbd(dbdpp, dbp);
145	}
146	return (dbp);
147}
148
149/*
150 * Build a database for the directory "ds".
151 */
152static struct db *
153find_so(const char *ds)
154{
155	int fd;				/* descriptor on directory */
156	int n;				/* bytes from getdents */
157	char *cp;			/* working char * */
158	rtld_stat_t sb;			/* buffer for stat'ing directory */
159	struct db *dbp;			/* database */
160	static caddr_t buf = NULL;	/* buffer for doing getdents */
161	static long bs;			/* cached blocksize for getdents */
162	struct link_object *tlop;	/* working link object ptr. */
163	struct dirent *dp;		/* directory entry ptr. */
164	struct dbe *ep;			/* working db_entry ptr. */
165	char *mnp;			/* where minor version begins */
166	char *mjp;			/* where major version begins */
167	int m;				/* the major number */
168	int to_min;			/* index into string of minor */
169	int cplen;			/* length of X */
170	int index;			/* the hash value */
171
172	/*
173	 * Try to open directory.  Failing that, just return silently.
174	 */
175	if ((fd = open(ds, O_RDONLY)) == -1)
176		return ((struct db *)NULL);
177
178	/*
179	 * If we have not yet gotten a buffer for reading directories,
180	 * allocate it now.  Size it according to the most efficient size
181	 * for the first directory we open successfully.
182	 */
183	if (!buf) {
184		if (rtld_fstat(fd, &sb) == -1) {
185			(void) close(fd);
186			return ((struct db *)NULL);
187		}
188		bs = sb.st_blksize;
189		buf = calloc(bs, 1);
190	}
191
192	/*
193	 * Have a directory, have a buffer.  Allocate up a database
194	 * and initialize it.
195	 */
196	dbp = calloc(sizeof (struct db), 1);
197	dbp->db_name = RELPTR(dbp, calloc((strlen(ds) + 1), 1));
198	(void) strcpy((char *)&AP(dbp)[dbp->db_name], ds);
199
200	/*
201	 * Scan the directory looking for shared libraries.  getdents()
202	 * failures are silently ignored and terminate the scan.
203	 */
204	/* LINTED */
205	while ((n = getdents(fd, (struct dirent *)buf, bs)) > 0)
206		/* LINTED */
207		for (dp = (struct dirent *)buf;
208		    /* LINTED */
209		    dp && (dp < (struct dirent *)(buf + n));
210		    /* LINTED */
211		    dp = (struct dirent *)((dp->d_reclen == 0) ?
212		    NULL : (char *)dp + dp->d_reclen)) {
213
214			/*
215			 * If file starts with a "lib", then extract the X
216			 * from libX.
217			 */
218			cp = dp->d_name;
219			if ((cplen = extract_name(&cp)) == -1)
220				continue;
221
222			/*
223			 * Is the next component ".so."?
224			 */
225			if (strncmp(MSG_ORIG(MSG_FIL_DOTSODOT), (cp + cplen),
226			    MSG_FIL_DOTSODOT_SIZE))
227				continue;
228
229			/*
230			 * Check if next component is the major number and
231			 * whether following components are legal.
232			 */
233			mnp = mjp = (dp->d_name + MSG_FIL_LIB_SIZE + cplen +
234			    MSG_FIL_DOTSODOT_SIZE);
235			if (!(stol(mjp, '.', &mnp, &m) && (*mnp == '.') &&
236			    rest_ok(mnp + 1)))
237				continue;
238			to_min = mnp - dp->d_name + 1;
239
240			/*
241			 * Have libX.so.major.minor - attempt to add it to the
242			 * cache. If there is another with the same major
243			 * number then the chose the object with the highest
244			 * minor number
245			 */
246			index = hash(cp, cplen, m);
247			ep = &(dbp->db_hash[index]);
248			if (ep->dbe_lop == NULL) {
249				ep->dbe_lop = (long)get_lo(dbp, cp,
250				    cplen, m, to_min);
251				/* LINTED */
252				tlop = (struct link_object *)
253				    &AP(dbp)[ep->dbe_lop];
254				(void) strcpy(&AP(dbp)[tlop->lo_next],
255				    dp->d_name);
256				continue;
257			}
258			for (ep = &(dbp->db_hash[index]); ep;
259			    /* LINTED */
260			    ep = (struct dbe *)&AP(dbp)[ep->dbe_next]) {
261				/* LINTED */
262				tlop = (struct link_object *)
263				    &AP(dbp)[ep->dbe_lop];
264
265				/*
266				 * Choose the highest minor version
267				 */
268				if ((tlop->lo_major == m) &&
269				    (strncmp(&AP(dbp)[tlop->lo_name],
270				    cp, cplen) == 0) &&
271				    (*(&AP(dbp)[tlop->lo_name +
272				    cplen]) == '\0')) {
273					if (verscmp(dp->d_name + to_min,
274					    (char *)(&AP(dbp)[tlop->lo_next]
275					    + to_min)) > 0)
276						(void) strcpy(&AP(dbp)
277						    [tlop->lo_next],
278						    dp->d_name);
279					break;
280				}
281				if (ep->dbe_next == NULL) {
282					ep->dbe_next = RELPTR(dbp,
283					    calloc(sizeof (struct dbe), 1));
284					/* LINTED */
285					ep  = (struct dbe *)
286					    &AP(dbp)[ep->dbe_next];
287					ep->dbe_lop = (long)get_lo(dbp,
288					    cp, cplen, m, to_min);
289					/* LINTED */
290					tlop = (struct link_object *)
291					    &AP(dbp)[ep->dbe_lop];
292					(void) strcpy(&AP(dbp)[tlop->lo_next],
293					    dp->d_name);
294					break;
295				}
296			}
297		}
298	fix_lo(dbp);
299	(void) close(fd);
300	return (dbp);
301}
302
303/*
304 * Allocate and fill in the fields for a link_object
305 */
306static struct link_object *
307get_lo(dbp, cp, cplen, m, n)
308	struct db *dbp;			/* data base */
309	char *cp;			/* ptr. to X of libX */
310	int cplen;			/* length of X */
311	int m;				/* major version */
312	int n;				/* index to minor version */
313{
314	struct link_object *lop;	/* link_object to be returned */
315	struct link_object *tlop;	/* working copy of the above */
316
317	/*
318	 * Allocate a link object prototype in the database heap.
319	 * Store the numeric major (interface) number, but the minor
320	 * number is stored in the database as an index to the string
321	 * representing the minor version.  By keeping the minor version
322	 * as a string, "subfields" (i.e., major.minor[.other.fields. etc.])
323	 * are permitted.  Although not meaningful to the link editor, this
324	 * permits run-time substitution of arbitrary customer revisions,
325	 * although introducing the confusion of overloading the lo_minor
326	 * field in the database (!)
327	 */
328	lop = (struct link_object *)RELPTR(dbp,
329	    calloc(sizeof (struct link_object), 1));
330	/* LINTED */
331	tlop = (struct link_object *)&AP(dbp)[(long)lop];
332	tlop->lo_major = m;
333	tlop->lo_minor = n;
334
335	/*
336	 * Allocate space for the complete path name on the host program's
337	 * heap -- as we have to save it from the directory buffer which
338	 * might otherwise get re-used on us.  Note that this space
339	 * is wasted -- we can not assume that it can be reclaimed.
340	 */
341	tlop->lo_next = (long)RELPTR(dbp, calloc(MAXNAMLEN, 1));
342
343	/*
344	 * Store the prototype name in the link object in the database.
345	 */
346	tlop->lo_name = (long)RELPTR(dbp, calloc((cplen + 1), 1));
347	(void) strncpy((char *)&AP(dbp)[tlop->lo_name], cp, cplen);
348	return (lop);
349}
350
351/*
352 * Pull the "X" from libX, set name to X and return the
353 * length of X
354 */
355static int
356extract_name(name)
357	char **name;
358{
359	char *ls;			/* string after LIB root */
360	char *dp;			/* string before first delimiter */
361
362	if (strncmp(*name, MSG_ORIG(MSG_FIL_LIB), MSG_FIL_LIB_SIZE) == 0) {
363		ls = *name + MSG_FIL_LIB_SIZE;
364		if ((dp = (char *)strchr(ls, '.')) != (char *)0) {
365			*name = ls;
366			return (dp - ls);
367		}
368	}
369	return (-1);
370}
371
372/*
373 * Make a pass through the data base to set the dbe_name of a dbe.  This
374 * is necessary because there may be several revisions of a library
375 * but only one will be chosen.
376 */
377static void
378fix_lo(dbp)
379	struct db *dbp;
380{
381	int i;				/* loop temporary */
382	int dirlen = strlen(&AP(dbp)[dbp->db_name]);
383					/* length of directory pathname */
384	char *cp;			/* working temporary */
385	char *tp;			/* working temporary */
386	struct dbe *ep;			/* working copy of dbe */
387	struct link_object *lop;	/* working copy of link_object */
388
389	for (i = 0; i < DB_HASH; i++) {
390		for (ep = &(dbp->db_hash[i]); ep && ep->dbe_lop;
391		    (ep = ep->dbe_next == 0 ? NULL :
392		    /* LINTED */
393		    (struct dbe *)&AP(dbp)[ep->dbe_next])) {
394			/* LINTED */
395			lop = (struct link_object *)&AP(dbp)[ep->dbe_lop];
396			tp = &AP(dbp)[lop->lo_next];
397			ep->dbe_name = RELPTR(dbp,
398			    calloc((dirlen + strlen(tp) + 2), 1));
399			lop->lo_minor += dirlen + 1;
400			cp = strncpy(&AP(dbp)[ep->dbe_name],
401			    &AP(dbp)[dbp->db_name], dirlen);
402			cp = strncpy(cp + dirlen, MSG_ORIG(MSG_STR_SLASH),
403			    MSG_STR_SLASH_SIZE);
404			(void) strcpy(cp + 1, tp);
405		}
406	}
407}
408
409/*
410 * Allocate a new dbd, append it after dbdpp and set the dbd_dbp to dbp.
411 */
412static struct dbd *
413new_dbd(dbdpp, dbp)
414	struct dbd **dbdpp;		/* insertion point */
415	struct db *dbp;			/* db associated with this dbd */
416{
417	struct dbd *dbdp;		/* working dbd ptr. */
418
419	dbdp = malloc(sizeof (struct dbd));
420	dbdp->dbd_db = dbp;
421	dbdp->dbd_next = NULL;
422	*dbdpp = dbdp;
423	return (dbdp);
424}
425
426/*
427 * Calculate hash index for link object.
428 * This is based on X.major from libX.so.major.minor.
429 */
430static int
431hash(np, nchrs, m)
432	char *np; 			/* X of libX */
433	int nchrs;			/* no of chrs. to hash on */
434	int m;				/* the major version */
435{
436	int h;				/* for loop counter */
437	char *cp;			/* working (char *) ptr */
438
439	for (h = 0, cp = np; h < nchrs; h++, cp++)
440		h = (h << 1) + *cp;
441	h += (h << 1) + m;
442	h = ((h & 0x7fffffff) % DB_HASH);
443	return (h);
444}
445
446/*
447 * Test whether the string is of digit[.digit]* format
448 */
449static int
450rest_ok(str)
451	char *str;			/* input string */
452{
453	int dummy;			/* integer place holder */
454	int legal = 1;			/* return flag */
455
456	while (!EMPTY(str)) {
457		if (!stol(str, '.', &str, &dummy)) {
458			legal = 0;
459			break;
460		}
461		if (EMPTY(str))
462			break;
463		else
464			/* LINTED */
465			(SKIP_DOT(str));
466	}
467	return (legal);
468}
469
470/*
471 * Compare 2 strings and test whether they are of the form digit[.digit]*.
472 * It will return -1, 0, or 1 depending on whether c1p is less, equal or
473 * greater than c2p
474 */
475static int
476verscmp(const char *c1p, const char *c2p)
477{
478	char	*l_c1p = (char *)c1p;	/* working copy of c1p */
479	char	*l_c2p = (char *)c2p;	/* working copy of c2p */
480	int	l_c1p_ok = 0;		/* is c1p a legal string */
481	int	c2p_dig = 0;		/* int that c1p currently */
482					/*	represents */
483	int	c1p_dig = 0;		/* int that c2p currently */
484					/*	represents */
485
486	while (((l_c1p_ok = stol(l_c1p, '.', &l_c1p, &c1p_dig)) == 1) &&
487	    stol(l_c2p, '.', &l_c2p, &c2p_dig) && (c2p_dig == c1p_dig)) {
488		if (EMPTY(l_c1p) && EMPTY(l_c2p))
489			return (0);
490		else if (EMPTY(l_c1p) && !EMPTY(l_c2p) &&
491		    rest_ok(SKIP_DOT(l_c2p)))
492			return (-1);
493		else if (EMPTY(l_c2p) && !EMPTY(l_c1p) &&
494		    rest_ok(SKIP_DOT(l_c1p)))
495			return (1);
496		l_c1p++; l_c2p++;
497	};
498	if (!l_c1p_ok)
499		return (-1);
500	else if (c1p_dig < c2p_dig)
501		return (-1);
502	else if ((c1p_dig > c2p_dig) && rest_ok(SKIP_DOT(l_c1p)))
503		return (1);
504	else return (-1);
505}
506
507/*
508 * "stol" attempts to interpret a collection of characters between delimiters
509 * as a decimal digit. It stops interpreting when it reaches a delimiter or
510 * when character does not represent a digit. In the first case it returns
511 * success and the latter failure.
512 */
513static int
514stol(cp, delimit, ptr, i)
515	char *cp;			/* ptr to input string */
516	char delimit;			/* delimiter */
517	char **ptr;			/* left pointing to next del. or */
518					/* illegal character */
519	int *i;				/* digit that the string represents */
520{
521	int c = 0;			/* current char */
522	int n = 0;			/* working copy of i */
523	int neg = 0;			/* is number negative */
524
525	if (ptr != (char **)0)
526		*ptr = cp; /* in case no number is formed */
527
528	if (EMPTY(cp))
529		return (0);
530
531	if (!isdigit(c = *cp) && (c == '-')) {
532		neg++;
533		c = *++cp;
534	};
535	if (EMPTY(cp) || !isdigit(c))
536		return (0);
537
538	while (isdigit(c = *cp) && (*cp++ != '\0')) {
539		n *= 10;
540		n += c - '0';
541	};
542	if (ptr != (char **)0)
543		*ptr = cp;
544
545	if ((*cp == '\0') || (*cp == delimit)) {
546		*i = neg ? -n : n;
547		return (1);
548	};
549	return (0);
550}
551