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