dylib_roots.c   [plain text]


#include <stdlib.h>
#include <stdio.h>
#include <strings.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#ifndef __OPENSTEP__
#include <fts.h>
#endif
#include <sys/errno.h>
#include "stuff/bool.h"
#include "stuff/SymLoc.h"
#include "stuff/ofile.h"
#include "stuff/errors.h"
#include "stuff/allocate.h"
#include "stuff/dylib_roots.h"

struct check_block {
    char *install_name;
    enum bool check_result;
};

static void check_for_install_name(
    struct ofile *ofile,
    char *arch_name,
    void *cookie);

char *
get_symfile_for_dylib(
char *install_name,
char *release_name,
enum bool *found_project,
enum bool disablewarnings,
enum bool no_error_if_missing)
{
    const char *symroot;

	symroot = symLocForDylib(install_name, release_name, found_project,
				 disablewarnings, no_error_if_missing);
	if(symroot == NULL)
	    return(NULL);
	return(find_dylib_in_root(install_name, symroot));
}

char *
get_dstfile_for_dylib(
char *install_name,
char *release_name,
enum bool *found_project,
enum bool disablewarnings,
enum bool no_error_if_missing)
{
    const char *dstroot;
    char *image_file_name;
    struct check_block block;
    struct stat stat_buf;

	dstroot = dstLocForDylib(install_name, release_name, found_project,
				 disablewarnings, no_error_if_missing);
	if(dstroot == NULL)
	    return(NULL);

	if(*install_name == '/'){
	    image_file_name = makestr(dstroot, install_name, NULL);
	    block.install_name = install_name;
	    block.check_result = TRUE;
	    /*
	     * To avoid the error message generated by ofile_process() if the
	     * file does not exist just move on to trying to find it in the
	     * dstroot.
	     */
	    if(disablewarnings == TRUE){
		if(stat(image_file_name, &stat_buf) == -1){
		    free(image_file_name);
		    goto try_to_find_in_dstroot;
		}
	    }
	    ofile_process(image_file_name, NULL, 0, TRUE,
			  TRUE, TRUE, FALSE, check_for_install_name, &block);
	    if(block.check_result == TRUE)
		return(image_file_name);
	    free(image_file_name);
	}
try_to_find_in_dstroot:
	return(find_dylib_in_root(install_name, dstroot));
	return(NULL);
}

char *
find_dylib_in_root(
char *install_name,
const char *root)
{
#ifndef __OPENSTEP__
    char *base_name, start[MAXPATHLEN + 1], *image_file_name;
    char const *paths[2];
    FTS *fts;
    FTSENT *ftsent;
    struct check_block block;

	block.install_name = install_name;
	block.check_result = FALSE;

#ifdef BIG_DEBUG
	printf("In find_dylib_in_root(install_name = %s, root = %s)\n",
	       install_name, root);
#endif
	if(realpath(root, start) == NULL){
#ifdef DEBUG
	    printf("realpath() failed for: %s (%s, errno = %d)\n", root,
		   strerror(errno), errno);
#endif
	    return(NULL);
	}
#ifdef BIG_DEBUG
	printf("realpath() = %s for root: %s\n", start, root);
#endif

	base_name = strrchr(install_name, '/');
	if(base_name == NULL || base_name[1] == '\0')
	    base_name = install_name;
	else
	    base_name = base_name + 1;

	paths[0] = start;
	paths[1] = NULL;
	fts = fts_open((char * const *)paths, FTS_PHYSICAL, NULL);
	if(fts == NULL){
#ifdef DEBUG
	    printf("fts_open() failed for: %s (%s, errno = %d)\n", start,
		   strerror(errno), errno);
#endif
	    return(NULL);
	}

	while((ftsent = fts_read(fts)) != NULL){
#ifdef BIG_DEBUG
	    printf("fts_path = %s fts_name = %s\n",
		   ftsent->fts_path, ftsent->fts_name);
#endif
	    if(S_ISREG(ftsent->fts_statp->st_mode) &&
	       !S_ISLNK(ftsent->fts_statp->st_mode) &&
	       strcmp(base_name, ftsent->fts_name) == 0){
#ifdef BIG_DEBUG
		printf("got a match: fts_path = %s fts_name = %s\n",
		       ftsent->fts_path, ftsent->fts_name);
#endif
		/*
		 * Now that we found a file with the same base_name in the root
		 * check to see that it is a dynamic library with the correct
		 * install name.  Assume it is an if it is not then the
		 * routine check_for_install_name() will reset the check_result
		 * in the block passed to it back to FALSE.
		 */
		block.check_result = TRUE;
		ofile_process(ftsent->fts_path, NULL, 0, TRUE,
			      TRUE, TRUE, FALSE, check_for_install_name,&block);
		if(block.check_result == TRUE){
		    image_file_name = allocate(ftsent->fts_pathlen + 1);
		    strcpy(image_file_name, ftsent->fts_path);
#ifdef BIG_DEBUG
		    printf("returning %s\n", image_file_name);
#endif
		    if(fts_close(fts) == -1)
			system_error("fts_close() failed");
		    return(image_file_name);
		}
	    }
	}
	if(errno != 0){
#ifdef DEBUG
	    printf("fts_read() failed for (%s, errno = %d)\n",
		   strerror(errno), errno);
#endif
	    if(fts_close(fts) == -1)
		system_error("fts_close() failed");
	    return(NULL);
	}
	if(fts_close(fts) == -1){
	    system_error("fts_close() failed");
	    return(NULL);
	}

#endif /* !defined(__OPENSTEP___) */

	return(NULL);
}

static
void
check_for_install_name(
struct ofile *ofile,
char *arch_name,
void *cookie)
{
    unsigned long i;
    struct check_block *block;
    struct load_command *lc;
    struct dylib_command *dl;
    char *name;

#ifdef BIG_DEBUG
	printf("In check_for_install_name() ofile->file_name = %s",
	       ofile->file_name);
	if(arch_name != NULL)
	    printf(" arch_name = %s\n", arch_name);
	else
	    printf("\n");
#endif /* BIG_DEBUG */

	block = (struct check_block *)cookie;
	if(ofile->mh == NULL){
	    block->check_result = FALSE;
	    return;
	}

	lc = ofile->load_commands;
	for(i = 0; i < ofile->mh->ncmds; i++){
	    if(lc->cmd == LC_ID_DYLIB){
		dl = (struct dylib_command *)lc;
		name = (char *)lc + dl->dylib.name.offset;
		if(strncmp(name, "@executable_path/",
			   sizeof("@executable_path") - 1) == 0)
		    return;
		if(strcmp(name, block->install_name) != 0)
		    block->check_result = FALSE;
		return;
	    }
	    lc = (struct load_command *)((char *)lc + lc->cmdsize);
	}
	block->check_result = FALSE;
	return;
}