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 31using namespace std; 32 33 34// #pragma mark - struct entry_ref 35 36 37entry_ref::entry_ref() 38 : 39 device((dev_t)-1), 40 directory((ino_t)-1), 41 name(NULL) 42{ 43} 44 45 46entry_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 56entry_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 66entry_ref::~entry_ref() 67{ 68 free(name); 69} 70 71 72status_t 73entry_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 89bool 90entry_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 100bool 101entry_ref::operator!=(const entry_ref& ref) const 102{ 103 return !(*this == ref); 104} 105 106 107entry_ref& 108entry_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 123BEntry::BEntry() 124 : 125 fDirFd(-1), 126 fName(NULL), 127 fCStatus(B_NO_INIT) 128{ 129} 130 131 132BEntry::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 142BEntry::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 152BEntry::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 162BEntry::BEntry(const BEntry& entry) 163 : 164 fDirFd(-1), 165 fName(NULL), 166 fCStatus(B_NO_INIT) 167{ 168 *this = entry; 169} 170 171 172BEntry::~BEntry() 173{ 174 Unset(); 175} 176 177 178status_t 179BEntry::InitCheck() const 180{ 181 return fCStatus; 182} 183 184 185bool 186BEntry::Exists() const 187{ 188 // just stat the beast 189 struct stat st; 190 return GetStat(&st) == B_OK; 191} 192 193 194const char* 195BEntry::Name() const 196{ 197 if (fCStatus != B_OK) 198 return NULL; 199 200 return fName; 201} 202 203 204status_t 205BEntry::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 230status_t 231BEntry::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 249status_t 250BEntry::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 260void 261BEntry::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 276status_t 277BEntry::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 297status_t 298BEntry::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 310status_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 342status_t 343BEntry::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 370status_t 371BEntry::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 383status_t 384BEntry::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 409status_t 410BEntry::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 431status_t 432BEntry::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 444bool 445BEntry::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 468bool 469BEntry::operator!=(const BEntry& item) const 470{ 471 return !(*this == item); 472} 473 474 475BEntry& 476BEntry::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 497void BEntry::_PennyEntry1(){} 498void BEntry::_PennyEntry2(){} 499void BEntry::_PennyEntry3(){} 500void BEntry::_PennyEntry4(){} 501void BEntry::_PennyEntry5(){} 502void BEntry::_PennyEntry6(){} 503 504 505status_t 506BEntry::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 516status_t 517BEntry::_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 646status_t 647BEntry::_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 662status_t 663BEntry::_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 683void 684BEntry::_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 710status_t 711BEntry::_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 720status_t 721BEntry::_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 736status_t 737get_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 750bool 751operator<(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