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