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// LongDirEntry 133struct LongDirEntry { 134 char _[sizeof(struct dirent) + B_FILE_NAME_LENGTH + 1]; 135 struct dirent* dirent() { return (struct dirent*)_; } 136}; 137 138// AttributeHeader 139struct AttributeHeader { 140 uint32 type; 141}; 142 143// AttributeDirectory 144class AttributeDirectory { 145public: 146 AttributeDirectory() 147 : fFileFD(-1), 148 fFakeDir(NULL), 149 fListing(NULL), 150 fListingLength(-1), 151 fListingIndex(0) 152 { 153 } 154 155 ~AttributeDirectory() 156 { 157 if (fFileFD >= 0) 158 close(fFileFD); 159 160 if (fFakeDir) { 161 AttrDirMap::iterator it = sAttributeDirectories.find(fFakeDir); 162 if (it != sAttributeDirectories.end()) 163 sAttributeDirectories.erase(it); 164 165 closedir(fFakeDir); 166 } 167 168 free(fListing); 169 } 170 171 static AttributeDirectory* Get(DIR* dir) 172 { 173 AttrDirMap::iterator it = sAttributeDirectories.find(dir); 174 if (it == sAttributeDirectories.end()) 175 return NULL; 176 return it->second; 177 } 178 179 status_t Init(const char* path, int fileFD) 180 { 181 // open a fake DIR 182 if (!fFakeDir) { 183 fFakeDir = opendir("."); 184 if (!fFakeDir) 185 return B_ERROR; 186 187 sAttributeDirectories[fFakeDir] = this; 188 } 189 190 string tempPath; 191 if (!path) { 192 // We've got no path. If the file descriptor is one of our own and 193 // not a system FD, we need to get a path for it. 194 Descriptor* descriptor = get_descriptor(fileFD); 195 if (descriptor && !descriptor->IsSystemFD()) { 196 status_t error = descriptor->GetPath(tempPath); 197 if (error != B_OK) 198 return error; 199 path = tempPath.c_str(); 200 fileFD = -1; 201 } 202 } 203 204 if (path) { 205 // A path was given -- check, if it's a symlink. If so we need to 206 // keep the path, otherwise we open a FD. 207 struct stat st; 208 if (lstat(path, &st)) 209 return B_ENTRY_NOT_FOUND; 210 211 if (S_ISLNK(st.st_mode)) { 212 fFileFD = -1; 213 fPath = path; 214 } else { 215 fFileFD = open(path, O_RDONLY); 216 if (fFileFD < 0) 217 return errno; 218 fPath = ""; 219 } 220 } else { 221 // FD was given -- dup it. 222 fFileFD = dup(fileFD); 223 if (fFileFD < 0) 224 return errno; 225 fPath = ""; 226 } 227 228 fListingLength = -1; 229 fListingIndex = 0; 230 231 return B_OK; 232 } 233 234 DIR* FakeDir() const { return fFakeDir; } 235 236 status_t ReadDir(struct dirent** _entry) 237 { 238 // make sure we have a current listing 239 status_t error = _CheckListing(); 240 if (error != B_OK) 241 return error; 242 243 // keep going until we find an entry or hit the end of dir 244 while (fListingIndex < fListingLength) { 245 // get next entry name 246 const char* name = fListing + fListingIndex; 247 int nameLen = strlen(name); 248 fListingIndex += nameLen + 1; 249 250 // check the attribute namespace 251 string demangledName; 252 if (!demangle_attribute_name(name, demangledName)) 253 continue; 254 name = demangledName.c_str(); 255 nameLen = demangledName.length(); 256 257 if (nameLen == 0) { 258 // Uh, weird attribute. 259 return B_ERROR; 260 } 261 262 // prepare the dirent 263 strcpy(fDirent.dirent()->d_name, name); 264 fDirent.dirent()->d_ino = 0; 265// TODO: We need the node ID! 266 267 *_entry = fDirent.dirent(); 268 return B_OK; 269 } 270 271 // end of dir 272 *_entry = NULL; 273 return B_OK; 274 } 275 276 void RewindDir() 277 { 278 fListingIndex = 0; 279 } 280 281private: 282 status_t _CheckListing() 283 { 284 if (fListing && fListingLength >= 0) 285 return B_OK; 286 287 char listing[kMaxAttributeListingLength]; 288 289 // get the listing 290 ssize_t length = list_attributes(fFileFD, fPath.c_str(), listing, 291 kMaxAttributeListingLength); 292 if (length < 0) 293 return errno; 294 295 // clone the on-stack listing 296 char* newListing = (char*)realloc(fListing, length); 297 if (!newListing) 298 return B_NO_MEMORY; 299 memcpy(newListing, listing, length); 300 301 fListing = newListing; 302 fListingLength = length; 303 fListingIndex = 0; 304 305 return B_OK; 306 } 307 308private: 309 int fFileFD; 310 string fPath; 311 DIR* fFakeDir; 312 LongDirEntry fDirent; 313 char* fListing; 314 int fListingLength; 315 int fListingIndex; 316}; 317 318// LocalFD 319#include "LocalFD.h" 320 321} // unnamed namspace 322 323 324// # pragma mark - Public API 325 326 327// fs_open_attr_dir 328DIR * 329fs_open_attr_dir(const char *path) 330{ 331 AttributeDirectory* attrDir = new AttributeDirectory; 332 333 status_t error = attrDir->Init(path, -1); 334 if (error != B_OK) { 335 errno = error; 336 delete attrDir; 337 return NULL; 338 } 339 340 return attrDir->FakeDir(); 341} 342 343// fs_fopen_attr_dir 344DIR * 345fs_fopen_attr_dir(int fd) 346{ 347 AttributeDirectory* attrDir = new AttributeDirectory; 348 349 status_t error = attrDir->Init(NULL, fd); 350 if (error != B_OK) { 351 errno = error; 352 delete attrDir; 353 return NULL; 354 } 355 356 return attrDir->FakeDir(); 357} 358 359// fs_close_attr_dir 360int 361fs_close_attr_dir(DIR *dir) 362{ 363 AttributeDirectory* attrDir = AttributeDirectory::Get(dir); 364 if (!attrDir) { 365 errno = B_BAD_VALUE; 366 return -1; 367 } 368 369 delete attrDir; 370 return 0; 371} 372 373// fs_read_attr_dir 374struct dirent * 375fs_read_attr_dir(DIR *dir) 376{ 377 // get attr dir 378 AttributeDirectory* attrDir = AttributeDirectory::Get(dir); 379 if (!attrDir) { 380 errno = B_BAD_VALUE; 381 return NULL; 382 } 383 384 // read attr dir 385 dirent* entry = NULL; 386 status_t error = attrDir->ReadDir(&entry); 387 if (error != B_OK) { 388 errno = error; 389 return NULL; 390 } 391 392 return entry; 393} 394 395// fs_rewind_attr_dir 396void 397fs_rewind_attr_dir(DIR *dir) 398{ 399 // get attr dir 400 AttributeDirectory* attrDir = AttributeDirectory::Get(dir); 401 if (attrDir) 402 attrDir->RewindDir(); 403} 404 405// fs_fopen_attr 406int 407fs_fopen_attr(int fd, const char *attribute, uint32 type, int openMode) 408{ 409 if (fd < 0) { 410 errno = B_BAD_VALUE; 411 return -1; 412 } 413 414 AttributeDescriptor* descriptor = new(std::nothrow) AttributeDescriptor(fd, 415 attribute, type, openMode); 416 if (descriptor == NULL) { 417 errno = B_NO_MEMORY; 418 return -1; 419 } 420 421 status_t error = descriptor->Init(); 422 if (error != B_OK) { 423 delete descriptor; 424 errno = error; 425 return -1; 426 } 427 428 int attributeFD = add_descriptor(descriptor); 429 if (attributeFD < 0) { 430 delete descriptor; 431 errno = B_NO_MEMORY; 432 return -1; 433 } 434 435 return attributeFD; 436} 437 438// fs_close_attr 439int 440fs_close_attr(int fd) 441{ 442 status_t error = delete_descriptor(fd); 443 if (error != 0) { 444 errno = error; 445 return -1; 446 } 447 448 return 0; 449} 450 451// fs_read_attr 452ssize_t 453fs_read_attr(int fd, const char *_attribute, uint32 type, off_t pos, 454 void *buffer, size_t readBytes) 455{ 456 // check params 457 if (pos < 0 || pos + readBytes > kMaxAttributeLength 458 || !_attribute || !buffer) { 459 errno = B_BAD_VALUE; 460 return -1; 461 } 462 463 // resolve FD 464 LocalFD localFD; 465 status_t error = localFD.Init(fd); 466 if (error != B_OK) { 467 errno = error; 468 return -1; 469 } 470 471 // mangle the attribute name 472 string attribute = mangle_attribute_name(_attribute); 473 474 // read the attribute 475 char attributeBuffer[sizeof(AttributeHeader) + kMaxAttributeLength]; 476 ssize_t bytesRead = sizeof(attributeBuffer); 477 if (localFD.Path()) { 478 bytesRead = get_attribute(-1, localFD.Path(), attribute.c_str(), 479 attributeBuffer, bytesRead); 480 } else { 481 bytesRead = get_attribute(localFD.FD(), NULL, attribute.c_str(), 482 attributeBuffer, bytesRead); 483 } 484 if (bytesRead < 0) { 485 // Make sure, the error code is B_ENTRY_NOT_FOUND, if the attribute 486 // doesn't exist. 487 if (errno == ENOATTR || errno == ENODATA) 488 errno = B_ENTRY_NOT_FOUND; 489 return -1; 490 } 491 492 // check length for sanity 493 if ((size_t)bytesRead < sizeof(AttributeHeader)) { 494 fprintf(stderr, "fs_read_attr(): attribute \"%s\" is shorter than the " 495 "AttributeHeader!\n", attribute.c_str()); 496 errno = B_ERROR; 497 return -1; 498 } 499 500 // copy the result into the provided buffer 501 bytesRead -= sizeof(AttributeHeader) + pos; 502 if (bytesRead < 0) { 503 // that means pos > <attribute size> 504 errno = B_BAD_VALUE; 505 return -1; 506 } 507 508 if (bytesRead > 0) { 509 if ((size_t)bytesRead > readBytes) 510 bytesRead = readBytes; 511 memcpy(buffer, attributeBuffer + sizeof(AttributeHeader) + pos, 512 bytesRead); 513 } 514 515 return bytesRead; 516} 517 518// fs_write_attr 519ssize_t 520fs_write_attr(int fd, const char *_attribute, uint32 type, off_t pos, 521 const void *buffer, size_t writeBytes) 522{ 523 // check params 524 if (pos < 0 || pos + writeBytes > kMaxAttributeLength 525 || _attribute == NULL || (writeBytes > 0 && buffer == NULL)) { 526 errno = B_BAD_VALUE; 527 return -1; 528 } 529 530 // resolve FD 531 LocalFD localFD; 532 status_t error = localFD.Init(fd); 533 if (error != B_OK) { 534 errno = error; 535 return -1; 536 } 537 538 // mangle the attribute name 539 string attribute = mangle_attribute_name(_attribute); 540 541 // prepare an attribute buffer 542 char attributeBuffer[sizeof(AttributeHeader) + kMaxAttributeLength]; 543 AttributeHeader* header = (AttributeHeader*)attributeBuffer; 544 header->type = type; 545 memset(attributeBuffer + sizeof(AttributeHeader), 0, pos); 546 if (writeBytes > 0) { 547 memcpy(attributeBuffer + sizeof(AttributeHeader) + pos, buffer, 548 writeBytes); 549 } 550 551 // write the attribute 552 ssize_t toWrite = sizeof(AttributeHeader) + pos + writeBytes; 553 int result; 554 if (localFD.Path()) { 555 result = set_attribute(-1, localFD.Path(), attribute.c_str(), 556 attributeBuffer, toWrite); 557 } else { 558 result = set_attribute(localFD.FD(), NULL, attribute.c_str(), 559 attributeBuffer, toWrite); 560 } 561 if (result < 0) { 562 // Setting user attributes on symlinks is not allowed (xattr). So, if 563 // this is a symlink and we're only supposed to write a "BEOS:TYPE" 564 // attribute we silently pretend to have succeeded. 565 if (localFD.IsSymlink() && strcmp(_attribute, "BEOS:TYPE") == 0) 566 return writeBytes; 567 return -1; 568 } 569 570 return writeBytes; 571} 572 573// fs_remove_attr 574int 575fs_remove_attr(int fd, const char *_attribute) 576{ 577 // check params 578 if (!_attribute) { 579 errno = B_BAD_VALUE; 580 return -1; 581 } 582 583 // resolve FD 584 LocalFD localFD; 585 status_t error = localFD.Init(fd); 586 if (error != B_OK) { 587 errno = error; 588 return -1; 589 } 590 591 // mangle the attribute name 592 string attribute = mangle_attribute_name(_attribute); 593 594 // remove attribute 595 int result; 596 if (localFD.Path()) 597 result = remove_attribute(-1, localFD.Path(), attribute.c_str()); 598 else 599 result = remove_attribute(localFD.FD(), NULL, attribute.c_str()); 600 601 if (result < 0) { 602 // Make sure, the error code is B_ENTRY_NOT_FOUND, if the attribute 603 // doesn't exist. 604 if (errno == ENOATTR || errno == ENODATA) 605 errno = B_ENTRY_NOT_FOUND; 606 return -1; 607 } 608 return 0; 609} 610 611// fs_stat_attr 612int 613fs_stat_attr(int fd, const char *_attribute, struct attr_info *attrInfo) 614{ 615 if (!_attribute || !attrInfo) { 616 errno = B_BAD_VALUE; 617 return -1; 618 } 619 620 // resolve FD 621 LocalFD localFD; 622 status_t error = localFD.Init(fd); 623 if (error != B_OK) { 624 errno = error; 625 return -1; 626 } 627 628 // mangle the attribute name 629 string attribute = mangle_attribute_name(_attribute); 630 631 // read the attribute 632 char attributeBuffer[sizeof(AttributeHeader) + kMaxAttributeLength]; 633 ssize_t bytesRead = sizeof(AttributeHeader) + kMaxAttributeLength; 634 if (localFD.Path()) { 635 bytesRead = get_attribute(-1, localFD.Path(), attribute.c_str(), 636 attributeBuffer, bytesRead); 637 } else { 638 bytesRead = get_attribute(localFD.FD(), NULL, attribute.c_str(), 639 attributeBuffer, bytesRead); 640 } 641 if (bytesRead < 0) { 642 // Make sure, the error code is B_ENTRY_NOT_FOUND, if the attribute 643 // doesn't exist. 644 if (errno == ENOATTR || errno == ENODATA) 645 errno = B_ENTRY_NOT_FOUND; 646 return -1; 647 } 648 649 // check length for sanity 650 if ((size_t)bytesRead < sizeof(AttributeHeader)) { 651 fprintf(stderr, "fs_stat_attr(): attribute \"%s\" is shorter than the " 652 "AttributeHeader!\n", attribute.c_str()); 653 errno = B_ERROR; 654 return -1; 655 } 656 657 attrInfo->size = bytesRead - sizeof(AttributeHeader); 658 attrInfo->type = ((AttributeHeader*)attributeBuffer)->type; 659 660 return 0; 661} 662 663 664// #pragma mark - Private Syscalls 665 666 667#ifndef BUILDING_FS_SHELL 668 669// _kern_open_attr_dir 670int 671_kern_open_attr_dir(int fd, const char *path) 672{ 673 // get node ref for the node 674 struct stat st; 675 status_t error = _kern_read_stat(fd, path, false, &st, 676 sizeof(struct stat)); 677 if (error != B_OK) { 678 errno = error; 679 return -1; 680 } 681 NodeRef ref(st); 682 683 DIR* dir; 684 if (path) { 685 // If a path was given, get a usable path. 686 string realPath; 687 status_t error = get_path(fd, path, realPath); 688 if (error != B_OK) 689 return error; 690 691 dir = fs_open_attr_dir(realPath.c_str()); 692 } else 693 dir = fs_fopen_attr_dir(fd); 694 695 if (!dir) 696 return errno; 697 698 // create descriptor 699 AttrDirDescriptor *descriptor = new AttrDirDescriptor(dir, ref); 700 return add_descriptor(descriptor); 701} 702 703// _kern_rename_attr 704status_t 705_kern_rename_attr(int fromFile, const char *fromName, int toFile, 706 const char *toName) 707{ 708 // not supported ATM 709 return B_BAD_VALUE; 710} 711 712// _kern_remove_attr 713status_t 714_kern_remove_attr(int fd, const char *name) 715{ 716 if (!name) 717 return B_BAD_VALUE; 718 719 if (fs_remove_attr(fd, name) < 0) 720 return errno; 721 return B_OK; 722} 723 724#endif // ! BUILDING_FS_SHELL 725