Blame src/core/lt_path.c

47891e
/*******************************************************************/
47891e
/*  sltdl: a surrogate ltdl implementation                         */
84a51a
/*  Copyright (C) 2019 SysDeer Technologies, LLC                   */
47891e
/*  Released under the Standard MIT License; see COPYING.SLTDL.    */
47891e
/*******************************************************************/
47891e
2d052c
#include <glob.h>
47891e
#include <limits.h>
303f79
#include <dlfcn.h>
2d052c
#include <errno.h>
bb0941
#include <fcntl.h>
47891e
#include <stdio.h>
47891e
#include <stdlib.h>
47891e
#include <string.h>
bb0941
#include <unistd.h>
47891e
#include <pthread.h>
303f79
47891e
#include <sltdl/sltdl.h>
47891e
#include "sltdl_core.h"
303f79
#include "sltdl_module.h"
47891e
3f4643
#ifdef  O_EXEC
3f4643
#define SLTDL_MODULE_OPEN_OPTIONS (O_CLOEXEC|O_EXEC)
3f4643
#else
3f4643
#define SLTDL_MODULE_OPEN_OPTIONS (O_CLOEXEC)
3f4643
#endif
3f4643
8744a5
static int     lt_status;
47891e
static off_t   lt_plen;
47891e
static off_t   lt_plocs;
47891e
static char *  lt_upath;
47891e
static char *  lt_vpath;
47891e
static char ** lt_vmark;
47891e
static char ** lt_pathv;
47891e
303f79
static struct lt_modctx * lt_modv_head;
303f79
static struct lt_modctx * lt_modv_tail;
303f79
303f79
static struct lt_modctx * lt_modv_next;
303f79
static struct lt_modctx * lt_modv_cap;
303f79
8744a5
static int lt_setstatus(int ret, int status)
8744a5
{
8744a5
	lt_status = status;
8744a5
	return ret;
8744a5
}
8744a5
47891e
const char * lt_dlgetsearchpath(void)
47891e
{
47891e
	return lt_upath;
47891e
}
47891e
47891e
static int lt_dlsetsearchpath_locked(const char * path)
47891e
{
47891e
	const char *    ch;
47891e
	char *          uch;
47891e
	char *          vch;
47891e
	char **         pathv;
47891e
	off_t           elements;
47891e
47891e
	if (path[0] != '/')
178478
		return lt_setstatus(1,SLTDL_ERR_PATH_INVALID_FIRST_CHAR);
47891e
47891e
	elements = 1;
47891e
47891e
	for (ch=&path[1]; *ch; ch++) {
47891e
		if (*ch == ':') {
47891e
			if (ch[1] != '/')
178478
				return lt_setstatus(1,SLTDL_ERR_PATH_INVALID_FIRST_CHAR);
47891e
47891e
			elements++;
47891e
		}
47891e
	}
47891e
47891e
	if (++elements > lt_plocs) {
47891e
		if (lt_pathv) {
47891e
			free(lt_pathv);
47891e
			lt_pathv = 0;
47891e
		}
47891e
47891e
		lt_plocs  = elements;
47891e
		lt_plocs += 0x3f;
47891e
		lt_plocs |= 0x3f;
47891e
		lt_plocs ^= 0x3f;
47891e
47891e
		if (!(lt_pathv = calloc(lt_plocs,sizeof(char *))))
178478
			return lt_setstatus(1,SLTDL_ERR_SYSTEM_ERROR);
47891e
	}
47891e
47891e
	if ((ch - path) > lt_plen) {
47891e
		if (lt_upath) {
47891e
			free(lt_upath);
47891e
			lt_upath = 0;
47891e
		}
47891e
47891e
		lt_plen = ((ch - path + 1) <= 0x800)
47891e
			? 0x800 : ch - path + 1;
47891e
47891e
		lt_plen += 0x7ff;
47891e
		lt_plen |= 0x7ff;
47891e
		lt_plen ^= 0x7ff;
47891e
		if (!(lt_upath = calloc(2*lt_plen,1)))
178478
			return lt_setstatus(1,SLTDL_ERR_SYSTEM_ERROR);
47891e
47891e
		lt_vpath = &lt_upath[lt_plen];
47891e
	}
47891e
47891e
	pathv    = lt_pathv;
47891e
	*pathv++ = lt_vpath;
47891e
47891e
	for (ch=path, uch=lt_upath, vch=lt_vpath; *ch; ch++) {
47891e
		if (*ch == ':') {
47891e
			*uch++ = ch[0];
47891e
			*uch++ = ch[1];
47891e
47891e
			*vch++ = 0;
47891e
			*pathv = vch;
47891e
			*vch++ = ch[1];
47891e
47891e
			ch++;
47891e
			pathv++;
47891e
		} else {
47891e
			*uch++ = *ch;
47891e
			*vch++ = *ch;
47891e
		}
47891e
	}
47891e
47891e
	lt_vmark = pathv;
47891e
178478
	return lt_setstatus(0,SLTDL_ERR_OK);
47891e
}
47891e
47891e
int lt_dlsetsearchpath(const char * path)
47891e
{
8744a5
	int ret;
47891e
	lt_slock();
8744a5
	ret = lt_dlsetsearchpath_locked(path);
8744a5
	return lt_sunlock(ret,lt_status);
47891e
}
47891e
8744a5
static int lt_dladdsearchdir_locked(const char * path)
47891e
{
47891e
	int          ret;
47891e
	const char * ch;
47891e
	char *       buf;
47891e
	off_t        alen;
47891e
	off_t        plen;
47891e
47891e
	if (path[0] != '/')
178478
		return lt_setstatus(1,SLTDL_ERR_PATH_INVALID_FIRST_CHAR);
47891e
47891e
	for (ch=path; *ch; ch++)
47891e
		if (*ch == ':')
178478
			return lt_setstatus(1,SLTDL_ERR_PATH_INVALID_SEPARATTOR_CHAR);
47891e
47891e
	alen = strlen(path);
47891e
	plen = strlen(lt_upath);
47891e
47891e
	/* no allocation needed? */
47891e
	if (!lt_pathv[lt_plocs - 2] && ((plen + 1 + alen + 1) <= lt_plen)) {
47891e
		lt_upath[plen] = ':';
47891e
		lt_vpath[plen] = 0;
47891e
47891e
		plen++;
47891e
47891e
		strcpy(&lt_upath[plen],path);
47891e
		strcpy(&lt_vpath[plen],path);
47891e
47891e
		*lt_vmark++ = &lt_vpath[plen];
47891e
178478
		return lt_setstatus(0,SLTDL_ERR_OK);
47891e
	}
47891e
47891e
	/* (allocation needed) */
47891e
	if (!(buf = malloc(plen + 1 + alen + 1)))
178478
		return lt_setstatus(1,SLTDL_ERR_SYSTEM_ERROR);
47891e
47891e
	sprintf(buf,"%s:%s",lt_upath,path);
47891e
47891e
	ret = lt_dlsetsearchpath_locked(buf);
47891e
47891e
	free(buf);
47891e
8744a5
	return ret;
8744a5
}
8744a5
8744a5
int lt_dladdsearchdir(const char * path)
8744a5
{
8744a5
	int ret;
8744a5
	lt_slock();
8744a5
	ret = lt_dladdsearchdir_locked(path);
8744a5
	return lt_sunlock(ret,lt_status);
47891e
}
47891e
47891e
int lt_dlinsertsearchdir(const char * mark, const char * path)
47891e
{
47891e
	int          ret;
47891e
	const char * ch;
47891e
	char *       buf;
47891e
	char *       dst;
47891e
	off_t        alen;
47891e
	off_t        plen;
47891e
	off_t        slen;
47891e
	off_t        offset;
47891e
	char **      pathv;
47891e
47891e
	if (!mark)
47891e
		return lt_dladdsearchdir(path);
47891e
8744a5
	lt_slock();
8744a5
8744a5
	if (path[0] != '/')
178478
		return lt_sunlock(1,SLTDL_ERR_PATH_INVALID_FIRST_CHAR);
8744a5
47891e
	for (ch=path; *ch; ch++)
47891e
		if (*ch == ':')
178478
			return lt_sunlock(1,SLTDL_ERR_PATH_INVALID_SEPARATTOR_CHAR);
47891e
47891e
	alen = strlen(path);
47891e
	plen = strlen(lt_upath);
47891e
47891e
	if ((mark < lt_upath) || (mark >= &lt_upath[plen]))
178478
		return lt_sunlock(1,SLTDL_ERR_PATH_INVALID_MARK);
47891e
47891e
	if ((mark > lt_upath) && (mark[-1] != ':'))
178478
		return lt_sunlock(1,SLTDL_ERR_PATH_INVALID_MARK);
47891e
47891e
	mark = &lt_vpath[mark - lt_upath];
47891e
47891e
	/* no allocation needed? */
47891e
	if (!lt_pathv[lt_plocs - 2] && ((plen + 1 + alen + 1) <= lt_plen)) {
47891e
		for (pathv=lt_vmark; pathv>=lt_pathv; pathv--) {
47891e
			slen    = strlen(pathv[-1]);
47891e
			offset  = pathv[-1] - lt_vpath;
47891e
			offset += alen + 1;
47891e
47891e
			memcpy(&lt_upath[offset],pathv[-1],slen);
47891e
			memcpy(&lt_vpath[offset],&lt_upath[offset],slen);
47891e
47891e
			if (pathv < lt_vmark)
47891e
				lt_upath[offset + slen] = ':';
47891e
76a627
			pathv[0] = pathv[-1] + alen + 1;
47891e
47891e
			if (pathv[-1] == mark) {
47891e
				offset = mark - lt_vpath;
47891e
				strcpy(&lt_vpath[offset],path);
47891e
				memcpy(&lt_upath[offset],path,alen);
47891e
				lt_upath[offset+alen] = ':';
47891e
				lt_vmark++;
178478
				return lt_sunlock(0,SLTDL_ERR_OK);
47891e
			}
47891e
		}
47891e
	}
47891e
47891e
	/* (allocation needed) */
47891e
	if (!(buf = malloc(plen + 1 + alen + 1)))
178478
		return lt_sunlock(1,SLTDL_ERR_SYSTEM_ERROR);
47891e
47891e
	for (dst=buf, pathv=lt_pathv; *pathv; pathv++) {
47891e
		if (*pathv == mark)
47891e
			dst += sprintf(dst,"%s:",path);
47891e
47891e
		if (pathv[1])
47891e
			dst += sprintf(dst,"%s:",*pathv);
47891e
		else
47891e
			dst += sprintf(dst,"%s",*pathv);
47891e
	}
47891e
47891e
	ret = lt_dlsetsearchpath_locked(buf);
47891e
47891e
	free(buf);
47891e
8744a5
	return lt_sunlock(ret,lt_status);
47891e
}
bb0941
303f79
static int lt_dlpathopen_locked(
303f79
	const char *  module,
303f79
	const char ** extv,
303f79
	char **       mpath)
