1 2 #include <BeOSBuildCompatibility.h> 3 #include <syscalls.h> 4 5 #include <dirent.h> 6 #include <errno.h> 7 #include <fcntl.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <unistd.h> 11 #include <sys/stat.h> 12 13 #include <string> 14 15 #include <fs_attr.h> 16 17 #include "fs_impl.h" 18 #include "fs_descriptors.h" 19 20 21 // Include the interface to the host platform attributes support, if it shall be 22 // used to tag files with unique IDs to identify their attribute directory. 23 #if HAIKU_HOST_USE_XATTR_REF 24 # if defined(HAIKU_HOST_PLATFORM_LINUX) || defined(HAIKU_HOST_PLATFORM_MSYS) 25 # include "fs_attr_xattr.h" 26 # elif defined(HAIKU_HOST_PLATFORM_FREEBSD) 27 # include "fs_attr_extattr.h" 28 # elif defined(HAIKU_HOST_PLATFORM_DARWIN) 29 # include "fs_attr_bsdxattr.h" 30 # else 31 # error No attribute support for this host platform! 32 # endif 33 #endif 34 35 36 using namespace std; 37 using namespace BPrivate; 38 39 static const char *sAttributeDirBasePath = HAIKU_BUILD_ATTRIBUTES_DIR; 40 41 #if HAIKU_HOST_USE_XATTR_REF 42 static const char* const kIDAttributeName = "id"; 43 #endif 44 45 46 // init_attribute_dir_base_dir 47 static status_t 48 init_attribute_dir_base_dir() 49 { 50 static bool initialized = false; 51 static status_t initError; 52 53 if (initialized) 54 return initError; 55 56 // stat the dir 57 struct stat st; 58 initError = B_OK; 59 if (lstat(sAttributeDirBasePath, &st) == 0) { 60 if (!S_ISDIR(st.st_mode)) { 61 // the attribute dir base dir is no directory 62 fprintf(stderr, "init_attribute_dir_base_dir(): The Attribute " 63 "directory base directory exists, but is no directory!\n"); 64 initError = B_FILE_ERROR; 65 } 66 67 } else { 68 // doesn't exist yet: create it 69 if (mkdir(sAttributeDirBasePath, S_IRWXU | S_IRWXG | S_IRWXO) < 0) 70 initError = errno; 71 } 72 73 initialized = true; 74 return initError; 75 } 76 77 // escape_attr_name 78 static string 79 escape_attr_name(const char *name) 80 { 81 string escapedName("_"); 82 while (*name != '\0') { 83 // we replace '/' with "_s" and '_' with "__" 84 if (*name == '/') 85 escapedName += "_s"; 86 else if (*name == '_') 87 escapedName += "__"; 88 else 89 escapedName += *name; 90 91 name++; 92 } 93 94 return escapedName; 95 } 96 97 // deescape_attr_name 98 static string 99 deescape_attr_name(const char *name) 100 { 101 if (name[0] != '_') { 102 debugger("deescape_attr_name(): name doesn't start with '_'!\n"); 103 return "___"; 104 } 105 name++; 106 107 string deescapedName; 108 while (*name != '\0') { 109 if (*name == '_') { 110 name++; 111 if (*name == 's') { 112 deescapedName += '/'; 113 } else if (*name == '_') { 114 deescapedName += '_'; 115 } else { 116 debugger("deescape_attr_name(): name contains invalid escaped " 117 "sequence!\n"); 118 name--; 119 } 120 } else 121 deescapedName += *name; 122 123 name++; 124 } 125 126 return deescapedName; 127 } 128 129 130 #if HAIKU_HOST_USE_XATTR_REF 131 132 133 static status_t 134 make_unique_node_id(string& _id) 135 { 136 // open random device 137 int fd = open("/dev/urandom", O_RDONLY); 138 if (fd < 0) { 139 fd = open("/dev/random", O_RDONLY); 140 if (fd < 0) 141 return B_NOT_SUPPORTED; 142 } 143 144 // read bytes 145 uint8 buffer[16]; 146 ssize_t bytesRead = read(fd, buffer, sizeof(buffer)); 147 status_t error = B_OK; 148 if (bytesRead < 0) 149 error = errno; 150 close(fd); 151 152 if (error != B_OK) 153 return error; 154 155 if (bytesRead != (ssize_t)sizeof(buffer)) 156 error = B_ERROR; 157 158 // convert to hex string 159 static const char* const kHexChars = "0123456789abcdef"; 160 _id.clear(); 161 for (size_t i = 0; i < sizeof(buffer); i++) { 162 _id += kHexChars[buffer[i] >> 4]; 163 _id += kHexChars[buffer[i] & 0xf]; 164 } 165 166 return B_OK; 167 } 168 169 170 static status_t 171 get_id_attribute(const char *path, int fd, string& _id) 172 { 173 // list_attributes() and remove_attribute() are unused here -- prevent the 174 // warning 175 (void)list_attributes; 176 (void)remove_attribute; 177 178 string attributeName(kAttributeNamespace); 179 attributeName += kIDAttributeName; 180 181 char buffer[64]; 182 ssize_t bytesRead = get_attribute(fd, path, attributeName.c_str(), buffer, 183 sizeof(buffer)); 184 if (bytesRead < 0) { 185 // On Linux only priviledged users are allowed to set attributes on 186 // symlinks. So, if this is a symlink, we don't even try and instead 187 // construct a symlink specific node ID. 188 status_t error = errno; 189 struct stat st; 190 if (path == NULL || lstat(path, &st) < 0 || !S_ISLNK(st.st_mode)) 191 return error; 192 193 char buffer[32]; 194 snprintf(buffer, sizeof(buffer), "symlink-%" B_PRIdINO, st.st_ino); 195 _id = buffer; 196 return B_OK; 197 } 198 199 _id = string(buffer, bytesRead); 200 return B_OK; 201 } 202 203 204 static status_t 205 set_id_attribute(const char *path, int fd, const char* id) 206 { 207 string attributeName(kAttributeNamespace); 208 attributeName += kIDAttributeName; 209 210 if (set_attribute(fd, path, attributeName.c_str(), id, strlen(id)) < 0) 211 return errno; 212 return B_OK; 213 } 214 215 216 static string 217 get_attribute_dir_path(NodeRef ref, const char *path, int fd) 218 { 219 string id; 220 status_t error = get_id_attribute(path, fd, id); 221 if (error != B_OK) 222 id = "_no_attributes_"; 223 224 string attrDirPath(sAttributeDirBasePath); 225 attrDirPath += '/'; 226 attrDirPath += id; 227 return attrDirPath; 228 } 229 230 231 static status_t 232 get_attribute_dir_path_needed(NodeRef ref, const char *path, int fd, 233 string& _attrDirPath) 234 { 235 string id; 236 status_t error = get_id_attribute(path, fd, id); 237 if (error != B_OK) { 238 error = make_unique_node_id(id); 239 if (error != B_OK) 240 return error; 241 242 error = set_id_attribute(path, fd, id.c_str()); 243 if (error != B_OK) 244 return error; 245 } 246 247 _attrDirPath = sAttributeDirBasePath; 248 _attrDirPath += '/'; 249 _attrDirPath += id; 250 return B_OK; 251 } 252 253 254 #else 255 256 257 static string 258 get_attribute_dir_path(NodeRef ref, const char *path, int fd) 259 { 260 string attrDirPath(sAttributeDirBasePath); 261 char buffer[32]; 262 sprintf(buffer, "/%" B_PRIdINO, ref.node); 263 attrDirPath += buffer; 264 return attrDirPath; 265 } 266 267 268 static status_t 269 get_attribute_dir_path_needed(NodeRef ref, const char *path, int fd, 270 string& _attrDirPath) 271 { 272 _attrDirPath = get_attribute_dir_path(ref, path, fd); 273 return B_OK; 274 } 275 276 277 #endif 278 279 280 // ensure_attribute_dir_exists 281 static status_t 282 ensure_attribute_dir_exists(NodeRef ref, const char *path, int fd) 283 { 284 // init the base directory and get the attribute directory path 285 status_t error = init_attribute_dir_base_dir(); 286 if (error != B_OK) 287 return error; 288 289 string attrDirPath; 290 error = get_attribute_dir_path_needed(ref, path, fd, attrDirPath); 291 if (error != B_OK) 292 return error; 293 294 // stat the dir 295 struct stat st; 296 if (lstat(attrDirPath.c_str(), &st) == 0) { 297 if (!S_ISDIR(st.st_mode)) { 298 // the attribute dir is no directory 299 fprintf(stderr, "ensure_attribute_dir_exists(): Attribute " 300 "directory for node %lld exists, but is no directory!\n", 301 (long long)ref.node); 302 return B_FILE_ERROR; 303 } 304 305 return B_OK; 306 } 307 308 // doesn't exist yet: create it 309 if (mkdir(attrDirPath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) < 0) 310 return errno; 311 312 return B_OK; 313 } 314 315 // open_attr_dir 316 static DIR * 317 open_attr_dir(NodeRef ref, const char *path, int fd) 318 { 319 // make sure the directory exists 320 status_t error = ensure_attribute_dir_exists(ref, path, fd); 321 if (error != B_OK) { 322 errno = error; 323 return NULL; 324 } 325 326 // open it 327 string dirPath(get_attribute_dir_path(ref, path, fd)); 328 return opendir(dirPath.c_str()); 329 } 330 331 // get_attribute_path 332 static status_t 333 get_attribute_path(NodeRef ref, const char *path, int fd, 334 const char *attribute, string &attrPath, string &typePath) 335 { 336 if (!attribute || strlen(attribute) == 0) 337 return B_BAD_VALUE; 338 339 // make sure the attribute dir for the node exits 340 status_t error = ensure_attribute_dir_exists(ref, path, fd); 341 if (error != B_OK) { 342 errno = error; 343 return -1; 344 } 345 346 // construct the attribute path 347 attrPath = get_attribute_dir_path(ref, path, fd) + '/'; 348 string attrName(escape_attr_name(attribute)); 349 typePath = attrPath + "t" + attrName; 350 attrPath += attrName; 351 352 return B_OK; 353 } 354 355 356 // get_attribute_path_virtual_fd 357 static status_t 358 get_attribute_path_virtual_fd(int fd, const char *attribute, string &attrPath, 359 string &typePath) 360 { 361 // stat the file to get a NodeRef 362 struct stat st; 363 status_t error = _kern_read_stat(fd, NULL, false, &st, sizeof(st)); 364 if (error != B_OK) 365 return error; 366 NodeRef ref(st); 367 368 // Try to get a path. If we can't get a path, this is must be a "real" 369 // (i.e. system) file descriptor, which is just as well. 370 string path; 371 bool pathValid = (get_path(fd, NULL, path) == B_OK); 372 373 // get the attribute path 374 return get_attribute_path(ref, (pathValid ? path.c_str() : NULL), 375 (pathValid ? -1 : fd), attribute, attrPath, typePath); 376 } 377 378 379 // get_attribute_path 380 static status_t 381 get_attribute_path(int fd, const char *attribute, string &attrPath, 382 string &typePath) 383 { 384 if (get_descriptor(fd)) { 385 // This is a virtual file descriptor -- we have a special function 386 // for handling it. 387 return get_attribute_path_virtual_fd(fd, attribute, attrPath, 388 typePath); 389 } else { 390 // This is a real (i.e. system) file descriptor -- fstat() it and 391 // build the path. 392 393 // stat the file to get a NodeRef 394 struct stat st; 395 if (fstat(fd, &st) < 0) 396 return errno; 397 NodeRef ref(st); 398 399 return get_attribute_path(ref, NULL, fd, attribute, attrPath, typePath); 400 } 401 } 402 403 404 // # pragma mark - Public API 405 406 407 // fs_open_attr_dir 408 DIR * 409 fs_open_attr_dir(const char *path) 410 { 411 struct stat st; 412 if (lstat(path, &st)) 413 return NULL; 414 415 return open_attr_dir(NodeRef(st), path, -1); 416 } 417 418 // fs_fopen_attr_dir 419 DIR * 420 fs_fopen_attr_dir(int fd) 421 { 422 struct stat st; 423 424 status_t error = _kern_read_stat(fd, NULL, false, &st, 425 sizeof(struct stat)); 426 if (error != B_OK) { 427 errno = error; 428 return NULL; 429 } 430 431 // Try to get a path. If we can't get a path, this is must be a "real" 432 // (i.e. system) file descriptor, which is just as well. 433 string path; 434 bool pathValid = (get_path(fd, NULL, path) == B_OK); 435 436 // get the attribute path 437 return open_attr_dir(NodeRef(st), (pathValid ? path.c_str() : NULL), 438 (pathValid ? -1 : fd)); 439 } 440 441 // fs_close_attr_dir 442 int 443 fs_close_attr_dir(DIR *dir) 444 { 445 return closedir(dir); 446 } 447 448 // fs_read_attr_dir 449 struct dirent * 450 fs_read_attr_dir(DIR *dir) 451 { 452 struct dirent *entry = NULL; 453 while (true) { 454 // read the next entry 455 entry = readdir(dir); 456 if (!entry) 457 return NULL; 458 459 // ignore administrative entries; the 460 if (entry->d_name[0] == '_') { 461 string attrName = deescape_attr_name(entry->d_name); 462 strcpy(entry->d_name, attrName.c_str()); 463 return entry; 464 } 465 } 466 } 467 468 // fs_rewind_attr_dir 469 void 470 fs_rewind_attr_dir(DIR *dir) 471 { 472 rewinddir(dir); 473 } 474 475 // fs_fopen_attr 476 int 477 fs_fopen_attr(int fd, const char *attribute, uint32 type, int openMode) 478 { 479 if (!attribute) { 480 errno = B_BAD_VALUE; 481 return -1; 482 } 483 484 // get the attribute path 485 string attrPath; 486 string typePath; 487 status_t error = get_attribute_path(fd, attribute, attrPath, typePath); 488 if (error != B_OK) { 489 errno = error; 490 return -1; 491 } 492 493 // check, if the attribute already exists 494 struct stat st; 495 bool exists = (lstat(attrPath.c_str(), &st) == 0); 496 497 // open the attribute 498 int attrFD = open(attrPath.c_str(), openMode, S_IRWXU | S_IRWXG | S_IRWXO); 499 if (attrFD < 0) 500 return -1; 501 502 // set the type, if the attribute didn't exist yet 503 if (!exists) { 504 // create a file prefixed "t" 505 int typeFD = creat(typePath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO); 506 if (typeFD >= 0) { 507 // write the type into the file 508 if (write(typeFD, &type, sizeof(type)) < 0) 509 error = errno; 510 511 close(typeFD); 512 513 } else 514 error = errno; 515 516 // remove type and attribute file, if something went wrong 517 if (error != B_OK) { 518 if (typeFD > 0) { 519 unlink(typePath.c_str()); 520 } 521 522 close(attrFD); 523 unlink(attrPath.c_str()); 524 525 errno = error; 526 return -1; 527 } 528 } 529 530 return attrFD; 531 } 532 533 // fs_close_attr 534 int 535 fs_close_attr(int fd) 536 { 537 return close(fd); 538 } 539 540 // fs_read_attr 541 ssize_t 542 fs_read_attr(int fd, const char *attribute, uint32 type, off_t pos, 543 void *buffer, size_t readBytes) 544 { 545 // open the attribute 546 int attrFD = fs_fopen_attr(fd, attribute, type, O_RDONLY); 547 if (attrFD < 0) 548 return attrFD; 549 550 // read 551 ssize_t bytesRead = read_pos(attrFD, pos, buffer, readBytes); 552 status_t error = errno; 553 554 // close the attribute 555 fs_close_attr(attrFD); 556 557 if (bytesRead < 0) { 558 errno = error; 559 return -1; 560 } 561 562 return bytesRead; 563 } 564 565 // fs_write_attr 566 ssize_t 567 fs_write_attr(int fd, const char *attribute, uint32 type, off_t pos, 568 const void *buffer, size_t readBytes) 569 { 570 // open the attribute 571 int attrFD = fs_fopen_attr(fd, attribute, type, 572 O_WRONLY | O_CREAT | O_TRUNC); 573 if (attrFD < 0) { 574 // Setting user attributes on symlinks is not allowed (xattr). So, if 575 // this is a symlink and we're only supposed to write a "BEOS:TYPE" 576 // attribute we silently pretend to have succeeded. 577 struct stat st; 578 if (strcmp(attribute, "BEOS:TYPE") == 0 && fstat(fd, &st) == 0 579 && S_ISLNK(st.st_mode)) { 580 return readBytes; 581 } 582 return attrFD; 583 } 584 585 // read 586 ssize_t bytesWritten = write_pos(attrFD, pos, buffer, readBytes); 587 status_t error = errno; 588 589 // close the attribute 590 fs_close_attr(attrFD); 591 592 if (bytesWritten < 0) { 593 errno = error; 594 return -1; 595 } 596 597 return bytesWritten; 598 } 599 600 // fs_remove_attr 601 int 602 fs_remove_attr(int fd, const char *attribute) 603 { 604 if (!attribute) { 605 errno = B_BAD_VALUE; 606 return -1; 607 } 608 609 // get the attribute path 610 string attrPath; 611 string typePath; 612 status_t error = get_attribute_path(fd, attribute, attrPath, typePath); 613 if (error != B_OK) { 614 errno = error; 615 return -1; 616 } 617 618 // remove the attribute 619 if (unlink(attrPath.c_str()) < 0) 620 return -1; 621 622 unlink(typePath.c_str()); 623 624 return B_OK; 625 } 626 627 // fs_stat_attr 628 int 629 fs_stat_attr(int fd, const char *attribute, struct attr_info *attrInfo) 630 { 631 if (!attribute || !attrInfo) { 632 errno = B_BAD_VALUE; 633 return -1; 634 } 635 636 // get the attribute path 637 string attrPath; 638 string typePath; 639 status_t error = get_attribute_path(fd, attribute, attrPath, typePath); 640 if (error != B_OK) { 641 errno = error; 642 return -1; 643 } 644 645 // stat the attribute file to get the size of the attribute 646 struct stat st; 647 if (lstat(attrPath.c_str(), &st) < 0) 648 return -1; 649 650 attrInfo->size = st.st_size; 651 652 // now open the attribute type file and read the attribute's type 653 int typeFD = open(typePath.c_str(), O_RDONLY); 654 if (typeFD < 0) 655 return -1; 656 657 ssize_t bytesRead = read(typeFD, &attrInfo->type, sizeof(attrInfo->type)); 658 if (bytesRead < 0) 659 error = errno; 660 else if (bytesRead < (ssize_t)sizeof(attrInfo->type)) 661 error = B_FILE_ERROR; 662 663 close(typeFD); 664 665 // fail on error 666 if (error != B_OK) { 667 errno = error; 668 return -1; 669 } 670 671 return 0; 672 } 673 674 675 // #pragma mark - Private Syscalls 676 677 678 // _kern_open_attr_dir 679 int 680 _kern_open_attr_dir(int fd, const char *path) 681 { 682 // get node ref for the node 683 struct stat st; 684 status_t error = _kern_read_stat(fd, path, false, &st, 685 sizeof(struct stat)); 686 if (error != B_OK) { 687 errno = error; 688 return -1; 689 } 690 NodeRef ref(st); 691 692 // If a path was given, get a usable path. 693 string realPath; 694 if (path) { 695 error = get_path(fd, path, realPath); 696 if (error != B_OK) 697 return error; 698 } 699 700 // open the attr dir 701 DIR *dir = open_attr_dir(ref, (path ? realPath.c_str() : NULL), 702 (path ? -1 : fd)); 703 if (!dir) 704 return errno; 705 706 // create descriptor 707 AttrDirDescriptor *descriptor = new AttrDirDescriptor(dir, ref); 708 return add_descriptor(descriptor); 709 } 710 711 // _kern_rename_attr 712 status_t 713 _kern_rename_attr(int fromFile, const char *fromName, int toFile, 714 const char *toName) 715 { 716 if (!fromName || !toName) 717 return B_BAD_VALUE; 718 719 // get the attribute paths 720 string fromAttrPath; 721 string fromTypePath; 722 status_t error = get_attribute_path_virtual_fd(fromFile, fromName, 723 fromAttrPath, fromTypePath); 724 if (error != B_OK) 725 return error; 726 727 string toAttrPath; 728 string toTypePath; 729 error = get_attribute_path_virtual_fd(toFile, toName, toAttrPath, 730 toTypePath); 731 if (error != B_OK) 732 return error; 733 734 // rename the attribute and type files 735 if (rename(fromAttrPath.c_str(), toAttrPath.c_str()) < 0) 736 return errno; 737 738 if (rename(fromTypePath.c_str(), toTypePath.c_str()) < 0) { 739 // renaming the type file failed: try to rename back the attribute file 740 error = errno; 741 742 rename(toAttrPath.c_str(), fromAttrPath.c_str()); 743 744 return error; 745 } 746 747 return B_OK; 748 } 749 750 // _kern_remove_attr 751 status_t 752 _kern_remove_attr(int fd, const char *name) 753 { 754 if (!name) 755 return B_BAD_VALUE; 756 757 // get the attribute path 758 string attrPath; 759 string typePath; 760 status_t error = get_attribute_path_virtual_fd(fd, name, attrPath, 761 typePath); 762 if (error != B_OK) 763 return error; 764 765 // remove the attribute 766 if (unlink(attrPath.c_str()) < 0) 767 return errno; 768 769 unlink(typePath.c_str()); 770 771 return B_OK; 772 } 773 774 775 // __get_attribute_dir_path 776 extern "C" bool __get_attribute_dir_path(const struct stat* st, 777 const char* path, char* buffer); 778 bool 779 __get_attribute_dir_path(const struct stat* st, const char* path, char* buffer) 780 { 781 NodeRef ref(*st); 782 string dirPath = get_attribute_dir_path(ref, path, -1); 783 strcpy(buffer, dirPath.c_str()); 784 return true; 785 } 786