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