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