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