xref: /haiku/src/system/libroot/os/find_paths.cpp (revision 3b07762c548ec4016dea480d1061577cd15ec614)
1 /*
2  * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <find_directory_private.h>
8 
9 #include <errno.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <sys/stat.h>
13 
14 #include <algorithm>
15 
16 #include <fs_attr.h>
17 
18 #include <architecture_private.h>
19 #include <AutoDeleter.h>
20 #include <directories.h>
21 #include <syscalls.h>
22 
23 #include "PathBuffer.h"
24 
25 
26 static size_t kHomeInstallationLocationIndex = 1;
27 
28 static const path_base_directory kArchitectureSpecificBaseDirectories[] = {
29 	B_FIND_PATH_ADD_ONS_DIRECTORY,
30 	B_FIND_PATH_BIN_DIRECTORY,
31 	B_FIND_PATH_DEVELOP_LIB_DIRECTORY,
32 	B_FIND_PATH_HEADERS_DIRECTORY,
33 };
34 
35 static size_t kArchitectureSpecificBaseDirectoryCount =
36 	sizeof(kArchitectureSpecificBaseDirectories)
37 		/ sizeof(kArchitectureSpecificBaseDirectories[0]);
38 
39 
40 namespace {
41 
42 
43 struct InstallationLocations {
44 public:
45 	static const size_t	kCount = 4;
46 
47 public:
48 	InstallationLocations()
49 		:
50 		fReferenceCount(1)
51 	{
52 		fLocations[0] = kUserNonpackagedDirectory;
53 		fLocations[1] = kUserConfigDirectory;
54 		fLocations[2] = kSystemNonpackagedDirectory;
55 		fLocations[3] = kSystemDirectory;
56 	}
57 
58 	InstallationLocations(const char* home)
59 		:
60 		fReferenceCount(1)
61 	{
62 		static const char* const kNonPackagedSuffix = "/non-packaged";
63 		char* homeNonPackaged
64 			= (char*)malloc(strlen(home) + strlen(kNonPackagedSuffix) + 1);
65 		fLocations[0] = homeNonPackaged;
66 		if (homeNonPackaged != NULL) {
67 			strcpy(homeNonPackaged, home);
68 			strcat(homeNonPackaged, kNonPackagedSuffix);
69 		}
70 
71 		fLocations[1] = strdup(home);
72 
73 		fLocations[2] = kSystemNonpackagedDirectory;
74 		fLocations[3] = kSystemDirectory;
75 	}
76 
77 	~InstallationLocations()
78 	{
79 		free(const_cast<char*>(fLocations[0]));
80 		free(const_cast<char*>(fLocations[1]));
81 	}
82 
83 	bool IsValid() const
84 	{
85 		return fLocations[0] != NULL && fLocations[1] != NULL;
86 	}
87 
88 	static InstallationLocations* Default()
89 	{
90 		static char sBuffer[sizeof(InstallationLocations)];
91 		static InstallationLocations* sDefaultLocations
92 			= new(&sBuffer) InstallationLocations;
93 		return sDefaultLocations;
94 	}
95 
96 	static InstallationLocations* Get()
97 	{
98 		InstallationLocations* defaultLocations = Default();
99 
100 		// Get the home config installation location and create a new object,
101 		// if it differs from the default.
102 		char homeInstallationLocation[B_PATH_NAME_LENGTH];
103 		if (__find_directory(B_USER_CONFIG_DIRECTORY, -1, false,
104 					homeInstallationLocation, sizeof(homeInstallationLocation))
105 				== B_OK) {
106 			_kern_normalize_path(homeInstallationLocation, true,
107 				homeInstallationLocation);
108 				// failure is OK
109 			if (strcmp(homeInstallationLocation,
110 						defaultLocations->At(kHomeInstallationLocationIndex))
111 					!= 0) {
112 				InstallationLocations* locations
113 					= new(std::nothrow) InstallationLocations(
114 						homeInstallationLocation);
115 				if (locations != NULL && locations->IsValid())
116 					return locations;
117 			}
118 		}
119 
120 		atomic_add(&defaultLocations->fReferenceCount, 1);
121 		return defaultLocations;
122 	}
123 
124 	void Put()
125 	{
126 		if (atomic_add(&fReferenceCount, -1) == 1)
127 			delete this;
128 	}
129 
130 	const char* At(size_t index) const
131 	{
132 		return fLocations[index];
133 	}
134 
135 	const char* LocationFor(const char* path, size_t& _index)
136 	{
137 		for (size_t i = 0; i < kCount; i++) {
138 			size_t length = strlen(fLocations[i]);
139 			if (strncmp(path, fLocations[i], length) == 0
140 				&& (path[length] == '/' || path[length] == '\0')) {
141 				_index = i;
142 				return fLocations[i];
143 			}
144 		}
145 
146 		return NULL;
147 	}
148 
149 private:
150 	int32		fReferenceCount;
151 	const char*	fLocations[kCount];
152 };
153 
154 
155 }	// unnamed namespace
156 
157 
158 /*!	Returns the installation location relative path for the given base directory
159 	constant and installation location index. A '%' in the returned path must be
160 	replaced by "" for the primary architecture and by "/<arch>" for a secondary
161 	architecture.
162  */
163 static const char*
164 get_relative_directory_path(size_t installationLocationIndex,
165 	path_base_directory baseDirectory)
166 {
167 	switch (baseDirectory) {
168 		case B_FIND_PATH_INSTALLATION_LOCATION_DIRECTORY:
169 			return "";
170 		case B_FIND_PATH_ADD_ONS_DIRECTORY:
171 			return "/add-ons%";
172 		case B_FIND_PATH_APPS_DIRECTORY:
173 			return "/apps";
174 		case B_FIND_PATH_BIN_DIRECTORY:
175 			return "/bin%";
176 		case B_FIND_PATH_BOOT_DIRECTORY:
177 			return "/boot";
178 		case B_FIND_PATH_CACHE_DIRECTORY:
179 			return "/cache";
180 		case B_FIND_PATH_DATA_DIRECTORY:
181 			return "/data";
182 		case B_FIND_PATH_DEVELOP_DIRECTORY:
183 			return "/develop";
184 		case B_FIND_PATH_DEVELOP_LIB_DIRECTORY:
185 			return "/develop/lib%";
186 		case B_FIND_PATH_DOCUMENTATION_DIRECTORY:
187 			return "/documentation";
188 		case B_FIND_PATH_ETC_DIRECTORY:
189 			return "/settings/etc";
190 		case B_FIND_PATH_FONTS_DIRECTORY:
191 			return "/data/fonts";
192 		case B_FIND_PATH_HEADERS_DIRECTORY:
193 			return "/develop/headers%";
194 		case B_FIND_PATH_LIB_DIRECTORY:
195 			return "/lib";
196 		case B_FIND_PATH_LOG_DIRECTORY:
197 			return "/log";
198 		case B_FIND_PATH_MEDIA_NODES_DIRECTORY:
199 			return "/add-ons%/media";
200 		case B_FIND_PATH_PACKAGES_DIRECTORY:
201 			return "/packages";
202 		case B_FIND_PATH_PREFERENCES_DIRECTORY:
203 			return "/preferences";
204 		case B_FIND_PATH_SERVERS_DIRECTORY:
205 			return "/servers";
206 		case B_FIND_PATH_SETTINGS_DIRECTORY:
207 			return installationLocationIndex == kHomeInstallationLocationIndex
208 				? "/settings/global" : "/settings";
209 		case B_FIND_PATH_SOUNDS_DIRECTORY:
210 			return "/data/sounds";
211 		case B_FIND_PATH_SPOOL_DIRECTORY:
212 			return "/var/spool";
213 		case B_FIND_PATH_TRANSLATORS_DIRECTORY:
214 			return "/add-ons%/Translators";
215 		case B_FIND_PATH_VAR_DIRECTORY:
216 			return "/var";
217 
218 		case B_FIND_PATH_IMAGE_PATH:
219 		case B_FIND_PATH_PACKAGE_PATH:
220 		default:
221 			return NULL;
222 	}
223 }
224 
225 
226 static status_t
227 create_directory(char* path)
228 {
229 	// find the first directory that doesn't exist
230 	char* slash = path;
231 	bool found = false;
232 	while (!found && (slash = strchr(slash + 1, '/')) != NULL) {
233 		*slash = '\0';
234 		struct stat st;
235 		if (lstat(path, &st) != 0)
236 			break;
237 		*slash = '/';
238 	}
239 
240 	if (found)
241 		return B_OK;
242 
243 	// create directories
244 	while (slash != NULL) {
245 		*slash = '\0';
246 		bool created = mkdir(path, 0755);
247 		*slash = '/';
248 
249 		if (!created)
250 			return errno;
251 
252 		slash = strchr(slash + 1, '/');
253 	}
254 
255 	return B_OK;
256 }
257 
258 
259 static bool
260 is_in_range(const void* pointer, const void* base, size_t size)
261 {
262 	return pointer >= base && (addr_t)pointer < (addr_t)base + size;
263 }
264 
265 
266 static status_t
267 find_image(const void* codePointer, image_info& _info)
268 {
269 	int32 cookie = 0;
270 
271 	while (get_next_image_info(B_CURRENT_TEAM, &cookie, &_info) == B_OK) {
272 		if (codePointer == NULL ? _info.type == B_APP_IMAGE
273 				: (is_in_range(codePointer, _info.text, _info.text_size)
274 					|| is_in_range(codePointer, _info.data, _info.data_size))) {
275 			return B_OK;
276 		}
277 	}
278 
279 	return B_ENTRY_NOT_FOUND;
280 }
281 
282 
283 static status_t
284 copy_path(const char* path, char* buffer, size_t bufferSize)
285 {
286 	if (strlcpy(buffer, path, bufferSize) >= bufferSize)
287 		return B_BUFFER_OVERFLOW;
288 	return B_OK;
289 }
290 
291 
292 static status_t
293 normalize_path(const char* path, char* buffer, size_t bufferSize)
294 {
295 	status_t error;
296 	if (bufferSize >= B_PATH_NAME_LENGTH) {
297 		error = _kern_normalize_path(path, true, buffer);
298 	} else {
299 		char normalizedPath[B_PATH_NAME_LENGTH];
300 		error = _kern_normalize_path(path, true, normalizedPath);
301 		if (error == B_OK)
302 			error = copy_path(path, buffer, bufferSize);
303 	}
304 
305 	if (error != B_OK)
306 		return error;
307 
308 	// make sure the path exists
309 	struct stat st;
310 	if (lstat(buffer, &st) != 0)
311 		return errno;
312 
313 	return B_OK;
314 }
315 
316 
317 static status_t
318 normalize_longest_existing_path_prefix(const char* path, char* buffer,
319 	size_t bufferSize)
320 {
321 	if (strlcpy(buffer, path, bufferSize) >= bufferSize)
322 		return B_NAME_TOO_LONG;
323 
324 	// Until we have an existing path, chop off leaf components.
325 	for (;;) {
326 		struct stat st;
327 		if (lstat(buffer, &st) == 0)
328 			break;
329 
330 		// Chop off the leaf, but fail, it it's "..", since then we'd actually
331 		// construct a subpath.
332 		char* lastSlash = strrchr(buffer, '/');
333 		if (lastSlash == NULL || strcmp(lastSlash + 1, "..") == 0)
334 			return B_ENTRY_NOT_FOUND;
335 
336 		*lastSlash = '\0';
337 	}
338 
339 	// normalize the existing prefix path
340 	size_t prefixLength = strlen(buffer);
341 	status_t error = normalize_path(buffer, buffer, bufferSize);
342 	if (error != B_OK)
343 		return error;
344 
345 	// Re-append the non-existent suffix. Remove duplicate slashes and "."
346 	// components.
347 	const char* bufferEnd = buffer + bufferSize;
348 	char* end = buffer + strlen(buffer);
349 	const char* remainder = path + prefixLength + 1;
350 	while (*remainder != '\0') {
351 		// find component start
352 		if (*remainder == '/') {
353 			remainder++;
354 			continue;
355 		}
356 
357 		// find component end
358 		const char* componentEnd = strchr(remainder, '/');
359 		if (componentEnd == NULL)
360 			componentEnd = remainder + strlen(remainder);
361 
362 		// skip "." components
363 		size_t componentLength = componentEnd - remainder;
364 		if (componentLength == 1 && *remainder == '.') {
365 			remainder++;
366 			continue;
367 		}
368 
369 		// append the component
370 		if (end + 1 + componentLength >= bufferEnd)
371 			return B_BUFFER_OVERFLOW;
372 
373 		*end++ = '/';
374 		memcpy(end, remainder, componentLength);
375 		end += componentLength;
376 		remainder += componentLength;
377 	}
378 
379 	*end = '\0';
380 	return B_OK;
381 }
382 
383 
384 static status_t
385 get_file_attribute(const char* path, const char* attribute, char* nameBuffer,
386 	size_t bufferSize)
387 {
388 	int fd = fs_open_attr(path, attribute, B_STRING_TYPE,
389 		O_RDONLY | O_NOTRAVERSE);
390 	if (fd < 0)
391 		return errno;
392 
393 	status_t error = B_OK;
394 	ssize_t bytesRead = read(fd, nameBuffer, bufferSize - 1);
395 	if (bytesRead < 0)
396 		error = bytesRead;
397 	else if (bytesRead == 0)
398 		error = B_ENTRY_NOT_FOUND;
399 	else
400 		nameBuffer[bytesRead] = '\0';
401 
402 	fs_close_attr(fd);
403 
404 	return error;
405 }
406 
407 
408 static status_t
409 normalize_dependency(const char* dependency, char* buffer, size_t bufferSize)
410 {
411 	if (strlcpy(buffer, dependency, bufferSize) >= bufferSize)
412 		return B_NAME_TOO_LONG;
413 
414 	// replace all ':' with '~'
415 	char* colon = buffer - 1;
416 	while ((colon = strchr(colon + 1, ':')) != NULL)
417 		*colon = '~';
418 
419 	return B_OK;
420 }
421 
422 
423 static ssize_t
424 process_path(const char* installationLocation, const char* architecture,
425 	const char* relativePath, const char* subPath, uint32 flags,
426 	char* pathBuffer, size_t bufferSize)
427 {
428 	// copy the installation location
429 	PathBuffer buffer(pathBuffer, bufferSize);
430 	buffer.Append(installationLocation);
431 
432 	// append the relative path, expanding the architecture placeholder
433 	if (const char* placeholder = strchr(relativePath, '%')) {
434 		buffer.Append(relativePath, placeholder - relativePath);
435 
436 		if (architecture != NULL) {
437 			buffer.Append("/", 1);
438 			buffer.Append(architecture);
439 		}
440 
441 		buffer.Append(placeholder + 1);
442 	} else
443 		buffer.Append(relativePath);
444 
445 	// append subpath, if given
446 	if (subPath != NULL) {
447 		buffer.Append("/", 1);
448 		buffer.Append(subPath);
449 	}
450 
451 	size_t totalLength = buffer.Length();
452 	if (totalLength >= bufferSize)
453 		return B_BUFFER_OVERFLOW;
454 
455 	// handle the flags
456 	char* path = pathBuffer;
457 
458 	status_t error = B_OK;
459 	if ((flags & B_FIND_PATH_CREATE_DIRECTORY) != 0) {
460 		// create the directory
461 		error = create_directory(path);
462 	} else if ((flags & B_FIND_PATH_CREATE_PARENT_DIRECTORY) != 0) {
463 		// create the parent directory
464 		char* lastSlash = strrchr(path, '/');
465 		*lastSlash = '\0';
466 		error = create_directory(path);
467 		*lastSlash = '/';
468 	}
469 
470 	if (error != B_OK)
471 		return error;
472 
473 	if ((flags & B_FIND_PATH_EXISTING_ONLY) != 0) {
474 		// check if the entry exists
475 		struct stat st;
476 		if (lstat(path, &st) != 0)
477 			return 0;
478 	}
479 
480 	return totalLength + 1;
481 }
482 
483 
484 status_t
485 internal_path_for_path(char* referencePath, size_t referencePathSize,
486 	const char* dependency, const char* architecture,
487 	path_base_directory baseDirectory, const char* subPath, uint32 flags,
488 	char* pathBuffer, size_t bufferSize)
489 {
490 	if (strcmp(architecture, __get_primary_architecture()) == 0)
491 		architecture = NULL;
492 
493 	// resolve dependency
494 	char packageName[B_FILE_NAME_LENGTH];
495 		// Temporarily used here, permanently used below where
496 		// B_FIND_PATH_PACKAGE_PATH is handled.
497 	if (dependency != NULL) {
498 		// get the versioned package name
499 		status_t error = get_file_attribute(referencePath, "SYS:PACKAGE",
500 			packageName, sizeof(packageName));
501 		if (error != B_OK)
502 			return error;
503 
504 		// normalize the dependency name
505 		char normalizedDependency[B_FILE_NAME_LENGTH];
506 		error = normalize_dependency(dependency, normalizedDependency,
507 			sizeof(normalizedDependency));
508 		if (error != B_OK)
509 			return error;
510 
511 		// Compute the path of the dependency symlink. This will yield the
512 		// installation location path when normalized.
513 		if (snprintf(referencePath, referencePathSize,
514 					kSystemPackageLinksDirectory "/%s/%s", packageName,
515 					normalizedDependency)
516 				>= (ssize_t)referencePathSize) {
517 			return B_BUFFER_OVERFLOW;
518 		}
519 	}
520 
521 	// handle B_FIND_PATH_IMAGE_PATH
522 	if (baseDirectory == B_FIND_PATH_IMAGE_PATH)
523 		return copy_path(referencePath, pathBuffer, bufferSize);
524 
525 	// Handle B_FIND_PATH_PACKAGE_PATH: get the package file name and
526 	// simply adjust our arguments to look the package file up in the packages
527 	// directory.
528 	if (baseDirectory == B_FIND_PATH_PACKAGE_PATH) {
529 		status_t error = get_file_attribute(referencePath, "SYS:PACKAGE_FILE",
530 			packageName, sizeof(packageName));
531 		if (error != B_OK)
532 			return error;
533 
534 		dependency = NULL;
535 		subPath = packageName;
536 		baseDirectory = B_FIND_PATH_PACKAGES_DIRECTORY;
537 		flags = B_FIND_PATH_EXISTING_ONLY;
538 	}
539 
540 	// normalize
541 	status_t error = normalize_path(referencePath, referencePath,
542 		referencePathSize);
543 	if (error != B_OK)
544 		return error;
545 
546 	// get the installation location
547 	InstallationLocations* installationLocations = InstallationLocations::Get();
548 	MethodDeleter<InstallationLocations> installationLocationsDeleter(
549 		installationLocations, &InstallationLocations::Put);
550 
551 	size_t installationLocationIndex;
552 	const char* installationLocation = installationLocations->LocationFor(
553 		referencePath, installationLocationIndex);
554 	if (installationLocation == NULL)
555 		return B_ENTRY_NOT_FOUND;
556 
557 	// get base dir and process the path
558 	const char* relativePath = get_relative_directory_path(
559 		installationLocationIndex, baseDirectory);
560 	if (relativePath == NULL)
561 		return B_BAD_VALUE;
562 
563 	ssize_t pathSize = process_path(installationLocation, architecture,
564 		relativePath, subPath, flags, pathBuffer, bufferSize);
565 	if (pathSize <= 0)
566 		return pathSize == 0 ? B_ENTRY_NOT_FOUND : pathSize;
567 	return B_OK;
568 }
569 
570 
571 // #pragma mark -
572 
573 
574 status_t
575 __find_path(const void* codePointer, path_base_directory baseDirectory,
576 	const char* subPath, char* pathBuffer, size_t bufferSize)
577 {
578 	return __find_path_etc(codePointer, NULL, NULL, baseDirectory, subPath, 0,
579 		pathBuffer, bufferSize);
580 }
581 
582 
583 status_t
584 __find_path_etc(const void* codePointer, const char* dependency,
585 	const char* architecture, path_base_directory baseDirectory,
586 	const char* subPath, uint32 flags, char* pathBuffer, size_t bufferSize)
587 {
588 	if (pathBuffer == NULL)
589 		return B_BAD_VALUE;
590 
591 	// resolve codePointer to image info
592 	image_info imageInfo;
593 	status_t error = find_image(codePointer, imageInfo);
594 	if (error != B_OK)
595 		return error;
596 
597 	if (architecture == NULL)
598 		architecture = __get_architecture();
599 
600 	return internal_path_for_path(imageInfo.name, sizeof(imageInfo.name),
601 		dependency, architecture, baseDirectory, subPath, flags, pathBuffer,
602 		bufferSize);
603 }
604 
605 
606 status_t
607 __find_path_for_path(const char* path, path_base_directory baseDirectory,
608 	const char* subPath, char* pathBuffer, size_t bufferSize)
609 {
610 	return __find_path_for_path_etc(path, NULL, NULL, baseDirectory, subPath, 0,
611 		pathBuffer, bufferSize);
612 }
613 
614 
615 status_t
616 __find_path_for_path_etc(const char* path, const char* dependency,
617 	const char* architecture, path_base_directory baseDirectory,
618 	const char* subPath, uint32 flags, char* pathBuffer, size_t bufferSize)
619 {
620 	if (baseDirectory == B_FIND_PATH_IMAGE_PATH)
621 		return B_BAD_VALUE;
622 
623 	char referencePath[B_PATH_NAME_LENGTH];
624 	if (strlcpy(referencePath, path, sizeof(referencePath))
625 			>= sizeof(referencePath)) {
626 		return B_NAME_TOO_LONG;
627 	}
628 
629 	if (architecture == NULL)
630 		architecture = __guess_architecture_for_path(path);
631 
632 	return internal_path_for_path(referencePath, sizeof(referencePath),
633 		dependency, architecture, baseDirectory, subPath, flags, pathBuffer,
634 		bufferSize);
635 }
636 
637 
638 status_t
639 __find_paths(path_base_directory baseDirectory, const char* subPath,
640 	char*** _paths, size_t* _pathCount)
641 {
642 	return __find_paths_etc(NULL, baseDirectory, subPath, 0, _paths,
643 		_pathCount);
644 }
645 
646 
647 status_t
648 __find_paths_etc(const char* architecture, path_base_directory baseDirectory,
649 	const char* subPath, uint32 flags, char*** _paths, size_t* _pathCount)
650 {
651 	if (_paths == NULL || _pathCount == NULL)
652 		return B_BAD_VALUE;
653 
654 	// Analyze architecture. If NULL, use the caller's architecture. If the
655 	// effective architecture is the primary one, set architecture to NULL to
656 	// indicate that we don't need to insert an architecture subdirectory
657 	// component.
658 	if (architecture == NULL)
659 		architecture = __get_architecture();
660 	if (strcmp(architecture, __get_primary_architecture()) == 0)
661 		architecture = NULL;
662 	size_t architectureSize = architecture != NULL
663 		? strlen(architecture) + 1 : 0;
664 
665 	size_t subPathLength = subPath != NULL ? strlen(subPath) + 1 : 0;
666 
667 	// get the installation locations
668 	InstallationLocations* installationLocations = InstallationLocations::Get();
669 	MethodDeleter<InstallationLocations> installationLocationsDeleter(
670 		installationLocations, &InstallationLocations::Put);
671 
672 	// Get the relative paths and compute the total size to allocate.
673 	const char* relativePaths[InstallationLocations::kCount];
674 	size_t totalSize = 0;
675 
676 	for (size_t i = 0; i < InstallationLocations::kCount; i++) {
677 		relativePaths[i] = get_relative_directory_path(i, baseDirectory);
678 		if (relativePaths[i] == NULL)
679 			return B_BAD_VALUE;
680 
681 		totalSize += strlen(installationLocations->At(i))
682 			+ strlen(relativePaths[i]) + subPathLength + 1;
683 		if (strchr(relativePaths[i], '%') != NULL)
684 			totalSize += architectureSize - 1;
685 	}
686 
687 	// allocate storage
688 	char** paths = (char**)malloc(sizeof(char*) * InstallationLocations::kCount
689 		+ totalSize);
690 	if (paths == NULL)
691 		return B_NO_MEMORY;
692 	MemoryDeleter pathsDeleter(paths);
693 
694 	// construct and process the paths
695 	size_t count = 0;
696 	char* pathBuffer = (char*)(paths + InstallationLocations::kCount);
697 	const char* pathBufferEnd = pathBuffer + totalSize;
698 	for (size_t i = 0; i < InstallationLocations::kCount; i++) {
699 		ssize_t pathSize = process_path(installationLocations->At(i),
700 			architecture, relativePaths[i], subPath, flags, pathBuffer,
701 			pathBufferEnd - pathBuffer);
702 		if (pathSize < 0)
703 			return pathSize;
704 		if (pathSize > 0) {
705 			paths[count++] = pathBuffer;
706 			pathBuffer += pathSize;
707 		}
708 	}
709 
710 	if (count == 0)
711 		return B_ENTRY_NOT_FOUND;
712 
713 	*_paths = paths;
714 	*_pathCount = count;
715 	pathsDeleter.Detach();
716 
717 	return B_OK;
718 }
719 
720 
721 const char*
722 __guess_secondary_architecture_from_path(const char* path,
723 	const char* const* secondaryArchitectures,
724 	size_t secondaryArchitectureCount)
725 {
726 	// Get the longest existing prefix path and normalize it.
727 	char prefix[B_PATH_NAME_LENGTH];
728 	if (normalize_longest_existing_path_prefix(path, prefix, sizeof(prefix))
729 			!= B_OK) {
730 		return NULL;
731 	}
732 
733 	// get an installation location relative path
734 	InstallationLocations* installationLocations = InstallationLocations::Get();
735 	MethodDeleter<InstallationLocations> installationLocationsDeleter(
736 		installationLocations, &InstallationLocations::Put);
737 
738 	size_t installationLocationIndex;
739 	const char* installationLocation = installationLocations->LocationFor(
740 		prefix, installationLocationIndex);
741 	if (installationLocation == NULL)
742 		return NULL;
743 
744 	const char* relativePath = prefix + strlen(installationLocation);
745 	if (relativePath[0] != '/')
746 		return NULL;
747 
748 	// Iterate through the known paths that would indicate a secondary
749 	// architecture and try to match them with our given path.
750 	for (size_t i = 0; i < kArchitectureSpecificBaseDirectoryCount; i++) {
751 		const char* basePath = get_relative_directory_path(
752 			installationLocationIndex, kArchitectureSpecificBaseDirectories[i]);
753 		const char* placeholder = strchr(basePath, '%');
754 		if (placeholder == NULL)
755 			continue;
756 
757 		// match the part up to the architecture placeholder
758 		size_t prefixLength = placeholder - basePath;
759 		if (strncmp(relativePath, basePath, prefixLength) != 0
760 			|| relativePath[prefixLength] != '/') {
761 			continue;
762 		}
763 
764 		// match the architecture
765 		const char* architecturePart = relativePath + prefixLength + 1;
766 		for (size_t k = 0; k < secondaryArchitectureCount; k++) {
767 			const char* architecture = secondaryArchitectures[k];
768 			size_t architectureLength = strlen(architecture);
769 			if (strncmp(architecturePart, architecture, architectureLength) == 0
770 				&& (architecturePart[architectureLength] == '/'
771 					|| architecturePart[architectureLength] == '\0')) {
772 				return architecture;
773 			}
774 		}
775 	}
776 
777 	return NULL;
778 }
779 
780 
781 B_DEFINE_WEAK_ALIAS(__find_path, find_path);
782 B_DEFINE_WEAK_ALIAS(__find_path_etc, find_path_etc);
783 B_DEFINE_WEAK_ALIAS(__find_path_for_path, find_path_for_path);
784 B_DEFINE_WEAK_ALIAS(__find_path_for_path_etc, find_path_for_path_etc);
785 B_DEFINE_WEAK_ALIAS(__find_paths, find_paths);
786 B_DEFINE_WEAK_ALIAS(__find_paths_etc, find_paths_etc);
787