1/**
2 * @file locate_images.cpp
3 * Command-line helper
4 *
5 * @remark Copyright 2002 OProfile authors
6 * @remark Read the file COPYING
7 *
8 * @author Philippe Elie
9 * @author John Levon
10 */
11
12#include "file_manip.h"
13#include "locate_images.h"
14#include "string_manip.h"
15
16#include <cerrno>
17#include <iostream>
18#include <sstream>
19#include <cstdlib>
20
21using namespace std;
22
23
24int extra_images::suid;
25
26extra_images::extra_images()
27	:
28	uid(++suid)
29{
30}
31
32
33void extra_images::populate(vector<string> const & paths,
34			    string const & prefix_path)
35{
36	vector<string>::const_iterator cit = paths.begin();
37	vector<string>::const_iterator end = paths.end();
38	for (; cit != end; ++cit) {
39		string const path = op_realpath(prefix_path + *cit);
40		list<string> file_list;
41		create_file_list(file_list, path, "*", true);
42		list<string>::const_iterator lit = file_list.begin();
43		list<string>::const_iterator lend = file_list.end();
44		for (; lit != lend; ++lit) {
45			value_type v(op_basename(*lit), op_dirname(*lit));
46			images.insert(v);
47		}
48	}
49}
50
51
52void extra_images::populate(vector<string> const & paths,
53			    string const & archive_path_,
54			    string const & root_path_)
55{
56	archive_path = archive_path_;
57	if (!archive_path.empty())
58		archive_path = op_realpath(archive_path);
59
60	root_path = op_realpath(root_path_);
61	if (!root_path.empty())
62		root_path = op_realpath(root_path);
63
64	if (root_path.empty() && archive_path.empty())
65		populate(paths, "");
66	if (!archive_path.empty())
67		populate(paths, archive_path);
68	if (!root_path.empty() && root_path != archive_path)
69		populate(paths, root_path);
70}
71
72
73vector<string> const extra_images::find(string const & name) const
74{
75	extra_images::matcher match(name);
76	return find(match);
77}
78
79
80vector<string> const
81extra_images::find(extra_images::matcher const & match) const
82{
83	vector<string> matches;
84
85	const_iterator cit = images.begin();
86	const_iterator end = images.end();
87
88	for (; cit != end; ++cit) {
89		if (match(cit->first))
90			matches.push_back(cit->second + '/' + cit->first);
91	}
92
93	return matches;
94}
95
96
97namespace {
98
99/**
100 * Function object for matching a module filename, which
101 * has its own special mangling rules in 2.6 kernels.
102 */
103struct module_matcher : public extra_images::matcher {
104public:
105	explicit module_matcher(string const & s)
106		: extra_images::matcher(s) {}
107
108	virtual bool operator()(string const & candidate) const {
109		if (candidate.length() != value.length())
110			return false;
111
112		for (string::size_type i = 0 ; i < value.length() ; ++i) {
113			if (value[i] == candidate[i])
114				continue;
115			if (value[i] == '_' &&
116				(candidate[i] == ',' || candidate[i] == '-'))
117				continue;
118			return false;
119		}
120
121		return true;
122	}
123};
124
125} // anon namespace
126
127string const extra_images::locate_image(string const & image_name,
128			   image_error & error, bool fixup) const
129{
130	// Skip search since root_path can be non empty and we want
131	// to lookup only in root_path in this case.
132	if (!archive_path.empty()) {
133		string image = op_realpath(archive_path + image_name);
134		if (op_file_readable(image)) {
135			error = image_ok;
136			return fixup ? image : image_name;
137		}
138
139		if (errno == EACCES) {
140			error = image_unreadable;
141			return image_name;
142		}
143	}
144
145	// We catch a case where root_path.empty() since we skipped a
146	// search in "/" above when archive_path is empty. The case where
147	// root_path.empty() && archive_path.empty() is the normal one, none
148	// of --root or archive: as been given on command line.
149	if (!root_path.empty() || archive_path.empty()) {
150		string image = op_realpath(root_path + image_name);
151		if (op_file_readable(image)) {
152			error = image_ok;
153			return fixup ? image : image_name;
154		}
155	}
156
157	error = image_not_found;
158	return image_name;
159}
160
161string const extra_images::find_image_path(string const & image_name,
162	image_error & error, bool fixup) const
163{
164	error = image_ok;
165
166	string const image = locate_image(image_name, error, fixup);
167	if (error != image_not_found)
168		return image;
169
170	string const base = op_basename(image);
171
172	vector<string> result = find(base);
173
174	// not found, try a module search
175	if (result.empty())
176		result = find(module_matcher(base + ".ko"));
177
178	if (result.empty()) {
179		error = image_not_found;
180		return image_name;
181	}
182
183	if (result.size() == 1) {
184		error = image_ok;
185		return fixup ? result[0] : image_name;
186	}
187
188#ifdef ANDROID
189	// On Android, we often have both stripped and unstripped versions of the same
190	// library in the image path.  Choose the first one found instead of issuing a
191	// multiple match error.
192	error = image_ok;
193	return fixup ? result[0] : image_name;
194#else
195	// We can't get multiple result except if only one result is prefixed
196	// by archive_path or by root_path.
197	size_t count = 0;
198	size_t index = 0;
199	for (size_t i = 0; i < result.size() && count < 2; ++i) {
200		if (is_prefix(result[i], archive_path)) {
201			index = i;
202			++count;
203		}
204	}
205
206	if (count == 0) {
207		for (size_t i = 0; i < result.size() && count < 2; ++i) {
208			if (is_prefix(result[i], root_path)) {
209				index = i;
210				++count;
211			}
212		}
213	}
214
215	if (count == 1) {
216		error = image_ok;
217		return fixup ? result[index] : image_name;
218	}
219
220	error = image_multiple_match;
221	return image_name;
222#endif
223}
224
225
226string extra_images::strip_path_prefix(string const & image) const
227{
228	if (archive_path.length() && is_prefix(image, archive_path))
229		return image.substr(archive_path.size());
230	if (root_path.length() && is_prefix(image, root_path))
231		return image.substr(root_path.size());
232	return image;
233}
234