bb0941
{
bb0941
	int           fdat;
bb0941
	int           fdmod;
bb0941
	char **       ppath;
bb0941
	const char ** pext;
303f79
	size_t        plen;
bb0941
	size_t        mlen;
bb0941
	size_t        elen;
bb0941
	char          path[1024];
bb0941
bb0941
	if ((mlen = strlen(module)) >= sizeof(path))
178478
		return lt_setstatus(-1,SLTDL_ERR_PATH_INVALID_LEN);
bb0941
bb0941
	memcpy(path,module,mlen);
bb0941
b21404
	if (path[0] == '/') {
b21404
		if ((fdmod = open(path,SLTDL_MODULE_OPEN_OPTIONS,0)) >= 0) {
b21404
			if (mpath) {
b21404
				if (!(*mpath = realpath(path,0))) {
b21404
					close(fdmod);
178478
					return lt_setstatus(-1,SLTDL_ERR_SYSTEM_ERROR);
b21404
				}
b21404
			}
b21404
178478
			return lt_setstatus(fdmod,SLTDL_ERR_OK);
b21404
		}
b21404
178478
		return lt_setstatus(-1,SLTDL_ERR_PATH_NO_ENTRY);
b21404
	}
b21404
d4d5c9
	for (ppath=lt_pathv; ppath && *ppath; ppath++) {
bb0941
		fdat = open(*ppath,O_RDONLY|O_DIRECTORY|O_CLOEXEC,0);
bb0941
bb0941
		if (fdat >= 0) {
bb0941
			for (pext=extv; *pext; pext++) {
bb0941
				if (mlen + (elen = strlen(*pext)) >= (sizeof(path))) {
bb0941
					close(fdat);
178478
					return lt_setstatus(-1,SLTDL_ERR_PATH_INVALID_LEN);
bb0941
				}
bb0941
bb0941
				memcpy(&path[mlen],*pext,elen);
bb0941
				path[mlen+elen] = 0;
bb0941
3f4643
				fdmod = openat(fdat,path,SLTDL_MODULE_OPEN_OPTIONS,0);
bb0941
bb0941
				if (fdmod >= 0) {
303f79
					if (mpath) {
303f79
						plen  = strlen(*ppath);
303f79
						plen += mlen + 1 + elen + 1;
303f79
303f79
						if (!(*mpath = malloc(plen))) {
303f79
							close(fdat);
303f79
							close(fdmod);
178478
							return lt_setstatus(-1,SLTDL_ERR_SYSTEM_ERROR);
303f79
						}
303f79
303f79
						sprintf(*mpath,"%s/%s",*ppath,path);
303f79
					}
303f79
bb0941
					close(fdat);
178478
					return lt_setstatus(fdmod,SLTDL_ERR_OK);
bb0941
				}
bb0941
			}
bb0941
bb0941
			close(fdat);
bb0941
		}
bb0941
	}
bb0941
178478
	return lt_setstatus(-1,SLTDL_ERR_PATH_NO_ENTRY);
709fde
}
709fde
709fde
int lt_dlpathopen(const char * module, const char ** extv)
709fde
{
8744a5
	int ret;
709fde
	lt_slock();
8744a5
	ret = lt_dlpathopen_locked(module,extv,0);
8744a5
	return lt_sunlock(ret,lt_status);
303f79
}
303f79
303f79
static struct lt_modctx * lt_dlopen_locked(
8c3ccc
	const struct lt_symdef *  symtbl,
01192f
	const char *              module,
01192f
	const char **             extv,
01192f
	int                       mode)
