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