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 const char* 356 BEntry::Name() const 357 { 358 if (fCStatus != B_OK) 359 return NULL; 360 361 return fName; 362 } 363 364 365 /*! \brief Fills in a stat structure for the entry. The information is copied into 366 the \c stat structure pointed to by \a result. 367 368 \b NOTE: The BStatable object does not cache the stat structure; every time you 369 call GetStat(), fresh stat information is retrieved. 370 371 \param result pointer to a pre-allocated structure into which the stat information will be copied 372 \return 373 - \c B_OK - Success 374 - "error code" - Failure 375 */ 376 status_t 377 BEntry::GetStat(struct stat *result) const 378 { 379 if (fCStatus != B_OK) 380 return B_NO_INIT; 381 return _kern_read_stat(fDirFd, fName, false, result, sizeof(struct stat)); 382 } 383 384 385 /*! \brief Reinitializes the BEntry to the path or directory path combination, 386 resolving symlinks if traverse is true 387 388 \return 389 - \c B_OK - Success 390 - "error code" - Failure 391 */ 392 status_t 393 BEntry::SetTo(const BDirectory *dir, const char *path, bool traverse) 394 { 395 // check params 396 if (!dir) 397 return (fCStatus = B_BAD_VALUE); 398 if (path && path[0] == '\0') // R5 behaviour 399 path = NULL; 400 401 // if path is absolute, let the path-only SetTo() do the job 402 if (BPrivate::Storage::is_absolute_path(path)) 403 return SetTo(path, traverse); 404 405 Unset(); 406 407 if (dir->InitCheck() != B_OK) 408 fCStatus = B_BAD_VALUE; 409 410 // dup() the dir's FD and let set() do the rest 411 int dirFD = _kern_dup(dir->get_fd()); 412 if (dirFD < 0) 413 return (fCStatus = dirFD); 414 return (fCStatus = set(dirFD, path, traverse)); 415 } 416 417 418 /*! \brief Reinitializes the BEntry to the entry_ref, resolving symlinks if 419 traverse is true 420 421 \return 422 - \c B_OK - Success 423 - "error code" - Failure 424 */ 425 status_t 426 BEntry::SetTo(const entry_ref *ref, bool traverse) 427 { 428 Unset(); 429 if (ref == NULL) 430 return (fCStatus = B_BAD_VALUE); 431 432 // open the directory and let set() do the rest 433 int dirFD = _kern_open_dir_entry_ref(ref->device, ref->directory, NULL); 434 if (dirFD < 0) 435 return (fCStatus = dirFD); 436 return (fCStatus = set(dirFD, ref->name, traverse)); 437 } 438 439 440 /*! \brief Reinitializes the BEntry object to the path, resolving symlinks if 441 traverse is true 442 443 \return 444 - \c B_OK - Success 445 - "error code" - Failure 446 */ 447 status_t 448 BEntry::SetTo(const char *path, bool traverse) 449 { 450 Unset(); 451 // check the argument 452 if (!path) 453 return (fCStatus = B_BAD_VALUE); 454 return (fCStatus = set(-1, path, traverse)); 455 } 456 457 458 /*! \brief Reinitializes the BEntry to an uninitialized BEntry object */ 459 void 460 BEntry::Unset() 461 { 462 // Close the directory 463 if (fDirFd >= 0) { 464 _kern_close(fDirFd); 465 // BPrivate::Storage::close_dir(fDirFd); 466 } 467 468 // Free our leaf name 469 free(fName); 470 471 fDirFd = -1; 472 fName = NULL; 473 fCStatus = B_NO_INIT; 474 } 475 476 477 /*! \brief Gets an entry_ref structure for the BEntry. 478 479 \param ref pointer to a preallocated entry_ref into which the result is copied 480 \return 481 - \c B_OK - Success 482 - "error code" - Failure 483 484 */ 485 status_t 486 BEntry::GetRef(entry_ref *ref) const 487 { 488 if (fCStatus != B_OK) 489 return B_NO_INIT; 490 491 if (ref == NULL) 492 return B_BAD_VALUE; 493 494 struct stat st; 495 status_t error = _kern_read_stat(fDirFd, NULL, false, &st, 496 sizeof(struct stat)); 497 if (error == B_OK) { 498 ref->device = st.st_dev; 499 ref->directory = st.st_ino; 500 error = ref->set_name(fName); 501 } 502 return error; 503 } 504 505 506 /*! \brief Gets the path for the BEntry. 507 508 \param path pointer to a pre-allocated BPath object into which the result is stored 509 \return 510 - \c B_OK - Success 511 - "error code" - Failure 512 513 */ 514 status_t 515 BEntry::GetPath(BPath *path) const 516 { 517 if (fCStatus != B_OK) 518 return B_NO_INIT; 519 520 if (path == NULL) 521 return B_BAD_VALUE; 522 523 return path->SetTo(this); 524 } 525 526 527 /*! \brief Gets the parent of the BEntry as another BEntry. 528 529 If the function fails, the argument is Unset(). Destructive calls to GetParent() are 530 allowed, i.e.: 531 532 \code 533 BEntry entry("/boot/home/fido"); 534 status_t err; 535 char name[B_FILE_NAME_LENGTH]; 536 537 // Spit out the path components backwards, one at a time. 538 do { 539 entry.GetName(name); 540 printf("> %s\n", name); 541 } while ((err=entry.GetParent(&entry)) == B_OK); 542 543 // Complain for reasons other than reaching the top. 544 if (err != B_ENTRY_NOT_FOUND) 545 printf(">> Error: %s\n", strerror(err)); 546 \endcode 547 548 will output: 549 550 \code 551 > fido 552 > home 553 > boot 554 > . 555 \endcode 556 557 \param entry pointer to a pre-allocated BEntry object into which the result is stored 558 \return 559 - \c B_OK - Success 560 - \c B_ENTRY_NOT_FOUND - Attempted to get the parent of the root directory \c "/" 561 - "error code" - Failure 562 */ 563 status_t 564 BEntry::GetParent(BEntry *entry) const 565 { 566 // check parameter and initialization 567 if (fCStatus != B_OK) 568 return B_NO_INIT; 569 if (entry == NULL) 570 return B_BAD_VALUE; 571 572 // check whether we are the root directory 573 // It is sufficient to check whether our leaf name is ".". 574 if (strcmp(fName, ".") == 0) 575 return B_ENTRY_NOT_FOUND; 576 577 // open the parent directory 578 char leafName[B_FILE_NAME_LENGTH]; 579 int parentFD = _kern_open_parent_dir(fDirFd, leafName, B_FILE_NAME_LENGTH); 580 if (parentFD < 0) 581 return parentFD; 582 583 // set close on exec flag on dir FD 584 fcntl(parentFD, F_SETFD, FD_CLOEXEC); 585 586 // init the entry 587 entry->Unset(); 588 entry->fDirFd = parentFD; 589 entry->fCStatus = entry->set_name(leafName); 590 if (entry->fCStatus != B_OK) 591 entry->Unset(); 592 return entry->fCStatus; 593 } 594 595 596 /*! \brief Gets the parent of the BEntry as a BDirectory. 597 598 If the function fails, the argument is Unset(). 599 600 \param dir pointer to a pre-allocated BDirectory object into which the result is stored 601 \return 602 - \c B_OK - Success 603 - \c B_ENTRY_NOT_FOUND - Attempted to get the parent of the root directory \c "/" 604 - "error code" - Failure 605 */ 606 status_t 607 BEntry::GetParent(BDirectory *dir) const 608 { 609 // check initialization and parameter 610 if (fCStatus != B_OK) 611 return B_NO_INIT; 612 if (dir == NULL) 613 return B_BAD_VALUE; 614 // check whether we are the root directory 615 // It is sufficient to check whether our leaf name is ".". 616 if (strcmp(fName, ".") == 0) 617 return B_ENTRY_NOT_FOUND; 618 // get a node ref for the directory and init it 619 struct stat st; 620 status_t error = _kern_read_stat(fDirFd, NULL, false, &st, 621 sizeof(struct stat)); 622 if (error != B_OK) 623 return error; 624 node_ref ref; 625 ref.device = st.st_dev; 626 ref.node = st.st_ino; 627 return dir->SetTo(&ref); 628 // TODO: This can be optimized: We already have a FD for the directory, 629 // so we could dup() it and set it on the directory. We just need a private 630 // API for being able to do that. 631 } 632 633 634 /*! \brief Gets the name of the entry's leaf. 635 636 \c buffer must be pre-allocated and of sufficient 637 length to hold the entire string. A length of \c B_FILE_NAME_LENGTH is recommended. 638 639 \param buffer pointer to a pre-allocated string into which the result is copied 640 \return 641 - \c B_OK - Success 642 - "error code" - Failure 643 */ 644 status_t 645 BEntry::GetName(char *buffer) const 646 { 647 status_t result = B_ERROR; 648 649 if (fCStatus != B_OK) { 650 result = B_NO_INIT; 651 } else if (buffer == NULL) { 652 result = B_BAD_VALUE; 653 } else { 654 strcpy(buffer, fName); 655 result = B_OK; 656 } 657 658 return result; 659 } 660 661 662 /*! \brief Renames the BEntry to path, replacing an existing entry if clobber is true. 663 664 NOTE: The BEntry must refer to an existing file. If it is abstract, this method will fail. 665 666 \param path Pointer to a string containing the new name for the entry. May 667 be absolute or relative. If relative, the entry is renamed within its 668 current directory. 669 \param clobber If \c false and a file with the name given by \c path already exists, 670 the method will fail. If \c true and such a file exists, it will 671 be overwritten. 672 \return 673 - \c B_OK - Success 674 - \c B_ENTRY_EXISTS - The new location is already taken and \c clobber was \c false 675 - \c B_ENTRY_NOT_FOUND - Attempted to rename an abstract entry 676 - "error code" - Failure 677 678 */ 679 status_t 680 BEntry::Rename(const char *path, bool clobber) 681 { 682 // check parameter and initialization 683 if (path == NULL) 684 return B_BAD_VALUE; 685 if (fCStatus != B_OK) 686 return B_NO_INIT; 687 // get an entry representing the target location 688 BEntry target; 689 status_t error; 690 if (BPrivate::Storage::is_absolute_path(path)) { 691 error = target.SetTo(path); 692 } else { 693 int dirFD = _kern_dup(fDirFd); 694 if (dirFD < 0) 695 return dirFD; 696 // init the entry 697 error = target.fCStatus = target.set(dirFD, path, false); 698 } 699 if (error != B_OK) 700 return error; 701 return _Rename(target, clobber); 702 } 703 704 705 /*! \brief Moves the BEntry to directory or directory+path combination, replacing an existing entry if clobber is true. 706 707 NOTE: The BEntry must refer to an existing file. If it is abstract, this method will fail. 708 709 \param dir Pointer to a pre-allocated BDirectory into which the entry should be moved. 710 \param path Optional new leaf name for the entry. May be a simple leaf or a relative path; 711 either way, \c path is reckoned off of \c dir. If \c NULL, the entry retains 712 its previous leaf name. 713 \param clobber If \c false and an entry already exists at the specified destination, 714 the method will fail. If \c true and such an entry exists, it will 715 be overwritten. 716 \return 717 - \c B_OK - Success 718 - \c B_ENTRY_EXISTS - The new location is already taken and \c clobber was \c false 719 - \c B_ENTRY_NOT_FOUND - Attempted to move an abstract entry 720 - "error code" - Failure 721 */ 722 status_t 723 BEntry::MoveTo(BDirectory *dir, const char *path, bool clobber) 724 { 725 // check parameters and initialization 726 if (fCStatus != B_OK) 727 return B_NO_INIT; 728 if (dir == NULL) 729 return B_BAD_VALUE; 730 if (dir->InitCheck() != B_OK) 731 return B_BAD_VALUE; 732 // NULL path simply means move without renaming 733 if (path == NULL) 734 path = fName; 735 // get an entry representing the target location 736 BEntry target; 737 status_t error = target.SetTo(dir, path); 738 if (error != B_OK) 739 return error; 740 return _Rename(target, clobber); 741 } 742 743 744 /*! \brief Removes the entry from the file system. 745 746 NOTE: If any file descriptors are open on the file when Remove() is called, 747 the chunk of data they refer to will continue to exist until all such file 748 descriptors are closed. The BEntry object, however, becomes abstract and 749 no longer refers to any actual data in the filesystem. 750 751 \return 752 - B_OK - Success 753 - "error code" - Failure 754 */ 755 status_t 756 BEntry::Remove() 757 { 758 if (fCStatus != B_OK) 759 return B_NO_INIT; 760 return _kern_unlink(fDirFd, fName); 761 } 762 763 764 /*! \brief Returns true if the BEntry and \c item refer to the same entry or 765 if they are both uninitialized. 766 767 \return 768 - true - Both BEntry objects refer to the same entry or they are both uninitialzed 769 - false - The BEntry objects refer to different entries 770 */ 771 bool 772 BEntry::operator==(const BEntry &item) const 773 { 774 // First check statuses 775 if (this->InitCheck() != B_OK && item.InitCheck() != B_OK) { 776 return true; 777 } else if (this->InitCheck() == B_OK && item.InitCheck() == B_OK) { 778 779 // Directories don't compare well directly, so we'll 780 // compare entry_refs instead 781 entry_ref ref1, ref2; 782 if (this->GetRef(&ref1) != B_OK) 783 return false; 784 if (item.GetRef(&ref2) != B_OK) 785 return false; 786 return (ref1 == ref2); 787 788 } else { 789 return false; 790 } 791 792 } 793 794 795 /*! \brief Returns false if the BEntry and \c item refer to the same entry or 796 if they are both uninitialized. 797 798 \return 799 - true - The BEntry objects refer to different entries 800 - false - Both BEntry objects refer to the same entry or they are both uninitialzed 801 */ 802 bool 803 BEntry::operator!=(const BEntry &item) const 804 { 805 return !(*this == item); 806 } 807 808 809 /*! \brief Reinitializes the BEntry to be a copy of the argument 810 811 \return 812 - A reference to the copy 813 */ 814 BEntry& 815 BEntry::operator=(const BEntry &item) 816 { 817 if (this == &item) 818 return *this; 819 820 Unset(); 821 if (item.fCStatus == B_OK) { 822 fDirFd = _kern_dup(item.fDirFd); 823 if (fDirFd >= 0) 824 fCStatus = set_name(item.fName); 825 else 826 fCStatus = fDirFd; 827 828 if (fCStatus != B_OK) 829 Unset(); 830 } 831 832 return *this; 833 } 834 835 836 /*! Reserved for future use. */ 837 void BEntry::_PennyEntry1(){} 838 void BEntry::_PennyEntry2(){} 839 void BEntry::_PennyEntry3(){} 840 void BEntry::_PennyEntry4(){} 841 void BEntry::_PennyEntry5(){} 842 void BEntry::_PennyEntry6(){} 843 844 845 /*! \brief Updates the BEntry with the data from the stat structure according to the mask. 846 */ 847 status_t 848 BEntry::set_stat(struct stat &st, uint32 what) 849 { 850 if (fCStatus != B_OK) 851 return B_FILE_ERROR; 852 853 return _kern_write_stat(fDirFd, fName, false, &st, sizeof(struct stat), 854 what); 855 } 856 857 858 /*! Sets the Entry to point to the entry specified by the path \a path relative 859 to the given directory. If \a traverse is \c true and the given entry is a 860 symlink, the object is recursively set to point to the entry pointed to by 861 the symlink. 862 863 If \a path is an absolute path, \a dirFD is ignored. 864 If \a dirFD is -1, path is considered relative to the current directory 865 (unless it is an absolute path, that is). 866 867 The ownership of the file descriptor \a dirFD is transferred to the 868 function, regardless of whether it succeeds or fails. The caller must not 869 close the FD afterwards. 870 871 \param dirFD File descriptor of a directory relative to which path is to 872 be considered. May be -1, when the current directory shall be 873 considered. 874 \param path Pointer to a path relative to the given directory. 875 \param traverse If \c true and the given entry is a symlink, the object is 876 recursively set to point to the entry linked to by the symlink. 877 \return 878 - B_OK - Success 879 - "error code" - Failure 880 */ 881 status_t 882 BEntry::set(int dirFD, const char *path, bool traverse) 883 { 884 bool requireConcrete = false; 885 FDCloser fdCloser(dirFD); 886 char tmpPath[B_PATH_NAME_LENGTH]; 887 char leafName[B_FILE_NAME_LENGTH]; 888 int32 linkLimit = B_MAX_SYMLINKS; 889 while (true) { 890 if (!path || strcmp(path, ".") == 0) { 891 // "." 892 // if no dir FD is supplied, we need to open the current directory 893 // first 894 if (dirFD < 0) { 895 dirFD = _kern_open_dir(-1, "."); 896 if (dirFD < 0) 897 return dirFD; 898 fdCloser.SetTo(dirFD); 899 } 900 // get the parent directory 901 int parentFD = _kern_open_parent_dir(dirFD, leafName, 902 B_FILE_NAME_LENGTH); 903 if (parentFD < 0) 904 return parentFD; 905 dirFD = parentFD; 906 fdCloser.SetTo(dirFD); 907 break; 908 } else if (strcmp(path, "..") == 0) { 909 // ".." 910 // open the parent directory 911 int parentFD = _kern_open_dir(dirFD, ".."); 912 if (parentFD < 0) 913 return parentFD; 914 dirFD = parentFD; 915 fdCloser.SetTo(dirFD); 916 // get the parent's parent directory 917 parentFD = _kern_open_parent_dir(dirFD, leafName, 918 B_FILE_NAME_LENGTH); 919 if (parentFD < 0) 920 return parentFD; 921 dirFD = parentFD; 922 fdCloser.SetTo(dirFD); 923 break; 924 } else { 925 // an ordinary path; analyze it 926 char dirPath[B_PATH_NAME_LENGTH]; 927 status_t error = BPrivate::Storage::parse_path(path, dirPath, 928 leafName); 929 if (error != B_OK) 930 return error; 931 // special case: root directory ("/") 932 if (leafName[0] == '\0' && dirPath[0] == '/') 933 strcpy(leafName, "."); 934 if (leafName[0] == '\0') { 935 // the supplied path is already a leaf 936 error = BPrivate::Storage::check_entry_name(dirPath); 937 if (error != B_OK) 938 return error; 939 strcpy(leafName, dirPath); 940 // if no directory was given, we need to open the current dir 941 // now 942 if (dirFD < 0) { 943 char *cwd = getcwd(tmpPath, B_PATH_NAME_LENGTH); 944 if (!cwd) 945 return B_ERROR; 946 dirFD = _kern_open_dir(-1, cwd); 947 if (dirFD < 0) 948 return dirFD; 949 fdCloser.SetTo(dirFD); 950 } 951 } else if (strcmp(leafName, ".") == 0 952 || strcmp(leafName, "..") == 0) { 953 // We have to resolve this to get the entry name. Just open 954 // the dir and let the next iteration deal with it. 955 dirFD = _kern_open_dir(-1, path); 956 if (dirFD < 0) 957 return dirFD; 958 fdCloser.SetTo(dirFD); 959 path = NULL; 960 continue; 961 } else { 962 int parentFD = _kern_open_dir(dirFD, dirPath); 963 if (parentFD < 0) 964 return parentFD; 965 dirFD = parentFD; 966 fdCloser.SetTo(dirFD); 967 } 968 // traverse symlinks, if desired 969 if (!traverse) 970 break; 971 struct stat st; 972 error = _kern_read_stat(dirFD, leafName, false, &st, 973 sizeof(struct stat)); 974 if (error == B_ENTRY_NOT_FOUND && !requireConcrete) { 975 // that's fine -- the entry is abstract and was not target of 976 // a symlink we resolved 977 break; 978 } 979 if (error != B_OK) 980 return error; 981 // the entry is concrete 982 if (!S_ISLNK(st.st_mode)) 983 break; 984 requireConcrete = true; 985 // we need to traverse the symlink 986 if (--linkLimit < 0) 987 return B_LINK_LIMIT; 988 size_t bufferSize = B_PATH_NAME_LENGTH - 1; 989 error = _kern_read_link(dirFD, leafName, tmpPath, &bufferSize); 990 if (error < 0) 991 return error; 992 tmpPath[bufferSize] = '\0'; 993 path = tmpPath; 994 // next round... 995 } 996 } 997 998 // set close on exec flag on dir FD 999 fcntl(dirFD, F_SETFD, FD_CLOEXEC); 1000 1001 // set the result 1002 status_t error = set_name(leafName); 1003 if (error != B_OK) 1004 return error; 1005 fdCloser.Detach(); 1006 fDirFd = dirFD; 1007 return B_OK; 1008 } 1009 1010 1011 /*! \brief Handles string allocation, deallocation, and copying for the entry's leaf name. 1012 1013 \return 1014 - B_OK - Success 1015 - "error code" - Failure 1016 */ 1017 status_t 1018 BEntry::set_name(const char *name) 1019 { 1020 if (name == NULL) 1021 return B_BAD_VALUE; 1022 1023 free(fName); 1024 1025 fName = strdup(name); 1026 if (!fName) 1027 return B_NO_MEMORY; 1028 1029 return B_OK; 1030 } 1031 1032 1033 /*! \brief Renames the entry referred to by this object to the location 1034 specified by \a target. 1035 1036 If an entry exists at the target location, the method fails, unless 1037 \a clobber is \c true, in which case that entry is overwritten (doesn't 1038 work for non-empty directories, though). 1039 1040 If the operation was successful, this entry is made a clone of the 1041 supplied one and the supplied one is uninitialized. 1042 1043 \param target The entry specifying the target location. 1044 \param clobber If \c true, the an entry existing at the target location 1045 will be overwritten. 1046 \return \c B_OK, if everything went fine, another error code otherwise. 1047 */ 1048 status_t 1049 BEntry::_Rename(BEntry& target, bool clobber) 1050 { 1051 // check, if there's an entry in the way 1052 if (!clobber && target.Exists()) 1053 return B_FILE_EXISTS; 1054 // rename 1055 status_t error = _kern_rename(fDirFd, fName, target.fDirFd, target.fName); 1056 if (error == B_OK) { 1057 Unset(); 1058 fCStatus = target.fCStatus; 1059 fDirFd = target.fDirFd; 1060 fName = target.fName; 1061 target.fCStatus = B_NO_INIT; 1062 target.fDirFd = -1; 1063 target.fName = NULL; 1064 } 1065 return error; 1066 } 1067 1068 1069 /*! Debugging function, dumps the given entry to stdout. This function is not part of 1070 the R5 implementation, and thus calls to it will mean you can't link with the 1071 R5 Storage Kit. 1072 1073 \param name Pointer to a string to be printed along with the dump for identification 1074 purposes. 1075 */ 1076 void 1077 BEntry::Dump(const char *name) 1078 { 1079 if (name != NULL) { 1080 printf("------------------------------------------------------------\n"); 1081 printf("%s\n", name); 1082 printf("------------------------------------------------------------\n"); 1083 } 1084 1085 printf("fCStatus == %" B_PRId32 "\n", fCStatus); 1086 1087 struct stat st; 1088 if (fDirFd != -1 1089 && _kern_read_stat(fDirFd, NULL, false, &st, 1090 sizeof(struct stat)) == B_OK) { 1091 printf("dir.device == %d\n", (int)st.st_dev); 1092 printf("dir.inode == %lld\n", (long long)st.st_ino); 1093 } else { 1094 printf("dir == NullFd\n"); 1095 } 1096 1097 printf("leaf == '%s'\n", fName); 1098 printf("\n"); 1099 1100 } 1101 1102 1103 // #pragma mark - 1104 1105 1106 /*! \brief Returns an entry_ref for a given path. 1107 \param path The path name referring to the entry 1108 \param ref The entry_ref structure to be filled in 1109 \return 1110 - \c B_OK - Everything went fine. 1111 - \c B_BAD_VALUE - \c NULL \a path or \a ref. 1112 - \c B_ENTRY_NOT_FOUND - A (non-leaf) path component does not exist. 1113 - \c B_NO_MEMORY - Insufficient memory for successful completion. 1114 */ 1115 status_t 1116 get_ref_for_path(const char *path, entry_ref *ref) 1117 { 1118 status_t error = (path && ref ? B_OK : B_BAD_VALUE); 1119 if (error == B_OK) { 1120 BEntry entry(path); 1121 error = entry.InitCheck(); 1122 if (error == B_OK) 1123 error = entry.GetRef(ref); 1124 } 1125 return error; 1126 } 1127 1128 1129 /*! \brief Returns whether an entry is less than another. 1130 The components are compared in order \c device, \c directory, \c name. 1131 A \c NULL \c name is less than any non-null name. 1132 1133 \return 1134 - true - a < b 1135 - false - a >= b 1136 */ 1137 bool 1138 operator<(const entry_ref &a, const entry_ref &b) 1139 { 1140 return a.device < b.device 1141 || (a.device == b.device 1142 && (a.directory < b.directory 1143 || (a.directory == b.directory 1144 && ((a.name == NULL && b.name != NULL) 1145 || (a.name != NULL && b.name != NULL 1146 && strcmp(a.name, b.name) < 0))))); 1147 } 1148