303f79
{
01192f
	int                       fdmod;
01192f
	char *                    mpath;
01192f
	void *                    maddr;
01192f
	struct lt_modctx *        modctx;
01192f
	struct lt_modctx *        modctx_buf;
303f79
8c3ccc
	/* init */
8c3ccc
	mpath = 0;
8c3ccc
	maddr = 0;
8c3ccc
8c3ccc
	/* path open (module) */
8c3ccc
	if (module) {
8c3ccc
		if ((fdmod = lt_dlpathopen_locked(module,extv,&mpath)) < 0)
8c3ccc
			return 0;
303f79
8c3ccc
		close(fdmod);
8c3ccc
	}
303f79
303f79
	/* entry alloc */
303f79
	if (lt_modv_next == lt_modv_cap) {
303f79
		if (!(modctx_buf = calloc(64,sizeof(*modctx)))) {
303f79
			free(mpath);
178478
			lt_setstatus(0,SLTDL_ERR_SYSTEM_ERROR);
303f79
			return 0;
303f79
		}
303f79
303f79
		lt_modv_next = modctx_buf;
303f79
		lt_modv_cap  = &lt_modv_next[64];
303f79
	}
303f79
8c3ccc
	/* dlopen (module) */
8c3ccc
	if (module && !(maddr = dlopen(mpath,mode))) {
303f79
		free(mpath);
178478
		lt_setstatus(0,SLTDL_ERR_DLFCN_ERROR);
303f79
		return 0;
303f79
	}
303f79
8c3ccc
	/* module already dlopen'ed? */
8c3ccc
	for (modctx=lt_modv_head; module && modctx; modctx=modctx->mnext) {
303f79
		if (!strcmp(modctx->mpath,mpath)) {
303f79
			free(mpath);
303f79
			modctx->mrefs++;
303f79
			return modctx;
303f79
		}
303f79
	}
303f79
8c3ccc
	/* module or symtbl entry */
01192f
	modctx          = lt_modv_next;
8c3ccc
	modctx->symtbl  = symtbl;
01192f
	modctx->maddr   = maddr;
01192f
	modctx->mpath   = mpath;
01192f
	modctx->mrefs   = 1;
303f79
	lt_modv_next++;
303f79
303f79
	/* add to list */
303f79
	if (lt_modv_tail) {
303f79
		lt_modv_tail->mnext = modctx;
303f79
		lt_modv_tail        = modctx;
303f79
	} else {
303f79
		lt_modv_head        = modctx;
303f79
		lt_modv_tail        = modctx;
303f79
	}
303f79
303f79
	/* all done */
303f79
	return modctx;
303f79
}
303f79
303f79
struct lt_modctx * lt_dlopen(const char * module)
303f79
{
54b252
	char *             dot;
54b252
	size_t             slen;
303f79
	struct lt_modctx * modctx;
303f79
	const char *       extv[2] = {"",0};
54b252
	char               dsobuf[PATH_MAX];
303f79
303f79
	lt_slock();
54b252
54b252
	if ((slen = strlen(module)) >= PATH_MAX) {
178478
		lt_setstatus(0,SLTDL_ERR_SYSTEM_ERROR);
54b252
		lt_sunlock(0,lt_status);
54b252
		return 0;
54b252
	}
54b252
54b252
	strcpy(dsobuf,module);
54b252
54b252
	if ((dot = strrchr(dsobuf,'.')))
54b252
		if (!strcmp(dot,".la"))
54b252
			if ((slen = (dot - dsobuf)))
54b252
				if (PATH_MAX - slen > strlen(OS_LIB_SUFFIX))
54b252
					strcpy(dot,OS_LIB_SUFFIX);
54b252
54b252
	module = dsobuf;
8c3ccc
	modctx = lt_dlopen_locked(0,module,extv,RTLD_NOW);
8744a5
	lt_sunlock(0,lt_status);
303f79
	return modctx;
303f79
}
303f79
303f79
struct lt_modctx * lt_dlopenext(const char * module)
303f79
{
303f79
	struct lt_modctx * modctx;
303f79
	const char *       extv[3] = {"",OS_LIB_SUFFIX,0};
303f79
303f79
	lt_slock();
8c3ccc
	modctx = lt_dlopen_locked(0,module,extv,RTLD_NOW);
8744a5
	lt_sunlock(0,lt_status);
303f79
	return modctx;
bb0941
}
2af0aa
2af0aa
struct lt_modctx * lt_dlopenadvise(const char * module, struct lt_modctl * modctl)
2af0aa
{
2af0aa
	(void)modctl;
2af0aa
	return lt_dlopenext(module);
2af0aa
}
64a26c
64a26c
void * lt_dlsym(struct lt_modctx * modctx, const char * symname)
64a26c
{
abbfe7
	const struct lt_symdef * sym;
abbfe7
abbfe7
	if (modctx->maddr)
abbfe7
		return dlsym(modctx->maddr,symname);
abbfe7
abbfe7
	for (sym=modctx->symtbl; sym->name; sym++)
abbfe7
		if (!strcmp(sym->name,symname))
abbfe7
			return sym->address;
abbfe7
abbfe7
	return 0;
64a26c
}
64a26c
64a26c
int lt_dlclose(struct lt_modctx * modctx)
64a26c
{
64a26c
	struct lt_modctx * pmod;
64a26c
64a26c
	lt_slock();
64a26c
64a26c
	for (pmod=lt_modv_head; pmod ; pmod=pmod->mnext) {
64a26c
		if (pmod == modctx) {
64a26c
			if (pmod->mrefs) {
64a26c
				pmod->mrefs--;
178478
				return lt_sunlock(0,SLTDL_ERR_OK);
64a26c
			}
64a26c
178478
			return lt_sunlock(-1,SLTDL_ERR_MODULE_REF_COUNT);
64a26c
		}
64a26c
	}
64a26c
178478
	return lt_sunlock(-1,SLTDL_ERR_MODULE_PTR_INVALID);
64a26c
}
2d052c
2d052c
static int lt_dlforeachfile_one(
2d052c
	const char *  path,
2d052c
	int         (*fusr)(const char * direntry, void * data),
2d052c
	void *        data)
