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