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