2d052c
{
2d052c
	int           ret;
2d052c
	char **       argv;
2d052c
	glob_t        globbuf;
2d052c
	char          pathbuf[PATH_MAX];
2d052c
2d052c
	globbuf.gl_pathc = 0;
2d052c
	globbuf.gl_pathv = 0;
2d052c
	globbuf.gl_offs  = 0;
2d052c
2d052c
	ret = snprintf(
2d052c
		pathbuf,
2d052c
		sizeof(pathbuf),
2d052c
		"%s/" "*" OS_LIB_SUFFIX,
2d052c
		path);
2d052c
2d052c
	if (ret >= PATH_MAX) {
2d052c
		errno = EINVAL;
2d052c
		return -1;
2d052c
	}
2d052c
2d052c
	if ((ret = glob(pathbuf,0,0,&globbuf)) < 0)
2d052c
		return ret;
2d052c
2d052c
	for (argv=globbuf.gl_pathv; argv && *argv; argv++) {
2d052c
		if ((ret = fusr(*argv,data))) {
2d052c
			globfree(&globbuf);
2d052c
			return ret;
2d052c
		}
2d052c
	}
2d052c
2d052c
	globfree(&globbuf);
2d052c
2d052c
	return 0;
2d052c
}
2d052c
2d052c
int lt_dlforeachfile(
2d052c
	const char *  path,
2d052c
	int         (*fusr)(const char * direntry, void * data),
2d052c
	void *        data)
