1 //---------------------------------------------------------------------- 2 // This software is part of the Haiku distribution and is covered 3 // by the MIT license. 4 //--------------------------------------------------------------------- 5 /*! 6 \file Entry.cpp 7 BEntry and entry_ref implementations. 8 */ 9 10 #include <fcntl.h> 11 #include <new> 12 #include <stdio.h> 13 #include <string.h> 14 #include <unistd.h> 15 16 #include <Directory.h> 17 #include <Entry.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 527 // check whether we are the root directory 528 // It is sufficient to check whether our leaf name is ".". 529 if (strcmp(fName, ".") == 0) 530 return B_ENTRY_NOT_FOUND; 531 532 // open the parent directory 533 char leafName[B_FILE_NAME_LENGTH]; 534 int parentFD = _kern_open_parent_dir(fDirFd, leafName, B_FILE_NAME_LENGTH); 535 if (parentFD < 0) 536 return parentFD; 537 538 // set close on exec flag on dir FD 539 fcntl(parentFD, F_SETFD, FD_CLOEXEC); 540 541 // init the entry 542 entry->Unset(); 543 entry->fDirFd = parentFD; 544 entry->fCStatus = entry->set_name(leafName); 545 if (entry->fCStatus != B_OK) 546 entry->Unset(); 547 return entry->fCStatus; 548 } 549 550 /*! \brief Gets the parent of the BEntry as a BDirectory. 551 552 If the function fails, the argument is Unset(). 553 554 \param dir pointer to a pre-allocated BDirectory object into which the result is stored 555 \return 556 - \c B_OK - Success 557 - \c B_ENTRY_NOT_FOUND - Attempted to get the parent of the root directory \c "/" 558 - "error code" - Failure 559 */ 560 status_t 561 BEntry::GetParent(BDirectory *dir) const 562 { 563 // check initialization and parameter 564 if (fCStatus != B_OK) 565 return B_NO_INIT; 566 if (dir == NULL) 567 return B_BAD_VALUE; 568 // check whether we are the root directory 569 // It is sufficient to check whether our leaf name is ".". 570 if (strcmp(fName, ".") == 0) 571 return B_ENTRY_NOT_FOUND; 572 // get a node ref for the directory and init it 573 struct stat st; 574 status_t error = _kern_read_stat(fDirFd, NULL, false, &st, 575 sizeof(struct stat)); 576 if (error != B_OK) 577 return error; 578 node_ref ref; 579 ref.device = st.st_dev; 580 ref.node = st.st_ino; 581 return dir->SetTo(&ref); 582 // TODO: This can be optimized: We already have a FD for the directory, 583 // so we could dup() it and set it on the directory. We just need a private 584 // API for being able to do that. 585 } 586 587 /*! \brief Gets the name of the entry's leaf. 588 589 \c buffer must be pre-allocated and of sufficient 590 length to hold the entire string. A length of \c B_FILE_NAME_LENGTH is recommended. 591 592 \param buffer pointer to a pre-allocated string into which the result is copied 593 \return 594 - \c B_OK - Success 595 - "error code" - Failure 596 */ 597 status_t 598 BEntry::GetName(char *buffer) const 599 { 600 status_t result = B_ERROR; 601 602 if (fCStatus != B_OK) { 603 result = B_NO_INIT; 604 } else if (buffer == NULL) { 605 result = B_BAD_VALUE; 606 } else { 607 strcpy(buffer, fName); 608 result = B_OK; 609 } 610 611 return result; 612 } 613 614 /*! \brief Renames the BEntry to path, replacing an existing entry if clobber is true. 615 616 NOTE: The BEntry must refer to an existing file. If it is abstract, this method will fail. 617 618 \param path Pointer to a string containing the new name for the entry. May 619 be absolute or relative. If relative, the entry is renamed within its 620 current directory. 621 \param clobber If \c false and a file with the name given by \c path already exists, 622 the method will fail. If \c true and such a file exists, it will 623 be overwritten. 624 \return 625 - \c B_OK - Success 626 - \c B_ENTRY_EXISTS - The new location is already taken and \c clobber was \c false 627 - \c B_ENTRY_NOT_FOUND - Attempted to rename an abstract entry 628 - "error code" - Failure 629 630 */ 631 status_t 632 BEntry::Rename(const char *path, bool clobber) 633 { 634 // check parameter and initialization 635 if (path == NULL) 636 return B_BAD_VALUE; 637 if (fCStatus != B_OK) 638 return B_NO_INIT; 639 // get an entry representing the target location 640 BEntry target; 641 status_t error; 642 if (BPrivate::Storage::is_absolute_path(path)) { 643 error = target.SetTo(path); 644 } else { 645 int dirFD = _kern_dup(fDirFd); 646 if (dirFD < 0) 647 return dirFD; 648 // init the entry 649 error = target.fCStatus = target.set(dirFD, path, false); 650 } 651 if (error != B_OK) 652 return error; 653 return _Rename(target, clobber); 654 } 655 656 /*! \brief Moves the BEntry to directory or directory+path combination, replacing an existing entry if clobber is true. 657 658 NOTE: The BEntry must refer to an existing file. If it is abstract, this method will fail. 659 660 \param dir Pointer to a pre-allocated BDirectory into which the entry should be moved. 661 \param path Optional new leaf name for the entry. May be a simple leaf or a relative path; 662 either way, \c path is reckoned off of \c dir. If \c NULL, the entry retains 663 its previous leaf name. 664 \param clobber If \c false and an entry already exists at the specified destination, 665 the method will fail. If \c true and such an entry exists, it will 666 be overwritten. 667 \return 668 - \c B_OK - Success 669 - \c B_ENTRY_EXISTS - The new location is already taken and \c clobber was \c false 670 - \c B_ENTRY_NOT_FOUND - Attempted to move an abstract entry 671 - "error code" - Failure 672 */ 673 status_t 674 BEntry::MoveTo(BDirectory *dir, const char *path, bool clobber) 675 { 676 // check parameters and initialization 677 if (fCStatus != B_OK) 678 return B_NO_INIT; 679 if (dir == NULL) 680 return B_BAD_VALUE; 681 if (dir->InitCheck() != B_OK) 682 return B_BAD_VALUE; 683 // NULL path simply means move without renaming 684 if (path == NULL) 685 path = fName; 686 // get an entry representing the target location 687 BEntry target; 688 status_t error = target.SetTo(dir, path); 689 if (error != B_OK) 690 return error; 691 return _Rename(target, clobber); 692 } 693 694 /*! \brief Removes the entry from the file system. 695 696 NOTE: If any file descriptors are open on the file when Remove() is called, 697 the chunk of data they refer to will continue to exist until all such file 698 descriptors are closed. The BEntry object, however, becomes abstract and 699 no longer refers to any actual data in the filesystem. 700 701 \return 702 - B_OK - Success 703 - "error code" - Failure 704 */ 705 status_t 706 BEntry::Remove() 707 { 708 if (fCStatus != B_OK) 709 return B_NO_INIT; 710 711 if (IsDirectory()) 712 return _kern_remove_dir(fDirFd, fName); 713 714 return _kern_unlink(fDirFd, fName); 715 } 716 717 718 /*! \brief Returns true if the BEntry and \c item refer to the same entry or 719 if they are both uninitialized. 720 721 \return 722 - true - Both BEntry objects refer to the same entry or they are both uninitialzed 723 - false - The BEntry objects refer to different entries 724 */ 725 bool 726 BEntry::operator==(const BEntry &item) const 727 { 728 // First check statuses 729 if (this->InitCheck() != B_OK && item.InitCheck() != B_OK) { 730 return true; 731 } else if (this->InitCheck() == B_OK && item.InitCheck() == B_OK) { 732 733 // Directories don't compare well directly, so we'll 734 // compare entry_refs instead 735 entry_ref ref1, ref2; 736 if (this->GetRef(&ref1) != B_OK) 737 return false; 738 if (item.GetRef(&ref2) != B_OK) 739 return false; 740 return (ref1 == ref2); 741 742 } else { 743 return false; 744 } 745 746 } 747 748 /*! \brief Returns false if the BEntry and \c item refer to the same entry or 749 if they are both uninitialized. 750 751 \return 752 - true - The BEntry objects refer to different entries 753 - false - Both BEntry objects refer to the same entry or they are both uninitialzed 754 */ 755 bool 756 BEntry::operator!=(const BEntry &item) const 757 { 758 return !(*this == item); 759 } 760 761 /*! \brief Reinitializes the BEntry to be a copy of the argument 762 763 \return 764 - A reference to the copy 765 */ 766 BEntry& 767 BEntry::operator=(const BEntry &item) 768 { 769 if (this == &item) 770 return *this; 771 772 Unset(); 773 if (item.fCStatus == B_OK) { 774 fDirFd = _kern_dup(item.fDirFd); 775 if (fDirFd >= 0) 776 fCStatus = set_name(item.fName); 777 else 778 fCStatus = fDirFd; 779 780 if (fCStatus != B_OK) 781 Unset(); 782 } 783 784 return *this; 785 } 786 787 /*! Reserved for future use. */ 788 void BEntry::_PennyEntry1(){} 789 /*! Reserved for future use. */ 790 void BEntry::_PennyEntry2(){} 791 /*! Reserved for future use. */ 792 void BEntry::_PennyEntry3(){} 793 /*! Reserved for future use. */ 794 void BEntry::_PennyEntry4(){} 795 /*! Reserved for future use. */ 796 void BEntry::_PennyEntry5(){} 797 /*! Reserved for future use. */ 798 void BEntry::_PennyEntry6(){} 799 800 /*! \brief Updates the BEntry with the data from the stat structure according to the mask. 801 */ 802 status_t 803 BEntry::set_stat(struct stat &st, uint32 what) 804 { 805 if (fCStatus != B_OK) 806 return B_FILE_ERROR; 807 808 return _kern_write_stat(fDirFd, fName, false, &st, sizeof(struct stat), 809 what); 810 } 811 812 /*! Sets the Entry to point to the entry specified by the path \a path relative 813 to the given directory. If \a traverse is \c true and the given entry is a 814 symlink, the object is recursively set to point to the entry pointed to by 815 the symlink. 816 817 If \a path is an absolute path, \a dirFD is ignored. 818 If \a dirFD is -1, path is considered relative to the current directory 819 (unless it is an absolute path, that is). 820 821 The ownership of the file descriptor \a dirFD is transferred to the 822 function, regardless of whether it succeeds or fails. The caller must not 823 close the FD afterwards. 824 825 \param dirFD File descriptor of a directory relative to which path is to 826 be considered. May be -1, when the current directory shall be 827 considered. 828 \param path Pointer to a path relative to the given directory. 829 \param traverse If \c true and the given entry is a symlink, the object is 830 recursively set to point to the entry linked to by the symlink. 831 \return 832 - B_OK - Success 833 - "error code" - Failure 834 */ 835 status_t 836 BEntry::set(int dirFD, const char *path, bool traverse) 837 { 838 bool requireConcrete = false; 839 FDCloser fdCloser(dirFD); 840 char tmpPath[B_PATH_NAME_LENGTH]; 841 char leafName[B_FILE_NAME_LENGTH]; 842 int32 linkLimit = B_MAX_SYMLINKS; 843 while (true) { 844 if (!path || strcmp(path, ".") == 0) { 845 // "." 846 // if no dir FD is supplied, we need to open the current directory 847 // first 848 if (dirFD < 0) { 849 dirFD = _kern_open_dir(-1, "."); 850 if (dirFD < 0) 851 return dirFD; 852 fdCloser.SetTo(dirFD); 853 } 854 // get the parent directory 855 int parentFD = _kern_open_parent_dir(dirFD, leafName, 856 B_FILE_NAME_LENGTH); 857 if (parentFD < 0) 858 return parentFD; 859 dirFD = parentFD; 860 fdCloser.SetTo(dirFD); 861 break; 862 } else if (strcmp(path, "..") == 0) { 863 // ".." 864 // open the parent directory 865 int parentFD = _kern_open_dir(dirFD, ".."); 866 if (parentFD < 0) 867 return parentFD; 868 dirFD = parentFD; 869 fdCloser.SetTo(dirFD); 870 // get the parent's parent directory 871 parentFD = _kern_open_parent_dir(dirFD, leafName, 872 B_FILE_NAME_LENGTH); 873 if (parentFD < 0) 874 return parentFD; 875 dirFD = parentFD; 876 fdCloser.SetTo(dirFD); 877 break; 878 } else { 879 // an ordinary path; analyze it 880 char dirPath[B_PATH_NAME_LENGTH]; 881 status_t error = BPrivate::Storage::parse_path(path, dirPath, 882 leafName); 883 if (error != B_OK) 884 return error; 885 // special case: root directory ("/") 886 if (leafName[0] == '\0' && dirPath[0] == '/') 887 strcpy(leafName, "."); 888 if (leafName[0] == '\0') { 889 // the supplied path is already a leaf 890 error = BPrivate::Storage::check_entry_name(dirPath); 891 if (error != B_OK) 892 return error; 893 strcpy(leafName, dirPath); 894 // if no directory was given, we need to open the current dir 895 // now 896 if (dirFD < 0) { 897 char *cwd = getcwd(tmpPath, B_PATH_NAME_LENGTH); 898 if (!cwd) 899 return B_ERROR; 900 dirFD = _kern_open_dir(-1, cwd); 901 if (dirFD < 0) 902 return dirFD; 903 fdCloser.SetTo(dirFD); 904 } 905 } else { 906 int parentFD = _kern_open_dir(dirFD, dirPath); 907 if (parentFD < 0) 908 return parentFD; 909 dirFD = parentFD; 910 fdCloser.SetTo(dirFD); 911 } 912 // traverse symlinks, if desired 913 if (!traverse) 914 break; 915 struct stat st; 916 error = _kern_read_stat(dirFD, leafName, false, &st, 917 sizeof(struct stat)); 918 if (error == B_ENTRY_NOT_FOUND && !requireConcrete) { 919 // that's fine -- the entry is abstract and was not target of 920 // a symlink we resolved 921 break; 922 } 923 if (error != B_OK) 924 return error; 925 // the entry is concrete 926 if (!S_ISLNK(st.st_mode)) 927 break; 928 requireConcrete = true; 929 // we need to traverse the symlink 930 if (--linkLimit < 0) 931 return B_LINK_LIMIT; 932 size_t bufferSize = B_PATH_NAME_LENGTH; 933 error = _kern_read_link(dirFD, leafName, tmpPath, &bufferSize); 934 if (error < 0) 935 return error; 936 path = tmpPath; 937 // next round... 938 } 939 } 940 941 // set close on exec flag on dir FD 942 fcntl(dirFD, F_SETFD, FD_CLOEXEC); 943 944 // set the result 945 status_t error = set_name(leafName); 946 if (error != B_OK) 947 return error; 948 fdCloser.Detach(); 949 fDirFd = dirFD; 950 return B_OK; 951 } 952 953 /*! \brief Handles string allocation, deallocation, and copying for the entry's leaf name. 954 955 \return 956 - B_OK - Success 957 - "error code" - Failure 958 */ 959 status_t 960 BEntry::set_name(const char *name) 961 { 962 if (name == NULL) 963 return B_BAD_VALUE; 964 965 free(fName); 966 967 fName = strdup(name); 968 if (!fName) 969 return B_NO_MEMORY; 970 971 return B_OK; 972 } 973 974 // _Rename 975 /*! \brief Renames the entry referred to by this object to the location 976 specified by \a target. 977 978 If an entry exists at the target location, the method fails, unless 979 \a clobber is \c true, in which case that entry is overwritten (doesn't 980 work for non-empty directories, though). 981 982 If the operation was successful, this entry is made a clone of the 983 supplied one and the supplied one is uninitialized. 984 985 \param target The entry specifying the target location. 986 \param clobber If \c true, the an entry existing at the target location 987 will be overwritten. 988 \return \c B_OK, if everything went fine, another error code otherwise. 989 */ 990 status_t 991 BEntry::_Rename(BEntry& target, bool clobber) 992 { 993 // check, if there's an entry in the way 994 if (!clobber && target.Exists()) 995 return B_FILE_EXISTS; 996 // rename 997 status_t error = _kern_rename(fDirFd, fName, target.fDirFd, target.fName); 998 if (error == B_OK) { 999 Unset(); 1000 fCStatus = target.fCStatus; 1001 fDirFd = target.fDirFd; 1002 fName = target.fName; 1003 target.fCStatus = B_NO_INIT; 1004 target.fDirFd = -1; 1005 target.fName = NULL; 1006 } 1007 return error; 1008 } 1009 1010 1011 /*! Debugging function, dumps the given entry to stdout. This function is not part of 1012 the R5 implementation, and thus calls to it will mean you can't link with the 1013 R5 Storage Kit. 1014 1015 \param name Pointer to a string to be printed along with the dump for identification 1016 purposes. 1017 1018 */ 1019 void 1020 BEntry::Dump(const char *name) 1021 { 1022 if (name != NULL) { 1023 printf("------------------------------------------------------------\n"); 1024 printf("%s\n", name); 1025 printf("------------------------------------------------------------\n"); 1026 } 1027 1028 printf("fCStatus == %ld\n", fCStatus); 1029 1030 struct stat st; 1031 if (fDirFd != -1 1032 && _kern_read_stat(fDirFd, NULL, false, &st, 1033 sizeof(struct stat)) == B_OK) { 1034 printf("dir.device == %ld\n", st.st_dev); 1035 printf("dir.inode == %lld\n", st.st_ino); 1036 } else { 1037 printf("dir == NullFd\n"); 1038 } 1039 1040 printf("leaf == '%s'\n", fName); 1041 printf("\n"); 1042 1043 } 1044 1045 // get_ref_for_path 1046 /*! \brief Returns an entry_ref for a given path. 1047 \param path The path name referring to the entry 1048 \param ref The entry_ref structure to be filled in 1049 \return 1050 - \c B_OK - Everything went fine. 1051 - \c B_BAD_VALUE - \c NULL \a path or \a ref. 1052 - \c B_ENTRY_NOT_FOUND - A (non-leaf) path component does not exist. 1053 - \c B_NO_MEMORY - Insufficient memory for successful completion. 1054 */ 1055 status_t 1056 get_ref_for_path(const char *path, entry_ref *ref) 1057 { 1058 status_t error = (path && ref ? B_OK : B_BAD_VALUE); 1059 if (error == B_OK) { 1060 BEntry entry(path); 1061 error = entry.InitCheck(); 1062 if (error == B_OK) 1063 error = entry.GetRef(ref); 1064 } 1065 return error; 1066 } 1067 1068 // < 1069 /*! \brief Returns whether an entry is less than another. 1070 The components are compared in order \c device, \c directory, \c name. 1071 A \c NULL \c name is less than any non-null name. 1072 1073 \return 1074 - true - a < b 1075 - false - a >= b 1076 */ 1077 bool 1078 operator<(const entry_ref & a, const entry_ref & b) 1079 { 1080 return (a.device < b.device 1081 || (a.device == b.device 1082 && (a.directory < b.directory 1083 || (a.directory == b.directory 1084 && (a.name == NULL && b.name != NULL 1085 || (a.name != NULL && b.name != NULL 1086 && strcmp(a.name, b.name) < 0)))))); 1087 } 1088 1089 1090 1091 1092