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.
8 #include <find_directory_private.h>
19 #include <architecture_private.h>
20 #include <AutoDeleter.h>
21 #include <directories.h>
24 #include "PathBuffer.h"
27 static size_t kHomeInstallationLocationIndex
= 1;
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
,
36 static size_t kArchitectureSpecificBaseDirectoryCount
=
37 sizeof(kArchitectureSpecificBaseDirectories
)
38 / sizeof(kArchitectureSpecificBaseDirectories
[0]);
44 struct InstallationLocations
{
46 static const size_t kCount
= 4;
49 InstallationLocations()
53 fLocations
[0] = kUserNonpackagedDirectory
;
54 fLocations
[1] = kUserConfigDirectory
;
55 fLocations
[2] = kSystemNonpackagedDirectory
;
56 fLocations
[3] = kSystemDirectory
;
59 InstallationLocations(const char* home
)
63 static const char* const kNonPackagedSuffix
= "/non-packaged";
65 = (char*)malloc(strlen(home
) + strlen(kNonPackagedSuffix
) + 1);
66 fLocations
[0] = homeNonPackaged
;
67 if (homeNonPackaged
!= NULL
) {
68 strcpy(homeNonPackaged
, home
);
69 strcat(homeNonPackaged
, kNonPackagedSuffix
);
72 fLocations
[1] = strdup(home
);
74 fLocations
[2] = kSystemNonpackagedDirectory
;
75 fLocations
[3] = kSystemDirectory
;
78 ~InstallationLocations()
80 free(const_cast<char*>(fLocations
[0]));
81 free(const_cast<char*>(fLocations
[1]));
86 return fLocations
[0] != NULL
&& fLocations
[1] != NULL
;
89 bool IsUserIndex(size_t index
) const
91 return index
==0 || index
==1;
94 bool IsSystemIndex(size_t index
) const
96 return index
==2 || index
==3;
99 static InstallationLocations
* Default()
101 static char sBuffer
[sizeof(InstallationLocations
)];
102 static InstallationLocations
* sDefaultLocations
103 = new(&sBuffer
) InstallationLocations
;
104 return sDefaultLocations
;
107 static InstallationLocations
* Get()
109 InstallationLocations
* defaultLocations
= Default();
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
))
117 _kern_normalize_path(homeInstallationLocation
, true,
118 homeInstallationLocation
);
120 if (strcmp(homeInstallationLocation
,
121 defaultLocations
->At(kHomeInstallationLocationIndex
))
123 InstallationLocations
* locations
124 = new(std::nothrow
) InstallationLocations(
125 homeInstallationLocation
);
126 if (locations
!= NULL
&& locations
->IsValid())
132 atomic_add(&defaultLocations
->fReferenceCount
, 1);
133 return defaultLocations
;
138 if (atomic_add(&fReferenceCount
, -1) == 1)
142 const char* At(size_t index
) const
144 return fLocations
[index
];
147 const char* LocationFor(const char* path
, size_t& _index
)
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')) {
154 return fLocations
[i
];
162 int32 fReferenceCount
;
163 const char* fLocations
[kCount
];
167 } // unnamed namespace
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
176 get_relative_directory_path(size_t installationLocationIndex
,
177 path_base_directory baseDirectory
)
179 switch (baseDirectory
) {
180 case B_FIND_PATH_INSTALLATION_LOCATION_DIRECTORY
:
182 case B_FIND_PATH_ADD_ONS_DIRECTORY
:
184 case B_FIND_PATH_APPS_DIRECTORY
:
186 case B_FIND_PATH_BIN_DIRECTORY
:
188 case B_FIND_PATH_BOOT_DIRECTORY
:
190 case B_FIND_PATH_CACHE_DIRECTORY
:
192 case B_FIND_PATH_DATA_DIRECTORY
:
194 case B_FIND_PATH_DEVELOP_DIRECTORY
:
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
:
208 case B_FIND_PATH_LOG_DIRECTORY
:
210 case B_FIND_PATH_MEDIA_NODES_DIRECTORY
:
211 return "/add-ons%/media";
212 case B_FIND_PATH_PACKAGES_DIRECTORY
:
214 case B_FIND_PATH_PREFERENCES_DIRECTORY
:
215 return "/preferences";
216 case B_FIND_PATH_SERVERS_DIRECTORY
:
218 case B_FIND_PATH_SETTINGS_DIRECTORY
:
219 return installationLocationIndex
== kHomeInstallationLocationIndex
220 ? "/settings/global" : "/settings";
221 case B_FIND_PATH_SOUNDS_DIRECTORY
:
222 return "/data/sounds";
223 case B_FIND_PATH_SPOOL_DIRECTORY
:
225 case B_FIND_PATH_TRANSLATORS_DIRECTORY
:
226 return "/add-ons%/Translators";
227 case B_FIND_PATH_VAR_DIRECTORY
:
230 case B_FIND_PATH_IMAGE_PATH
:
231 case B_FIND_PATH_PACKAGE_PATH
:
239 create_directory(char* path
)
241 // find the first directory that doesn't exist
244 while (!found
&& (slash
= strchr(slash
+ 1, '/')) != NULL
) {
247 if (lstat(path
, &st
) != 0)
255 // create directories
256 while (slash
!= NULL
) {
258 bool created
= mkdir(path
, 0755);
264 slash
= strchr(slash
+ 1, '/');
272 is_in_range(const void* pointer
, const void* base
, size_t size
)
274 return pointer
>= base
&& (addr_t
)pointer
< (addr_t
)base
+ size
;
279 find_image(const void* codePointer
, image_info
& _info
)
283 while (get_next_image_info(B_CURRENT_TEAM
, &cookie
, &_info
) == B_OK
) {
284 if (codePointer
== NULL
? _info
.type
== B_APP_IMAGE
285 : (is_in_range(codePointer
, _info
.text
, _info
.text_size
)
286 || is_in_range(codePointer
, _info
.data
, _info
.data_size
))) {
291 return B_ENTRY_NOT_FOUND
;
296 copy_path(const char* path
, char* buffer
, size_t bufferSize
)
298 if (strlcpy(buffer
, path
, bufferSize
) >= bufferSize
)
299 return B_BUFFER_OVERFLOW
;
305 normalize_path(const char* path
, char* buffer
, size_t bufferSize
)
308 if (bufferSize
>= B_PATH_NAME_LENGTH
) {
309 error
= _kern_normalize_path(path
, true, buffer
);
311 char normalizedPath
[B_PATH_NAME_LENGTH
];
312 error
= _kern_normalize_path(path
, true, normalizedPath
);
314 error
= copy_path(path
, buffer
, bufferSize
);
320 // make sure the path exists
322 if (lstat(buffer
, &st
) != 0)
330 normalize_longest_existing_path_prefix(const char* path
, char* buffer
,
333 if (strlcpy(buffer
, path
, bufferSize
) >= bufferSize
)
334 return B_NAME_TOO_LONG
;
336 // Until we have an existing path, chop off leaf components.
339 if (lstat(buffer
, &st
) == 0)
342 // Chop off the leaf, but fail, it it's "..", since then we'd actually
343 // construct a subpath.
344 char* lastSlash
= strrchr(buffer
, '/');
345 if (lastSlash
== NULL
|| strcmp(lastSlash
+ 1, "..") == 0)
346 return B_ENTRY_NOT_FOUND
;
351 // normalize the existing prefix path
352 size_t prefixLength
= strlen(buffer
);
353 status_t error
= normalize_path(buffer
, buffer
, bufferSize
);
357 // Re-append the non-existent suffix. Remove duplicate slashes and "."
359 const char* bufferEnd
= buffer
+ bufferSize
;
360 char* end
= buffer
+ strlen(buffer
);
361 const char* remainder
= path
+ prefixLength
+ 1;
362 while (*remainder
!= '\0') {
363 // find component start
364 if (*remainder
== '/') {
369 // find component end
370 const char* componentEnd
= strchr(remainder
, '/');
371 if (componentEnd
== NULL
)
372 componentEnd
= remainder
+ strlen(remainder
);
374 // skip "." components
375 size_t componentLength
= componentEnd
- remainder
;
376 if (componentLength
== 1 && *remainder
== '.') {
381 // append the component
382 if (end
+ 1 + componentLength
>= bufferEnd
)
383 return B_BUFFER_OVERFLOW
;
386 memcpy(end
, remainder
, componentLength
);
387 end
+= componentLength
;
388 remainder
+= componentLength
;
397 get_file_attribute(const char* path
, const char* attribute
, char* nameBuffer
,
400 int fd
= fs_open_attr(path
, attribute
, B_STRING_TYPE
,
401 O_RDONLY
| O_NOTRAVERSE
);
405 status_t error
= B_OK
;
406 ssize_t bytesRead
= read(fd
, nameBuffer
, bufferSize
- 1);
409 else if (bytesRead
== 0)
410 error
= B_ENTRY_NOT_FOUND
;
412 nameBuffer
[bytesRead
] = '\0';
421 normalize_dependency(const char* dependency
, char* buffer
, size_t bufferSize
)
423 if (strlcpy(buffer
, dependency
, bufferSize
) >= bufferSize
)
424 return B_NAME_TOO_LONG
;
426 // replace all ':' with '~'
427 char* colon
= buffer
- 1;
428 while ((colon
= strchr(colon
+ 1, ':')) != NULL
)
436 process_path(const char* installationLocation
, const char* architecture
,
437 const char* relativePath
, const char* subPath
, uint32 flags
,
438 char* pathBuffer
, size_t bufferSize
)
440 // copy the installation location
441 PathBuffer
buffer(pathBuffer
, bufferSize
);
442 buffer
.Append(installationLocation
);
444 // append the relative path, expanding the architecture placeholder
445 if (const char* placeholder
= strchr(relativePath
, '%')) {
446 buffer
.Append(relativePath
, placeholder
- relativePath
);
448 if (architecture
!= NULL
) {
449 buffer
.Append("/", 1);
450 buffer
.Append(architecture
);
453 buffer
.Append(placeholder
+ 1);
455 buffer
.Append(relativePath
);
457 // append subpath, if given
458 if (subPath
!= NULL
) {
459 buffer
.Append("/", 1);
460 buffer
.Append(subPath
);
463 size_t totalLength
= buffer
.Length();
464 if (totalLength
>= bufferSize
)
465 return B_BUFFER_OVERFLOW
;
468 char* path
= pathBuffer
;
470 status_t error
= B_OK
;
471 if ((flags
& B_FIND_PATH_CREATE_DIRECTORY
) != 0) {
472 // create the directory
473 error
= create_directory(path
);
474 } else if ((flags
& B_FIND_PATH_CREATE_PARENT_DIRECTORY
) != 0) {
475 // create the parent directory
476 char* lastSlash
= strrchr(path
, '/');
478 error
= create_directory(path
);
485 if ((flags
& B_FIND_PATH_EXISTING_ONLY
) != 0) {
486 // check if the entry exists
488 if (lstat(path
, &st
) != 0)
492 return totalLength
+ 1;
497 internal_path_for_path(char* referencePath
, size_t referencePathSize
,
498 const char* dependency
, const char* architecture
,
499 path_base_directory baseDirectory
, const char* subPath
, uint32 flags
,
500 char* pathBuffer
, size_t bufferSize
)
502 if (strcmp(architecture
, __get_primary_architecture()) == 0)
505 // resolve dependency
506 char packageName
[B_FILE_NAME_LENGTH
];
507 // Temporarily used here, permanently used below where
508 // B_FIND_PATH_PACKAGE_PATH is handled.
509 if (dependency
!= NULL
) {
510 // get the versioned package name
511 status_t error
= get_file_attribute(referencePath
, "SYS:PACKAGE",
512 packageName
, sizeof(packageName
));
516 // normalize the dependency name
517 char normalizedDependency
[B_FILE_NAME_LENGTH
];
518 error
= normalize_dependency(dependency
, normalizedDependency
,
519 sizeof(normalizedDependency
));
523 // Compute the path of the dependency symlink. This will yield the
524 // installation location path when normalized.
525 if (snprintf(referencePath
, referencePathSize
,
526 kSystemPackageLinksDirectory
"/%s/%s", packageName
,
527 normalizedDependency
)
528 >= (ssize_t
)referencePathSize
) {
529 return B_BUFFER_OVERFLOW
;
533 // handle B_FIND_PATH_IMAGE_PATH
534 if (baseDirectory
== B_FIND_PATH_IMAGE_PATH
)
535 return copy_path(referencePath
, pathBuffer
, bufferSize
);
537 // Handle B_FIND_PATH_PACKAGE_PATH: get the package file name and
538 // simply adjust our arguments to look the package file up in the packages
540 if (baseDirectory
== B_FIND_PATH_PACKAGE_PATH
) {
541 status_t error
= get_file_attribute(referencePath
, "SYS:PACKAGE_FILE",
542 packageName
, sizeof(packageName
));
547 subPath
= packageName
;
548 baseDirectory
= B_FIND_PATH_PACKAGES_DIRECTORY
;
549 flags
= B_FIND_PATH_EXISTING_ONLY
;
553 status_t error
= normalize_path(referencePath
, referencePath
,
558 // get the installation location
559 InstallationLocations
* installationLocations
= InstallationLocations::Get();
560 MethodDeleter
<InstallationLocations
> installationLocationsDeleter(
561 installationLocations
, &InstallationLocations::Put
);
563 size_t installationLocationIndex
;
564 const char* installationLocation
= installationLocations
->LocationFor(
565 referencePath
, installationLocationIndex
);
566 if (installationLocation
== NULL
)
567 return B_ENTRY_NOT_FOUND
;
569 // get base dir and process the path
570 const char* relativePath
= get_relative_directory_path(
571 installationLocationIndex
, baseDirectory
);
572 if (relativePath
== NULL
)
575 ssize_t pathSize
= process_path(installationLocation
, architecture
,
576 relativePath
, subPath
, flags
, pathBuffer
, bufferSize
);
578 return pathSize
== 0 ? B_ENTRY_NOT_FOUND
: pathSize
;
587 __find_path(const void* codePointer
, path_base_directory baseDirectory
,
588 const char* subPath
, char* pathBuffer
, size_t bufferSize
)
590 return __find_path_etc(codePointer
, NULL
, NULL
, baseDirectory
, subPath
, 0,
591 pathBuffer
, bufferSize
);
596 __find_path_etc(const void* codePointer
, const char* dependency
,
597 const char* architecture
, path_base_directory baseDirectory
,
598 const char* subPath
, uint32 flags
, char* pathBuffer
, size_t bufferSize
)
600 if (pathBuffer
== NULL
)
603 // resolve codePointer to image info
604 image_info imageInfo
;
605 status_t error
= find_image(codePointer
, imageInfo
);
609 if (architecture
== NULL
)
610 architecture
= __get_architecture();
612 return internal_path_for_path(imageInfo
.name
, sizeof(imageInfo
.name
),
613 dependency
, architecture
, baseDirectory
, subPath
, flags
, pathBuffer
,
619 __find_path_for_path(const char* path
, path_base_directory baseDirectory
,
620 const char* subPath
, char* pathBuffer
, size_t bufferSize
)
622 return __find_path_for_path_etc(path
, NULL
, NULL
, baseDirectory
, subPath
, 0,
623 pathBuffer
, bufferSize
);
628 __find_path_for_path_etc(const char* path
, const char* dependency
,
629 const char* architecture
, path_base_directory baseDirectory
,
630 const char* subPath
, uint32 flags
, char* pathBuffer
, size_t bufferSize
)
632 if (baseDirectory
== B_FIND_PATH_IMAGE_PATH
)
635 char referencePath
[B_PATH_NAME_LENGTH
];
636 if (strlcpy(referencePath
, path
, sizeof(referencePath
))
637 >= sizeof(referencePath
)) {
638 return B_NAME_TOO_LONG
;
641 if (architecture
== NULL
)
642 architecture
= __guess_architecture_for_path(path
);
644 return internal_path_for_path(referencePath
, sizeof(referencePath
),
645 dependency
, architecture
, baseDirectory
, subPath
, flags
, pathBuffer
,
651 __find_paths(path_base_directory baseDirectory
, const char* subPath
,
652 char*** _paths
, size_t* _pathCount
)
654 return __find_paths_etc(NULL
, baseDirectory
, subPath
, 0, _paths
,
660 __find_paths_etc(const char* architecture
, path_base_directory baseDirectory
,
661 const char* subPath
, uint32 flags
, char*** _paths
, size_t* _pathCount
)
663 if (_paths
== NULL
|| _pathCount
== NULL
)
666 // Analyze architecture. If NULL, use the caller's architecture. If the
667 // effective architecture is the primary one, set architecture to NULL to
668 // indicate that we don't need to insert an architecture subdirectory
670 if (architecture
== NULL
)
671 architecture
= __get_architecture();
672 if (strcmp(architecture
, __get_primary_architecture()) == 0)
674 size_t architectureSize
= architecture
!= NULL
675 ? strlen(architecture
) + 1 : 0;
677 size_t subPathLength
= subPath
!= NULL
? strlen(subPath
) + 1 : 0;
679 // get the installation locations
680 InstallationLocations
* installationLocations
= InstallationLocations::Get();
681 MethodDeleter
<InstallationLocations
> installationLocationsDeleter(
682 installationLocations
, &InstallationLocations::Put
);
684 // Get the relative paths and compute the total size to allocate.
685 const char* relativePaths
[InstallationLocations::kCount
];
686 size_t totalSize
= 0;
688 for (size_t i
= 0; i
< InstallationLocations::kCount
; i
++) {
689 if (((flags
& B_FIND_PATHS_USER_ONLY
) != 0
690 && !installationLocations
->IsUserIndex(i
))
691 || ((flags
& B_FIND_PATHS_SYSTEM_ONLY
) != 0
692 && !installationLocations
->IsSystemIndex(i
)))
695 relativePaths
[i
] = get_relative_directory_path(i
, baseDirectory
);
696 if (relativePaths
[i
] == NULL
)
699 totalSize
+= strlen(installationLocations
->At(i
))
700 + strlen(relativePaths
[i
]) + subPathLength
+ 1;
701 if (strchr(relativePaths
[i
], '%') != NULL
)
702 totalSize
+= architectureSize
- 1;
706 char** paths
= (char**)malloc(sizeof(char*) * InstallationLocations::kCount
710 MemoryDeleter
pathsDeleter(paths
);
712 // construct and process the paths
714 char* pathBuffer
= (char*)(paths
+ InstallationLocations::kCount
);
715 const char* pathBufferEnd
= pathBuffer
+ totalSize
;
716 for (size_t i
= 0; i
< InstallationLocations::kCount
; i
++) {
717 if (((flags
& B_FIND_PATHS_USER_ONLY
) != 0
718 && !installationLocations
->IsUserIndex(i
))
719 || ((flags
& B_FIND_PATHS_SYSTEM_ONLY
) != 0
720 && !installationLocations
->IsSystemIndex(i
)))
723 ssize_t pathSize
= process_path(installationLocations
->At(i
),
724 architecture
, relativePaths
[i
], subPath
, flags
, pathBuffer
,
725 pathBufferEnd
- pathBuffer
);
729 paths
[count
++] = pathBuffer
;
730 pathBuffer
+= pathSize
;
735 return B_ENTRY_NOT_FOUND
;
739 pathsDeleter
.Detach();
746 __guess_secondary_architecture_from_path(const char* path
,
747 const char* const* secondaryArchitectures
,
748 size_t secondaryArchitectureCount
)
750 // Get the longest existing prefix path and normalize it.
751 char prefix
[B_PATH_NAME_LENGTH
];
752 if (normalize_longest_existing_path_prefix(path
, prefix
, sizeof(prefix
))
757 // get an installation location relative path
758 InstallationLocations
* installationLocations
= InstallationLocations::Get();
759 MethodDeleter
<InstallationLocations
> installationLocationsDeleter(
760 installationLocations
, &InstallationLocations::Put
);
762 size_t installationLocationIndex
;
763 const char* installationLocation
= installationLocations
->LocationFor(
764 prefix
, installationLocationIndex
);
765 if (installationLocation
== NULL
)
768 const char* relativePath
= prefix
+ strlen(installationLocation
);
769 if (relativePath
[0] != '/')
772 // Iterate through the known paths that would indicate a secondary
773 // architecture and try to match them with our given path.
774 for (size_t i
= 0; i
< kArchitectureSpecificBaseDirectoryCount
; i
++) {
775 const char* basePath
= get_relative_directory_path(
776 installationLocationIndex
, kArchitectureSpecificBaseDirectories
[i
]);
777 const char* placeholder
= strchr(basePath
, '%');
778 if (placeholder
== NULL
)
781 // match the part up to the architecture placeholder
782 size_t prefixLength
= placeholder
- basePath
;
783 if (strncmp(relativePath
, basePath
, prefixLength
) != 0
784 || relativePath
[prefixLength
] != '/') {
788 // match the architecture
789 const char* architecturePart
= relativePath
+ prefixLength
+ 1;
790 for (size_t k
= 0; k
< secondaryArchitectureCount
; k
++) {
791 const char* architecture
= secondaryArchitectures
[k
];
792 size_t architectureLength
= strlen(architecture
);
793 if (strncmp(architecturePart
, architecture
, architectureLength
) == 0
794 && (architecturePart
[architectureLength
] == '/'
795 || architecturePart
[architectureLength
] == '\0')) {
805 B_DEFINE_WEAK_ALIAS(__find_path
, find_path
);
806 B_DEFINE_WEAK_ALIAS(__find_path_etc
, find_path_etc
);
807 B_DEFINE_WEAK_ALIAS(__find_path_for_path
, find_path_for_path
);
808 B_DEFINE_WEAK_ALIAS(__find_path_for_path_etc
, find_path_for_path_etc
);
809 B_DEFINE_WEAK_ALIAS(__find_paths
, find_paths
);
810 B_DEFINE_WEAK_ALIAS(__find_paths_etc
, find_paths_etc
);