1/*
2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23#ifndef RLD
24#include <stdlib.h>
25#include <stdio.h>
26#include <strings.h>
27#include <unistd.h>
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <sys/param.h>
31#ifndef __OPENSTEP__
32#include <fts.h>
33#endif
34#include <sys/errno.h>
35#include "stuff/bool.h"
36#include "stuff/SymLoc.h"
37#include "stuff/ofile.h"
38#include "stuff/errors.h"
39#include "stuff/allocate.h"
40#include "stuff/dylib_roots.h"
41
42struct check_block {
43    char *install_name;
44    enum bool check_result;
45};
46
47static void check_for_dylib(
48    struct ofile *ofile,
49    char *arch_name,
50    void *cookie);
51
52char *
53get_symfile_for_dylib(
54char *install_name,
55char *release_name,
56enum bool *found_project,
57enum bool disablewarnings,
58enum bool no_error_if_missing)
59{
60    const char *symroot;
61
62	symroot = symLocForDylib(install_name, release_name, found_project,
63				 disablewarnings, no_error_if_missing);
64	if(symroot == NULL)
65	    return(NULL);
66	return(find_dylib_in_root(install_name, symroot));
67}
68
69char *
70get_dstfile_for_dylib(
71char *install_name,
72char *release_name,
73enum bool *found_project,
74enum bool disablewarnings,
75enum bool no_error_if_missing)
76{
77    const char *dstroot;
78    char *image_file_name;
79    struct check_block block;
80    struct stat stat_buf;
81
82	dstroot = dstLocForDylib(install_name, release_name, found_project,
83				 disablewarnings, no_error_if_missing);
84	if(dstroot == NULL)
85	    return(NULL);
86
87	if(*install_name == '/'){
88	    image_file_name = makestr(dstroot, install_name, NULL);
89	    block.install_name = install_name;
90	    block.check_result = TRUE;
91	    /*
92	     * To avoid the error message generated by ofile_process() if the
93	     * file does not exist just move on to trying to find it in the
94	     * dstroot.
95	     */
96	    if(disablewarnings == TRUE){
97		if(stat(image_file_name, &stat_buf) == -1){
98		    free(image_file_name);
99		    goto try_to_find_in_dstroot;
100		}
101	    }
102	    ofile_process(image_file_name, NULL, 0, TRUE,
103			  TRUE, TRUE, FALSE, check_for_dylib, &block);
104	    if(block.check_result == TRUE)
105		return(image_file_name);
106	    free(image_file_name);
107	}
108try_to_find_in_dstroot:
109	return(find_dylib_in_root(install_name, dstroot));
110	return(NULL);
111}
112
113char *
114find_dylib_in_root(
115char *install_name,
116const char *root)
117{
118#ifndef __OPENSTEP__
119    char *base_name, start[MAXPATHLEN + 1], *image_file_name;
120    char const *paths[2];
121    FTS *fts;
122    FTSENT *ftsent;
123    struct check_block block;
124
125	block.install_name = install_name;
126	block.check_result = FALSE;
127
128#ifdef BIG_DEBUG
129	printf("In find_dylib_in_root(install_name = %s, root = %s)\n",
130	       install_name, root);
131#endif
132	if(realpath(root, start) == NULL){
133#ifdef DEBUG
134	    printf("realpath() failed for: %s (%s, errno = %d)\n", root,
135		   strerror(errno), errno);
136#endif
137	    return(NULL);
138	}
139#ifdef BIG_DEBUG
140	printf("realpath() = %s for root: %s\n", start, root);
141#endif
142
143	base_name = strrchr(install_name, '/');
144	if(base_name == NULL || base_name[1] == '\0')
145	    base_name = install_name;
146	else
147	    base_name = base_name + 1;
148
149	paths[0] = start;
150	paths[1] = NULL;
151	fts = fts_open((char * const *)paths, FTS_PHYSICAL, NULL);
152	if(fts == NULL){
153#ifdef DEBUG
154	    printf("fts_open() failed for: %s (%s, errno = %d)\n", start,
155		   strerror(errno), errno);
156#endif
157	    return(NULL);
158	}
159
160	while((ftsent = fts_read(fts)) != NULL){
161#ifdef BIG_DEBUG
162	    printf("fts_path = %s fts_name = %s\n",
163		   ftsent->fts_path, ftsent->fts_name);
164#endif
165	    if(S_ISREG(ftsent->fts_statp->st_mode) &&
166	       !S_ISLNK(ftsent->fts_statp->st_mode) &&
167	       strcmp(base_name, ftsent->fts_name) == 0){
168#ifdef BIG_DEBUG
169		printf("got a match: fts_path = %s fts_name = %s\n",
170		       ftsent->fts_path, ftsent->fts_name);
171#endif
172		/*
173		 * Now that we found a file with the same base_name in the root
174		 * check to see that it is a dynamic library. Assume it is an
175		 * if it is not then the routine check_for_dylib() will
176		 * reset the check_result in the block passed to
177		 * it back to FALSE.
178		 */
179		block.check_result = TRUE;
180		ofile_process(ftsent->fts_path, NULL, 0, TRUE,
181			      TRUE, TRUE, FALSE, check_for_dylib,&block);
182		if(block.check_result == TRUE){
183		    image_file_name = allocate(ftsent->fts_pathlen + 1);
184		    strcpy(image_file_name, ftsent->fts_path);
185#ifdef BIG_DEBUG
186		    printf("returning %s\n", image_file_name);
187#endif
188		    if(fts_close(fts) == -1)
189			system_error("fts_close() failed");
190		    return(image_file_name);
191		}
192	    }
193	}
194	if(errno != 0){
195#ifdef DEBUG
196	    printf("fts_read() failed for (%s, errno = %d)\n",
197		   strerror(errno), errno);
198#endif
199	    if(fts_close(fts) == -1)
200		system_error("fts_close() failed");
201	    return(NULL);
202	}
203	if(fts_close(fts) == -1){
204	    system_error("fts_close() failed");
205	    return(NULL);
206	}
207
208#endif /* !defined(__OPENSTEP___) */
209
210	return(NULL);
211}
212
213static
214void
215check_for_dylib(
216struct ofile *ofile,
217char *arch_name,
218void *cookie)
219{
220    uint32_t i;
221    struct check_block *block;
222    struct load_command *lc;
223    uint32_t ncmds;
224
225#ifdef BIG_DEBUG
226	printf("In check_for_dylib() ofile->file_name = %s",
227	       ofile->file_name);
228	if(arch_name != NULL)
229	    printf(" arch_name = %s\n", arch_name);
230	else
231	    printf("\n");
232#endif /* BIG_DEBUG */
233
234	block = (struct check_block *)cookie;
235        if(ofile->mh != NULL){
236	    ncmds = ofile->mh->ncmds;
237	} else if (ofile->mh64 != NULL) {
238	    ncmds = ofile->mh64->ncmds;
239	} else {
240	    block->check_result = FALSE;
241	    return;
242	}
243
244	lc = ofile->load_commands;
245	for(i = 0; i < ncmds; i++){
246	    if(lc->cmd == LC_ID_DYLIB){
247		return;
248	    }
249	    lc = (struct load_command *)((char *)lc + lc->cmdsize);
250	}
251	block->check_result = FALSE;
252	return;
253}
254#endif /* !defined(RLD) */
255