1 /* 2 * Copyright 2002-2009, 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 status_t 195 BEntry::SetTo(const BDirectory* dir, const char* path, bool traverse) 196 { 197 // check params 198 if (!dir) 199 return (fCStatus = B_BAD_VALUE); 200 if (path && path[0] == '\0') // R5 behaviour 201 path = NULL; 202 203 // if path is absolute, let the path-only SetTo() do the job 204 if (BPrivate::Storage::is_absolute_path(path)) 205 return SetTo(path, traverse); 206 207 Unset(); 208 209 if (dir->InitCheck() != B_OK) 210 fCStatus = B_BAD_VALUE; 211 212 // dup() the dir's FD and let set() do the rest 213 int dirFD = _kern_dup(dir->get_fd()); 214 if (dirFD < 0) 215 return (fCStatus = dirFD); 216 return (fCStatus = _SetTo(dirFD, path, traverse)); 217 } 218 219 220 status_t 221 BEntry::SetTo(const entry_ref* ref, bool traverse) 222 { 223 Unset(); 224 if (ref == NULL) 225 return (fCStatus = B_BAD_VALUE); 226 227 // if ref-name is absolute, let the path-only SetTo() do the job 228 if (BPrivate::Storage::is_absolute_path(ref->name)) 229 return SetTo(ref->name, traverse); 230 231 // open the directory and let set() do the rest 232 int dirFD = _kern_open_dir_entry_ref(ref->device, ref->directory, NULL); 233 if (dirFD < 0) 234 return (fCStatus = dirFD); 235 return (fCStatus = _SetTo(dirFD, ref->name, traverse)); 236 } 237 238 239 status_t 240 BEntry::SetTo(const char* path, bool traverse) 241 { 242 Unset(); 243 // check the argument 244 if (!path) 245 return (fCStatus = B_BAD_VALUE); 246 return (fCStatus = _SetTo(-1, path, traverse)); 247 } 248 249 250 void 251 BEntry::Unset() 252 { 253 // Close the directory 254 if (fDirFd >= 0) 255 _kern_close(fDirFd); 256 257 // Free our leaf name 258 free(fName); 259 260 fDirFd = -1; 261 fName = NULL; 262 fCStatus = B_NO_INIT; 263 } 264 265 266 status_t 267 BEntry::GetRef(entry_ref* ref) const 268 { 269 if (fCStatus != B_OK) 270 return B_NO_INIT; 271 272 if (ref == NULL) 273 return B_BAD_VALUE; 274 275 struct stat st; 276 status_t error = _kern_read_stat(fDirFd, NULL, false, &st, 277 sizeof(struct stat)); 278 if (error == B_OK) { 279 ref->device = st.st_dev; 280 ref->directory = st.st_ino; 281 error = ref->set_name(fName); 282 } 283 return error; 284 } 285 286 287 status_t 288 BEntry::GetPath(BPath* path) const 289 { 290 if (fCStatus != B_OK) 291 return B_NO_INIT; 292 293 if (path == NULL) 294 return B_BAD_VALUE; 295 296 return path->SetTo(this); 297 } 298 299 300 status_t BEntry::GetParent(BEntry* entry) const 301 { 302 // check parameter and initialization 303 if (fCStatus != B_OK) 304 return B_NO_INIT; 305 if (entry == NULL) 306 return B_BAD_VALUE; 307 308 // check whether we are the root directory 309 // It is sufficient to check whether our leaf name is ".". 310 if (strcmp(fName, ".") == 0) 311 return B_ENTRY_NOT_FOUND; 312 313 // open the parent directory 314 char leafName[B_FILE_NAME_LENGTH]; 315 int parentFD = _kern_open_parent_dir(fDirFd, leafName, B_FILE_NAME_LENGTH); 316 if (parentFD < 0) 317 return parentFD; 318 319 // set close on exec flag on dir FD 320 fcntl(parentFD, F_SETFD, FD_CLOEXEC); 321 322 // init the entry 323 entry->Unset(); 324 entry->fDirFd = parentFD; 325 entry->fCStatus = entry->_SetName(leafName); 326 if (entry->fCStatus != B_OK) 327 entry->Unset(); 328 return entry->fCStatus; 329 } 330 331 332 status_t 333 BEntry::GetParent(BDirectory* dir) const 334 { 335 // check initialization and parameter 336 if (fCStatus != B_OK) 337 return B_NO_INIT; 338 if (dir == NULL) 339 return B_BAD_VALUE; 340 // check whether we are the root directory 341 // It is sufficient to check whether our leaf name is ".". 342 if (strcmp(fName, ".") == 0) 343 return B_ENTRY_NOT_FOUND; 344 // get a node ref for the directory and init it 345 struct stat st; 346 status_t error = _kern_read_stat(fDirFd, NULL, false, &st, 347 sizeof(struct stat)); 348 if (error != B_OK) 349 return error; 350 node_ref ref; 351 ref.device = st.st_dev; 352 ref.node = st.st_ino; 353 return dir->SetTo(&ref); 354 // TODO: This can be optimized: We already have a FD for the directory, 355 // so we could dup() it and set it on the directory. We just need a private 356 // API for being able to do that. 357 } 358 359 360 status_t 361 BEntry::GetName(char* buffer) const 362 { 363 status_t result = B_ERROR; 364 365 if (fCStatus != B_OK) { 366 result = B_NO_INIT; 367 } else if (buffer == NULL) { 368 result = B_BAD_VALUE; 369 } else { 370 strcpy(buffer, fName); 371 result = B_OK; 372 } 373 374 return result; 375 } 376 377 378 status_t 379 BEntry::Rename(const char* path, bool clobber) 380 { 381 // check parameter and initialization 382 if (path == NULL) 383 return B_BAD_VALUE; 384 if (fCStatus != B_OK) 385 return B_NO_INIT; 386 // get an entry representing the target location 387 BEntry target; 388 status_t error; 389 if (BPrivate::Storage::is_absolute_path(path)) { 390 error = target.SetTo(path); 391 } else { 392 int dirFD = _kern_dup(fDirFd); 393 if (dirFD < 0) 394 return dirFD; 395 // init the entry 396 error = target.fCStatus = target._SetTo(dirFD, path, false); 397 } 398 if (error != B_OK) 399 return error; 400 return _Rename(target, clobber); 401 } 402 403 404 status_t 405 BEntry::MoveTo(BDirectory* dir, const char* path, bool clobber) 406 { 407 // check parameters and initialization 408 if (fCStatus != B_OK) 409 return B_NO_INIT; 410 if (dir == NULL) 411 return B_BAD_VALUE; 412 if (dir->InitCheck() != B_OK) 413 return B_BAD_VALUE; 414 // NULL path simply means move without renaming 415 if (path == NULL) 416 path = fName; 417 // get an entry representing the target location 418 BEntry target; 419 status_t error = target.SetTo(dir, path); 420 if (error != B_OK) 421 return error; 422 return _Rename(target, clobber); 423 } 424 425 426 status_t 427 BEntry::Remove() 428 { 429 if (fCStatus != B_OK) 430 return B_NO_INIT; 431 432 if (IsDirectory()) 433 return _kern_remove_dir(fDirFd, fName); 434 435 return _kern_unlink(fDirFd, fName); 436 } 437 438 439 bool 440 BEntry::operator==(const BEntry& item) const 441 { 442 // First check statuses 443 if (this->InitCheck() != B_OK && item.InitCheck() != B_OK) { 444 return true; 445 } else if (this->InitCheck() == B_OK && item.InitCheck() == B_OK) { 446 447 // Directories don't compare well directly, so we'll 448 // compare entry_refs instead 449 entry_ref ref1, ref2; 450 if (this->GetRef(&ref1) != B_OK) 451 return false; 452 if (item.GetRef(&ref2) != B_OK) 453 return false; 454 return (ref1 == ref2); 455 456 } else { 457 return false; 458 } 459 460 } 461 462 463 bool 464 BEntry::operator!=(const BEntry& item) const 465 { 466 return !(*this == item); 467 } 468 469 470 BEntry& 471 BEntry::operator=(const BEntry& item) 472 { 473 if (this == &item) 474 return *this; 475 476 Unset(); 477 if (item.fCStatus == B_OK) { 478 fDirFd = _kern_dup(item.fDirFd); 479 if (fDirFd >= 0) 480 fCStatus = _SetName(item.fName); 481 else 482 fCStatus = fDirFd; 483 484 if (fCStatus != B_OK) 485 Unset(); 486 } 487 488 return *this; 489 } 490 491 492 void BEntry::_PennyEntry1(){} 493 void BEntry::_PennyEntry2(){} 494 void BEntry::_PennyEntry3(){} 495 void BEntry::_PennyEntry4(){} 496 void BEntry::_PennyEntry5(){} 497 void BEntry::_PennyEntry6(){} 498 499 500 status_t 501 BEntry::set_stat(struct stat& st, uint32 what) 502 { 503 if (fCStatus != B_OK) 504 return B_FILE_ERROR; 505 506 return _kern_write_stat(fDirFd, fName, false, &st, sizeof(struct stat), 507 what); 508 } 509 510 511 status_t 512 BEntry::_SetTo(int dirFD, const char* path, bool traverse) 513 { 514 bool requireConcrete = false; 515 FDCloser fdCloser(dirFD); 516 char tmpPath[B_PATH_NAME_LENGTH]; 517 char leafName[B_FILE_NAME_LENGTH]; 518 int32 linkLimit = B_MAX_SYMLINKS; 519 while (true) { 520 if (!path || strcmp(path, ".") == 0) { 521 // "." 522 // if no dir FD is supplied, we need to open the current directory 523 // first 524 if (dirFD < 0) { 525 dirFD = _kern_open_dir(-1, "."); 526 if (dirFD < 0) 527 return dirFD; 528 fdCloser.SetTo(dirFD); 529 } 530 // get the parent directory 531 int parentFD = _kern_open_parent_dir(dirFD, leafName, 532 B_FILE_NAME_LENGTH); 533 if (parentFD < 0) 534 return parentFD; 535 dirFD = parentFD; 536 fdCloser.SetTo(dirFD); 537 break; 538 } else if (strcmp(path, "..") == 0) { 539 // ".." 540 // open the parent directory 541 int parentFD = _kern_open_dir(dirFD, ".."); 542 if (parentFD < 0) 543 return parentFD; 544 dirFD = parentFD; 545 fdCloser.SetTo(dirFD); 546 // get the parent's parent directory 547 parentFD = _kern_open_parent_dir(dirFD, leafName, 548 B_FILE_NAME_LENGTH); 549 if (parentFD < 0) 550 return parentFD; 551 dirFD = parentFD; 552 fdCloser.SetTo(dirFD); 553 break; 554 } else { 555 // an ordinary path; analyze it 556 char dirPath[B_PATH_NAME_LENGTH]; 557 status_t error = BPrivate::Storage::parse_path(path, dirPath, 558 leafName); 559 if (error != B_OK) 560 return error; 561 // special case: root directory ("/") 562 if (leafName[0] == '\0' && dirPath[0] == '/') 563 strcpy(leafName, "."); 564 if (leafName[0] == '\0') { 565 // the supplied path is already a leaf 566 error = BPrivate::Storage::check_entry_name(dirPath); 567 if (error != B_OK) 568 return error; 569 strcpy(leafName, dirPath); 570 // if no directory was given, we need to open the current dir 571 // now 572 if (dirFD < 0) { 573 char* cwd = getcwd(tmpPath, B_PATH_NAME_LENGTH); 574 if (!cwd) 575 return B_ERROR; 576 dirFD = _kern_open_dir(-1, cwd); 577 if (dirFD < 0) 578 return dirFD; 579 fdCloser.SetTo(dirFD); 580 } 581 } else if (strcmp(leafName, ".") == 0 582 || strcmp(leafName, "..") == 0) { 583 // We have to resolve this to get the entry name. Just open 584 // the dir and let the next iteration deal with it. 585 dirFD = _kern_open_dir(-1, path); 586 if (dirFD < 0) 587 return dirFD; 588 fdCloser.SetTo(dirFD); 589 path = NULL; 590 continue; 591 } else { 592 int parentFD = _kern_open_dir(dirFD, dirPath); 593 if (parentFD < 0) 594 return parentFD; 595 dirFD = parentFD; 596 fdCloser.SetTo(dirFD); 597 } 598 // traverse symlinks, if desired 599 if (!traverse) 600 break; 601 struct stat st; 602 error = _kern_read_stat(dirFD, leafName, false, &st, 603 sizeof(struct stat)); 604 if (error == B_ENTRY_NOT_FOUND && !requireConcrete) { 605 // that's fine -- the entry is abstract and was not target of 606 // a symlink we resolved 607 break; 608 } 609 if (error != B_OK) 610 return error; 611 // the entry is concrete 612 if (!S_ISLNK(st.st_mode)) 613 break; 614 requireConcrete = true; 615 // we need to traverse the symlink 616 if (--linkLimit < 0) 617 return B_LINK_LIMIT; 618 size_t bufferSize = B_PATH_NAME_LENGTH - 1; 619 error = _kern_read_link(dirFD, leafName, tmpPath, &bufferSize); 620 if (error < 0) 621 return error; 622 tmpPath[bufferSize] = '\0'; 623 path = tmpPath; 624 // next round... 625 } 626 } 627 628 // set close on exec flag on dir FD 629 fcntl(dirFD, F_SETFD, FD_CLOEXEC); 630 631 // set the result 632 status_t error = _SetName(leafName); 633 if (error != B_OK) 634 return error; 635 fdCloser.Detach(); 636 fDirFd = dirFD; 637 return B_OK; 638 } 639 640 641 status_t 642 BEntry::_SetName(const char* name) 643 { 644 if (name == NULL) 645 return B_BAD_VALUE; 646 647 free(fName); 648 649 fName = strdup(name); 650 if (fName == NULL) 651 return B_NO_MEMORY; 652 653 return B_OK; 654 } 655 656 657 status_t 658 BEntry::_Rename(BEntry& target, bool clobber) 659 { 660 // check, if there's an entry in the way 661 if (!clobber && target.Exists()) 662 return B_FILE_EXISTS; 663 // rename 664 status_t error = _kern_rename(fDirFd, fName, target.fDirFd, target.fName); 665 if (error == B_OK) { 666 Unset(); 667 fCStatus = target.fCStatus; 668 fDirFd = target.fDirFd; 669 fName = target.fName; 670 target.fCStatus = B_NO_INIT; 671 target.fDirFd = -1; 672 target.fName = NULL; 673 } 674 return error; 675 } 676 677 678 void 679 BEntry::_Dump(const char* name) 680 { 681 if (name != NULL) { 682 printf("------------------------------------------------------------\n"); 683 printf("%s\n", name); 684 printf("------------------------------------------------------------\n"); 685 } 686 687 printf("fCStatus == %ld\n", fCStatus); 688 689 struct stat st; 690 if (fDirFd != -1 691 && _kern_read_stat(fDirFd, NULL, false, &st, 692 sizeof(struct stat)) == B_OK) { 693 printf("dir.device == %ld\n", st.st_dev); 694 printf("dir.inode == %lld\n", st.st_ino); 695 } else { 696 printf("dir == NullFd\n"); 697 } 698 699 printf("leaf == '%s'\n", fName); 700 printf("\n"); 701 702 } 703 704 705 status_t 706 BEntry::_GetStat(struct stat* st) const 707 { 708 if (fCStatus != B_OK) 709 return B_NO_INIT; 710 711 return _kern_read_stat(fDirFd, fName, false, st, sizeof(struct stat)); 712 } 713 714 715 status_t 716 BEntry::_GetStat(struct stat_beos* st) const 717 { 718 struct stat newStat; 719 status_t error = _GetStat(&newStat); 720 if (error != B_OK) 721 return error; 722 723 convert_to_stat_beos(&newStat, st); 724 return B_OK; 725 } 726 727 728 // #pragma mark - 729 730 731 status_t 732 get_ref_for_path(const char* path, entry_ref* ref) 733 { 734 status_t error = path && ref ? B_OK : B_BAD_VALUE; 735 if (error == B_OK) { 736 BEntry entry(path); 737 error = entry.InitCheck(); 738 if (error == B_OK) 739 error = entry.GetRef(ref); 740 } 741 return error; 742 } 743 744 745 bool 746 operator<(const entry_ref& a, const entry_ref& b) 747 { 748 return (a.device < b.device 749 || (a.device == b.device 750 && (a.directory < b.directory 751 || (a.directory == b.directory 752 && ((a.name == NULL && b.name != NULL) 753 || (a.name != NULL && b.name != NULL 754 && strcmp(a.name, b.name) < 0)))))); 755 } 756 757 758 // #pragma mark - symbol versions 759 760 761 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST 762 # if __GNUC__ == 2 // gcc 2 763 764 B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP4stat", 765 "GetStat__C6BEntryP4stat@@LIBBE_TEST"); 766 767 # else // gcc 4 768 769 // Haiku GetStat() 770 B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP4stat", 771 "_ZNK6BEntry7GetStatEP4stat@@LIBBE_TEST"); 772 773 # endif // gcc 4 774 #else // !HAIKU_TARGET_PLATFORM_LIBBE_TEST 775 # if __GNUC__ == 2 // gcc 2 776 777 // BeOS compatible GetStat() 778 B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP9stat_beos", 779 "GetStat__C6BEntryP4stat@LIBBE_BASE"); 780 781 // Haiku GetStat() 782 B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP4stat", 783 "GetStat__C6BEntryP4stat@@LIBBE_1_ALPHA1"); 784 785 # else // gcc 4 786 787 // BeOS compatible GetStat() 788 B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP9stat_beos", 789 "_ZNK6BEntry7GetStatEP4stat@LIBBE_BASE"); 790 791 // Haiku GetStat() 792 B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP4stat", 793 "_ZNK6BEntry7GetStatEP4stat@@LIBBE_1_ALPHA1"); 794 795 # endif // gcc 4 796 #endif // !HAIKU_TARGET_PLATFORM_LIBBE_TEST 797