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