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