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 status_t 506 BEntry::set_stat(struct stat& st, uint32 what) 507 { 508 if (fCStatus != B_OK) 509 return B_FILE_ERROR; 510 511 return _kern_write_stat(fDirFd, fName, false, &st, sizeof(struct stat), 512 what); 513 } 514 515 516 status_t 517 BEntry::_SetTo(int dirFD, const char* path, bool traverse) 518 { 519 bool requireConcrete = false; 520 FDCloser fdCloser(dirFD); 521 char tmpPath[B_PATH_NAME_LENGTH]; 522 char leafName[B_FILE_NAME_LENGTH]; 523 int32 linkLimit = B_MAX_SYMLINKS; 524 while (true) { 525 if (!path || strcmp(path, ".") == 0) { 526 // "." 527 // if no dir FD is supplied, we need to open the current directory 528 // first 529 if (dirFD < 0) { 530 dirFD = _kern_open_dir(-1, "."); 531 if (dirFD < 0) 532 return dirFD; 533 fdCloser.SetTo(dirFD); 534 } 535 // get the parent directory 536 int parentFD = _kern_open_parent_dir(dirFD, leafName, 537 B_FILE_NAME_LENGTH); 538 if (parentFD < 0) 539 return parentFD; 540 dirFD = parentFD; 541 fdCloser.SetTo(dirFD); 542 break; 543 } else if (strcmp(path, "..") == 0) { 544 // ".." 545 // open the parent directory 546 int parentFD = _kern_open_dir(dirFD, ".."); 547 if (parentFD < 0) 548 return parentFD; 549 dirFD = parentFD; 550 fdCloser.SetTo(dirFD); 551 // get the parent's parent directory 552 parentFD = _kern_open_parent_dir(dirFD, leafName, 553 B_FILE_NAME_LENGTH); 554 if (parentFD < 0) 555 return parentFD; 556 dirFD = parentFD; 557 fdCloser.SetTo(dirFD); 558 break; 559 } else { 560 // an ordinary path; analyze it 561 char dirPath[B_PATH_NAME_LENGTH]; 562 status_t error = BPrivate::Storage::parse_path(path, dirPath, 563 leafName); 564 if (error != B_OK) 565 return error; 566 // special case: root directory ("/") 567 if (leafName[0] == '\0' && dirPath[0] == '/') 568 strcpy(leafName, "."); 569 if (leafName[0] == '\0') { 570 // the supplied path is already a leaf 571 error = BPrivate::Storage::check_entry_name(dirPath); 572 if (error != B_OK) 573 return error; 574 strcpy(leafName, dirPath); 575 // if no directory was given, we need to open the current dir 576 // now 577 if (dirFD < 0) { 578 char* cwd = getcwd(tmpPath, B_PATH_NAME_LENGTH); 579 if (!cwd) 580 return B_ERROR; 581 dirFD = _kern_open_dir(-1, cwd); 582 if (dirFD < 0) 583 return dirFD; 584 fdCloser.SetTo(dirFD); 585 } 586 } else if (strcmp(leafName, ".") == 0 587 || strcmp(leafName, "..") == 0) { 588 // We have to resolve this to get the entry name. Just open 589 // the dir and let the next iteration deal with it. 590 dirFD = _kern_open_dir(-1, path); 591 if (dirFD < 0) 592 return dirFD; 593 fdCloser.SetTo(dirFD); 594 path = NULL; 595 continue; 596 } else { 597 int parentFD = _kern_open_dir(dirFD, dirPath); 598 if (parentFD < 0) 599 return parentFD; 600 dirFD = parentFD; 601 fdCloser.SetTo(dirFD); 602 } 603 // traverse symlinks, if desired 604 if (!traverse) 605 break; 606 struct stat st; 607 error = _kern_read_stat(dirFD, leafName, false, &st, 608 sizeof(struct stat)); 609 if (error == B_ENTRY_NOT_FOUND && !requireConcrete) { 610 // that's fine -- the entry is abstract and was not target of 611 // a symlink we resolved 612 break; 613 } 614 if (error != B_OK) 615 return error; 616 // the entry is concrete 617 if (!S_ISLNK(st.st_mode)) 618 break; 619 requireConcrete = true; 620 // we need to traverse the symlink 621 if (--linkLimit < 0) 622 return B_LINK_LIMIT; 623 size_t bufferSize = B_PATH_NAME_LENGTH - 1; 624 error = _kern_read_link(dirFD, leafName, tmpPath, &bufferSize); 625 if (error < 0) 626 return error; 627 tmpPath[bufferSize] = '\0'; 628 path = tmpPath; 629 // next round... 630 } 631 } 632 633 // set close on exec flag on dir FD 634 fcntl(dirFD, F_SETFD, FD_CLOEXEC); 635 636 // set the result 637 status_t error = _SetName(leafName); 638 if (error != B_OK) 639 return error; 640 fdCloser.Detach(); 641 fDirFd = dirFD; 642 return B_OK; 643 } 644 645 646 status_t 647 BEntry::_SetName(const char* name) 648 { 649 if (name == NULL) 650 return B_BAD_VALUE; 651 652 free(fName); 653 654 fName = strdup(name); 655 if (fName == NULL) 656 return B_NO_MEMORY; 657 658 return B_OK; 659 } 660 661 662 status_t 663 BEntry::_Rename(BEntry& target, bool clobber) 664 { 665 // check, if there's an entry in the way 666 if (!clobber && target.Exists()) 667 return B_FILE_EXISTS; 668 // rename 669 status_t error = _kern_rename(fDirFd, fName, target.fDirFd, target.fName); 670 if (error == B_OK) { 671 Unset(); 672 fCStatus = target.fCStatus; 673 fDirFd = target.fDirFd; 674 fName = target.fName; 675 target.fCStatus = B_NO_INIT; 676 target.fDirFd = -1; 677 target.fName = NULL; 678 } 679 return error; 680 } 681 682 683 void 684 BEntry::_Dump(const char* name) 685 { 686 if (name != NULL) { 687 printf("------------------------------------------------------------\n"); 688 printf("%s\n", name); 689 printf("------------------------------------------------------------\n"); 690 } 691 692 printf("fCStatus == %" B_PRId32 "\n", fCStatus); 693 694 struct stat st; 695 if (fDirFd != -1 696 && _kern_read_stat(fDirFd, NULL, false, &st, 697 sizeof(struct stat)) == B_OK) { 698 printf("dir.device == %" B_PRIdDEV "\n", st.st_dev); 699 printf("dir.inode == %" B_PRIdINO "\n", st.st_ino); 700 } else { 701 printf("dir == NullFd\n"); 702 } 703 704 printf("leaf == '%s'\n", fName); 705 printf("\n"); 706 707 } 708 709 710 status_t 711 BEntry::_GetStat(struct stat* st) const 712 { 713 if (fCStatus != B_OK) 714 return B_NO_INIT; 715 716 return _kern_read_stat(fDirFd, fName, false, st, sizeof(struct stat)); 717 } 718 719 720 status_t 721 BEntry::_GetStat(struct stat_beos* st) const 722 { 723 struct stat newStat; 724 status_t error = _GetStat(&newStat); 725 if (error != B_OK) 726 return error; 727 728 convert_to_stat_beos(&newStat, st); 729 return B_OK; 730 } 731 732 733 // #pragma mark - 734 735 736 status_t 737 get_ref_for_path(const char* path, entry_ref* ref) 738 { 739 status_t error = path && ref ? B_OK : B_BAD_VALUE; 740 if (error == B_OK) { 741 BEntry entry(path); 742 error = entry.InitCheck(); 743 if (error == B_OK) 744 error = entry.GetRef(ref); 745 } 746 return error; 747 } 748 749 750 bool 751 operator<(const entry_ref& a, const entry_ref& b) 752 { 753 return (a.device < b.device 754 || (a.device == b.device 755 && (a.directory < b.directory 756 || (a.directory == b.directory 757 && ((a.name == NULL && b.name != NULL) 758 || (a.name != NULL && b.name != NULL 759 && strcmp(a.name, b.name) < 0)))))); 760 } 761 762 763 // #pragma mark - symbol versions 764 765 766 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST 767 # if __GNUC__ == 2 // gcc 2 768 769 B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP4stat", 770 "GetStat__C6BEntryP4stat@@LIBBE_TEST"); 771 772 # else // gcc 4 773 774 // Haiku GetStat() 775 B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP4stat", 776 "_ZNK6BEntry7GetStatEP4stat@@LIBBE_TEST"); 777 778 # endif // gcc 4 779 #else // !HAIKU_TARGET_PLATFORM_LIBBE_TEST 780 # if __GNUC__ == 2 // gcc 2 781 782 // BeOS compatible GetStat() 783 B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP9stat_beos", 784 "GetStat__C6BEntryP4stat@LIBBE_BASE"); 785 786 // Haiku GetStat() 787 B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP4stat", 788 "GetStat__C6BEntryP4stat@@LIBBE_1_ALPHA1"); 789 790 # else // gcc 4 791 792 // BeOS compatible GetStat() 793 B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP9stat_beos", 794 "_ZNK6BEntry7GetStatEP4stat@LIBBE_BASE"); 795 796 // Haiku GetStat() 797 B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP4stat", 798 "_ZNK6BEntry7GetStatEP4stat@@LIBBE_1_ALPHA1"); 799 800 # endif // gcc 4 801 #endif // !HAIKU_TARGET_PLATFORM_LIBBE_TEST 802