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 /*! 12 \file Entry.cpp 13 BEntry and entry_ref implementations. 14 */ 15 16 17 #include <Entry.h> 18 19 #include <fcntl.h> 20 #include <new> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <unistd.h> 25 26 #include <compat/sys/stat.h> 27 28 #include <Directory.h> 29 #include <Path.h> 30 #include <SymLink.h> 31 32 #include <syscalls.h> 33 34 #include "storage_support.h" 35 36 37 using namespace std; 38 39 40 // #pragma mark - struct entry_ref 41 42 43 /*! \struct entry_ref 44 \brief A filesystem entry represented as a name in a concrete directory. 45 46 entry_refs may refer to pre-existing (concrete) files, as well as 47 non-existing (abstract) files. However, the parent directory of the file 48 \b must exist. 49 50 The result of this dichotomy is a blending of the persistence gained by 51 referring to entries with a reference to their internal filesystem node and 52 the flexibility gained by referring to entries by name. 53 54 For example, if the directory in which the entry resides (or a directory 55 further up in the hierarchy) is moved or renamed, the entry_ref will still 56 refer to the correct file (whereas a pathname to the previous location of 57 the file would now be invalid). 58 59 On the other hand, say that the entry_ref refers to a concrete file. If the 60 file itself is renamed, the entry_ref now refers to an abstract file with 61 the old name (the upside in this case is that abstract entries may be 62 represented by entry_refs without preallocating an internal filesystem node 63 for them). 64 */ 65 66 67 /*! Creates an unitialized entry_ref. 68 */ 69 entry_ref::entry_ref() 70 : 71 device((dev_t)-1), 72 directory((ino_t)-1), 73 name(NULL) 74 { 75 } 76 77 78 /*! \brief Creates an entry_ref initialized to the given file name in the given 79 directory on the given device. 80 81 \p name may refer to either a pre-existing file in the given directory, or 82 a non-existent file. No explicit checking is done to verify validity of the 83 given arguments, but later use of the entry_ref will fail if \p dev is not 84 a valid device or \p dir is a not a directory on \p dev. 85 86 \param dev the device on which the entry's parent directory resides 87 \param dir the directory in which the entry resides 88 \param name the leaf name of the entry, which is not required to exist 89 */ 90 entry_ref::entry_ref(dev_t dev, ino_t dir, const char* name) 91 : 92 device(dev), 93 directory(dir), 94 name(NULL) 95 { 96 set_name(name); 97 } 98 99 100 /*! \brief Creates a copy of the given entry_ref. 101 102 \param ref a reference to an entry_ref to copy 103 */ 104 entry_ref::entry_ref(const entry_ref& ref) 105 : 106 device(ref.device), 107 directory(ref.directory), 108 name(NULL) 109 { 110 set_name(ref.name); 111 } 112 113 114 /*! \brief Destroys the object and frees the storage allocated for the leaf 115 name, if necessary. 116 */ 117 entry_ref::~entry_ref() 118 { 119 free(name); 120 } 121 122 123 /*! \brief Set the entry_ref's leaf name, freeing the storage allocated for any 124 previous name and then making a copy of the new name. 125 126 \param name pointer to a null-terminated string containing the new name for 127 the entry. May be \c NULL. 128 */ 129 status_t 130 entry_ref::set_name(const char* name) 131 { 132 free(this->name); 133 134 if (name == NULL) { 135 this->name = NULL; 136 } else { 137 this->name = strdup(name); 138 if (!this->name) 139 return B_NO_MEMORY; 140 } 141 142 return B_OK; 143 } 144 145 146 /*! \brief Compares the entry_ref with another entry_ref, returning true if 147 they are equal. 148 \return 149 - \c true - The entry_refs are equal 150 - \c false - The entry_refs are not equal 151 */ 152 bool 153 entry_ref::operator==(const entry_ref& ref) const 154 { 155 return (device == ref.device 156 && directory == ref.directory 157 && (name == ref.name 158 || (name != NULL && ref.name != NULL 159 && strcmp(name, ref.name) == 0))); 160 } 161 162 163 /*! \brief Compares the entry_ref with another entry_ref, returning true if 164 they are not equal. 165 \return 166 - \c true - The entry_refs are not equal 167 - \c false - The entry_refs are equal 168 */ 169 bool 170 entry_ref::operator!=(const entry_ref& ref) const 171 { 172 return !(*this == ref); 173 } 174 175 176 /*! \brief Makes the entry_ref a copy of the entry_ref specified by \a ref. 177 \param ref the entry_ref to copy 178 \return 179 - A reference to the copy 180 */ 181 entry_ref& 182 entry_ref::operator=(const entry_ref& ref) 183 { 184 if (this == &ref) 185 return *this; 186 187 device = ref.device; 188 directory = ref.directory; 189 set_name(ref.name); 190 return *this; 191 } 192 193 194 /*! \var dev_t entry_ref::device 195 \brief The device id of the storage device on which the entry resides 196 197 */ 198 199 /*! \var ino_t entry_ref::directory 200 \brief The inode number of the directory in which the entry resides 201 */ 202 203 /*! \var char *entry_ref::name 204 \brief The leaf name of the entry 205 */ 206 207 208 // #pragma mark - BEntry 209 210 211 /*! \class BEntry 212 \brief A location in the filesystem 213 214 The BEntry class defines objects that represent "locations" in the file system 215 hierarchy. Each location (or entry) is given as a name within a directory. For 216 example, when you create a BEntry thus: 217 218 \code 219 BEntry entry("/boot/home/fido"); 220 \endcode 221 222 ...you're telling the BEntry object to represent the location of the file 223 called fido within the directory \c "/boot/home". 224 225 \author <a href='mailto:bonefish@users.sf.net'>Ingo Weinhold</a> 226 \author <a href='mailto:tylerdauwalder@users.sf.net'>Tyler Dauwalder</a> 227 \author <a href='mailto:scusack@users.sf.net'>Simon Cusack</a> 228 229 \version 0.0.0 230 */ 231 232 233 /*! \brief Creates an uninitialized BEntry object. 234 Should be followed by a call to one of the SetTo functions, or an 235 assignment: 236 - SetTo(const BDirectory*, const char*, bool) 237 - SetTo(const entry_ref*, bool) 238 - SetTo(const char*, bool) 239 - operator=(const BEntry&) 240 */ 241 BEntry::BEntry() 242 : 243 fDirFd(-1), 244 fName(NULL), 245 fCStatus(B_NO_INIT) 246 { 247 } 248 249 250 /*! \brief Creates a BEntry initialized to the given directory and path 251 combination. 252 253 If traverse is true and \c dir/path refers to a symlink, the BEntry will 254 refer to the linked file; if false, the BEntry will refer to the symlink 255 itself. 256 257 \param dir directory in which \a path resides 258 \param path relative path reckoned off of \a dir 259 \param traverse whether or not to traverse symlinks 260 \see SetTo(const BDirectory*, const char *, bool) 261 */ 262 BEntry::BEntry(const BDirectory* dir, const char* path, bool traverse) 263 : 264 fDirFd(-1), 265 fName(NULL), 266 fCStatus(B_NO_INIT) 267 { 268 SetTo(dir, path, traverse); 269 } 270 271 272 /*! \brief Creates a BEntry for the file referred to by the given entry_ref. 273 If traverse is true and \a ref refers to a symlink, the BEntry 274 will refer to the linked file; if false, the BEntry will refer 275 to the symlink itself. 276 277 \param ref the entry_ref referring to the given file 278 \param traverse whether or not symlinks are to be traversed 279 \see SetTo(const entry_ref*, bool) 280 */ 281 BEntry::BEntry(const entry_ref* ref, bool traverse) 282 : 283 fDirFd(-1), 284 fName(NULL), 285 fCStatus(B_NO_INIT) 286 { 287 SetTo(ref, traverse); 288 } 289 290 291 /*! \brief Creates a BEntry initialized to the given path. 292 293 If \a path is relative, it will be reckoned off the current working 294 directory. If \a path refers to a symlink and traverse is true, the BEntry 295 will refer to the linked file. If traverse is false, the BEntry will refer 296 to the symlink itself. 297 298 \param path the file of interest 299 \param traverse whether or not symlinks are to be traversed 300 \see SetTo(const char*, bool) 301 */ 302 BEntry::BEntry(const char* path, bool traverse) 303 : 304 fDirFd(-1), 305 fName(NULL), 306 fCStatus(B_NO_INIT) 307 { 308 SetTo(path, traverse); 309 } 310 311 312 /*! \brief Creates a copy of the given BEntry. 313 \param entry the entry to be copied 314 \see operator=(const BEntry&) 315 */ 316 BEntry::BEntry(const BEntry& entry) 317 : 318 fDirFd(-1), 319 fName(NULL), 320 fCStatus(B_NO_INIT) 321 { 322 *this = entry; 323 } 324 325 326 /*! \brief Frees all of the BEntry's allocated resources. 327 \see Unset() 328 */ 329 BEntry::~BEntry() 330 { 331 Unset(); 332 } 333 334 335 /*! \brief Returns the result of the most recent construction or SetTo() call. 336 \return 337 - \c B_OK Success 338 - \c B_NO_INIT The object has been Unset() or is uninitialized 339 - <code>some error code</code> 340 */ 341 status_t 342 BEntry::InitCheck() const 343 { 344 return fCStatus; 345 } 346 347 348 /*! \brief Returns true if the Entry exists in the filesytem, false otherwise. 349 \return 350 - \c true - The entry exists 351 - \c false - The entry does not exist 352 */ 353 bool 354 BEntry::Exists() const 355 { 356 // just stat the beast 357 struct stat st; 358 return GetStat(&st) == B_OK; 359 } 360 361 362 /*! \fn status_t BEntry::GetStat(struct stat *result) const 363 \brief Fills in a stat structure for the entry. The information is copied 364 into the \c stat structure pointed to by \a result. 365 366 \b NOTE: The BStatable object does not cache the stat structure; every time 367 you call GetStat(), fresh stat information is retrieved. 368 369 \param result pointer to a pre-allocated structure into which the stat 370 information will be copied 371 \return 372 - \c B_OK - Success 373 - "error code" - Failure 374 */ 375 376 377 /*! \brief Reinitializes the BEntry to the path or directory path combination, 378 resolving symlinks if traverse is true 379 380 \return 381 - \c B_OK - Success 382 - "error code" - Failure 383 */ 384 status_t 385 BEntry::SetTo(const BDirectory* dir, const char* path, bool traverse) 386 { 387 // check params 388 if (!dir) 389 return (fCStatus = B_BAD_VALUE); 390 if (path && path[0] == '\0') // R5 behaviour 391 path = NULL; 392 393 // if path is absolute, let the path-only SetTo() do the job 394 if (BPrivate::Storage::is_absolute_path(path)) 395 return SetTo(path, traverse); 396 397 Unset(); 398 399 if (dir->InitCheck() != B_OK) 400 fCStatus = B_BAD_VALUE; 401 402 // dup() the dir's FD and let set() do the rest 403 int dirFD = _kern_dup(dir->get_fd()); 404 if (dirFD < 0) 405 return (fCStatus = dirFD); 406 return (fCStatus = _SetTo(dirFD, path, traverse)); 407 } 408 409 410 /*! \brief Reinitializes the BEntry to the entry_ref, resolving symlinks if 411 traverse is true. 412 413 \return 414 - \c B_OK - Success 415 - "error code" - Failure 416 */ 417 status_t 418 BEntry::SetTo(const entry_ref* ref, bool traverse) 419 { 420 Unset(); 421 if (ref == NULL) 422 return (fCStatus = B_BAD_VALUE); 423 424 // if ref-name is absolute, let the path-only SetTo() do the job 425 if (BPrivate::Storage::is_absolute_path(ref->name)) 426 return SetTo(ref->name, traverse); 427 428 // open the directory and let set() do the rest 429 int dirFD = _kern_open_dir_entry_ref(ref->device, ref->directory, NULL); 430 if (dirFD < 0) 431 return (fCStatus = dirFD); 432 return (fCStatus = _SetTo(dirFD, ref->name, traverse)); 433 } 434 435 436 /*! \brief Reinitializes the BEntry object to the path, resolving symlinks if 437 traverse is true. 438 439 \return 440 - \c B_OK - Success 441 - "error code" - Failure 442 */ 443 status_t 444 BEntry::SetTo(const char* path, bool traverse) 445 { 446 Unset(); 447 // check the argument 448 if (!path) 449 return (fCStatus = B_BAD_VALUE); 450 return (fCStatus = _SetTo(-1, path, traverse)); 451 } 452 453 454 /*! \brief Reinitializes the BEntry to an uninitialized BEntry object */ 455 void 456 BEntry::Unset() 457 { 458 // Close the directory 459 if (fDirFd >= 0) 460 _kern_close(fDirFd); 461 462 // Free our leaf name 463 free(fName); 464 465 fDirFd = -1; 466 fName = NULL; 467 fCStatus = B_NO_INIT; 468 } 469 470 471 /*! \brief Gets an entry_ref structure for the BEntry. 472 473 \param ref pointer to a preallocated entry_ref into which the result is 474 copied 475 \return 476 - \c B_OK - Success 477 - "error code" - Failure 478 479 */ 480 status_t 481 BEntry::GetRef(entry_ref* ref) const 482 { 483 if (fCStatus != B_OK) 484 return B_NO_INIT; 485 486 if (ref == NULL) 487 return B_BAD_VALUE; 488 489 struct stat st; 490 status_t error = _kern_read_stat(fDirFd, NULL, false, &st, 491 sizeof(struct stat)); 492 if (error == B_OK) { 493 ref->device = st.st_dev; 494 ref->directory = st.st_ino; 495 error = ref->set_name(fName); 496 } 497 return error; 498 } 499 500 501 /*! \brief Gets the path for the BEntry. 502 503 \param path pointer to a pre-allocated BPath object into which the result 504 is stored 505 \return 506 - \c B_OK - Success 507 - "error code" - Failure 508 509 */ 510 status_t 511 BEntry::GetPath(BPath* path) const 512 { 513 if (fCStatus != B_OK) 514 return B_NO_INIT; 515 516 if (path == NULL) 517 return B_BAD_VALUE; 518 519 return path->SetTo(this); 520 } 521 522 523 /*! \brief Gets the parent of the BEntry as another BEntry. 524 525 If the function fails, the argument is Unset(). Destructive calls to 526 GetParent() are allowed, i.e.: 527 528 \code 529 BEntry entry("/boot/home/fido"); 530 status_t err; 531 char name[B_FILE_NAME_LENGTH]; 532 533 // Spit out the path components backwards, one at a time. 534 do { 535 entry.GetName(name); 536 printf("> %s\n", name); 537 } while ((err=entry.GetParent(&entry)) == B_OK); 538 539 // Complain for reasons other than reaching the top. 540 if (err != B_ENTRY_NOT_FOUND) 541 printf(">> Error: %s\n", strerror(err)); 542 \endcode 543 544 will output: 545 546 \code 547 > fido 548 > home 549 > boot 550 > . 551 \endcode 552 553 \param entry pointer to a pre-allocated BEntry object into which the result 554 is stored 555 \return 556 - \c B_OK - Success 557 - \c B_ENTRY_NOT_FOUND - Attempted to get the parent of the root directory 558 \c "/" 559 - "error code" - Failure 560 */ 561 status_t BEntry::GetParent(BEntry* entry) const 562 { 563 // check parameter and initialization 564 if (fCStatus != B_OK) 565 return B_NO_INIT; 566 if (entry == NULL) 567 return B_BAD_VALUE; 568 569 // check whether we are the root directory 570 // It is sufficient to check whether our leaf name is ".". 571 if (strcmp(fName, ".") == 0) 572 return B_ENTRY_NOT_FOUND; 573 574 // open the parent directory 575 char leafName[B_FILE_NAME_LENGTH]; 576 int parentFD = _kern_open_parent_dir(fDirFd, leafName, B_FILE_NAME_LENGTH); 577 if (parentFD < 0) 578 return parentFD; 579 580 // set close on exec flag on dir FD 581 fcntl(parentFD, F_SETFD, FD_CLOEXEC); 582 583 // init the entry 584 entry->Unset(); 585 entry->fDirFd = parentFD; 586 entry->fCStatus = entry->_SetName(leafName); 587 if (entry->fCStatus != B_OK) 588 entry->Unset(); 589 return entry->fCStatus; 590 } 591 592 593 /*! \brief Gets the parent of the BEntry as a BDirectory. 594 595 If the function fails, the argument is Unset(). 596 597 \param dir pointer to a pre-allocated BDirectory object into which the 598 result is stored 599 \return 600 - \c B_OK - Success 601 - \c B_ENTRY_NOT_FOUND - Attempted to get the parent of the root directory 602 \c "/" 603 - "error code" - Failure 604 */ 605 status_t 606 BEntry::GetParent(BDirectory* dir) const 607 { 608 // check initialization and parameter 609 if (fCStatus != B_OK) 610 return B_NO_INIT; 611 if (dir == NULL) 612 return B_BAD_VALUE; 613 // check whether we are the root directory 614 // It is sufficient to check whether our leaf name is ".". 615 if (strcmp(fName, ".") == 0) 616 return B_ENTRY_NOT_FOUND; 617 // get a node ref for the directory and init it 618 struct stat st; 619 status_t error = _kern_read_stat(fDirFd, NULL, false, &st, 620 sizeof(struct stat)); 621 if (error != B_OK) 622 return error; 623 node_ref ref; 624 ref.device = st.st_dev; 625 ref.node = st.st_ino; 626 return dir->SetTo(&ref); 627 // TODO: This can be optimized: We already have a FD for the directory, 628 // so we could dup() it and set it on the directory. We just need a private 629 // API for being able to do that. 630 } 631 632 633 /*! \brief Gets the name of the entry's leaf. 634 635 \c buffer must be pre-allocated and of sufficient 636 length to hold the entire string. A length of \c B_FILE_NAME_LENGTH is 637 recommended. 638 639 \param buffer pointer to a pre-allocated string into which the result is 640 copied 641 \return 642 - \c B_OK - Success 643 - "error code" - Failure 644 */ 645 status_t 646 BEntry::GetName(char* buffer) const 647 { 648 status_t result = B_ERROR; 649 650 if (fCStatus != B_OK) { 651 result = B_NO_INIT; 652 } else if (buffer == NULL) { 653 result = B_BAD_VALUE; 654 } else { 655 strcpy(buffer, fName); 656 result = B_OK; 657 } 658 659 return result; 660 } 661 662 663 /*! \brief Renames the BEntry to path, replacing an existing entry if clobber 664 is true. 665 666 NOTE: The BEntry must refer to an existing file. If it is abstract, this 667 method will fail. 668 669 \param path Pointer to a string containing the new name for the entry. May 670 be absolute or relative. If relative, the entry is renamed within its 671 current directory. 672 \param clobber If \c false and a file with the name given by \c path already 673 exists, the method will fail. If \c true and such a file exists, it will 674 be overwritten. 675 \return 676 - \c B_OK - Success 677 - \c B_ENTRY_EXISTS - The new location is already taken and \c clobber was 678 \c false 679 - \c B_ENTRY_NOT_FOUND - Attempted to rename an abstract entry 680 - "error code" - Failure 681 682 */ 683 status_t 684 BEntry::Rename(const char* path, bool clobber) 685 { 686 // check parameter and initialization 687 if (path == NULL) 688 return B_BAD_VALUE; 689 if (fCStatus != B_OK) 690 return B_NO_INIT; 691 // get an entry representing the target location 692 BEntry target; 693 status_t error; 694 if (BPrivate::Storage::is_absolute_path(path)) { 695 error = target.SetTo(path); 696 } else { 697 int dirFD = _kern_dup(fDirFd); 698 if (dirFD < 0) 699 return dirFD; 700 // init the entry 701 error = target.fCStatus = target._SetTo(dirFD, path, false); 702 } 703 if (error != B_OK) 704 return error; 705 return _Rename(target, clobber); 706 } 707 708 709 /*! \brief Moves the BEntry to directory or directory+path combination, 710 replacing an existing entry if clobber is true. 711 712 NOTE: The BEntry must refer to an existing file. If it is abstract, this 713 method will fail. 714 715 \param dir Pointer to a pre-allocated BDirectory into which the entry should 716 be moved. 717 \param path Optional new leaf name for the entry. May be a simple leaf or a 718 relative path; either way, \c path is reckoned off of \c dir. If 719 \c NULL, the entry retains its previous leaf name. 720 \param clobber If \c false and an entry already exists at the specified 721 destination, the method will fail. If \c true and such an entry exists, 722 it will be overwritten. 723 \return - \c B_OK - Success 724 - \c B_ENTRY_EXISTS - The new location is already taken and \c clobber 725 was \c false 726 - \c B_ENTRY_NOT_FOUND - Attempted to move an abstract entry 727 - "error code" - Failure 728 */ 729 status_t 730 BEntry::MoveTo(BDirectory* dir, const char* path, bool clobber) 731 { 732 // check parameters and initialization 733 if (fCStatus != B_OK) 734 return B_NO_INIT; 735 if (dir == NULL) 736 return B_BAD_VALUE; 737 if (dir->InitCheck() != B_OK) 738 return B_BAD_VALUE; 739 // NULL path simply means move without renaming 740 if (path == NULL) 741 path = fName; 742 // get an entry representing the target location 743 BEntry target; 744 status_t error = target.SetTo(dir, path); 745 if (error != B_OK) 746 return error; 747 return _Rename(target, clobber); 748 } 749 750 751 /*! \brief Removes the entry from the file system. 752 753 NOTE: If any file descriptors are open on the file when Remove() is called, 754 the chunk of data they refer to will continue to exist until all such file 755 descriptors are closed. The BEntry object, however, becomes abstract and 756 no longer refers to any actual data in the filesystem. 757 758 \return 759 - B_OK - Success 760 - "error code" - Failure 761 */ 762 status_t 763 BEntry::Remove() 764 { 765 if (fCStatus != B_OK) 766 return B_NO_INIT; 767 768 if (IsDirectory()) 769 return _kern_remove_dir(fDirFd, fName); 770 771 return _kern_unlink(fDirFd, fName); 772 } 773 774 775 /*! \brief Returns true if the BEntry and \c item refer to the same entry or 776 if they are both uninitialized. 777 778 \return \c true - Both BEntry objects refer to the same entry or they are 779 both uninitialzed 780 \c false - The BEntry objects refer to different entries 781 */ 782 bool 783 BEntry::operator==(const BEntry& item) const 784 { 785 // First check statuses 786 if (this->InitCheck() != B_OK && item.InitCheck() != B_OK) { 787 return true; 788 } else if (this->InitCheck() == B_OK && item.InitCheck() == B_OK) { 789 790 // Directories don't compare well directly, so we'll 791 // compare entry_refs instead 792 entry_ref ref1, ref2; 793 if (this->GetRef(&ref1) != B_OK) 794 return false; 795 if (item.GetRef(&ref2) != B_OK) 796 return false; 797 return (ref1 == ref2); 798 799 } else { 800 return false; 801 } 802 803 } 804 805 806 /*! \brief Returns false if the BEntry and \c item refer to the same entry or 807 if they are both uninitialized. 808 809 \return \c true - The BEntry objects refer to different entries 810 \c false - Both BEntry objects refer to the same entry or they are 811 both uninitialzed 812 */ 813 bool 814 BEntry::operator!=(const BEntry& item) const 815 { 816 return !(*this == item); 817 } 818 819 820 /*! \brief Reinitializes the BEntry to be a copy of the argument 821 822 \return 823 - A reference to the copy 824 */ 825 BEntry& 826 BEntry::operator=(const BEntry& item) 827 { 828 if (this == &item) 829 return *this; 830 831 Unset(); 832 if (item.fCStatus == B_OK) { 833 fDirFd = _kern_dup(item.fDirFd); 834 if (fDirFd >= 0) 835 fCStatus = _SetName(item.fName); 836 else 837 fCStatus = fDirFd; 838 839 if (fCStatus != B_OK) 840 Unset(); 841 } 842 843 return *this; 844 } 845 846 847 void BEntry::_PennyEntry1(){} 848 void BEntry::_PennyEntry2(){} 849 void BEntry::_PennyEntry3(){} 850 void BEntry::_PennyEntry4(){} 851 void BEntry::_PennyEntry5(){} 852 void BEntry::_PennyEntry6(){} 853 854 855 /*! \brief Updates the BEntry with the data from the stat structure according 856 to the mask. 857 */ 858 status_t 859 BEntry::set_stat(struct stat& st, uint32 what) 860 { 861 if (fCStatus != B_OK) 862 return B_FILE_ERROR; 863 864 return _kern_write_stat(fDirFd, fName, false, &st, sizeof(struct stat), 865 what); 866 } 867 868 869 /*! Sets the Entry to point to the entry specified by the path \a path relative 870 to the given directory. If \a traverse is \c true and the given entry is a 871 symlink, the object is recursively set to point to the entry pointed to by 872 the symlink. 873 874 If \a path is an absolute path, \a dirFD is ignored. 875 If \a dirFD is -1, path is considered relative to the current directory 876 (unless it is an absolute path, that is). 877 878 The ownership of the file descriptor \a dirFD is transferred to the 879 function, regardless of whether it succeeds or fails. The caller must not 880 close the FD afterwards. 881 882 \param dirFD File descriptor of a directory relative to which path is to 883 be considered. May be -1, when the current directory shall be 884 considered. 885 \param path Pointer to a path relative to the given directory. 886 \param traverse If \c true and the given entry is a symlink, the object is 887 recursively set to point to the entry linked to by the symlink. 888 \return 889 - B_OK - Success 890 - "error code" - Failure 891 */ 892 status_t 893 BEntry::_SetTo(int dirFD, const char* path, bool traverse) 894 { 895 bool requireConcrete = false; 896 FDCloser fdCloser(dirFD); 897 char tmpPath[B_PATH_NAME_LENGTH]; 898 char leafName[B_FILE_NAME_LENGTH]; 899 int32 linkLimit = B_MAX_SYMLINKS; 900 while (true) { 901 if (!path || strcmp(path, ".") == 0) { 902 // "." 903 // if no dir FD is supplied, we need to open the current directory 904 // first 905 if (dirFD < 0) { 906 dirFD = _kern_open_dir(-1, "."); 907 if (dirFD < 0) 908 return dirFD; 909 fdCloser.SetTo(dirFD); 910 } 911 // get the parent directory 912 int parentFD = _kern_open_parent_dir(dirFD, leafName, 913 B_FILE_NAME_LENGTH); 914 if (parentFD < 0) 915 return parentFD; 916 dirFD = parentFD; 917 fdCloser.SetTo(dirFD); 918 break; 919 } else if (strcmp(path, "..") == 0) { 920 // ".." 921 // open the parent directory 922 int parentFD = _kern_open_dir(dirFD, ".."); 923 if (parentFD < 0) 924 return parentFD; 925 dirFD = parentFD; 926 fdCloser.SetTo(dirFD); 927 // get the parent's parent directory 928 parentFD = _kern_open_parent_dir(dirFD, leafName, 929 B_FILE_NAME_LENGTH); 930 if (parentFD < 0) 931 return parentFD; 932 dirFD = parentFD; 933 fdCloser.SetTo(dirFD); 934 break; 935 } else { 936 // an ordinary path; analyze it 937 char dirPath[B_PATH_NAME_LENGTH]; 938 status_t error = BPrivate::Storage::parse_path(path, dirPath, 939 leafName); 940 if (error != B_OK) 941 return error; 942 // special case: root directory ("/") 943 if (leafName[0] == '\0' && dirPath[0] == '/') 944 strcpy(leafName, "."); 945 if (leafName[0] == '\0') { 946 // the supplied path is already a leaf 947 error = BPrivate::Storage::check_entry_name(dirPath); 948 if (error != B_OK) 949 return error; 950 strcpy(leafName, dirPath); 951 // if no directory was given, we need to open the current dir 952 // now 953 if (dirFD < 0) { 954 char* cwd = getcwd(tmpPath, B_PATH_NAME_LENGTH); 955 if (!cwd) 956 return B_ERROR; 957 dirFD = _kern_open_dir(-1, cwd); 958 if (dirFD < 0) 959 return dirFD; 960 fdCloser.SetTo(dirFD); 961 } 962 } else if (strcmp(leafName, ".") == 0 963 || strcmp(leafName, "..") == 0) { 964 // We have to resolve this to get the entry name. Just open 965 // the dir and let the next iteration deal with it. 966 dirFD = _kern_open_dir(-1, path); 967 if (dirFD < 0) 968 return dirFD; 969 fdCloser.SetTo(dirFD); 970 path = NULL; 971 continue; 972 } else { 973 int parentFD = _kern_open_dir(dirFD, dirPath); 974 if (parentFD < 0) 975 return parentFD; 976 dirFD = parentFD; 977 fdCloser.SetTo(dirFD); 978 } 979 // traverse symlinks, if desired 980 if (!traverse) 981 break; 982 struct stat st; 983 error = _kern_read_stat(dirFD, leafName, false, &st, 984 sizeof(struct stat)); 985 if (error == B_ENTRY_NOT_FOUND && !requireConcrete) { 986 // that's fine -- the entry is abstract and was not target of 987 // a symlink we resolved 988 break; 989 } 990 if (error != B_OK) 991 return error; 992 // the entry is concrete 993 if (!S_ISLNK(st.st_mode)) 994 break; 995 requireConcrete = true; 996 // we need to traverse the symlink 997 if (--linkLimit < 0) 998 return B_LINK_LIMIT; 999 size_t bufferSize = B_PATH_NAME_LENGTH - 1; 1000 error = _kern_read_link(dirFD, leafName, tmpPath, &bufferSize); 1001 if (error < 0) 1002 return error; 1003 tmpPath[bufferSize] = '\0'; 1004 path = tmpPath; 1005 // next round... 1006 } 1007 } 1008 1009 // set close on exec flag on dir FD 1010 fcntl(dirFD, F_SETFD, FD_CLOEXEC); 1011 1012 // set the result 1013 status_t error = _SetName(leafName); 1014 if (error != B_OK) 1015 return error; 1016 fdCloser.Detach(); 1017 fDirFd = dirFD; 1018 return B_OK; 1019 } 1020 1021 1022 /*! \brief Handles string allocation, deallocation, and copying for the entry's 1023 leaf name. 1024 1025 \return 1026 - B_OK - Success 1027 - "error code" - Failure 1028 */ 1029 status_t 1030 BEntry::_SetName(const char* name) 1031 { 1032 if (name == NULL) 1033 return B_BAD_VALUE; 1034 1035 free(fName); 1036 1037 fName = strdup(name); 1038 if (fName == NULL) 1039 return B_NO_MEMORY; 1040 1041 return B_OK; 1042 } 1043 1044 1045 /*! \brief Renames the entry referred to by this object to the location 1046 specified by \a target. 1047 1048 If an entry exists at the target location, the method fails, unless 1049 \a clobber is \c true, in which case that entry is overwritten (doesn't 1050 work for non-empty directories, though). 1051 1052 If the operation was successful, this entry is made a clone of the 1053 supplied one and the supplied one is uninitialized. 1054 1055 \param target The entry specifying the target location. 1056 \param clobber If \c true, the an entry existing at the target location 1057 will be overwritten. 1058 \return \c B_OK, if everything went fine, another error code otherwise. 1059 */ 1060 status_t 1061 BEntry::_Rename(BEntry& target, bool clobber) 1062 { 1063 // check, if there's an entry in the way 1064 if (!clobber && target.Exists()) 1065 return B_FILE_EXISTS; 1066 // rename 1067 status_t error = _kern_rename(fDirFd, fName, target.fDirFd, target.fName); 1068 if (error == B_OK) { 1069 Unset(); 1070 fCStatus = target.fCStatus; 1071 fDirFd = target.fDirFd; 1072 fName = target.fName; 1073 target.fCStatus = B_NO_INIT; 1074 target.fDirFd = -1; 1075 target.fName = NULL; 1076 } 1077 return error; 1078 } 1079 1080 1081 /*! Debugging function, dumps the given entry to stdout. 1082 1083 \param name Pointer to a string to be printed along with the dump for 1084 identification purposes. 1085 */ 1086 void 1087 BEntry::_Dump(const char* name) 1088 { 1089 if (name != NULL) { 1090 printf("------------------------------------------------------------\n"); 1091 printf("%s\n", name); 1092 printf("------------------------------------------------------------\n"); 1093 } 1094 1095 printf("fCStatus == %ld\n", fCStatus); 1096 1097 struct stat st; 1098 if (fDirFd != -1 1099 && _kern_read_stat(fDirFd, NULL, false, &st, 1100 sizeof(struct stat)) == B_OK) { 1101 printf("dir.device == %ld\n", st.st_dev); 1102 printf("dir.inode == %lld\n", st.st_ino); 1103 } else { 1104 printf("dir == NullFd\n"); 1105 } 1106 1107 printf("leaf == '%s'\n", fName); 1108 printf("\n"); 1109 1110 } 1111 1112 1113 status_t 1114 BEntry::_GetStat(struct stat* st) const 1115 { 1116 if (fCStatus != B_OK) 1117 return B_NO_INIT; 1118 1119 return _kern_read_stat(fDirFd, fName, false, st, sizeof(struct stat)); 1120 } 1121 1122 1123 status_t 1124 BEntry::_GetStat(struct stat_beos* st) const 1125 { 1126 struct stat newStat; 1127 status_t error = _GetStat(&newStat); 1128 if (error != B_OK) 1129 return error; 1130 1131 convert_to_stat_beos(&newStat, st); 1132 return B_OK; 1133 } 1134 1135 1136 // #pragma mark - 1137 1138 1139 /*! \brief Returns an entry_ref for a given path. 1140 \param path The path name referring to the entry 1141 \param ref The entry_ref structure to be filled in 1142 \return 1143 - \c B_OK - Everything went fine. 1144 - \c B_BAD_VALUE - \c NULL \a path or \a ref. 1145 - \c B_ENTRY_NOT_FOUND - A (non-leaf) path component does not exist. 1146 - \c B_NO_MEMORY - Insufficient memory for successful completion. 1147 */ 1148 status_t 1149 get_ref_for_path(const char* path, entry_ref* ref) 1150 { 1151 status_t error = path && ref ? B_OK : B_BAD_VALUE; 1152 if (error == B_OK) { 1153 BEntry entry(path); 1154 error = entry.InitCheck(); 1155 if (error == B_OK) 1156 error = entry.GetRef(ref); 1157 } 1158 return error; 1159 } 1160 1161 1162 /*! \brief Returns whether an entry is less than another. 1163 The components are compared in order \c device, \c directory, \c name. 1164 A \c NULL \c name is less than any non-null name. 1165 1166 \return 1167 - true - a < b 1168 - false - a >= b 1169 */ 1170 bool 1171 operator<(const entry_ref& a, const entry_ref& b) 1172 { 1173 return (a.device < b.device 1174 || (a.device == b.device 1175 && (a.directory < b.directory 1176 || (a.directory == b.directory 1177 && ((a.name == NULL && b.name != NULL) 1178 || (a.name != NULL && b.name != NULL 1179 && strcmp(a.name, b.name) < 0)))))); 1180 } 1181 1182 1183 // #pragma mark - symbol versions 1184 1185 1186 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST 1187 # if __GNUC__ == 2 // gcc 2 1188 1189 B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP4stat", 1190 "GetStat__C6BEntryP4stat@@LIBBE_TEST"); 1191 1192 # else // gcc 4 1193 1194 // Haiku GetStat() 1195 B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP4stat", 1196 "_ZNK6BEntry7GetStatEP4stat@@LIBBE_TEST"); 1197 1198 # endif // gcc 4 1199 #else // !HAIKU_TARGET_PLATFORM_LIBBE_TEST 1200 # if __GNUC__ == 2 // gcc 2 1201 1202 // BeOS compatible GetStat() 1203 B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP9stat_beos", 1204 "GetStat__C6BEntryP4stat@LIBBE_BASE"); 1205 1206 // Haiku GetStat() 1207 B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP4stat", 1208 "GetStat__C6BEntryP4stat@@LIBBE_1_ALPHA1"); 1209 1210 # else // gcc 4 1211 1212 // BeOS compatible GetStat() 1213 B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP9stat_beos", 1214 "_ZNK6BEntry7GetStatEP4stat@LIBBE_BASE"); 1215 1216 // Haiku GetStat() 1217 B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP4stat", 1218 "_ZNK6BEntry7GetStatEP4stat@@LIBBE_1_ALPHA1"); 1219 1220 # endif // gcc 4 1221 #endif // !HAIKU_TARGET_PLATFORM_LIBBE_TEST 1222