1/* 2 * Copyright 2005-2008, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6/*! Emulation BeOS-style attributes by mapping them to untyped attributes of 7 the host platform (xattr on Linux, extattr on FreeBSD). 8*/ 9 10 11#ifdef BUILDING_FS_SHELL 12# include "compat.h" 13# define B_OK 0 14# define B_BAD_VALUE EINVAL 15# define B_FILE_ERROR EBADF 16# define B_ERROR EINVAL 17# define B_ENTRY_NOT_FOUND ENOENT 18# define B_NO_MEMORY ENOMEM 19#else 20# include <BeOSBuildCompatibility.h> 21# include <syscalls.h> 22 23# include "fs_impl.h" 24# include "fs_descriptors.h" 25#endif 26 27#include <dirent.h> 28#include <errno.h> 29#include <fcntl.h> 30#include <stdio.h> 31#include <stdlib.h> 32#include <unistd.h> 33#include <sys/stat.h> 34 35#include <map> 36#include <string> 37 38#include <fs_attr.h> 39 40 41// Include the interface to the host platform attributes support. 42#if defined(HAIKU_HOST_PLATFORM_LINUX) 43# include "fs_attr_xattr.h" 44#elif defined(HAIKU_HOST_PLATFORM_FREEBSD) 45# include "fs_attr_extattr.h" 46#elif defined(HAIKU_HOST_PLATFORM_DARWIN) 47# include "fs_attr_bsdxattr.h" 48#else 49# error No attribute support for this host platform! 50#endif 51 52 53namespace BPrivate {} 54using namespace BPrivate; 55using std::map; 56using std::string; 57 58// the maximum length of an attribute listing we support 59static const size_t kMaxAttributeListingLength = 10240; 60 61// the maximum attribute length we support 62static const size_t kMaxAttributeLength = 10240 * 4; 63 64 65// mangle_attribute_name 66static string 67mangle_attribute_name(const char* name) 68{ 69 // prepend our xattr namespace and translate: 70 // '/' -> "%\" 71 // '%' -> "%%" 72 73 string mangledName = kAttributeNamespace; 74 for (int i = 0; name[i] != '\0'; i++) { 75 char c = name[i]; 76 switch (c) { 77 case '/': 78 mangledName += "%\\"; 79 break; 80 case '%': 81 mangledName += "%%"; 82 break; 83 default: 84 mangledName += c; 85 break; 86 } 87 } 88 return mangledName; 89} 90 91 92// demangle_attribute_name 93static bool 94demangle_attribute_name(const char* name, string& demangledName) 95{ 96 // chop of our xattr namespace and translate: 97 // "%\" -> '/' 98 // "%%" -> '%' 99 100 if (strncmp(name, kAttributeNamespace, kAttributeNamespaceLen) != 0) 101 return false; 102 103 name += kAttributeNamespaceLen; 104 105 demangledName = ""; 106 107 for (int i = 0; name[i] != '\0'; i++) { 108 char c = name[i]; 109 if (c == '%') { 110 c = name[++i]; 111 if (c == '%') 112 demangledName += c; 113 else if (c == '\\') 114 demangledName += '/'; 115 else 116 return false; 117 } else 118 demangledName += c; 119 } 120 121 return true; 122} 123 124 125namespace { 126 127class AttributeDirectory; 128 129typedef map<DIR*, AttributeDirectory*> AttrDirMap; 130static AttrDirMap sAttributeDirectories; 131 132// LongDirent 133struct LongDirent : dirent { 134 char name[B_FILE_NAME_LENGTH]; 135}; 136 137// AttributeHeader 138struct AttributeHeader { 139 uint32 type; 140}; 141 142// AttributeDirectory 143class AttributeDirectory { 144public: 145 AttributeDirectory() 146 : fFileFD(-1), 147 fFakeDir(NULL), 148 fListing(NULL), 149 fListingLength(-1), 150 fListingIndex(0) 151 { 152 } 153 154 ~AttributeDirectory() 155 { 156 if (fFileFD >= 0) 157 close(fFileFD); 158 159 if (fFakeDir) { 160 AttrDirMap::iterator it = sAttributeDirectories.find(fFakeDir); 161 if (it != sAttributeDirectories.end()) 162 sAttributeDirectories.erase(it); 163 164 closedir(fFakeDir); 165 } 166 167 free(fListing); 168 } 169 170 static AttributeDirectory* Get(DIR* dir) 171 { 172 AttrDirMap::iterator it = sAttributeDirectories.find(dir); 173 if (it == sAttributeDirectories.end()) 174 return NULL; 175 return it->second; 176 } 177 178 status_t Init(const char* path, int fileFD) 179 { 180 // open a fake DIR 181 if (!fFakeDir) { 182 fFakeDir = opendir("."); 183 if (!fFakeDir) 184 return B_ERROR; 185 186 sAttributeDirectories[fFakeDir] = this; 187 } 188 189 string tempPath; 190 if (!path) { 191 // We've got no path. If the file descriptor is one of our own and 192 // not a system FD, we need to get a path for it. 193 Descriptor* descriptor = get_descriptor(fileFD); 194 if (descriptor && !descriptor->IsSystemFD()) { 195 status_t error = descriptor->GetPath(tempPath); 196 if (error != B_OK) 197 return error; 198 path = tempPath.c_str(); 199 fileFD = -1; 200 } 201 } 202 203 if (path) { 204 // A path was given -- check, if it's a symlink. If so we need to 205 // keep the path, otherwise we open a FD. 206 struct stat st; 207 if (lstat(path, &st)) 208 return B_ENTRY_NOT_FOUND; 209 210 if (S_ISLNK(st.st_mode)) { 211 fFileFD = -1; 212 fPath = path; 213 } else { 214 fFileFD = open(path, O_RDONLY); 215 if (fFileFD < 0) 216 return errno; 217 fPath = ""; 218 } 219 } else { 220 // FD was given -- dup it. 221 fFileFD = dup(fileFD); 222 if (fFileFD < 0) 223 return errno; 224 fPath = ""; 225 } 226 227 fListingLength = -1; 228 fListingIndex = 0; 229 230 return B_OK; 231 } 232 233 DIR* FakeDir() const { return fFakeDir; } 234 235 status_t ReadDir(struct dirent** _entry) 236 { 237 // make sure we have a current listing 238 status_t error = _CheckListing(); 239 if (error != B_OK) 240 return error; 241 242 // keep going until we find an entry or hit the end of dir 243 while (fListingIndex < fListingLength) { 244 // get next entry name 245 const char* name = fListing + fListingIndex; 246 int nameLen = strlen(name); 247 fListingIndex += nameLen + 1; 248 249 // check the attribute namespace 250 string demangledName; 251 if (!demangle_attribute_name(name, demangledName)) 252 continue; 253 name = demangledName.c_str(); 254 nameLen = demangledName.length(); 255 256 if (nameLen == 0) { 257 // Uh, weird attribute. 258 return B_ERROR; 259 } 260 261 // prepare the dirent 262 strcpy(fDirent.d_name, name); 263 fDirent.d_ino = 0; 264// TODO: We need the node ID! 265 266 *_entry = &fDirent; 267 return B_OK; 268 } 269 270 // end of dir 271 *_entry = NULL; 272 return B_OK; 273 } 274 275 void RewindDir() 276 { 277 fListingIndex = 0; 278 } 279 280private: 281 status_t _CheckListing() 282 { 283 if (fListing && fListingLength >= 0) 284 return B_OK; 285 286 char listing[kMaxAttributeListingLength]; 287 288 // get the listing 289 ssize_t length = list_attributes(fFileFD, fPath.c_str(), listing, 290 kMaxAttributeListingLength); 291 if (length < 0) 292 return errno; 293 294 // clone the on-stack listing 295 char* newListing = (char*)realloc(fListing, length); 296 if (!newListing) 297 return B_NO_MEMORY; 298 memcpy(newListing, listing, length); 299 300 fListing = newListing; 301 fListingLength = length; 302 fListingIndex = 0; 303 304 return B_OK; 305 } 306 307private: 308 int fFileFD; 309 string fPath; 310 DIR* fFakeDir; 311 LongDirent fDirent; 312 char* fListing; 313 int fListingLength; 314 int fListingIndex; 315}; 316 317// LocalFD 318class LocalFD { 319public: 320 LocalFD() 321 { 322 } 323 324 ~LocalFD() 325 { 326 } 327 328 status_t Init(int fd) 329 { 330#ifndef BUILDING_FS_SHELL 331 Descriptor* descriptor = get_descriptor(fd); 332 if (descriptor && !descriptor->IsSystemFD()) { 333 // we need to get a path 334 fFD = -1; 335 return descriptor->GetPath(fPath); 336 } 337#endif 338 339 fFD = fd; 340 fPath = ""; 341 return B_OK; 342 } 343 344 int FD() const 345 { 346 return fFD; 347 } 348 349 const char* Path() const 350 { 351 return (fFD < 0 ? fPath.c_str() : NULL); 352 } 353 354 bool IsSymlink() const 355 { 356 struct stat st; 357 int result; 358 if (Path()) 359 result = lstat(Path(), &st); 360 else 361 result = fstat(fFD, &st); 362 363 return (result == 0 && S_ISLNK(st.st_mode)); 364 } 365 366private: 367 string fPath; 368 int fFD; 369}; 370 371} // unnamed namspace 372 373 374// # pragma mark - Public API 375 376 377// fs_open_attr_dir 378DIR * 379fs_open_attr_dir(const char *path) 380{ 381 AttributeDirectory* attrDir = new AttributeDirectory; 382 383 status_t error = attrDir->Init(path, -1); 384 if (error != B_OK) { 385 errno = error; 386 delete attrDir; 387 return NULL; 388 } 389 390 return attrDir->FakeDir(); 391} 392 393// fs_fopen_attr_dir 394DIR * 395fs_fopen_attr_dir(int fd) 396{ 397 AttributeDirectory* attrDir = new AttributeDirectory; 398 399 status_t error = attrDir->Init(NULL, fd); 400 if (error != B_OK) { 401 errno = error; 402 delete attrDir; 403 return NULL; 404 } 405 406 return attrDir->FakeDir(); 407} 408 409// fs_close_attr_dir 410int 411fs_close_attr_dir(DIR *dir) 412{ 413 AttributeDirectory* attrDir = AttributeDirectory::Get(dir); 414 if (!attrDir) { 415 errno = B_BAD_VALUE; 416 return -1; 417 } 418 419 delete attrDir; 420 return 0; 421} 422 423// fs_read_attr_dir 424struct dirent * 425fs_read_attr_dir(DIR *dir) 426{ 427 // get attr dir 428 AttributeDirectory* attrDir = AttributeDirectory::Get(dir); 429 if (!attrDir) { 430 errno = B_BAD_VALUE; 431 return NULL; 432 } 433 434 // read attr dir 435 dirent* entry = NULL; 436 status_t error = attrDir->ReadDir(&entry); 437 if (error != B_OK) { 438 errno = error; 439 return NULL; 440 } 441 442 return entry; 443} 444 445// fs_rewind_attr_dir 446void 447fs_rewind_attr_dir(DIR *dir) 448{ 449 // get attr dir 450 AttributeDirectory* attrDir = AttributeDirectory::Get(dir); 451 if (attrDir) 452 attrDir->RewindDir(); 453} 454 455// fs_fopen_attr 456int 457fs_fopen_attr(int fd, const char *attribute, uint32 type, int openMode) 458{ 459 if (fd < 0) { 460 errno = B_BAD_VALUE; 461 return -1; 462 } 463 464 AttributeDescriptor* descriptor = new(std::nothrow) AttributeDescriptor(fd, 465 attribute, type, openMode); 466 if (descriptor == NULL) { 467 errno = B_NO_MEMORY; 468 return -1; 469 } 470 471 status_t error = descriptor->Init(); 472 if (error != B_OK) { 473 delete descriptor; 474 errno = error; 475 return -1; 476 } 477 478 int attributeFD = add_descriptor(descriptor); 479 if (attributeFD < 0) { 480 delete descriptor; 481 errno = B_NO_MEMORY; 482 return -1; 483 } 484 485 return attributeFD; 486} 487 488// fs_close_attr 489int 490fs_close_attr(int fd) 491{ 492 status_t error = delete_descriptor(fd); 493 if (error != 0) { 494 errno = error; 495 return -1; 496 } 497 498 return 0; 499} 500 501// fs_read_attr 502ssize_t 503fs_read_attr(int fd, const char *_attribute, uint32 type, off_t pos, 504 void *buffer, size_t readBytes) 505{ 506 // check params 507 if (pos < 0 || pos + readBytes > kMaxAttributeLength 508 || !_attribute || !buffer) { 509 errno = B_BAD_VALUE; 510 return -1; 511 } 512 513 // resolve FD 514 LocalFD localFD; 515 status_t error = localFD.Init(fd); 516 if (error != B_OK) { 517 errno = error; 518 return -1; 519 } 520 521 // mangle the attribute name 522 string attribute = mangle_attribute_name(_attribute); 523 524 // read the attribute 525 char attributeBuffer[sizeof(AttributeHeader) + kMaxAttributeLength]; 526 ssize_t bytesRead = min_c((size_t)kMaxAttributeLength, readBytes) 527 + sizeof(AttributeHeader); 528 if (localFD.Path()) { 529 bytesRead = get_attribute(-1, localFD.Path(), attribute.c_str(), 530 attributeBuffer, bytesRead); 531 } else { 532 bytesRead = get_attribute(localFD.FD(), NULL, attribute.c_str(), 533 attributeBuffer, bytesRead); 534 } 535 if (bytesRead < 0) { 536 // Make sure, the error code is B_ENTRY_NOT_FOUND, if the attribute 537 // doesn't exist. 538 if (errno == ENOATTR || errno == ENODATA) 539 errno = B_ENTRY_NOT_FOUND; 540 return -1; 541 } 542 543 // check length for sanity 544 if ((size_t)bytesRead < sizeof(AttributeHeader)) { 545 fprintf(stderr, "fs_read_attr(): attribute \"%s\" is shorter than the " 546 "AttributeHeader!\n", attribute.c_str()); 547 errno = B_ERROR; 548 return -1; 549 } 550 551 // copy the result into the provided buffer 552 bytesRead -= sizeof(AttributeHeader); 553 if (bytesRead > pos) { 554 memcpy(buffer, attributeBuffer + sizeof(AttributeHeader) + pos, 555 bytesRead - pos); 556 } else 557 bytesRead = 0; 558 559 return bytesRead; 560} 561 562// fs_write_attr 563ssize_t 564fs_write_attr(int fd, const char *_attribute, uint32 type, off_t pos, 565 const void *buffer, size_t writeBytes) 566{ 567 // check params 568 if (pos < 0 || pos + writeBytes > kMaxAttributeLength 569 || _attribute == NULL || (writeBytes > 0 && buffer == NULL)) { 570 errno = B_BAD_VALUE; 571 return -1; 572 } 573 574 // resolve FD 575 LocalFD localFD; 576 status_t error = localFD.Init(fd); 577 if (error != B_OK) { 578 errno = error; 579 return -1; 580 } 581 582 // mangle the attribute name 583 string attribute = mangle_attribute_name(_attribute); 584 585 // prepare an attribute buffer 586 char attributeBuffer[sizeof(AttributeHeader) + kMaxAttributeLength]; 587 AttributeHeader* header = (AttributeHeader*)attributeBuffer; 588 header->type = type; 589 memset(attributeBuffer + sizeof(AttributeHeader), 0, pos); 590 if (writeBytes > 0) { 591 memcpy(attributeBuffer + sizeof(AttributeHeader) + pos, buffer, 592 writeBytes); 593 } 594 595 // write the attribute 596 ssize_t toWrite = sizeof(AttributeHeader) + pos + writeBytes; 597 int result; 598 if (localFD.Path()) { 599 result = set_attribute(-1, localFD.Path(), attribute.c_str(), 600 attributeBuffer, toWrite); 601 } else { 602 result = set_attribute(localFD.FD(), NULL, attribute.c_str(), 603 attributeBuffer, toWrite); 604 } 605 if (result < 0) { 606 // Setting user attributes on symlinks is not allowed (xattr). So, if 607 // this is a symlink and we're only supposed to write a "BEOS:TYPE" 608 // attribute we silently pretend to have succeeded. 609 if (localFD.IsSymlink() && strcmp(_attribute, "BEOS:TYPE") == 0) 610 return writeBytes; 611 return -1; 612 } 613 614 return writeBytes; 615} 616 617// fs_remove_attr 618int 619fs_remove_attr(int fd, const char *_attribute) 620{ 621 // check params 622 if (!_attribute) { 623 errno = B_BAD_VALUE; 624 return -1; 625 } 626 627 // resolve FD 628 LocalFD localFD; 629 status_t error = localFD.Init(fd); 630 if (error != B_OK) { 631 errno = error; 632 return -1; 633 } 634 635 // mangle the attribute name 636 string attribute = mangle_attribute_name(_attribute); 637 638 // remove attribute 639 int result; 640 if (localFD.Path()) 641 result = remove_attribute(-1, localFD.Path(), attribute.c_str()); 642 else 643 result = remove_attribute(localFD.FD(), NULL, attribute.c_str()); 644 645 if (result < 0) { 646 // Make sure, the error code is B_ENTRY_NOT_FOUND, if the attribute 647 // doesn't exist. 648 if (errno == ENOATTR || errno == ENODATA) 649 errno = B_ENTRY_NOT_FOUND; 650 return -1; 651 } 652 return 0; 653} 654 655// fs_stat_attr 656int 657fs_stat_attr(int fd, const char *_attribute, struct attr_info *attrInfo) 658{ 659 if (!_attribute || !attrInfo) { 660 errno = B_BAD_VALUE; 661 return -1; 662 } 663 664 // resolve FD 665 LocalFD localFD; 666 status_t error = localFD.Init(fd); 667 if (error != B_OK) { 668 errno = error; 669 return -1; 670 } 671 672 // mangle the attribute name 673 string attribute = mangle_attribute_name(_attribute); 674 675 // read the attribute 676 char attributeBuffer[sizeof(AttributeHeader) + kMaxAttributeLength]; 677 ssize_t bytesRead = sizeof(AttributeHeader) + kMaxAttributeLength; 678 if (localFD.Path()) { 679 bytesRead = get_attribute(-1, localFD.Path(), attribute.c_str(), 680 attributeBuffer, bytesRead); 681 } else { 682 bytesRead = get_attribute(localFD.FD(), NULL, attribute.c_str(), 683 attributeBuffer, bytesRead); 684 } 685 if (bytesRead < 0) { 686 // Make sure, the error code is B_ENTRY_NOT_FOUND, if the attribute 687 // doesn't exist. 688 if (errno == ENOATTR || errno == ENODATA) 689 errno = B_ENTRY_NOT_FOUND; 690 return -1; 691 } 692 693 // check length for sanity 694 if ((size_t)bytesRead < sizeof(AttributeHeader)) { 695 fprintf(stderr, "fs_stat_attr(): attribute \"%s\" is shorter than the " 696 "AttributeHeader!\n", attribute.c_str()); 697 errno = B_ERROR; 698 return -1; 699 } 700 701 attrInfo->size = bytesRead - sizeof(AttributeHeader); 702 attrInfo->type = ((AttributeHeader*)attributeBuffer)->type; 703 704 return 0; 705} 706 707 708// #pragma mark - Private Syscalls 709 710 711#ifndef BUILDING_FS_SHELL 712 713// _kern_open_attr_dir 714int 715_kern_open_attr_dir(int fd, const char *path) 716{ 717 // get node ref for the node 718 struct stat st; 719 status_t error = _kern_read_stat(fd, path, false, &st, 720 sizeof(struct stat)); 721 if (error != B_OK) { 722 errno = error; 723 return -1; 724 } 725 NodeRef ref(st); 726 727 DIR* dir; 728 if (path) { 729 // If a path was given, get a usable path. 730 string realPath; 731 status_t error = get_path(fd, path, realPath); 732 if (error != B_OK) 733 return error; 734 735 dir = fs_open_attr_dir(realPath.c_str()); 736 } else 737 dir = fs_fopen_attr_dir(fd); 738 739 if (!dir) 740 return errno; 741 742 // create descriptor 743 AttrDirDescriptor *descriptor = new AttrDirDescriptor(dir, ref); 744 return add_descriptor(descriptor); 745} 746 747// _kern_rename_attr 748status_t 749_kern_rename_attr(int fromFile, const char *fromName, int toFile, 750 const char *toName) 751{ 752 // not supported ATM 753 return B_BAD_VALUE; 754} 755 756// _kern_remove_attr 757status_t 758_kern_remove_attr(int fd, const char *name) 759{ 760 if (!name) 761 return B_BAD_VALUE; 762 763 if (fs_remove_attr(fd, name) < 0) 764 return errno; 765 return B_OK; 766} 767 768#endif // ! BUILDING_FS_SHELL 769