1 /* 2 * Copyright 2002-2012, Haiku Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Tyler Dauwalder 7 * Ingo Weinhold, bonefish@users.sf.net 8 */ 9 10 11 #include <Entry.h> 12 13 #include <fcntl.h> 14 #include <new> 15 #include <stdio.h> 16 #include <stdlib.h> 17 #include <string.h> 18 #include <unistd.h> 19 20 #include <compat/sys/stat.h> 21 22 #include <Directory.h> 23 #include <Path.h> 24 #include <SymLink.h> 25 26 #include <syscalls.h> 27 28 #include "storage_support.h" 29 30 31 using namespace std; 32 33 34 // #pragma mark - struct entry_ref 35 36 37 entry_ref::entry_ref() 38 : 39 device((dev_t)-1), 40 directory((ino_t)-1), 41 name(NULL) 42 { 43 } 44 45 46 entry_ref::entry_ref(dev_t dev, ino_t dir, const char* name) 47 : 48 device(dev), 49 directory(dir), 50 name(NULL) 51 { 52 set_name(name); 53 } 54 55 56 entry_ref::entry_ref(const entry_ref& ref) 57 : 58 device(ref.device), 59 directory(ref.directory), 60 name(NULL) 61 { 62 set_name(ref.name); 63 } 64 65 66 entry_ref::~entry_ref() 67 { 68 free(name); 69 } 70 71 72 status_t 73 entry_ref::set_name(const char* name) 74 { 75 free(this->name); 76 77 if (name == NULL) { 78 this->name = NULL; 79 } else { 80 this->name = strdup(name); 81 if (!this->name) 82 return B_NO_MEMORY; 83 } 84 85 return B_OK; 86 } 87 88 89 bool 90 entry_ref::operator==(const entry_ref& ref) const 91 { 92 return (device == ref.device 93 && directory == ref.directory 94 && (name == ref.name 95 || (name != NULL && ref.name != NULL 96 && strcmp(name, ref.name) == 0))); 97 } 98 99 100 bool 101 entry_ref::operator!=(const entry_ref& ref) const 102 { 103 return !(*this == ref); 104 } 105 106 107 entry_ref& 108 entry_ref::operator=(const entry_ref& ref) 109 { 110 if (this == &ref) 111 return *this; 112 113 device = ref.device; 114 directory = ref.directory; 115 set_name(ref.name); 116 return *this; 117 } 118 119 120 // #pragma mark - BEntry 121 122 123 BEntry::BEntry() 124 : 125 fDirFd(-1), 126 fName(NULL), 127 fCStatus(B_NO_INIT) 128 { 129 } 130 131 132 BEntry::BEntry(const BDirectory* dir, const char* path, bool traverse) 133 : 134 fDirFd(-1), 135 fName(NULL), 136 fCStatus(B_NO_INIT) 137 { 138 SetTo(dir, path, traverse); 139 } 140 141 142 BEntry::BEntry(const entry_ref* ref, bool traverse) 143 : 144 fDirFd(-1), 145 fName(NULL), 146 fCStatus(B_NO_INIT) 147 { 148 SetTo(ref, traverse); 149 } 150 151 152 BEntry::BEntry(const char* path, bool traverse) 153 : 154 fDirFd(-1), 155 fName(NULL), 156 fCStatus(B_NO_INIT) 157 { 158 SetTo(path, traverse); 159 } 160 161 162 BEntry::BEntry(const BEntry& entry) 163 : 164 fDirFd(-1), 165 fName(NULL), 166 fCStatus(B_NO_INIT) 167 { 168 *this = entry; 169 } 170 171 172 BEntry::~BEntry() 173 { 174 Unset(); 175 } 176 177 178 status_t 179 BEntry::InitCheck() const 180 { 181 return fCStatus; 182 } 183 184 185 bool 186 BEntry::Exists() const 187 { 188 // just stat the beast 189 struct stat st; 190 return GetStat(&st) == B_OK; 191 } 192 193 194 const char* 195 BEntry::Name() const 196 { 197 if (fCStatus != B_OK) 198 return NULL; 199 200 return fName; 201 } 202 203 204 status_t 205 BEntry::SetTo(const BDirectory* dir, const char* path, bool traverse) 206 { 207 // check params 208 if (!dir) 209 return (fCStatus = B_BAD_VALUE); 210 if (path && path[0] == '\0') // R5 behaviour 211 path = NULL; 212 213 // if path is absolute, let the path-only SetTo() do the job 214 if (BPrivate::Storage::is_absolute_path(path)) 215 return SetTo(path, traverse); 216 217 Unset(); 218 219 if (dir->InitCheck() != B_OK) 220 fCStatus = B_BAD_VALUE; 221 222 // dup() the dir's FD and let set() do the rest 223 int dirFD = _kern_dup(dir->get_fd()); 224 if (dirFD < 0) 225 return (fCStatus = dirFD); 226 return (fCStatus = _SetTo(dirFD, path, traverse)); 227 } 228 229 230 status_t 231 BEntry::SetTo(const entry_ref* ref, bool traverse) 232 { 233 Unset(); 234 if (ref == NULL) 235 return (fCStatus = B_BAD_VALUE); 236 237 // if ref-name is absolute, let the path-only SetTo() do the job 238 if (BPrivate::Storage::is_absolute_path(ref->name)) 239 return SetTo(ref->name, traverse); 240 241 // open the directory and let set() do the rest 242 int dirFD = _kern_open_dir_entry_ref(ref->device, ref->directory, NULL); 243 if (dirFD < 0) 244 return (fCStatus = dirFD); 245 return (fCStatus = _SetTo(dirFD, ref->name, traverse)); 246 } 247 248 249 status_t 250 BEntry::SetTo(const char* path, bool traverse) 251 { 252 Unset(); 253 // check the argument 254 if (!path) 255 return (fCStatus = B_BAD_VALUE); 256 return (fCStatus = _SetTo(-1, path, traverse)); 257 } 258 259 260 void 261 BEntry::Unset() 262 { 263 // Close the directory 264 if (fDirFd >= 0) 265 _kern_close(fDirFd); 266 267 // Free our leaf name 268 free(fName); 269 270 fDirFd = -1; 271 fName = NULL; 272 fCStatus = B_NO_INIT; 273 } 274 275 276 status_t 277 BEntry::GetRef(entry_ref* ref) const 278 { 279 if (fCStatus != B_OK) 280 return B_NO_INIT; 281 282 if (ref == NULL) 283 return B_BAD_VALUE; 284 285 struct stat st; 286 status_t error = _kern_read_stat(fDirFd, NULL, false, &st, 287 sizeof(struct stat)); 288 if (error == B_OK) { 289 ref->device = st.st_dev; 290 ref->directory = st.st_ino; 291 error = ref->set_name(fName); 292 } 293 return error; 294 } 295 296 297 status_t 298 BEntry::GetPath(BPath* path) const 299 { 300 if (fCStatus != B_OK) 301 return B_NO_INIT; 302 303 if (path == NULL) 304 return B_BAD_VALUE; 305 306 return path->SetTo(this); 307 } 308 309 310 status_t BEntry::GetParent(BEntry* entry) const 311 { 312 // check parameter and initialization 313 if (fCStatus != B_OK) 314 return B_NO_INIT; 315 if (entry == NULL) 316 return B_BAD_VALUE; 317 318 // check whether we are the root directory 319 // It is sufficient to check whether our leaf name is ".". 320 if (strcmp(fName, ".") == 0) 321 return B_ENTRY_NOT_FOUND; 322 323 // open the parent directory 324 char leafName[B_FILE_NAME_LENGTH]; 325 int parentFD = _kern_open_parent_dir(fDirFd, leafName, B_FILE_NAME_LENGTH); 326 if (parentFD < 0) 327 return parentFD; 328 329 // set close on exec flag on dir FD 330 fcntl(parentFD, F_SETFD, FD_CLOEXEC); 331 332 // init the entry 333 entry->Unset(); 334 entry->fDirFd = parentFD; 335 entry->fCStatus = entry->_SetName(leafName); 336 if (entry->fCStatus != B_OK) 337 entry->Unset(); 338 return entry->fCStatus; 339 } 340 341 342 status_t 343 BEntry::GetParent(BDirectory* dir) const 344 { 345 // check initialization and parameter 346 if (fCStatus != B_OK) 347 return B_NO_INIT; 348 if (dir == NULL) 349 return B_BAD_VALUE; 350 // check whether we are the root directory 351 // It is sufficient to check whether our leaf name is ".". 352 if (strcmp(fName, ".") == 0) 353 return B_ENTRY_NOT_FOUND; 354 // get a node ref for the directory and init it 355 struct stat st; 356 status_t error = _kern_read_stat(fDirFd, NULL, false, &st, 357 sizeof(struct stat)); 358 if (error != B_OK) 359 return error; 360 node_ref ref; 361 ref.device = st.st_dev; 362 ref.node = st.st_ino; 363 return dir->SetTo(&ref); 364 // TODO: This can be optimized: We already have a FD for the directory, 365 // so we could dup() it and set it on the directory. We just need a private 366 // API for being able to do that. 367 } 368 369 370 status_t 371 BEntry::GetName(char* buffer) const 372 { 373 if (fCStatus != B_OK) 374 return B_NO_INIT; 375 if (buffer == NULL) 376 return B_BAD_VALUE; 377 378 strcpy(buffer, fName); 379 return B_OK; 380 } 381 382 383 status_t 384 BEntry::Rename(const char* path, bool clobber) 385 { 386 // check parameter and initialization 387 if (path == NULL) 388 return B_BAD_VALUE; 389 if (fCStatus != B_OK) 390 return B_NO_INIT; 391 // get an entry representing the target location 392 BEntry target; 393 status_t error; 394 if (BPrivate::Storage::is_absolute_path(path)) { 395 error = target.SetTo(path); 396 } else { 397 int dirFD = _kern_dup(fDirFd); 398 if (dirFD < 0) 399 return dirFD; 400 // init the entry 401 error = target.fCStatus = target._SetTo(dirFD, path, false); 402 } 403 if (error != B_OK) 404 return error; 405 return _Rename(target, clobber); 406 } 407 408 409 status_t 410 BEntry::MoveTo(BDirectory* dir, const char* path, bool clobber) 411 { 412 // check parameters and initialization 413 if (fCStatus != B_OK) 414 return B_NO_INIT; 415 if (dir == NULL) 416 return B_BAD_VALUE; 417 if (dir->InitCheck() != B_OK) 418 return B_BAD_VALUE; 419 // NULL path simply means move without renaming 420 if (path == NULL) 421 path = fName; 422 // get an entry representing the target location 423 BEntry target; 424 status_t error = target.SetTo(dir, path); 425 if (error != B_OK) 426 return error; 427 return _Rename(target, clobber); 428 } 429 430 431 status_t 432 BEntry::Remove() 433 { 434 if (fCStatus != B_OK) 435 return B_NO_INIT; 436 437 if (IsDirectory()) 438 return _kern_remove_dir(fDirFd, fName); 439 440 return _kern_unlink(fDirFd, fName); 441 } 442 443 444 bool 445 BEntry::operator==(const BEntry& item) const 446 { 447 // First check statuses 448 if (this->InitCheck() != B_OK && item.InitCheck() != B_OK) { 449 return true; 450 } else if (this->InitCheck() == B_OK && item.InitCheck() == B_OK) { 451 452 // Directories don't compare well directly, so we'll 453 // compare entry_refs instead 454 entry_ref ref1, ref2; 455 if (this->GetRef(&ref1) != B_OK) 456 return false; 457 if (item.GetRef(&ref2) != B_OK) 458 return false; 459 return (ref1 == ref2); 460 461 } else { 462 return false; 463 } 464 465 } 466 467 468 bool 469 BEntry::operator!=(const BEntry& item) const 470 { 471 return !(*this == item); 472 } 473 474 475 BEntry& 476 BEntry::operator=(const BEntry& item) 477 { 478 if (this == &item) 479 return *this; 480 481 Unset(); 482 if (item.fCStatus == B_OK) { 483 fDirFd = _kern_dup(item.fDirFd); 484 if (fDirFd >= 0) 485 fCStatus = _SetName(item.fName); 486 else 487 fCStatus = fDirFd; 488 489 if (fCStatus != B_OK) 490 Unset(); 491 } 492 493 return *this; 494 } 495 496 497 void BEntry::_PennyEntry1(){} 498 void BEntry::_PennyEntry2(){} 499 void BEntry::_PennyEntry3(){} 500 void BEntry::_PennyEntry4(){} 501 void BEntry::_PennyEntry5(){} 502 void BEntry::_PennyEntry6(){} 503 504 505 /*! Updates the BEntry with the data from the stat structure according 506 to the \a what mask. 507 508 \param st The stat structure to set. 509 \param what A mask 510 511 \returns A status code. 512 \retval B_OK Everything went fine. 513 \retval B_FILE_ERROR There was an error writing to the BEntry object. 514 */ 515 status_t 516 BEntry::set_stat(struct stat& st, uint32 what) 517 { 518 if (fCStatus != B_OK) 519 return B_FILE_ERROR; 520 521 return _kern_write_stat(fDirFd, fName, false, &st, sizeof(struct stat), 522 what); 523 } 524 525 526 /*! Sets the entry to point to the entry specified by the path \a path 527 relative to the given directory. 528 529 If \a traverse is \c true and the given entry is a symbolic link, the 530 object is recursively set to point to the entry pointed to by the symlink. 531 532 If \a path is an absolute path, \a dirFD is ignored. 533 534 If \a dirFD is -1, \a path is considered relative to the current directory 535 (unless it is an absolute path). 536 537 The ownership of the file descriptor \a dirFD is transferred to the 538 method, regardless of whether it succeeds or fails. The caller must not 539 close the FD afterwards. 540 541 \param dirFD File descriptor of a directory relative to which path is to 542 be considered. May be -1 if the current directory shall be considered. 543 \param path Pointer to a path relative to the given directory. 544 \param traverse If \c true and the given entry is a symbolic link, the 545 object is recursively set to point to the entry linked to by the 546 symbolic link. 547 548 \returns \c B_OK on success, or an error code on failure. 549 */ 550 status_t 551 BEntry::_SetTo(int dirFD, const char* path, bool traverse) 552 { 553 bool requireConcrete = false; 554 FDCloser fdCloser(dirFD); 555 char tmpPath[B_PATH_NAME_LENGTH]; 556 char leafName[B_FILE_NAME_LENGTH]; 557 int32 linkLimit = B_MAX_SYMLINKS; 558 while (true) { 559 if (!path || strcmp(path, ".") == 0) { 560 // "." 561 // if no dir FD is supplied, we need to open the current directory 562 // first 563 if (dirFD < 0) { 564 dirFD = _kern_open_dir(AT_FDCWD, "."); 565 if (dirFD < 0) 566 return dirFD; 567 fdCloser.SetTo(dirFD); 568 } 569 // get the parent directory 570 int parentFD = _kern_open_parent_dir(dirFD, leafName, 571 B_FILE_NAME_LENGTH); 572 if (parentFD < 0) 573 return parentFD; 574 dirFD = parentFD; 575 fdCloser.SetTo(dirFD); 576 break; 577 } else if (strcmp(path, "..") == 0) { 578 // ".." 579 // open the parent directory 580 int parentFD = _kern_open_dir(dirFD, ".."); 581 if (parentFD < 0) 582 return parentFD; 583 dirFD = parentFD; 584 fdCloser.SetTo(dirFD); 585 // get the parent's parent directory 586 parentFD = _kern_open_parent_dir(dirFD, leafName, 587 B_FILE_NAME_LENGTH); 588 if (parentFD < 0) 589 return parentFD; 590 dirFD = parentFD; 591 fdCloser.SetTo(dirFD); 592 break; 593 } else { 594 // an ordinary path; analyze it 595 char dirPath[B_PATH_NAME_LENGTH]; 596 status_t error = BPrivate::Storage::parse_path(path, dirPath, 597 leafName); 598 if (error != B_OK) 599 return error; 600 // special case: root directory ("/") 601 if (leafName[0] == '\0' && dirPath[0] == '/') 602 strcpy(leafName, "."); 603 if (leafName[0] == '\0') { 604 // the supplied path is already a leaf 605 error = BPrivate::Storage::check_entry_name(dirPath); 606 if (error != B_OK) 607 return error; 608 strcpy(leafName, dirPath); 609 // if no directory was given, we need to open the current dir 610 // now 611 if (dirFD < 0) { 612 char* cwd = getcwd(tmpPath, B_PATH_NAME_LENGTH); 613 if (!cwd) 614 return B_ERROR; 615 dirFD = _kern_open_dir(AT_FDCWD, cwd); 616 if (dirFD < 0) 617 return dirFD; 618 fdCloser.SetTo(dirFD); 619 } 620 } else if (strcmp(leafName, ".") == 0 621 || strcmp(leafName, "..") == 0) { 622 // We have to resolve this to get the entry name. Just open 623 // the dir and let the next iteration deal with it. 624 dirFD = _kern_open_dir(AT_FDCWD, path); 625 if (dirFD < 0) 626 return dirFD; 627 fdCloser.SetTo(dirFD); 628 path = NULL; 629 continue; 630 } else { 631 int parentFD = _kern_open_dir(dirFD, dirPath); 632 if (parentFD < 0) 633 return parentFD; 634 dirFD = parentFD; 635 fdCloser.SetTo(dirFD); 636 } 637 // traverse symlinks, if desired 638 if (!traverse) 639 break; 640 struct stat st; 641 error = _kern_read_stat(dirFD, leafName, false, &st, 642 sizeof(struct stat)); 643 if (error == B_ENTRY_NOT_FOUND && !requireConcrete) { 644 // that's fine -- the entry is abstract and was not target of 645 // a symlink we resolved 646 break; 647 } 648 if (error != B_OK) 649 return error; 650 // the entry is concrete 651 if (!S_ISLNK(st.st_mode)) 652 break; 653 requireConcrete = true; 654 // we need to traverse the symlink 655 if (--linkLimit < 0) 656 return B_LINK_LIMIT; 657 size_t bufferSize = B_PATH_NAME_LENGTH - 1; 658 error = _kern_read_link(dirFD, leafName, tmpPath, &bufferSize); 659 if (error < 0) 660 return error; 661 tmpPath[bufferSize] = '\0'; 662 path = tmpPath; 663 // next round... 664 } 665 } 666 667 // set close on exec flag on dir FD 668 fcntl(dirFD, F_SETFD, FD_CLOEXEC); 669 670 // set the result 671 status_t error = _SetName(leafName); 672 if (error != B_OK) 673 return error; 674 fdCloser.Detach(); 675 fDirFd = dirFD; 676 return B_OK; 677 } 678 679 680 /*! Handles string allocation, deallocation, and copying for the 681 leaf name of the entry. 682 683 \param name The leaf \a name of the entry. 684 685 \returns A status code. 686 \retval B_OK Everything went fine. 687 \retval B_BAD_VALUE \a name is \c NULL. 688 \retval B_NO_MEMORY Ran out of memory trying to allocate \a name. 689 */ 690 status_t 691 BEntry::_SetName(const char* name) 692 { 693 if (name == NULL) 694 return B_BAD_VALUE; 695 696 free(fName); 697 698 fName = strdup(name); 699 if (fName == NULL) 700 return B_NO_MEMORY; 701 702 return B_OK; 703 } 704 705 706 /*! Renames the entry referred to by this object to the location 707 specified by \a target. 708 709 If an entry exists at the target location, the method fails, unless 710 \a clobber is \c true, in which case that entry is overwritten (doesn't 711 work for non-empty directories, though). 712 713 If the operation was successful, this entry is made a clone of the 714 supplied one and the supplied one is uninitialized. 715 716 \param target The entry specifying the target location. 717 \param clobber If \c true, the an entry existing at the target location 718 will be overwritten. 719 720 \return \c B_OK, if everything went fine, another error code otherwise. 721 */ 722 status_t 723 BEntry::_Rename(BEntry& target, bool clobber) 724 { 725 // check, if there's an entry in the way 726 if (!clobber && target.Exists()) 727 return B_FILE_EXISTS; 728 // rename 729 status_t error = _kern_rename(fDirFd, fName, target.fDirFd, target.fName); 730 if (error == B_OK) { 731 Unset(); 732 fCStatus = target.fCStatus; 733 fDirFd = target.fDirFd; 734 fName = target.fName; 735 target.fCStatus = B_NO_INIT; 736 target.fDirFd = -1; 737 target.fName = NULL; 738 } 739 return error; 740 } 741 742 743 /*! Debugging function, dumps the given entry to stdout. 744 745 \param name A pointer to a string to be printed along with the dump for 746 identification purposes. 747 */ 748 void 749 BEntry::_Dump(const char* name) 750 { 751 if (name != NULL) { 752 printf("------------------------------------------------------------\n"); 753 printf("%s\n", name); 754 printf("------------------------------------------------------------\n"); 755 } 756 757 printf("fCStatus == %" B_PRId32 "\n", fCStatus); 758 759 struct stat st; 760 if (fDirFd != -1 761 && _kern_read_stat(fDirFd, NULL, false, &st, 762 sizeof(struct stat)) == B_OK) { 763 printf("dir.device == %" B_PRIdDEV "\n", st.st_dev); 764 printf("dir.inode == %" B_PRIdINO "\n", st.st_ino); 765 } else { 766 printf("dir == NullFd\n"); 767 } 768 769 printf("leaf == '%s'\n", fName); 770 printf("\n"); 771 772 } 773 774 775 status_t 776 BEntry::_GetStat(struct stat* st) const 777 { 778 if (fCStatus != B_OK) 779 return B_NO_INIT; 780 781 return _kern_read_stat(fDirFd, fName, false, st, sizeof(struct stat)); 782 } 783 784 785 status_t 786 BEntry::_GetStat(struct stat_beos* st) const 787 { 788 struct stat newStat; 789 status_t error = _GetStat(&newStat); 790 if (error != B_OK) 791 return error; 792 793 convert_to_stat_beos(&newStat, st); 794 return B_OK; 795 } 796 797 798 // #pragma mark - 799 800 801 status_t 802 get_ref_for_path(const char* path, entry_ref* ref) 803 { 804 status_t error = path && ref ? B_OK : B_BAD_VALUE; 805 if (error == B_OK) { 806 BEntry entry(path); 807 error = entry.InitCheck(); 808 if (error == B_OK) 809 error = entry.GetRef(ref); 810 } 811 return error; 812 } 813 814 815 bool 816 operator<(const entry_ref& a, const entry_ref& b) 817 { 818 return (a.device < b.device 819 || (a.device == b.device 820 && (a.directory < b.directory 821 || (a.directory == b.directory 822 && ((a.name == NULL && b.name != NULL) 823 || (a.name != NULL && b.name != NULL 824 && strcmp(a.name, b.name) < 0)))))); 825 } 826 827 828 // #pragma mark - symbol versions 829 830 831 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST 832 # if __GNUC__ == 2 // gcc 2 833 834 B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP4stat", 835 "GetStat__C6BEntryP4stat@@LIBBE_TEST"); 836 837 # else // gcc 4 838 839 // Haiku GetStat() 840 B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP4stat", 841 "_ZNK6BEntry7GetStatEP4stat@@LIBBE_TEST"); 842 843 # endif // gcc 4 844 #else // !HAIKU_TARGET_PLATFORM_LIBBE_TEST 845 # if __GNUC__ == 2 // gcc 2 846 847 // BeOS compatible GetStat() 848 B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP9stat_beos", 849 "GetStat__C6BEntryP4stat@LIBBE_BASE"); 850 851 // Haiku GetStat() 852 B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP4stat", 853 "GetStat__C6BEntryP4stat@@LIBBE_1_ALPHA1"); 854 855 # else // gcc 4 856 857 // BeOS compatible GetStat() 858 B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP9stat_beos", 859 "_ZNK6BEntry7GetStatEP4stat@LIBBE_BASE"); 860 861 // Haiku GetStat() 862 B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP4stat", 863 "_ZNK6BEntry7GetStatEP4stat@@LIBBE_1_ALPHA1"); 864 865 # endif // gcc 4 866 #endif // !HAIKU_TARGET_PLATFORM_LIBBE_TEST 867