2d052c
{
2d052c
	int           ret;
2d052c
	char **       pathv;
2d052c
	char *        spath;
2d052c
	char *        mark;
2d052c
	char *        ch;
2d052c
2d052c
	if (path) {
2d052c
		if (!(spath = strdup(path)))
2d052c
			return -1;
2d052c
2d052c
		mark = spath;
2d052c
		ch   = spath;
2d052c
2d052c
		for (; *ch; ) {
2d052c
			for (ch=mark; ch[0] && ch[0] != ':'; ch++)
2d052c
				(void)0;
2d052c
2d052c
			if (ch[0])
2d052c
				*ch++ = '\0';
2d052c
2d052c
			if ((ret = lt_dlforeachfile_one(mark,fusr,data))) {
2d052c
				free(spath);
2d052c
				return ret;
2d052c
			}
2d052c
2d052c
			mark = ch;
2d052c
		}
2d052c
2d052c
		free(spath);
2d052c
	} else {
2d052c
		for (pathv=lt_pathv; pathv && *pathv; pathv++)
2d052c
			if ((ret = lt_dlforeachfile_one(*pathv,fusr,data)))
2d052c
				return ret;
2d052c
	}
2d052c
2d052c
	return 0;
2d052c
}
1accae
1accae
int lt_dlloader_add(const struct lt_dlentry * ldr)
1accae
{
1accae
	(void)ldr;
1accae
	return -1;
1accae
}
1accae
1accae
const struct lt_dlentry * lt_dlloader_find(const char * ldrname)
1accae
{
1accae
	(void)ldrname;
1accae
	return 0;
1accae
}