1 /* 2 Open Tracker License 3 4 Terms and Conditions 5 6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved. 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy of 9 this software and associated documentation files (the "Software"), to deal in 10 the Software without restriction, including without limitation the rights to 11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 of the Software, and to permit persons to whom the Software is furnished to do 13 so, subject to the following conditions: 14 15 The above copyright notice and this permission notice applies to all licensees 16 and shall be included in all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 22 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN 23 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25 Except as contained in this notice, the name of Be Incorporated shall not be 26 used in advertising or otherwise to promote the sale, use or other dealings in 27 this Software without prior written authorization from Be Incorporated. 28 29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered 30 trademarks of Be Incorporated in the United States and other countries. Other 31 brand product names are registered trademarks or trademarks of their 32 respective holders. All rights reserved. 33 */ 34 35 // Tracker file system calls. 36 37 // Note - APIs/code in FSUtils.h and FSUtils.cpp is slated for a major cleanup 38 // -- in other words, you will find a lot of ugly cruft in here 39 40 // ToDo: 41 // Move most of preflight error checks to the Model level and only keep those 42 // that have to do with size, reading/writing and name collisions. 43 // Get rid of all the BList based APIs, use BObjectLists. 44 // Clean up the error handling, push most of the user interaction out of the 45 // low level FS calls. 46 47 #include <ctype.h> 48 #include <errno.h> 49 #include <string.h> 50 #include <unistd.h> 51 52 #include <Alert.h> 53 #include <Application.h> 54 #include <Debug.h> 55 #include <Directory.h> 56 #include <Entry.h> 57 #include <FindDirectory.h> 58 #include <NodeInfo.h> 59 #include <Path.h> 60 #include <Roster.h> 61 #include <Screen.h> 62 #include <String.h> 63 #include <SymLink.h> 64 #include <Volume.h> 65 #include <VolumeRoster.h> 66 67 #include <fs_attr.h> 68 #include <fs_info.h> 69 70 #include "Attributes.h" 71 #include "Bitmaps.h" 72 #include "Commands.h" 73 #include "FSUndoRedo.h" 74 #include "FSUtils.h" 75 #include "InfoWindow.h" 76 #include "MimeTypes.h" 77 #include "Model.h" 78 #include "OverrideAlert.h" 79 #include "StatusWindow.h" 80 #include "Thread.h" 81 #include "Tracker.h" 82 #include "TrackerSettings.h" 83 #include "Utilities.h" 84 85 86 enum { 87 kUserCanceled = B_ERRORS_END + 1, 88 kCopyCanceled = kUserCanceled, 89 kTrashCanceled 90 }; 91 92 enum ConflictCheckResult { 93 kCanceled = kUserCanceled, 94 kPrompt, 95 kReplace, 96 kReplaceAll, 97 kNoConflicts 98 }; 99 100 namespace BPrivate { 101 102 static status_t FSDeleteFolder(BEntry *, CopyLoopControl *, bool updateStatus, 103 bool deleteTopDir = true, bool upateFileNameInStatus = false); 104 static status_t MoveEntryToTrash(BEntry *, BPoint *, Undo &undo); 105 static void LowLevelCopy(BEntry *, StatStruct *, BDirectory *, char *destName, 106 CopyLoopControl *, BPoint *); 107 status_t DuplicateTask(BObjectList<entry_ref> *srcList); 108 static status_t MoveTask(BObjectList<entry_ref> *, BEntry *, BList *, uint32); 109 static status_t _DeleteTask(BObjectList<entry_ref> *, bool); 110 static status_t _RestoreTask(BObjectList<entry_ref> *); 111 status_t CalcItemsAndSize(CopyLoopControl* loopControl, 112 BObjectList<entry_ref> *refList, size_t blockSize, int32 *totalCount, 113 off_t *totalSize); 114 status_t MoveItem(BEntry *entry, BDirectory *destDir, BPoint *loc, 115 uint32 moveMode, const char *newName, Undo &undo, 116 CopyLoopControl* loopControl); 117 ConflictCheckResult PreFlightNameCheck(BObjectList<entry_ref> *srcList, 118 const BDirectory *destDir, int32 *collisionCount, uint32 moveMode); 119 status_t CheckName(uint32 moveMode, const BEntry *srcEntry, 120 const BDirectory *destDir, bool multipleCollisions, ConflictCheckResult &); 121 void CopyAttributes(CopyLoopControl *control, BNode *srcNode, BNode* destNode, void *buffer, 122 size_t bufsize); 123 void CopyPoseLocation(BNode *src, BNode *dest); 124 bool DirectoryMatchesOrContains(const BEntry *, directory_which); 125 bool DirectoryMatchesOrContains(const BEntry *, const char *additionalPath, 126 directory_which); 127 bool DirectoryMatches(const BEntry *, directory_which); 128 bool DirectoryMatches(const BEntry *, const char *additionalPath, 129 directory_which); 130 131 status_t empty_trash(void *); 132 133 #ifdef __HAIKU__ 134 #define OS_NAME "Haiku" 135 #else 136 #define OS_NAME "BeOS" 137 #endif 138 139 140 const char *kDeleteConfirmationStr = "Are you sure you want to delete the " 141 "selected item(s)? This operation cannot be reverted."; 142 143 const char *kReplaceStr = "You are trying to replace the item:\n" 144 "\t%s%s\n" 145 "with:\n" 146 "\t%s%s\n\n" 147 "Would you like to replace it with the one you are %s?"; 148 149 const char *kDirectoryReplaceStr = "An item named \"%s\" already exists in " 150 "this folder, and may contain\nitems with the same names. Would you like " 151 "to replace them with those contained in the folder you are %s?"; 152 153 const char *kSymLinkReplaceStr = "An item named \"%s\" already exists in this " 154 "folder. Would you like to replace it with the symbolic link you are " 155 "creating?"; 156 157 const char *kNoFreeSpace = "Sorry, there is not enough free space on the " 158 "destination volume to copy the selection."; 159 160 const char *kFileErrorString = "Error copying file \"%s\":\n\t%s\n\nWould " 161 "you like to continue?"; 162 const char *kFolderErrorString = "Error copying folder \"%s\":\n\t%s\n\nWould " 163 "you like to continue?"; 164 const char *kFileDeleteErrorString = "There was an error deleting \"%s\"" 165 ":\n\t%s"; 166 const char *kReplaceManyStr = "Some items already exist in this folder with " 167 "the same names as the items you are %s.\n \nWould you like to replace " 168 "them with the ones you are %s or be prompted for each one?"; 169 170 const char *kFindAlternativeStr = "Would you like to find some other suitable " 171 "application?"; 172 const char *kFindApplicationStr = "Would you like to find a suitable " 173 "application to open the file?"; 174 175 // Skip these attributes when copying in Tracker 176 const char *kSkipAttributes[] = { 177 kAttrPoseInfo, 178 NULL 179 }; 180 181 182 // #pragma mark - 183 184 185 CopyLoopControl::~CopyLoopControl() 186 { 187 } 188 189 190 void 191 CopyLoopControl::Init(uint32 jobKind) 192 { 193 } 194 195 196 void 197 CopyLoopControl::Init(int32 totalItems, off_t totalSize, 198 const entry_ref* destDir, bool showCount) 199 { 200 } 201 202 203 bool 204 CopyLoopControl::FileError(const char* message, const char* name, 205 status_t error, bool allowContinue) 206 { 207 return false; 208 } 209 210 211 void 212 CopyLoopControl::UpdateStatus(const char* name, const entry_ref& ref, 213 int32 count, bool optional) 214 { 215 } 216 217 218 bool 219 CopyLoopControl::CheckUserCanceled() 220 { 221 return false; 222 } 223 224 225 CopyLoopControl::OverwriteMode 226 CopyLoopControl::OverwriteOnConflict(const BEntry* srcEntry, 227 const char* destName, const BDirectory* destDir, bool srcIsDir, 228 bool dstIsDir) 229 { 230 return kReplace; 231 } 232 233 234 bool 235 CopyLoopControl::SkipEntry(const BEntry*, bool) 236 { 237 // Tracker makes no exceptions 238 return false; 239 } 240 241 242 void 243 CopyLoopControl::ChecksumChunk(const char*, size_t) 244 { 245 } 246 247 248 bool 249 CopyLoopControl::ChecksumFile(const entry_ref*) 250 { 251 return true; 252 } 253 254 255 bool 256 CopyLoopControl::SkipAttribute(const char*) 257 { 258 return false; 259 } 260 261 262 bool 263 CopyLoopControl::PreserveAttribute(const char*) 264 { 265 return false; 266 } 267 268 269 // #pragma mark - 270 271 272 TrackerCopyLoopControl::TrackerCopyLoopControl() 273 : 274 fThread(find_thread(NULL)), 275 fSourceList(NULL) 276 { 277 } 278 279 280 TrackerCopyLoopControl::TrackerCopyLoopControl(uint32 jobKind) 281 : 282 fThread(find_thread(NULL)), 283 fSourceList(NULL) 284 { 285 Init(jobKind); 286 } 287 288 289 TrackerCopyLoopControl::TrackerCopyLoopControl(int32 totalItems, 290 off_t totalSize) 291 : 292 fThread(find_thread(NULL)), 293 fSourceList(NULL) 294 { 295 Init(totalItems, totalItems); 296 } 297 298 299 TrackerCopyLoopControl::~TrackerCopyLoopControl() 300 { 301 if (gStatusWindow != NULL) 302 gStatusWindow->RemoveStatusItem(fThread); 303 } 304 305 306 void 307 TrackerCopyLoopControl::Init(uint32 jobKind) 308 { 309 if (gStatusWindow != NULL) 310 gStatusWindow->CreateStatusItem(fThread, (StatusWindowState)jobKind); 311 } 312 313 314 void 315 TrackerCopyLoopControl::Init(int32 totalItems, off_t totalSize, 316 const entry_ref* destDir, bool showCount) 317 { 318 if (gStatusWindow != NULL) { 319 gStatusWindow->InitStatusItem(fThread, totalItems, totalItems, 320 destDir, showCount); 321 } 322 } 323 324 325 bool 326 TrackerCopyLoopControl::FileError(const char *message, const char *name, 327 status_t error, bool allowContinue) 328 { 329 char buffer[512]; 330 sprintf(buffer, message, name, strerror(error)); 331 332 if (allowContinue) { 333 BAlert *alert = new BAlert("", buffer, "Cancel", "OK", 0, 334 B_WIDTH_AS_USUAL, B_STOP_ALERT); 335 alert->SetShortcut(0, B_ESCAPE); 336 return alert->Go() != 0; 337 } 338 339 BAlert *alert = new BAlert("", buffer, "Cancel", 0, 0, 340 B_WIDTH_AS_USUAL, B_STOP_ALERT); 341 alert->SetShortcut(0, B_ESCAPE); 342 alert->Go(); 343 return false; 344 } 345 346 347 void 348 TrackerCopyLoopControl::UpdateStatus(const char *name, const entry_ref&, 349 int32 count, bool optional) 350 { 351 if (gStatusWindow != NULL) 352 gStatusWindow->UpdateStatus(fThread, name, count, optional); 353 } 354 355 356 bool 357 TrackerCopyLoopControl::CheckUserCanceled() 358 { 359 if (gStatusWindow == NULL) 360 return false; 361 362 if (gStatusWindow->CheckCanceledOrPaused(fThread)) 363 return true; 364 365 if (fSourceList != NULL) { 366 // TODO: Check if the user dropped additional files onto this job. 367 // printf("%p->CheckUserCanceled()\n", this); 368 } 369 370 return false; 371 } 372 373 374 bool 375 TrackerCopyLoopControl::SkipAttribute(const char *attributeName) 376 { 377 for (const char **skipAttribute = kSkipAttributes; *skipAttribute; 378 skipAttribute++) { 379 if (strcmp(*skipAttribute, attributeName) == 0) 380 return true; 381 } 382 383 return false; 384 } 385 386 387 void 388 TrackerCopyLoopControl::SetSourceList(EntryList* list) 389 { 390 fSourceList = list; 391 } 392 393 394 // #pragma mark - 395 396 397 static BNode * 398 GetWritableNode(BEntry *entry, StatStruct *statBuf = 0) 399 { 400 // utility call that works around the problem with BNodes not being 401 // universally writeable 402 // BNodes created on files will fail to WriteAttr because they do not 403 // have the right r/w permissions 404 405 StatStruct localStatbuf; 406 407 if (!statBuf) { 408 statBuf = &localStatbuf; 409 if (entry->GetStat(statBuf) != B_OK) 410 return 0; 411 } 412 413 if (S_ISREG(statBuf->st_mode)) 414 return new BFile(entry, O_RDWR); 415 416 return new BNode(entry); 417 } 418 419 420 bool 421 CheckDevicesEqual(const entry_ref *srcRef, const Model *targetModel) 422 { 423 BDirectory destDir (targetModel->EntryRef()); 424 struct stat deststat; 425 destDir.GetStat(&deststat); 426 427 return srcRef->device == deststat.st_dev; 428 } 429 430 431 status_t 432 FSSetPoseLocation(ino_t destDirInode, BNode *destNode, BPoint point) 433 { 434 PoseInfo poseInfo; 435 poseInfo.fInvisible = false; 436 poseInfo.fInitedDirectory = destDirInode; 437 poseInfo.fLocation = point; 438 439 status_t result = destNode->WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, 440 &poseInfo, sizeof(poseInfo)); 441 442 if (result == sizeof(poseInfo)) 443 return B_OK; 444 445 return result; 446 } 447 448 449 status_t 450 FSSetPoseLocation(BEntry *entry, BPoint point) 451 { 452 BNode node(entry); 453 status_t result = node.InitCheck(); 454 if (result != B_OK) 455 return result; 456 457 BDirectory parent; 458 result = entry->GetParent(&parent); 459 if (result != B_OK) 460 return result; 461 462 node_ref destNodeRef; 463 result = parent.GetNodeRef(&destNodeRef); 464 if (result != B_OK) 465 return result; 466 467 return FSSetPoseLocation(destNodeRef.node, &node, point); 468 } 469 470 471 bool 472 FSGetPoseLocation(const BNode *node, BPoint *point) 473 { 474 PoseInfo poseInfo; 475 if (ReadAttr(node, kAttrPoseInfo, kAttrPoseInfoForeign, 476 B_RAW_TYPE, 0, &poseInfo, sizeof(poseInfo), &PoseInfo::EndianSwap) 477 == kReadAttrFailed) 478 return false; 479 480 if (poseInfo.fInitedDirectory == -1LL) 481 return false; 482 483 *point = poseInfo.fLocation; 484 485 return true; 486 } 487 488 489 static void 490 SetUpPoseLocation(ino_t sourceParentIno, ino_t destParentIno, 491 const BNode *sourceNode, BNode *destNode, BPoint *loc) 492 { 493 BPoint point; 494 if (!loc 495 // we don't have a position yet 496 && sourceParentIno != destParentIno 497 // we aren't copying into the same directory 498 && FSGetPoseLocation(sourceNode, &point)) 499 // the original has a valid inited location 500 loc = &point; 501 // copy the originals location 502 503 if (loc && loc != (BPoint *)-1) { 504 // loc of -1 is used when copying/moving into a window in list mode 505 // where copying positions would not work 506 // ToSo: 507 // should push all this logic to upper levels 508 FSSetPoseLocation(destParentIno, destNode, *loc); 509 } 510 } 511 512 513 void 514 FSMoveToFolder(BObjectList<entry_ref> *srcList, BEntry *destEntry, 515 uint32 moveMode, BList *pointList) 516 { 517 if (srcList->IsEmpty()) { 518 delete srcList; 519 delete pointList; 520 delete destEntry; 521 return; 522 } 523 524 LaunchInNewThread("MoveTask", B_NORMAL_PRIORITY, MoveTask, srcList, 525 destEntry, pointList, moveMode); 526 } 527 528 529 void 530 FSDelete(entry_ref *ref, bool async, bool confirm) 531 { 532 BObjectList<entry_ref> *list = new BObjectList<entry_ref>(1, true); 533 list->AddItem(ref); 534 FSDeleteRefList(list, async, confirm); 535 } 536 537 538 void 539 FSDeleteRefList(BObjectList<entry_ref> *list, bool async, bool confirm) 540 { 541 if (async) { 542 LaunchInNewThread("DeleteTask", B_NORMAL_PRIORITY, _DeleteTask, list, 543 confirm); 544 } else 545 _DeleteTask(list, confirm); 546 } 547 548 549 void 550 FSRestoreRefList(BObjectList<entry_ref> *list, bool async) 551 { 552 if (async) { 553 LaunchInNewThread("RestoreTask", B_NORMAL_PRIORITY, _RestoreTask, 554 list); 555 } else 556 _RestoreTask(list); 557 } 558 559 560 void 561 FSMoveToTrash(BObjectList<entry_ref> *srcList, BList *pointList, bool async) 562 { 563 if (srcList->IsEmpty()) { 564 delete srcList; 565 delete pointList; 566 return; 567 } 568 569 if (async) 570 LaunchInNewThread("MoveTask", B_NORMAL_PRIORITY, MoveTask, srcList, 571 (BEntry *)0, pointList, kMoveSelectionTo); 572 else 573 MoveTask(srcList, 0, pointList, kMoveSelectionTo); 574 } 575 576 577 static bool 578 IsDisksWindowIcon(BEntry *entry) 579 { 580 BPath path; 581 if (entry->InitCheck() != B_OK || entry->GetPath(&path) != B_OK) 582 return false; 583 584 return strcmp(path.Path(), "/") == 0; 585 } 586 587 enum { 588 kNotConfirmed, 589 kConfirmedHomeMove, 590 kConfirmedAll 591 }; 592 593 594 bool 595 ConfirmChangeIfWellKnownDirectory(const BEntry *entry, const char *action, 596 bool dontAsk, int32 *confirmedAlready) 597 { 598 // Don't let the user casually move/change important files/folders 599 // 600 // This is a cheap replacement for having a real UID support turned 601 // on and not running as root all the time 602 603 if (confirmedAlready && *confirmedAlready == kConfirmedAll) 604 return true; 605 606 if (!DirectoryMatchesOrContains(entry, B_SYSTEM_DIRECTORY) 607 && !DirectoryMatchesOrContains(entry, B_USER_DIRECTORY)) 608 // quick way out 609 return true; 610 611 const char *warning = NULL; 612 bool requireOverride = true; 613 614 if (DirectoryMatchesOrContains(entry, B_BEOS_DIRECTORY)) { 615 warning = "If you %s the system folder or its contents, you " 616 "won't be able to boot " OS_NAME "! Are you sure you want to do " 617 "this? To %s the system folder or its contents anyway, hold down " 618 "the Shift key and click \"Do it\"."; 619 } else if (DirectoryMatches(entry, B_USER_DIRECTORY)) { 620 warning = "If you %s the home folder, " OS_NAME " may not " 621 "behave properly! Are you sure you want to do this? " 622 "To %s the home anyway, click \"Do it\"."; 623 requireOverride = false; 624 } else if (DirectoryMatchesOrContains(entry, B_USER_CONFIG_DIRECTORY) 625 || DirectoryMatchesOrContains(entry, B_COMMON_SETTINGS_DIRECTORY)) { 626 627 if (DirectoryMatchesOrContains(entry, "beos_mime", 628 B_USER_SETTINGS_DIRECTORY) 629 || DirectoryMatchesOrContains(entry, "beos_mime", 630 B_COMMON_SETTINGS_DIRECTORY)) { 631 warning = "If you %s the mime settings, " OS_NAME " may not " 632 "behave properly! Are you sure you want to do this? " 633 "To %s the mime settings anyway, click \"Do it\"."; 634 requireOverride = false; 635 } else if (DirectoryMatches(entry, B_USER_CONFIG_DIRECTORY)) { 636 warning = "If you %s the config folder, " OS_NAME " may not " 637 "behave properly! Are you sure you want to do this? " 638 "To %s the config folder anyway, click \"Do it\"."; 639 requireOverride = false; 640 } else if (DirectoryMatches(entry, B_USER_SETTINGS_DIRECTORY) 641 || DirectoryMatches(entry, B_COMMON_SETTINGS_DIRECTORY)) { 642 warning = "If you %s the settings folder, " OS_NAME " may not " 643 "behave properly! Are you sure you want to do this? " 644 "To %s the settings folder anyway, click \"Do it\"."; 645 requireOverride = false; 646 } 647 } 648 649 if (!warning) 650 return true; 651 652 if (dontAsk) 653 return false; 654 655 if (confirmedAlready && *confirmedAlready == kConfirmedHomeMove 656 && !requireOverride) 657 // we already warned about moving home this time around 658 return true; 659 660 char buffer[256]; 661 sprintf(buffer, warning, action, action); 662 663 if ((new OverrideAlert("", buffer, "Do it", (requireOverride 664 ? B_SHIFT_KEY : 0), 665 "Cancel", 0, NULL, 0, B_WIDTH_AS_USUAL, 666 B_WARNING_ALERT))->Go() == 1) { 667 if (confirmedAlready) 668 *confirmedAlready = kNotConfirmed; 669 return false; 670 } 671 672 if (confirmedAlready) { 673 if (!requireOverride) 674 *confirmedAlready = kConfirmedHomeMove; 675 else 676 *confirmedAlready = kConfirmedAll; 677 } 678 679 return true; 680 } 681 682 683 static status_t 684 InitCopy(CopyLoopControl* loopControl, uint32 moveMode, 685 BObjectList<entry_ref> *srcList, BVolume *dstVol, BDirectory *destDir, 686 entry_ref *destRef, bool preflightNameCheck, bool needSizeCalculation, 687 int32 *collisionCount, ConflictCheckResult *preflightResult) 688 { 689 if (dstVol->IsReadOnly()) { 690 BAlert *alert = new BAlert("", 691 "You can't move or copy items to read-only volumes.", 692 "Cancel", 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 693 alert->SetShortcut(0, B_ESCAPE); 694 alert->Go(); 695 return B_ERROR; 696 } 697 698 int32 numItems = srcList->CountItems(); 699 int32 askOnceOnly = kNotConfirmed; 700 for (int32 index = 0; index < numItems; index++) { 701 // we could check for this while iterating through items in each of 702 // the copy loops, except it takes forever to call CalcItemsAndSize 703 BEntry entry((entry_ref *)srcList->ItemAt(index)); 704 if (IsDisksWindowIcon(&entry)) { 705 const char *errorStr; 706 if (moveMode == kCreateLink) 707 errorStr = "You cannot create a link to the root directory."; 708 else 709 errorStr = "You cannot copy or move the root directory."; 710 711 BAlert *alert = new BAlert("", errorStr, "Cancel", 0, 0, 712 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 713 alert->SetShortcut(0, B_ESCAPE); 714 alert->Go(); 715 return B_ERROR; 716 } 717 if (moveMode == kMoveSelectionTo 718 && !ConfirmChangeIfWellKnownDirectory(&entry, "move", false, 719 &askOnceOnly)) { 720 return B_ERROR; 721 } 722 } 723 724 if (preflightNameCheck) { 725 ASSERT(collisionCount); 726 ASSERT(preflightResult); 727 728 *preflightResult = kPrompt; 729 *collisionCount = 0; 730 731 *preflightResult = PreFlightNameCheck(srcList, destDir, collisionCount, 732 moveMode); 733 if (*preflightResult == kCanceled) // user canceled 734 return B_ERROR; 735 } 736 737 // set up the status display 738 switch (moveMode) { 739 case kCopySelectionTo: 740 case kDuplicateSelection: 741 case kMoveSelectionTo: 742 { 743 loopControl->Init(moveMode == kMoveSelectionTo ? kMoveState 744 : kCopyState); 745 746 int32 totalItems = 0; 747 off_t totalSize = 0; 748 if (needSizeCalculation) { 749 if (CalcItemsAndSize(loopControl, srcList, 750 dstVol->BlockSize(), &totalItems, &totalSize) != B_OK) { 751 return B_ERROR; 752 } 753 754 // check for free space before starting copy 755 if ((totalSize + (4 * kKBSize)) >= dstVol->FreeBytes()) { 756 BAlert *alert = new BAlert("", kNoFreeSpace, "Cancel", 757 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 758 alert->SetShortcut(0, B_ESCAPE); 759 alert->Go(); 760 return B_ERROR; 761 } 762 } 763 764 loopControl->Init(totalItems, totalSize, destRef); 765 break; 766 } 767 768 case kCreateLink: 769 if (numItems > 10) { 770 // this will be fast, only put up status if lots of items 771 // moved, links created 772 loopControl->Init(kCreateLinkState); 773 loopControl->Init(numItems, numItems, destRef); 774 } 775 break; 776 } 777 return B_OK; 778 } 779 780 781 // ToDo: 782 // get rid of this cruft 783 bool 784 delete_ref(void *ref) 785 { 786 delete (entry_ref*)ref; 787 return false; 788 } 789 790 791 bool 792 delete_point(void *point) 793 { 794 delete (BPoint*)point; 795 return false; 796 } 797 798 799 static status_t 800 MoveTask(BObjectList<entry_ref> *srcList, BEntry *destEntry, BList *pointList, 801 uint32 moveMode) 802 { 803 ASSERT(!srcList->IsEmpty()); 804 805 // extract information from src, dest models 806 // ## note that we're assuming all items come from the same volume 807 // ## by looking only at FirstItem here which is not a good idea 808 dev_t srcVolumeDevice = srcList->FirstItem()->device; 809 dev_t destVolumeDevice = srcVolumeDevice; 810 811 StatStruct deststat; 812 BVolume volume(srcVolumeDevice); 813 entry_ref destRef; 814 const entry_ref *destRefToCheck = NULL; 815 816 bool destIsTrash = false; 817 BDirectory destDir; 818 BDirectory *destDirToCheck = NULL; 819 bool needPreflightNameCheck = false; 820 bool sourceIsReadOnly = volume.IsReadOnly(); 821 volume.Unset(); 822 823 bool fromUndo = FSIsUndoMoveMode(moveMode); 824 moveMode = FSMoveMode(moveMode); 825 826 // if we're not passed a destEntry then we are supposed to move to trash 827 if (destEntry != NULL) { 828 destEntry->GetRef(&destRef); 829 destRefToCheck = &destRef; 830 831 destDir.SetTo(destEntry); 832 destDir.GetStat(&deststat); 833 destDirToCheck = &destDir; 834 835 destVolumeDevice = deststat.st_dev; 836 destIsTrash = FSIsTrashDir(destEntry); 837 volume.SetTo(destVolumeDevice); 838 839 needPreflightNameCheck = true; 840 } else if (moveMode == kDuplicateSelection) 841 volume.SetTo(srcVolumeDevice); 842 else { 843 // move is to trash 844 destIsTrash = true; 845 846 FSGetTrashDir(&destDir, srcVolumeDevice); 847 volume.SetTo(srcVolumeDevice); 848 849 BEntry entry; 850 destDir.GetEntry(&entry); 851 destDirToCheck = &destDir; 852 853 entry.GetRef(&destRef); 854 destRefToCheck = &destRef; 855 } 856 857 // change the move mode if needed 858 if (moveMode == kCopySelectionTo && destIsTrash) { 859 // cannot copy to trash 860 moveMode = kMoveSelectionTo; 861 } 862 863 if (moveMode == kMoveSelectionTo && sourceIsReadOnly) 864 moveMode = kCopySelectionTo; 865 866 bool needSizeCalculation = true; 867 if ((moveMode == kMoveSelectionTo && srcVolumeDevice == destVolumeDevice) 868 || destIsTrash) { 869 needSizeCalculation = false; 870 } 871 872 // we need the undo object later on, so we create it no matter 873 // if we really need it or not (it's very lightweight) 874 MoveCopyUndo undo(srcList, destDir, pointList, moveMode); 875 if (fromUndo) 876 undo.Remove(); 877 878 TrackerCopyLoopControl loopControl; 879 880 ConflictCheckResult conflictCheckResult = kPrompt; 881 int32 collisionCount = 0; 882 // TODO: Status item is created in InitCopy(), but it would be kind of 883 // neat to move all that into TrackerCopyLoopControl 884 status_t result = InitCopy(&loopControl, moveMode, srcList, 885 &volume, destDirToCheck, &destRef, needPreflightNameCheck, 886 needSizeCalculation, &collisionCount, &conflictCheckResult); 887 888 loopControl.SetSourceList(srcList); 889 890 if (result == B_OK) { 891 for (int32 i = 0; i < srcList->CountItems(); i++) { 892 BPoint *loc = (BPoint *)-1; 893 // a loc of -1 forces autoplacement, rather than copying the 894 // position of the original node 895 // TODO: 896 // Clean this mess up! 897 // What could be a cleaner design is to pass along some kind 898 // "filter" object that post-processes poses, i.e. adds the 899 // location or other stuff. It should not be a job of the 900 // copy-engine. 901 902 entry_ref *srcRef = srcList->ItemAt(i); 903 904 if (moveMode == kDuplicateSelection) { 905 BEntry entry(srcRef); 906 entry.GetParent(&destDir); 907 destDir.GetStat(&deststat); 908 volume.SetTo(srcRef->device); 909 } 910 911 // handle case where item is dropped into folder it already lives 912 // in which could happen if dragging from a query window 913 if (moveMode != kCreateLink 914 && moveMode != kCreateRelativeLink 915 && moveMode != kDuplicateSelection 916 && !destIsTrash 917 && (srcRef->device == destRef.device 918 && srcRef->directory == deststat.st_ino)) 919 continue; 920 921 if (loopControl.CheckUserCanceled()) 922 break; 923 924 BEntry sourceEntry(srcRef); 925 if (sourceEntry.InitCheck() != B_OK) { 926 BString error; 927 error << "Error moving \"" << srcRef->name << "\"."; 928 BAlert *alert = new BAlert("", error.String(), "Cancel", 0, 0, 929 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 930 alert->SetShortcut(0, B_ESCAPE); 931 alert->Go(); 932 break; 933 } 934 935 // are we moving item to trash? 936 if (destIsTrash) { 937 if (pointList) 938 loc = (BPoint *)pointList->ItemAt(i); 939 940 result = MoveEntryToTrash(&sourceEntry, loc, undo); 941 if (result != B_OK) { 942 BString error; 943 error << "Error moving \"" << srcRef->name 944 << "\" to Trash. (" << strerror(result) << ")"; 945 BAlert *alert = new BAlert("", error.String(), "Cancel", 946 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 947 alert->SetShortcut(0, B_ESCAPE); 948 alert->Go(); 949 break; 950 } 951 continue; 952 } 953 954 // resolve name collisions and hierarchy problems 955 if (CheckName(moveMode, &sourceEntry, &destDir, collisionCount > 1, 956 conflictCheckResult) != B_OK) { 957 // we will skip the current item, because we got a conflict 958 // and were asked to or because there was some conflict 959 960 // update the status because item got skipped and the status 961 // will not get updated by the move call 962 loopControl.UpdateStatus(srcRef->name, *srcRef, 1); 963 964 continue; 965 } 966 967 // get location to place this item 968 if (pointList && moveMode != kCopySelectionTo) { 969 loc = (BPoint *)pointList->ItemAt(i); 970 971 BNode *src_node = GetWritableNode(&sourceEntry); 972 if (src_node && src_node->InitCheck() == B_OK) { 973 PoseInfo poseInfo; 974 poseInfo.fInvisible = false; 975 poseInfo.fInitedDirectory = deststat.st_ino; 976 poseInfo.fLocation = *loc; 977 src_node->WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, 978 &poseInfo, sizeof(poseInfo)); 979 } 980 delete src_node; 981 } 982 983 if (pointList) 984 loc = (BPoint*)pointList->ItemAt(i); 985 986 result = MoveItem(&sourceEntry, &destDir, loc, moveMode, NULL, 987 undo, &loopControl); 988 if (result != B_OK) 989 break; 990 } 991 } 992 993 // duplicates of srcList, destFolder were created - dispose them 994 delete srcList; 995 delete destEntry; 996 997 // delete file location list and all Points within 998 if (pointList) { 999 pointList->DoForEach(delete_point); 1000 delete pointList; 1001 } 1002 1003 return B_OK; 1004 } 1005 1006 class FailWithAlert { 1007 public: 1008 static void FailOnError(status_t error, const char *string, 1009 const char *name = NULL) 1010 { 1011 if (error != B_OK) 1012 throw FailWithAlert(error, string, name); 1013 } 1014 1015 FailWithAlert(status_t error, const char *string, const char *name) 1016 : fString(string), 1017 fName(name), 1018 fError(error) 1019 { 1020 } 1021 1022 const char *fString; 1023 const char *fName; 1024 status_t fError; 1025 }; 1026 1027 class MoveError { 1028 public: 1029 static void FailOnError(status_t error) 1030 { 1031 if (error != B_OK) 1032 throw MoveError(error); 1033 } 1034 1035 MoveError(status_t error) 1036 : fError(error) 1037 { } 1038 1039 status_t fError; 1040 }; 1041 1042 1043 void 1044 CopyFile(BEntry *srcFile, StatStruct *srcStat, BDirectory *destDir, 1045 CopyLoopControl *loopControl, BPoint *loc, bool makeOriginalName, 1046 Undo &undo) 1047 { 1048 if (loopControl->SkipEntry(srcFile, true)) 1049 return; 1050 1051 node_ref node; 1052 destDir->GetNodeRef(&node); 1053 BVolume volume(node.device); 1054 1055 // check for free space first 1056 if ((srcStat->st_size + kKBSize) >= volume.FreeBytes()) { 1057 loopControl->FileError(kNoFreeSpace, "", B_DEVICE_FULL, false); 1058 throw (status_t)B_DEVICE_FULL; 1059 } 1060 1061 char destName[B_FILE_NAME_LENGTH]; 1062 srcFile->GetName(destName); 1063 entry_ref ref; 1064 srcFile->GetRef(&ref); 1065 1066 loopControl->UpdateStatus(destName, ref, 1024, true); 1067 1068 if (makeOriginalName) { 1069 FSMakeOriginalName(destName, destDir, " copy"); 1070 undo.UpdateEntry(srcFile, destName); 1071 } 1072 1073 BEntry conflictingEntry; 1074 if (destDir->FindEntry(destName, &conflictingEntry) == B_OK) { 1075 switch (loopControl->OverwriteOnConflict(srcFile, destName, destDir, 1076 false, false)) { 1077 case TrackerCopyLoopControl::kSkip: 1078 // we are about to ignore this entire directory 1079 return; 1080 1081 case TrackerCopyLoopControl::kReplace: 1082 if (!conflictingEntry.IsDirectory()) { 1083 ThrowOnError(conflictingEntry.Remove()); 1084 break; 1085 } 1086 // fall through if not a directory 1087 case TrackerCopyLoopControl::kMerge: 1088 // This flag implies that the attributes should be kept 1089 // on the file. Just ignore it. 1090 break; 1091 } 1092 } 1093 1094 try { 1095 LowLevelCopy(srcFile, srcStat, destDir, destName, loopControl, loc); 1096 } catch (status_t err) { 1097 if (err == kCopyCanceled) 1098 throw (status_t)err; 1099 1100 if (err != B_OK) { 1101 if (!loopControl->FileError(kFileErrorString, destName, err, 1102 true)) { 1103 throw (status_t)err; 1104 } else { 1105 // user selected continue in spite of error, update status bar 1106 loopControl->UpdateStatus(NULL, ref, (int32)srcStat->st_size); 1107 } 1108 } 1109 } 1110 } 1111 1112 1113 #ifdef _SILENTLY_CORRECT_FILE_NAMES 1114 static bool 1115 CreateFileSystemCompatibleName(const BDirectory *destDir, char *destName) 1116 { 1117 // Is it a FAT32 file system? (this is the only one we currently now about) 1118 1119 BEntry target; 1120 destDir->GetEntry(&target); 1121 entry_ref targetRef; 1122 fs_info info; 1123 if (target.GetRef(&targetRef) == B_OK 1124 && fs_stat_dev(targetRef.device, &info) == B_OK 1125 && !strcmp(info.fsh_name, "fat")) { 1126 bool wasInvalid = false; 1127 1128 // it's a FAT32 file system, now check the name 1129 1130 int32 length = strlen(destName) - 1; 1131 while (destName[length] == '.') { 1132 // invalid name, just cut off the dot at the end 1133 destName[length--] = '\0'; 1134 wasInvalid = true; 1135 } 1136 1137 char *invalid = destName; 1138 while ((invalid = strpbrk(invalid, "?<>\\:\"|*")) != NULL) { 1139 invalid[0] = '_'; 1140 wasInvalid = true; 1141 } 1142 1143 return wasInvalid; 1144 } 1145 1146 return false; 1147 } 1148 #endif 1149 1150 1151 static void 1152 LowLevelCopy(BEntry *srcEntry, StatStruct *srcStat, BDirectory *destDir, 1153 char *destName, CopyLoopControl *loopControl, BPoint *loc) 1154 { 1155 entry_ref ref; 1156 ThrowOnError(srcEntry->GetRef(&ref)); 1157 1158 if (S_ISLNK(srcStat->st_mode)) { 1159 // handle symbolic links 1160 BSymLink srcLink; 1161 BSymLink newLink; 1162 char linkpath[MAXPATHLEN]; 1163 1164 ThrowOnError(srcLink.SetTo(srcEntry)); 1165 ThrowIfNotSize(srcLink.ReadLink(linkpath, MAXPATHLEN-1)); 1166 1167 ThrowOnError(destDir->CreateSymLink(destName, linkpath, &newLink)); 1168 1169 node_ref destNodeRef; 1170 destDir->GetNodeRef(&destNodeRef); 1171 // copy or write new pose location as a first thing 1172 SetUpPoseLocation(ref.directory, destNodeRef.node, &srcLink, 1173 &newLink, loc); 1174 1175 BNodeInfo nodeInfo(&newLink); 1176 ThrowOnError(nodeInfo.SetType(B_LINK_MIMETYPE)); 1177 1178 newLink.SetPermissions(srcStat->st_mode); 1179 newLink.SetOwner(srcStat->st_uid); 1180 newLink.SetGroup(srcStat->st_gid); 1181 newLink.SetModificationTime(srcStat->st_mtime); 1182 newLink.SetCreationTime(srcStat->st_crtime); 1183 1184 return; 1185 } 1186 1187 BFile srcFile(srcEntry, O_RDONLY); 1188 ThrowOnInitCheckError(&srcFile); 1189 1190 const size_t kMinBufferSize = 1024 * 128; 1191 const size_t kMaxBufferSize = 1024 * 1024; 1192 1193 size_t bufsize = kMinBufferSize; 1194 if (bufsize < srcStat->st_size) { 1195 // File bigger than the buffer size: determine an optimal buffer size 1196 system_info sinfo; 1197 get_system_info(&sinfo); 1198 size_t freesize = static_cast<size_t>( 1199 (sinfo.max_pages - sinfo.used_pages) * B_PAGE_SIZE); 1200 bufsize = freesize / 4; // take 1/4 of RAM max 1201 bufsize -= bufsize % (16 * 1024); // Round to 16 KB boundaries 1202 if (bufsize < kMinBufferSize) // at least kMinBufferSize 1203 bufsize = kMinBufferSize; 1204 else if (bufsize > kMaxBufferSize) // no more than kMaxBufferSize 1205 bufsize = kMaxBufferSize; 1206 } 1207 1208 BFile destFile(destDir, destName, O_RDWR | O_CREAT); 1209 #ifdef _SILENTLY_CORRECT_FILE_NAMES 1210 if ((destFile.InitCheck() == B_BAD_VALUE 1211 || destFile.InitCheck() == B_NOT_ALLOWED) 1212 && CreateFileSystemCompatibleName(destDir, destName)) { 1213 destFile.SetTo(destDir, destName, B_CREATE_FILE | B_READ_WRITE); 1214 } 1215 #endif 1216 1217 ThrowOnInitCheckError(&destFile); 1218 1219 node_ref destNodeRef; 1220 destDir->GetNodeRef(&destNodeRef); 1221 // copy or write new pose location as a first thing 1222 SetUpPoseLocation(ref.directory, destNodeRef.node, &srcFile, 1223 &destFile, loc); 1224 1225 char *buffer = new char[bufsize]; 1226 try { 1227 // copy data portion of file 1228 while (true) { 1229 if (loopControl->CheckUserCanceled()) { 1230 // if copy was canceled, remove partial destination file 1231 destFile.Unset(); 1232 1233 BEntry destEntry; 1234 if (destDir->FindEntry(destName, &destEntry) == B_OK) 1235 destEntry.Remove(); 1236 1237 throw (status_t)kCopyCanceled; 1238 } 1239 1240 ASSERT(buffer); 1241 ssize_t bytes = srcFile.Read(buffer, bufsize); 1242 1243 if (bytes > 0) { 1244 ssize_t updateBytes = 0; 1245 if (bytes > 32 * 1024) { 1246 // when copying large chunks, update after read and after 1247 // write to get better update granularity 1248 updateBytes = bytes / 2; 1249 loopControl->UpdateStatus(NULL, ref, updateBytes, true); 1250 } 1251 1252 loopControl->ChecksumChunk(buffer, (size_t)bytes); 1253 1254 ssize_t result = destFile.Write(buffer, (size_t)bytes); 1255 if (result != bytes) 1256 throw (status_t)B_ERROR; 1257 1258 loopControl->UpdateStatus(NULL, ref, bytes - updateBytes, 1259 true); 1260 } else if (bytes < 0) { 1261 // read error 1262 throw (status_t)bytes; 1263 } else { 1264 // we are done 1265 break; 1266 } 1267 } 1268 1269 CopyAttributes(loopControl, &srcFile, &destFile, buffer, bufsize); 1270 } catch (...) { 1271 delete [] buffer; 1272 throw; 1273 } 1274 1275 destFile.SetPermissions(srcStat->st_mode); 1276 destFile.SetOwner(srcStat->st_uid); 1277 destFile.SetGroup(srcStat->st_gid); 1278 destFile.SetModificationTime(srcStat->st_mtime); 1279 destFile.SetCreationTime(srcStat->st_crtime); 1280 1281 delete [] buffer; 1282 1283 if (!loopControl->ChecksumFile(&ref)) { 1284 // File no good. Remove and quit. 1285 destFile.Unset(); 1286 1287 BEntry destEntry; 1288 if (destDir->FindEntry(destName, &destEntry) == B_OK) 1289 destEntry.Remove(); 1290 throw (status_t)kUserCanceled; 1291 } 1292 } 1293 1294 1295 void 1296 CopyAttributes(CopyLoopControl *control, BNode *srcNode, BNode *destNode, 1297 void *buffer, size_t bufsize) 1298 { 1299 // ToDo: 1300 // Add error checking 1301 // prior to coyping attributes, make sure indices are installed 1302 1303 // When calling CopyAttributes on files, have to make sure destNode 1304 // is a BFile opened R/W 1305 1306 srcNode->RewindAttrs(); 1307 char name[256]; 1308 while (srcNode->GetNextAttrName(name) == B_OK) { 1309 // Check to see if this attribute should be skipped. 1310 if (control->SkipAttribute(name)) 1311 continue; 1312 1313 attr_info info; 1314 if (srcNode->GetAttrInfo(name, &info) != B_OK) 1315 continue; 1316 1317 // Check to see if this attribute should be overwritten when it 1318 // already exists. 1319 if (control->PreserveAttribute(name)) { 1320 attr_info dest_info; 1321 if (destNode->GetAttrInfo(name, &dest_info) == B_OK) 1322 continue; 1323 } 1324 1325 // Special case for a size 0 attribute. It wouldn't be written at all 1326 // otherwise. 1327 if (info.size == 0) 1328 destNode->WriteAttr(name, info.type, 0, buffer, 0); 1329 1330 ssize_t bytes; 1331 ssize_t numToRead = (ssize_t)info.size; 1332 for (off_t offset = 0; numToRead > 0; offset += bytes) { 1333 size_t chunkSize = (size_t)numToRead; 1334 if (chunkSize > bufsize) 1335 chunkSize = bufsize; 1336 1337 bytes = srcNode->ReadAttr(name, info.type, offset, 1338 buffer, chunkSize); 1339 1340 if (bytes <= 0) 1341 break; 1342 1343 destNode->WriteAttr(name, info.type, offset, buffer, 1344 (size_t)bytes); 1345 1346 numToRead -= bytes; 1347 } 1348 } 1349 } 1350 1351 1352 static void 1353 CopyFolder(BEntry *srcEntry, BDirectory *destDir, CopyLoopControl *loopControl, 1354 BPoint *loc, bool makeOriginalName, Undo &undo, bool removeSource = false) 1355 { 1356 BDirectory newDir; 1357 BEntry entry; 1358 status_t err = B_OK; 1359 bool createDirectory = true; 1360 BEntry existingEntry; 1361 1362 if (loopControl->SkipEntry(srcEntry, false)) 1363 return; 1364 1365 entry_ref ref; 1366 srcEntry->GetRef(&ref); 1367 1368 char destName[B_FILE_NAME_LENGTH]; 1369 strcpy(destName, ref.name); 1370 1371 loopControl->UpdateStatus(ref.name, ref, 1024, true); 1372 1373 if (makeOriginalName) { 1374 FSMakeOriginalName(destName, destDir, " copy"); 1375 undo.UpdateEntry(srcEntry, destName); 1376 } 1377 1378 if (destDir->FindEntry(destName, &existingEntry) == B_OK) { 1379 // some entry with a conflicting name is already present in destDir 1380 // decide what to do about it 1381 bool isDirectory = existingEntry.IsDirectory(); 1382 1383 switch (loopControl->OverwriteOnConflict(srcEntry, destName, destDir, 1384 true, isDirectory)) { 1385 case TrackerCopyLoopControl::kSkip: 1386 // we are about to ignore this entire directory 1387 return; 1388 1389 1390 case TrackerCopyLoopControl::kReplace: 1391 if (!isDirectory) { 1392 // conflicting with a file or symbolic link, remove entry 1393 ThrowOnError(existingEntry.Remove()); 1394 break; 1395 } 1396 // fall through if directory, do not replace. 1397 case TrackerCopyLoopControl::kMerge: 1398 ASSERT(isDirectory); 1399 // do not create a new directory, use the current one 1400 newDir.SetTo(&existingEntry); 1401 createDirectory = false; 1402 break; 1403 } 1404 } 1405 1406 // loop through everything in src folder and copy it to new folder 1407 BDirectory srcDir(srcEntry); 1408 srcDir.Rewind(); 1409 1410 // create a new folder inside of destination folder 1411 if (createDirectory) { 1412 err = destDir->CreateDirectory(destName, &newDir); 1413 #ifdef _SILENTLY_CORRECT_FILE_NAMES 1414 if (err == B_BAD_VALUE) { 1415 // check if it's an invalid name on a FAT32 file system 1416 if (CreateFileSystemCompatibleName(destDir, destName)) 1417 err = destDir->CreateDirectory(destName, &newDir); 1418 } 1419 #endif 1420 if (err != B_OK) { 1421 if (!loopControl->FileError(kFolderErrorString, destName, err, 1422 true)) { 1423 throw err; 1424 } 1425 1426 // will allow rest of copy to continue 1427 return; 1428 } 1429 } 1430 1431 char *buffer; 1432 if (createDirectory && err == B_OK 1433 && (buffer = (char*)malloc(32768)) != 0) { 1434 CopyAttributes(loopControl, &srcDir, &newDir, buffer, 32768); 1435 // don't copy original pose location if new location passed 1436 free(buffer); 1437 } 1438 1439 StatStruct statbuf; 1440 srcDir.GetStat(&statbuf); 1441 dev_t sourceDeviceID = statbuf.st_dev; 1442 1443 // copy or write new pose location 1444 node_ref destNodeRef; 1445 destDir->GetNodeRef(&destNodeRef); 1446 SetUpPoseLocation(ref.directory, destNodeRef.node, &srcDir, 1447 &newDir, loc); 1448 1449 while (srcDir.GetNextEntry(&entry) == B_OK) { 1450 1451 if (loopControl->CheckUserCanceled()) 1452 throw (status_t)kUserCanceled; 1453 1454 entry.GetStat(&statbuf); 1455 1456 if (S_ISDIR(statbuf.st_mode)) { 1457 1458 // entry is a mount point, do not copy it 1459 if (statbuf.st_dev != sourceDeviceID) { 1460 PRINT(("Avoiding mount point %d, %d \n", statbuf.st_dev, 1461 sourceDeviceID)); 1462 continue; 1463 } 1464 1465 CopyFolder(&entry, &newDir, loopControl, 0, false, undo, 1466 removeSource); 1467 if (removeSource) 1468 FSDeleteFolder(&entry, loopControl, true, true, false); 1469 } else { 1470 CopyFile(&entry, &statbuf, &newDir, loopControl, 0, false, undo); 1471 if (removeSource) 1472 entry.Remove(); 1473 } 1474 } 1475 if (removeSource) 1476 srcEntry->Remove(); 1477 else 1478 srcEntry->Unset(); 1479 } 1480 1481 1482 status_t 1483 RecursiveMove(BEntry *entry, BDirectory *destDir, 1484 CopyLoopControl* loopControl) 1485 { 1486 char name[B_FILE_NAME_LENGTH]; 1487 if (entry->GetName(name) == B_OK) { 1488 if (destDir->Contains(name)) { 1489 BPath path (destDir, name); 1490 BDirectory subDir (path.Path()); 1491 entry_ref ref; 1492 entry->GetRef(&ref); 1493 BDirectory source(&ref); 1494 if (source.InitCheck() == B_OK) { 1495 source.Rewind(); 1496 BEntry current; 1497 while (source.GetNextEntry(¤t) == B_OK) { 1498 if (current.IsDirectory()) { 1499 RecursiveMove(¤t, &subDir, loopControl); 1500 current.Remove(); 1501 } else { 1502 current.GetName(name); 1503 if (loopControl->OverwriteOnConflict(¤t, name, 1504 &subDir, true, false) 1505 != TrackerCopyLoopControl::kSkip) { 1506 MoveError::FailOnError(current.MoveTo(&subDir, 1507 NULL, true)); 1508 } 1509 } 1510 } 1511 } 1512 entry->Remove(); 1513 } else { 1514 MoveError::FailOnError(entry->MoveTo(destDir)); 1515 } 1516 } 1517 1518 return B_OK; 1519 } 1520 1521 status_t 1522 MoveItem(BEntry *entry, BDirectory *destDir, BPoint *loc, uint32 moveMode, 1523 const char *newName, Undo &undo, CopyLoopControl* loopControl) 1524 { 1525 entry_ref ref; 1526 try { 1527 node_ref destNode; 1528 StatStruct statbuf; 1529 MoveError::FailOnError(entry->GetStat(&statbuf)); 1530 MoveError::FailOnError(entry->GetRef(&ref)); 1531 MoveError::FailOnError(destDir->GetNodeRef(&destNode)); 1532 1533 if (moveMode == kCreateLink || moveMode == kCreateRelativeLink) { 1534 PoseInfo poseInfo; 1535 char name[B_FILE_NAME_LENGTH]; 1536 strcpy(name, ref.name); 1537 1538 BSymLink link; 1539 FSMakeOriginalName(name, destDir, " link"); 1540 undo.UpdateEntry(entry, name); 1541 1542 BPath path; 1543 entry->GetPath(&path); 1544 if (loc && loc != (BPoint *)-1) { 1545 poseInfo.fInvisible = false; 1546 poseInfo.fInitedDirectory = destNode.node; 1547 poseInfo.fLocation = *loc; 1548 } 1549 1550 status_t err = B_ERROR; 1551 1552 if (moveMode == kCreateRelativeLink) { 1553 if (statbuf.st_dev == destNode.device) { 1554 // relative link only works on the same device 1555 char oldwd[B_PATH_NAME_LENGTH]; 1556 getcwd(oldwd, B_PATH_NAME_LENGTH); 1557 1558 BEntry destEntry; 1559 destDir -> GetEntry(&destEntry); 1560 BPath destPath; 1561 destEntry.GetPath(&destPath); 1562 1563 chdir(destPath.Path()); 1564 // change working dir to target dir 1565 1566 BString destString(destPath.Path()); 1567 destString.Append("/"); 1568 1569 BString srcString(path.Path()); 1570 srcString.RemoveLast(path.Leaf()); 1571 1572 // find index while paths are the same 1573 1574 const char *src = srcString.String(); 1575 const char *dest = destString.String(); 1576 const char *lastFolderSrc = src; 1577 const char *lastFolderDest = dest; 1578 1579 while (*src && *dest && *src == *dest) { 1580 ++src; 1581 if (*dest++ == '/') { 1582 lastFolderSrc = src; 1583 lastFolderDest = dest; 1584 } 1585 } 1586 src = lastFolderSrc; 1587 dest = lastFolderDest; 1588 1589 BString source; 1590 if (*dest == '\0' && *src != '\0') { 1591 // source is deeper in the same tree than the target 1592 source.Append(src); 1593 } else if (*dest != '\0') { 1594 // target is deeper in the same tree than the source 1595 while (*dest) { 1596 if (*dest == '/') 1597 source.Prepend("../"); 1598 ++dest; 1599 } 1600 source.Append(src); 1601 } 1602 1603 // else source and target are in the same dir 1604 1605 source.Append(path.Leaf()); 1606 err = destDir->CreateSymLink(name, source.String(), &link); 1607 1608 chdir(oldwd); 1609 // change working dir back to original 1610 } else 1611 moveMode = kCreateLink; 1612 // fall back to absolute link mode 1613 } 1614 1615 if (moveMode == kCreateLink) 1616 err = destDir->CreateSymLink(name, path.Path(), &link); 1617 1618 if (err == B_UNSUPPORTED) { 1619 throw FailWithAlert(err, "The target disk does not support " 1620 "creating links.", NULL); 1621 } 1622 1623 FailWithAlert::FailOnError(err, "Error creating link to \"%s\".", 1624 ref.name); 1625 1626 if (loc && loc != (BPoint *)-1) { 1627 link.WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, &poseInfo, 1628 sizeof(PoseInfo)); 1629 } 1630 1631 BNodeInfo nodeInfo(&link); 1632 nodeInfo.SetType(B_LINK_MIMETYPE); 1633 return B_OK; 1634 } 1635 1636 // if move is on same volume don't copy 1637 if (statbuf.st_dev == destNode.device && moveMode != kCopySelectionTo 1638 && moveMode != kDuplicateSelection) { 1639 1640 // for "Move" the size for status is always 1 - since file 1641 // size is irrelevant when simply moving to a new folder 1642 loopControl->UpdateStatus(ref.name, ref, 1); 1643 if (entry->IsDirectory()) 1644 return RecursiveMove(entry, destDir, loopControl); 1645 MoveError::FailOnError(entry->MoveTo(destDir, newName)); 1646 } else { 1647 bool makeOriginalName = (moveMode == kDuplicateSelection); 1648 if (S_ISDIR(statbuf.st_mode)) { 1649 CopyFolder(entry, destDir, loopControl, loc, makeOriginalName, 1650 undo, moveMode == kMoveSelectionTo); 1651 } else { 1652 CopyFile(entry, &statbuf, destDir, loopControl, loc, 1653 makeOriginalName, undo); 1654 if (moveMode == kMoveSelectionTo) 1655 entry->Remove(); 1656 } 1657 } 1658 } catch (status_t error) { 1659 // no alert, was already taken care of before 1660 return error; 1661 } catch (MoveError error) { 1662 BString errorString; 1663 errorString << "Error moving \"" << ref.name << '"'; 1664 (new BAlert("", errorString.String(), "OK", 0, 0, B_WIDTH_AS_USUAL, 1665 B_WARNING_ALERT))->Go(); 1666 return error.fError; 1667 } catch (FailWithAlert error) { 1668 char buffer[256]; 1669 if (error.fName) 1670 sprintf(buffer, error.fString, error.fName); 1671 else 1672 strcpy(buffer, error.fString); 1673 (new BAlert("", buffer, "OK", 0, 0, B_WIDTH_AS_USUAL, 1674 B_WARNING_ALERT))->Go(); 1675 1676 return error.fError; 1677 } 1678 1679 return B_OK; 1680 } 1681 1682 1683 void 1684 FSDuplicate(BObjectList<entry_ref> *srcList, BList *pointList) 1685 { 1686 LaunchInNewThread("DupTask", B_NORMAL_PRIORITY, MoveTask, srcList, 1687 (BEntry *)NULL, pointList, kDuplicateSelection); 1688 } 1689 1690 1691 #if 0 1692 status_t 1693 FSCopyFolder(BEntry *srcEntry, BDirectory *destDir, 1694 CopyLoopControl *loopControl, BPoint *loc, bool makeOriginalName) 1695 { 1696 try 1697 CopyFolder(srcEntry, destDir, loopControl, loc, makeOriginalName); 1698 catch (status_t error) { 1699 return error; 1700 1701 return B_OK; 1702 } 1703 #endif 1704 1705 1706 status_t 1707 FSCopyAttributesAndStats(BNode *srcNode, BNode *destNode) 1708 { 1709 char *buffer = new char[1024]; 1710 1711 // copy the attributes 1712 srcNode->RewindAttrs(); 1713 char name[256]; 1714 while (srcNode->GetNextAttrName(name) == B_OK) { 1715 attr_info info; 1716 if (srcNode->GetAttrInfo(name, &info) != B_OK) 1717 continue; 1718 1719 attr_info dest_info; 1720 if (destNode->GetAttrInfo(name, &dest_info) == B_OK) 1721 continue; 1722 1723 ssize_t bytes; 1724 ssize_t numToRead = (ssize_t)info.size; 1725 for (off_t offset = 0; numToRead > 0; offset += bytes) { 1726 size_t chunkSize = (size_t)numToRead; 1727 if (chunkSize > 1024) 1728 chunkSize = 1024; 1729 1730 bytes = srcNode->ReadAttr(name, info.type, offset, buffer, 1731 chunkSize); 1732 1733 if (bytes <= 0) 1734 break; 1735 1736 destNode->WriteAttr(name, info.type, offset, buffer, 1737 (size_t)bytes); 1738 1739 numToRead -= bytes; 1740 } 1741 } 1742 delete[] buffer; 1743 1744 // copy the file stats 1745 struct stat srcStat; 1746 srcNode->GetStat(&srcStat); 1747 destNode->SetPermissions(srcStat.st_mode); 1748 destNode->SetOwner(srcStat.st_uid); 1749 destNode->SetGroup(srcStat.st_gid); 1750 destNode->SetModificationTime(srcStat.st_mtime); 1751 destNode->SetCreationTime(srcStat.st_crtime); 1752 1753 return B_OK; 1754 } 1755 1756 1757 #if 0 1758 status_t 1759 FSCopyFile(BEntry* srcFile, StatStruct *srcStat, BDirectory* destDir, 1760 CopyLoopControl *loopControl, BPoint *loc, bool makeOriginalName) 1761 { 1762 try { 1763 CopyFile(srcFile, srcStat, destDir, loopControl, loc, 1764 makeOriginalName); 1765 } catch (status_t error) { 1766 return error; 1767 } 1768 1769 return B_OK; 1770 } 1771 #endif 1772 1773 1774 static status_t 1775 MoveEntryToTrash(BEntry *entry, BPoint *loc, Undo &undo) 1776 { 1777 BDirectory trash_dir; 1778 entry_ref ref; 1779 status_t result = entry->GetRef(&ref); 1780 if (result != B_OK) 1781 return result; 1782 1783 node_ref nodeRef; 1784 result = entry->GetNodeRef(&nodeRef); 1785 if (result != B_OK) 1786 return result; 1787 1788 StatStruct statbuf; 1789 result = entry->GetStat(&statbuf); 1790 if (entry->GetStat(&statbuf) != B_OK) 1791 return result; 1792 1793 // if it's a directory close the window and any child dir windows 1794 if (S_ISDIR(statbuf.st_mode)) { 1795 BDirectory dir(entry); 1796 1797 // if it's a volume, try to unmount 1798 if (dir.IsRootDirectory()) { 1799 BVolume volume(nodeRef.device); 1800 BVolume boot; 1801 1802 BVolumeRoster().GetBootVolume(&boot); 1803 if (volume == boot) { 1804 char name[B_FILE_NAME_LENGTH]; 1805 volume.GetName(name); 1806 char buffer[256]; 1807 sprintf(buffer, "Cannot unmount the boot volume \"%s\".", 1808 name); 1809 BAlert *alert = new BAlert("", buffer, "Cancel", 0, 0, 1810 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 1811 alert->SetShortcut(0, B_ESCAPE); 1812 alert->Go(); 1813 } else { 1814 BMessage message(kUnmountVolume); 1815 message.AddInt32("device_id", volume.Device()); 1816 be_app->PostMessage(&message); 1817 } 1818 return B_OK; 1819 } 1820 1821 // get trash directory on same volume as item being moved 1822 result = FSGetTrashDir(&trash_dir, nodeRef.device); 1823 if (result != B_OK) 1824 return result; 1825 1826 // check hierarchy before moving 1827 BEntry trashEntry; 1828 trash_dir.GetEntry(&trashEntry); 1829 1830 if (dir == trash_dir || dir.Contains(&trashEntry)) { 1831 (new BAlert("", "You cannot put the Trash, home or Desktop " 1832 "directory into the trash.", "OK", 0, 0, 1833 B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(); 1834 1835 // return no error so we don't get two dialogs 1836 return B_OK; 1837 } 1838 1839 BMessage message(kCloseWindowAndChildren); 1840 1841 node_ref parentNode; 1842 parentNode.device = statbuf.st_dev; 1843 parentNode.node = statbuf.st_ino; 1844 message.AddData("node_ref", B_RAW_TYPE, &parentNode, sizeof(node_ref)); 1845 be_app->PostMessage(&message); 1846 } else { 1847 // get trash directory on same volume as item being moved 1848 result = FSGetTrashDir(&trash_dir, nodeRef.device); 1849 if (result != B_OK) 1850 return result; 1851 } 1852 1853 // make sure name doesn't conflict with anything in trash already 1854 char name[B_FILE_NAME_LENGTH]; 1855 strcpy(name, ref.name); 1856 if (trash_dir.Contains(name)) { 1857 FSMakeOriginalName(name, &trash_dir, " copy"); 1858 undo.UpdateEntry(entry, name); 1859 } 1860 1861 BNode *src_node = 0; 1862 if (loc && loc != (BPoint *)-1 1863 && (src_node = GetWritableNode(entry, &statbuf)) != 0) { 1864 trash_dir.GetStat(&statbuf); 1865 PoseInfo poseInfo; 1866 poseInfo.fInvisible = false; 1867 poseInfo.fInitedDirectory = statbuf.st_ino; 1868 poseInfo.fLocation = *loc; 1869 src_node->WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, &poseInfo, 1870 sizeof(poseInfo)); 1871 delete src_node; 1872 } 1873 1874 BNode node(entry); 1875 BPath path; 1876 // Get path of entry before it's moved to the trash 1877 // and write it to the file as an attribute 1878 if (node.InitCheck() == B_OK && entry->GetPath(&path) == B_OK) { 1879 BString originalPath(path.Path()); 1880 node.WriteAttrString(kAttrOriginalPath, &originalPath); 1881 } 1882 1883 TrackerCopyLoopControl loopControl; 1884 MoveItem(entry, &trash_dir, loc, kMoveSelectionTo, name, undo, 1885 &loopControl); 1886 return B_OK; 1887 } 1888 1889 1890 ConflictCheckResult 1891 PreFlightNameCheck(BObjectList<entry_ref> *srcList, const BDirectory *destDir, 1892 int32 *collisionCount, uint32 moveMode) 1893 { 1894 1895 // count the number of name collisions in dest folder 1896 *collisionCount = 0; 1897 1898 int32 count = srcList->CountItems(); 1899 for (int32 i = 0; i < count; i++) { 1900 entry_ref *srcRef = srcList->ItemAt(i); 1901 BEntry entry(srcRef); 1902 BDirectory parent; 1903 entry.GetParent(&parent); 1904 1905 if (parent != *destDir) { 1906 if (destDir->Contains(srcRef->name)) 1907 (*collisionCount)++; 1908 } 1909 } 1910 1911 // prompt user only if there is more than one collision, otherwise the 1912 // single collision case will be handled as a "Prompt" case by CheckName 1913 if (*collisionCount > 1) { 1914 const char *verb = (moveMode == kMoveSelectionTo) ? "moving" 1915 : "copying"; 1916 char replaceMsg[256]; 1917 sprintf(replaceMsg, kReplaceManyStr, verb, verb); 1918 1919 BAlert *alert = new BAlert("", replaceMsg, 1920 "Cancel", "Prompt", "Replace all"); 1921 alert->SetShortcut(0, B_ESCAPE); 1922 switch (alert->Go()) { 1923 case 0: 1924 return kCanceled; 1925 1926 case 1: 1927 // user selected "Prompt" 1928 return kPrompt; 1929 1930 case 2: 1931 // user selected "Replace All" 1932 return kReplaceAll; 1933 } 1934 } 1935 1936 return kNoConflicts; 1937 } 1938 1939 1940 void 1941 FileStatToString(StatStruct *stat, char *buffer, int32 length) 1942 { 1943 tm timeData; 1944 localtime_r(&stat->st_mtime, &timeData); 1945 1946 sprintf(buffer, "\n\t(%Ld bytes, ", stat->st_size); 1947 uint32 pos = strlen(buffer); 1948 strftime(buffer + pos, length - pos,"%b %d %Y, %I:%M:%S %p)", &timeData); 1949 } 1950 1951 1952 status_t 1953 CheckName(uint32 moveMode, const BEntry *sourceEntry, 1954 const BDirectory *destDir, bool multipleCollisions, 1955 ConflictCheckResult &replaceAll) 1956 { 1957 if (moveMode == kDuplicateSelection) 1958 // when duplicating, we will never have a conflict 1959 return B_OK; 1960 1961 // see if item already exists in destination dir 1962 status_t err = B_OK; 1963 char name[B_FILE_NAME_LENGTH]; 1964 sourceEntry->GetName(name); 1965 bool sourceIsDirectory = sourceEntry->IsDirectory(); 1966 1967 BDirectory srcDirectory; 1968 if (sourceIsDirectory) { 1969 srcDirectory.SetTo(sourceEntry); 1970 BEntry destEntry; 1971 destDir->GetEntry(&destEntry); 1972 1973 if (moveMode != kCreateLink 1974 && moveMode != kCreateRelativeLink 1975 && (srcDirectory == *destDir 1976 || srcDirectory.Contains(&destEntry))) { 1977 (new BAlert("", "You can't move a folder into itself " 1978 "or any of its own sub-folders.", "OK", 0, 0, 1979 B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(); 1980 return B_ERROR; 1981 } 1982 } 1983 1984 if (FSIsTrashDir(sourceEntry) && moveMode != kCreateLink 1985 && moveMode != kCreateRelativeLink) { 1986 (new BAlert("", "You can't move or copy the trash.", 1987 "OK", 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(); 1988 return B_ERROR; 1989 } 1990 1991 BEntry entry; 1992 if (destDir->FindEntry(name, &entry) != B_OK) 1993 // no conflict, return 1994 return B_OK; 1995 1996 if (moveMode == kCreateLink || moveMode == kCreateRelativeLink) { 1997 // if we are creating link in the same directory, the conflict will 1998 // be handled later by giving the link a unique name 1999 sourceEntry->GetParent(&srcDirectory); 2000 2001 if (srcDirectory == *destDir) 2002 return B_OK; 2003 } 2004 2005 bool destIsDir = entry.IsDirectory(); 2006 // be sure not to replace the parent directory of the item being moved 2007 if (destIsDir) { 2008 BDirectory test_dir(&entry); 2009 if (test_dir.Contains(sourceEntry)) { 2010 (new BAlert("", "You can't replace a folder " 2011 "with one of its sub-folders.", "OK", 0, 0, 2012 B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(); 2013 return B_ERROR; 2014 } 2015 } 2016 2017 // Ensure user isn't trying to replace a file with folder or vice versa. 2018 if (moveMode != kCreateLink 2019 && moveMode != kCreateRelativeLink 2020 && destIsDir != sourceIsDirectory) { 2021 (new BAlert("", sourceIsDirectory 2022 ? "You cannot replace a file with a folder or a symbolic " 2023 "link." 2024 : "You cannot replace a folder or a symbolic link with a " 2025 "file.", "OK", 0, 0, B_WIDTH_AS_USUAL, 2026 B_WARNING_ALERT))->Go(); 2027 return B_ERROR; 2028 } 2029 2030 if (replaceAll != kReplaceAll) { 2031 // prompt user to determine whether to replace or not 2032 2033 char replaceMsg[512]; 2034 2035 if (moveMode == kCreateLink || moveMode == kCreateRelativeLink) 2036 sprintf(replaceMsg, kSymLinkReplaceStr, name); 2037 else if (sourceEntry->IsDirectory()) 2038 sprintf(replaceMsg, kDirectoryReplaceStr, name, 2039 moveMode == kMoveSelectionTo ? "moving" : "copying"); 2040 else { 2041 char sourceBuffer[96], destBuffer[96]; 2042 StatStruct statBuffer; 2043 2044 if (!sourceEntry->IsDirectory() && sourceEntry->GetStat( 2045 &statBuffer) == B_OK) { 2046 FileStatToString(&statBuffer, sourceBuffer, 96); 2047 } else 2048 sourceBuffer[0] = '\0'; 2049 2050 if (!entry.IsDirectory() && entry.GetStat(&statBuffer) == B_OK) 2051 FileStatToString(&statBuffer, destBuffer, 96); 2052 else 2053 destBuffer[0] = '\0'; 2054 2055 sprintf(replaceMsg, kReplaceStr, name, destBuffer, name, 2056 sourceBuffer, moveMode == kMoveSelectionTo ? "moving" 2057 : "copying"); 2058 } 2059 2060 // special case single collision (don't need Replace All shortcut) 2061 BAlert *alert; 2062 if (multipleCollisions || sourceIsDirectory) 2063 alert = new BAlert("", replaceMsg, "Skip", "Replace all"); 2064 else { 2065 alert = new BAlert("", replaceMsg, "Cancel", "Replace"); 2066 alert->SetShortcut(0, B_ESCAPE); 2067 } 2068 switch (alert->Go()) { 2069 case 0: // user selected "Cancel" or "Skip" 2070 replaceAll = kCanceled; 2071 return B_ERROR; 2072 2073 case 1: // user selected "Replace" or "Replace All" 2074 replaceAll = kReplaceAll; 2075 // doesn't matter which since a single 2076 // collision "Replace" is equivalent to a 2077 // "Replace All" 2078 break; 2079 } 2080 } 2081 2082 // delete destination item 2083 if (!destIsDir) 2084 err = entry.Remove(); 2085 else 2086 return B_OK; 2087 2088 if (err != B_OK) { 2089 BString error; 2090 error << "There was a problem trying to replace \"" 2091 << name << "\". The item might be open or busy."; 2092 BAlert *alert = new BAlert("", error.String(), "Cancel", 0, 0, 2093 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 2094 alert->SetShortcut(0, B_ESCAPE); 2095 alert->Go(); 2096 } 2097 2098 return err; 2099 } 2100 2101 2102 status_t 2103 FSDeleteFolder(BEntry *dir_entry, CopyLoopControl *loopControl, 2104 bool update_status, bool delete_top_dir, bool upateFileNameInStatus) 2105 { 2106 entry_ref ref; 2107 BEntry entry; 2108 BDirectory dir; 2109 status_t err; 2110 2111 dir.SetTo(dir_entry); 2112 dir.Rewind(); 2113 // loop through everything in folder and delete it, skipping trouble files 2114 for (;;) { 2115 if (dir.GetNextEntry(&entry) != B_OK) 2116 break; 2117 2118 entry.GetRef(&ref); 2119 2120 if (loopControl->CheckUserCanceled()) 2121 return kTrashCanceled; 2122 2123 if (entry.IsDirectory()) 2124 err = FSDeleteFolder(&entry, loopControl, update_status, true, 2125 upateFileNameInStatus); 2126 else { 2127 err = entry.Remove(); 2128 if (update_status) { 2129 loopControl->UpdateStatus(upateFileNameInStatus ? ref.name 2130 : "", ref, 1, true); 2131 } 2132 } 2133 2134 if (err == kTrashCanceled) 2135 return kTrashCanceled; 2136 else if (err == B_OK) 2137 dir.Rewind(); 2138 else { 2139 loopControl->FileError(kFileDeleteErrorString, ref.name, err, 2140 false); 2141 } 2142 } 2143 2144 if (loopControl->CheckUserCanceled()) 2145 return kTrashCanceled; 2146 2147 dir_entry->GetRef(&ref); 2148 2149 if (update_status && delete_top_dir) 2150 loopControl->UpdateStatus(NULL, ref, 1); 2151 2152 if (delete_top_dir) 2153 return dir_entry->Remove(); 2154 else 2155 return B_OK; 2156 } 2157 2158 2159 void 2160 FSMakeOriginalName(BString &string, const BDirectory *destDir, 2161 const char *suffix) 2162 { 2163 if (!destDir->Contains(string.String())) 2164 return; 2165 2166 FSMakeOriginalName(string.LockBuffer(B_FILE_NAME_LENGTH), 2167 const_cast<BDirectory *>(destDir), suffix ? suffix : " copy"); 2168 string.UnlockBuffer(); 2169 } 2170 2171 2172 void 2173 FSMakeOriginalName(char *name, BDirectory *destDir, const char *suffix) 2174 { 2175 char root[B_FILE_NAME_LENGTH]; 2176 char copybase[B_FILE_NAME_LENGTH]; 2177 char temp_name[B_FILE_NAME_LENGTH + 10]; 2178 int32 fnum; 2179 2180 // is this name already original? 2181 if (!destDir->Contains(name)) 2182 return; 2183 2184 // Determine if we're copying a 'copy'. This algorithm isn't perfect. 2185 // If you're copying a file whose REAL name ends with 'copy' then 2186 // this method will return "<filename> 1", not "<filename> copy" 2187 2188 // However, it will correctly handle file that contain 'copy' 2189 // elsewhere in their name. 2190 2191 bool copycopy = false; // are we copying a copy? 2192 int32 len = (int32)strlen(name); 2193 char *p = name + len - 1; // get pointer to end os name 2194 2195 // eat up optional numbers (if were copying "<filename> copy 34") 2196 while ((p > name) && isdigit(*p)) 2197 p--; 2198 2199 // eat up optional spaces 2200 while ((p > name) && isspace(*p)) 2201 p--; 2202 2203 // now look for the phrase " copy" 2204 if (p > name) { 2205 // p points to the last char of the word. For example, 'y' in 'copy' 2206 2207 if ((p - 4 > name) && (strncmp(p - 4, suffix, 5) == 0)) { 2208 // we found 'copy' in the right place. 2209 // so truncate after 'copy' 2210 *(p + 1) = '\0'; 2211 copycopy = true; 2212 2213 // save the 'root' name of the file, for possible later use. 2214 // that is copy everything but trailing " copy". Need to 2215 // NULL terminate after copy 2216 strncpy(root, name, (uint32)((p - name) - 4)); 2217 root[(p - name) - 4] = '\0'; 2218 } 2219 } 2220 2221 if (!copycopy) { 2222 /* 2223 The name can't be longer than B_FILE_NAME_LENGTH. 2224 The algoritm adds " copy XX" to the name. That's 8 characters. 2225 B_FILE_NAME_LENGTH already accounts for NULL termination so we 2226 don't need to save an extra char at the end. 2227 */ 2228 if (strlen(name) > B_FILE_NAME_LENGTH - 8) { 2229 // name is too long - truncate it! 2230 name[B_FILE_NAME_LENGTH - 8] = '\0'; 2231 } 2232 2233 strcpy(root, name); // save root name 2234 strcat(name, suffix); 2235 } 2236 2237 strcpy(copybase, name); 2238 2239 // if name already exists then add a number 2240 fnum = 1; 2241 strcpy(temp_name, name); 2242 while (destDir->Contains(temp_name)) { 2243 sprintf(temp_name, "%s %ld", copybase, ++fnum); 2244 2245 if (strlen(temp_name) > (B_FILE_NAME_LENGTH - 1)) { 2246 /* 2247 The name has grown too long. Maybe we just went from 2248 "<filename> copy 9" to "<filename> copy 10" and that extra 2249 character was too much. The solution is to further 2250 truncate the 'root' name and continue. 2251 ??? should we reset fnum or not ??? 2252 */ 2253 root[strlen(root) - 1] = '\0'; 2254 sprintf(temp_name, "%s%s %ld", root, suffix, fnum); 2255 } 2256 } 2257 2258 ASSERT((strlen(temp_name) <= (B_FILE_NAME_LENGTH - 1))); 2259 strcpy(name, temp_name); 2260 } 2261 2262 2263 status_t 2264 FSRecursiveCalcSize(BInfoWindow *window, CopyLoopControl* loopControl, 2265 BDirectory *dir, off_t *running_size, int32 *fileCount, int32 *dirCount) 2266 { 2267 dir->Rewind(); 2268 BEntry entry; 2269 while (dir->GetNextEntry(&entry) == B_OK) { 2270 // be sure window hasn't closed 2271 if (window && window->StopCalc()) 2272 return B_OK; 2273 2274 if (loopControl->CheckUserCanceled()) 2275 return kUserCanceled; 2276 2277 StatStruct statbuf; 2278 entry.GetStat(&statbuf); 2279 2280 if (S_ISDIR(statbuf.st_mode)) { 2281 BDirectory subdir(&entry); 2282 (*dirCount)++; 2283 (*running_size) += 1024; 2284 status_t result = FSRecursiveCalcSize(window, loopControl, &subdir, 2285 running_size, fileCount, dirCount); 2286 if (result != B_OK) 2287 return result; 2288 } else { 2289 (*fileCount)++; 2290 (*running_size) += statbuf.st_size + 1024; 2291 // Add to compensate for attributes. 2292 } 2293 } 2294 return B_OK; 2295 } 2296 2297 2298 status_t 2299 CalcItemsAndSize(CopyLoopControl* loopControl, BObjectList<entry_ref> *refList, 2300 size_t blockSize, int32 *totalCount, off_t *totalSize) 2301 { 2302 int32 fileCount = 0; 2303 int32 dirCount = 0; 2304 2305 // check block size for sanity 2306 if (blockSize < 0) { 2307 // This would point at an error to retrieve the block size from 2308 // the target volume. The code below cannot be used, it is only 2309 // meant to get the block size when item operations happen on 2310 // the source volume. 2311 blockSize = 2048; 2312 } else if (blockSize < 1024) { 2313 blockSize = 1024; 2314 if (entry_ref* ref = refList->ItemAt(0)) { 2315 // TODO: This assumes all entries in the list share the same 2316 // volume... 2317 BVolume volume(ref->device); 2318 if (volume.InitCheck() == B_OK) 2319 blockSize = volume.BlockSize(); 2320 } 2321 } 2322 // File systems like ReiserFS may advertize a large block size, but 2323 // stuff is still packed into blocks, so clamp maximum block size. 2324 if (blockSize > 8192) 2325 blockSize = 8192; 2326 2327 int32 num_items = refList->CountItems(); 2328 for (int32 i = 0; i < num_items; i++) { 2329 entry_ref *ref = refList->ItemAt(i); 2330 BEntry entry(ref); 2331 StatStruct statbuf; 2332 entry.GetStat(&statbuf); 2333 2334 if (loopControl->CheckUserCanceled()) 2335 return kUserCanceled; 2336 2337 if (S_ISDIR(statbuf.st_mode)) { 2338 BDirectory dir(&entry); 2339 dirCount++; 2340 (*totalSize) += blockSize; 2341 status_t result = FSRecursiveCalcSize(NULL, loopControl, &dir, 2342 totalSize, &fileCount, &dirCount); 2343 if (result != B_OK) 2344 return result; 2345 } else { 2346 fileCount++; 2347 (*totalSize) += statbuf.st_size + blockSize; 2348 } 2349 } 2350 2351 *totalCount += (fileCount + dirCount); 2352 return B_OK; 2353 } 2354 2355 2356 status_t 2357 FSGetTrashDir(BDirectory *trashDir, dev_t dev) 2358 { 2359 2360 BVolume volume(dev); 2361 status_t result = volume.InitCheck(); 2362 if (result != B_OK) 2363 return result; 2364 2365 BPath path; 2366 result = find_directory(B_TRASH_DIRECTORY, &path, true, &volume); 2367 if (result != B_OK) 2368 return result; 2369 2370 result = trashDir->SetTo(path.Path()); 2371 if (result != B_OK) 2372 return result; 2373 2374 return B_OK; 2375 } 2376 2377 2378 status_t 2379 FSGetDeskDir(BDirectory *deskDir) 2380 { 2381 BPath path; 2382 status_t result = find_directory(B_DESKTOP_DIRECTORY, &path, true); 2383 if (result != B_OK) 2384 return result; 2385 2386 result = deskDir->SetTo(path.Path()); 2387 if (result != B_OK) 2388 return result; 2389 2390 size_t size; 2391 const void* data = GetTrackerResources()-> 2392 LoadResource('ICON', kResDeskIcon, &size); 2393 if (data != NULL) 2394 deskDir->WriteAttr(kAttrLargeIcon, 'ICON', 0, data, size); 2395 2396 data = GetTrackerResources()-> 2397 LoadResource('MICN', kResDeskIcon, &size); 2398 if (data != NULL) 2399 deskDir->WriteAttr(kAttrMiniIcon, 'MICN', 0, data, size); 2400 2401 #ifdef __HAIKU__ 2402 data = GetTrackerResources()-> 2403 LoadResource(B_VECTOR_ICON_TYPE, kResDeskIcon, &size); 2404 if (data != NULL) 2405 deskDir->WriteAttr(kAttrIcon, B_VECTOR_ICON_TYPE, 0, data, size); 2406 #endif 2407 2408 return B_OK; 2409 } 2410 2411 2412 status_t 2413 FSGetBootDeskDir(BDirectory *deskDir) 2414 { 2415 BVolume bootVol; 2416 BVolumeRoster().GetBootVolume(&bootVol); 2417 BPath path; 2418 2419 status_t result = find_directory(B_DESKTOP_DIRECTORY, &path, true, 2420 &bootVol); 2421 if (result != B_OK) 2422 return result; 2423 2424 return deskDir->SetTo(path.Path()); 2425 } 2426 2427 2428 static bool 2429 FSIsDirFlavor(const BEntry *entry, directory_which directoryType) 2430 { 2431 StatStruct dir_stat; 2432 StatStruct entry_stat; 2433 BVolume volume; 2434 BPath path; 2435 2436 if (entry->GetStat(&entry_stat) != B_OK) 2437 return false; 2438 2439 if (volume.SetTo(entry_stat.st_dev) != B_OK) 2440 return false; 2441 2442 if (find_directory(directoryType, &path, false, &volume) != B_OK) 2443 return false; 2444 2445 stat(path.Path(), &dir_stat); 2446 2447 return dir_stat.st_ino == entry_stat.st_ino 2448 && dir_stat.st_dev == entry_stat.st_dev; 2449 } 2450 2451 2452 bool 2453 FSIsPrintersDir(const BEntry *entry) 2454 { 2455 return FSIsDirFlavor(entry, B_USER_PRINTERS_DIRECTORY); 2456 } 2457 2458 2459 bool 2460 FSIsTrashDir(const BEntry *entry) 2461 { 2462 return FSIsDirFlavor(entry, B_TRASH_DIRECTORY); 2463 } 2464 2465 2466 bool 2467 FSIsDeskDir(const BEntry *entry) 2468 { 2469 BPath path; 2470 status_t result = find_directory(B_DESKTOP_DIRECTORY, &path, true); 2471 if (result != B_OK) 2472 return false; 2473 2474 BEntry entryToCompare(path.Path()); 2475 return entryToCompare == *entry; 2476 } 2477 2478 2479 bool 2480 FSIsHomeDir(const BEntry *entry) 2481 { 2482 return FSIsDirFlavor(entry, B_USER_DIRECTORY); 2483 } 2484 2485 2486 bool 2487 DirectoryMatchesOrContains(const BEntry *entry, directory_which which) 2488 { 2489 BPath path; 2490 if (find_directory(which, &path, false, NULL) != B_OK) 2491 return false; 2492 2493 BEntry dirEntry(path.Path()); 2494 if (dirEntry.InitCheck() != B_OK) 2495 return false; 2496 2497 if (dirEntry == *entry) 2498 // root level match 2499 return true; 2500 2501 BDirectory dir(&dirEntry); 2502 return dir.Contains(entry); 2503 } 2504 2505 2506 bool 2507 DirectoryMatchesOrContains(const BEntry *entry, const char *additionalPath, 2508 directory_which which) 2509 { 2510 BPath path; 2511 if (find_directory(which, &path, false, NULL) != B_OK) 2512 return false; 2513 2514 path.Append(additionalPath); 2515 BEntry dirEntry(path.Path()); 2516 if (dirEntry.InitCheck() != B_OK) 2517 return false; 2518 2519 if (dirEntry == *entry) 2520 // root level match 2521 return true; 2522 2523 BDirectory dir(&dirEntry); 2524 return dir.Contains(entry); 2525 } 2526 2527 2528 bool 2529 DirectoryMatches(const BEntry *entry, directory_which which) 2530 { 2531 BPath path; 2532 if (find_directory(which, &path, false, NULL) != B_OK) 2533 return false; 2534 2535 BEntry dirEntry(path.Path()); 2536 if (dirEntry.InitCheck() != B_OK) 2537 return false; 2538 2539 return dirEntry == *entry; 2540 } 2541 2542 2543 bool 2544 DirectoryMatches(const BEntry *entry, const char *additionalPath, 2545 directory_which which) 2546 { 2547 BPath path; 2548 if (find_directory(which, &path, false, NULL) != B_OK) 2549 return false; 2550 2551 path.Append(additionalPath); 2552 BEntry dirEntry(path.Path()); 2553 if (dirEntry.InitCheck() != B_OK) 2554 return false; 2555 2556 return dirEntry == *entry; 2557 } 2558 2559 2560 extern status_t 2561 FSFindTrackerSettingsDir(BPath *path, bool autoCreate) 2562 { 2563 status_t result = find_directory (B_USER_SETTINGS_DIRECTORY, path, 2564 autoCreate); 2565 if (result != B_OK) 2566 return result; 2567 2568 path->Append("Tracker"); 2569 2570 return mkdir(path->Path(), 0777) ? B_OK : errno; 2571 } 2572 2573 2574 bool 2575 FSInTrashDir(const entry_ref *ref) 2576 { 2577 BEntry entry(ref); 2578 if (entry.InitCheck() != B_OK) 2579 return false; 2580 2581 BDirectory trashDir; 2582 if (FSGetTrashDir(&trashDir, ref->device) != B_OK) 2583 return false; 2584 2585 return trashDir.Contains(&entry); 2586 } 2587 2588 2589 void 2590 FSEmptyTrash() 2591 { 2592 if (find_thread("_tracker_empty_trash_") != B_OK) { 2593 resume_thread(spawn_thread(empty_trash, "_tracker_empty_trash_", 2594 B_NORMAL_PRIORITY, NULL)); 2595 } 2596 } 2597 2598 2599 status_t 2600 empty_trash(void *) 2601 { 2602 // empty trash on all mounted volumes 2603 status_t err = B_OK; 2604 2605 TrackerCopyLoopControl loopControl(kTrashState); 2606 2607 // calculate the sum total of all items on all volumes in trash 2608 BObjectList<entry_ref> srcList; 2609 int32 totalCount = 0; 2610 off_t totalSize = 0; 2611 2612 BVolumeRoster volumeRoster; 2613 BVolume volume; 2614 while (volumeRoster.GetNextVolume(&volume) == B_OK) { 2615 if (volume.IsReadOnly() || !volume.IsPersistent()) 2616 continue; 2617 2618 BDirectory trashDirectory; 2619 if (FSGetTrashDir(&trashDirectory, volume.Device()) != B_OK) 2620 continue; 2621 2622 BEntry entry; 2623 trashDirectory.GetEntry(&entry); 2624 2625 entry_ref ref; 2626 entry.GetRef(&ref); 2627 srcList.AddItem(&ref); 2628 err = CalcItemsAndSize(&loopControl, &srcList, volume.BlockSize(), 2629 &totalCount, &totalSize); 2630 if (err != B_OK) 2631 break; 2632 2633 srcList.MakeEmpty(); 2634 2635 // don't count trash directory itself 2636 totalCount--; 2637 } 2638 2639 if (err == B_OK) { 2640 loopControl.Init(totalCount, totalCount); 2641 2642 volumeRoster.Rewind(); 2643 while (volumeRoster.GetNextVolume(&volume) == B_OK) { 2644 if (volume.IsReadOnly() || !volume.IsPersistent()) 2645 continue; 2646 2647 BDirectory trashDirectory; 2648 if (FSGetTrashDir(&trashDirectory, volume.Device()) != B_OK) 2649 continue; 2650 2651 BEntry entry; 2652 trashDirectory.GetEntry(&entry); 2653 err = FSDeleteFolder(&entry, &loopControl, true, false); 2654 } 2655 } 2656 2657 if (err != B_OK && err != kTrashCanceled && err != kUserCanceled) { 2658 (new BAlert("", "Error emptying Trash!", "OK", NULL, NULL, 2659 B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(); 2660 } 2661 2662 return B_OK; 2663 } 2664 2665 2666 status_t 2667 _DeleteTask(BObjectList<entry_ref> *list, bool confirm) 2668 { 2669 if (confirm) { 2670 bool dontMoveToTrash = TrackerSettings().DontMoveFilesToTrash(); 2671 2672 if (!dontMoveToTrash) { 2673 BAlert *alert = new BAlert("", kDeleteConfirmationStr, 2674 "Cancel", "Move to Trash", "Delete", B_WIDTH_AS_USUAL, 2675 B_OFFSET_SPACING, B_WARNING_ALERT); 2676 2677 alert->SetShortcut(0, B_ESCAPE); 2678 alert->SetShortcut(1, 'm'); 2679 alert->SetShortcut(2, 'd'); 2680 2681 switch (alert->Go()) { 2682 case 0: 2683 delete list; 2684 return B_OK; 2685 case 1: 2686 FSMoveToTrash(list, NULL, false); 2687 return B_OK; 2688 } 2689 } else { 2690 BAlert *alert = new BAlert("", kDeleteConfirmationStr, 2691 "Cancel", "Delete", NULL, B_WIDTH_AS_USUAL, B_OFFSET_SPACING, 2692 B_WARNING_ALERT); 2693 2694 alert->SetShortcut(0, B_ESCAPE); 2695 alert->SetShortcut(1, 'd'); 2696 2697 if (!alert->Go()) { 2698 delete list; 2699 return B_OK; 2700 } 2701 } 2702 } 2703 2704 TrackerCopyLoopControl loopControl(kDeleteState); 2705 2706 // calculate the sum total of all items on all volumes in trash 2707 int32 totalItems = 0; 2708 int64 totalSize = 0; 2709 2710 status_t err = CalcItemsAndSize(&loopControl, list, 0, &totalItems, 2711 &totalSize); 2712 if (err == B_OK) { 2713 loopControl.Init(totalItems, totalItems); 2714 2715 int32 count = list->CountItems(); 2716 for (int32 index = 0; index < count; index++) { 2717 entry_ref ref(*list->ItemAt(index)); 2718 BEntry entry(&ref); 2719 loopControl.UpdateStatus(ref.name, ref, 1, true); 2720 if (entry.IsDirectory()) 2721 err = FSDeleteFolder(&entry, &loopControl, true, true, true); 2722 else 2723 err = entry.Remove(); 2724 } 2725 2726 if (err != kTrashCanceled && err != kUserCanceled && err != B_OK) 2727 (new BAlert("", "Error deleting items", "OK", NULL, NULL, 2728 B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(); 2729 } 2730 2731 delete list; 2732 2733 return B_OK; 2734 } 2735 2736 status_t 2737 FSRecursiveCreateFolder(BPath path) 2738 { 2739 BEntry entry(path.Path()); 2740 if (entry.InitCheck() != B_OK) { 2741 BPath parentPath; 2742 status_t err = path.GetParent(&parentPath); 2743 if (err != B_OK) 2744 return err; 2745 2746 err = FSRecursiveCreateFolder(parentPath); 2747 if (err != B_OK) 2748 return err; 2749 } 2750 2751 entry.SetTo(path.Path()); 2752 if (entry.Exists()) 2753 return B_FILE_EXISTS; 2754 else { 2755 char name[B_FILE_NAME_LENGTH]; 2756 BDirectory parent; 2757 2758 entry.GetParent(&parent); 2759 entry.GetName(name); 2760 parent.CreateDirectory(name, NULL); 2761 } 2762 2763 return B_OK; 2764 } 2765 2766 status_t 2767 _RestoreTask(BObjectList<entry_ref> *list) 2768 { 2769 TrackerCopyLoopControl loopControl(kRestoreFromTrashState); 2770 2771 // calculate the sum total of all items that will be restored 2772 int32 totalItems = 0; 2773 int64 totalSize = 0; 2774 2775 status_t err = CalcItemsAndSize(&loopControl, list, 0, &totalItems, 2776 &totalSize); 2777 if (err == B_OK) { 2778 loopControl.Init(totalItems, totalItems); 2779 2780 int32 count = list->CountItems(); 2781 for (int32 index = 0; index < count; index++) { 2782 entry_ref ref(*list->ItemAt(index)); 2783 BEntry entry(&ref); 2784 BPath originalPath; 2785 2786 loopControl.UpdateStatus(ref.name, ref, 1, true); 2787 2788 if (FSGetOriginalPath(&entry, &originalPath) != B_OK) 2789 continue; 2790 2791 BEntry originalEntry(originalPath.Path()); 2792 BPath parentPath; 2793 err = originalPath.GetParent(&parentPath); 2794 if (err != B_OK) 2795 continue; 2796 BEntry parentEntry(parentPath.Path()); 2797 2798 if (parentEntry.InitCheck() != B_OK || !parentEntry.Exists()) { 2799 if (FSRecursiveCreateFolder(parentPath) == B_OK) { 2800 originalEntry.SetTo(originalPath.Path()); 2801 if (entry.InitCheck() != B_OK) 2802 continue; 2803 } 2804 } 2805 2806 if (!originalEntry.Exists()) { 2807 BDirectory dir(parentPath.Path()); 2808 if (dir.InitCheck() == B_OK) { 2809 char leafName[B_FILE_NAME_LENGTH]; 2810 originalEntry.GetName(leafName); 2811 if (entry.MoveTo(&dir, leafName) == B_OK) { 2812 BNode node(&entry); 2813 if (node.InitCheck() == B_OK) 2814 node.RemoveAttr(kAttrOriginalPath); 2815 } 2816 } 2817 } 2818 2819 err = loopControl.CheckUserCanceled(); 2820 if (err != B_OK) 2821 break; 2822 } 2823 } 2824 2825 delete list; 2826 2827 return err; 2828 } 2829 2830 void 2831 FSCreateTrashDirs() 2832 { 2833 BVolume volume; 2834 BVolumeRoster roster; 2835 2836 roster.Rewind(); 2837 while (roster.GetNextVolume(&volume) == B_OK) { 2838 if (volume.IsReadOnly() || !volume.IsPersistent()) 2839 continue; 2840 2841 BPath path; 2842 find_directory(B_TRASH_DIRECTORY, &path, true, &volume); 2843 2844 BDirectory trashDir; 2845 if (FSGetTrashDir(&trashDir, volume.Device()) == B_OK) { 2846 // make trash invisible 2847 StatStruct sbuf; 2848 trashDir.GetStat(&sbuf); 2849 2850 PoseInfo poseInfo; 2851 poseInfo.fInvisible = true; 2852 poseInfo.fInitedDirectory = sbuf.st_ino; 2853 trashDir.WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, &poseInfo, 2854 sizeof(PoseInfo)); 2855 2856 size_t size; 2857 const void* data = GetTrackerResources()-> 2858 LoadResource('ICON', kResTrashIcon, &size); 2859 if (data != NULL) { 2860 trashDir.WriteAttr(kAttrLargeIcon, 'ICON', 0, 2861 data, size); 2862 } 2863 data = GetTrackerResources()-> 2864 LoadResource('MICN', kResTrashIcon, &size); 2865 if (data != NULL) { 2866 trashDir.WriteAttr(kAttrMiniIcon, 'MICN', 0, 2867 data, size); 2868 } 2869 #ifdef __HAIKU 2870 data = GetTrackerResources()-> 2871 LoadResource(B_VECTOR_ICON_TYPE, kResTrashIcon, &size); 2872 if (data != NULL) { 2873 trashDir.WriteAttr(kAttrIcon, B_VECTOR_ICON_TYPE, 0, 2874 data, size); 2875 } 2876 #endif 2877 } 2878 } 2879 } 2880 2881 2882 status_t 2883 FSCreateNewFolder(const entry_ref *ref) 2884 { 2885 node_ref node; 2886 node.device = ref->device; 2887 node.node = ref->directory; 2888 2889 BDirectory dir(&node); 2890 status_t result = dir.InitCheck(); 2891 if (result != B_OK) 2892 return result; 2893 2894 // ToDo: is that really necessary here? 2895 BString name(ref->name); 2896 FSMakeOriginalName(name, &dir, "-"); 2897 2898 BDirectory newDir; 2899 result = dir.CreateDirectory(name.String(), &newDir); 2900 if (result != B_OK) 2901 return result; 2902 2903 BNodeInfo nodeInfo(&newDir); 2904 nodeInfo.SetType(B_DIR_MIMETYPE); 2905 2906 return result; 2907 } 2908 2909 2910 status_t 2911 FSCreateNewFolderIn(const node_ref *dirNode, entry_ref *newRef, 2912 node_ref *newNode) 2913 { 2914 BDirectory dir(dirNode); 2915 status_t result = dir.InitCheck(); 2916 if (result == B_OK) { 2917 char name[B_FILE_NAME_LENGTH]; 2918 strcpy(name, "New folder"); 2919 2920 int32 fnum = 1; 2921 while (dir.Contains(name)) { 2922 // if base name already exists then add a number 2923 // ToDo: 2924 // move this logic ot FSMakeOriginalName 2925 if (++fnum > 9) 2926 sprintf(name, "New folder%ld", fnum); 2927 else 2928 sprintf(name, "New folder %ld", fnum); 2929 } 2930 2931 BDirectory newDir; 2932 result = dir.CreateDirectory(name, &newDir); 2933 if (result == B_OK) { 2934 BEntry entry; 2935 newDir.GetEntry(&entry); 2936 entry.GetRef(newRef); 2937 entry.GetNodeRef(newNode); 2938 2939 BNodeInfo nodeInfo(&newDir); 2940 nodeInfo.SetType(B_DIR_MIMETYPE); 2941 2942 // add undo item 2943 NewFolderUndo undo(*newRef); 2944 return B_OK; 2945 } 2946 } 2947 2948 BAlert *alert = new BAlert("", "Sorry, could not create a new folder.", 2949 "Cancel", 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 2950 alert->SetShortcut(0, B_ESCAPE); 2951 alert->Go(); 2952 return result; 2953 } 2954 2955 2956 ReadAttrResult 2957 ReadAttr(const BNode *node, const char *hostAttrName, 2958 const char *foreignAttrName, type_code type, off_t offset, void *buffer, 2959 size_t length, void (*swapFunc)(void *), bool isForeign) 2960 { 2961 if (!isForeign && node->ReadAttr(hostAttrName, type, offset, buffer, 2962 length) == (ssize_t)length) { 2963 return kReadAttrNativeOK; 2964 } 2965 2966 // PRINT(("trying %s\n", foreignAttrName)); 2967 // try the other endianness 2968 if (node->ReadAttr(foreignAttrName, type, offset, buffer, length) 2969 != (ssize_t)length) { 2970 return kReadAttrFailed; 2971 } 2972 2973 // PRINT(("got %s\n", foreignAttrName)); 2974 if (!swapFunc) 2975 return kReadAttrForeignOK; 2976 2977 (swapFunc)(buffer); 2978 // run the endian swapper 2979 2980 return kReadAttrForeignOK; 2981 } 2982 2983 2984 ReadAttrResult 2985 GetAttrInfo(const BNode *node, const char *hostAttrName, 2986 const char *foreignAttrName, type_code *type, size_t *size) 2987 { 2988 attr_info info; 2989 2990 if (node->GetAttrInfo(hostAttrName, &info) == B_OK) { 2991 if (type) 2992 *type = info.type; 2993 if (size) 2994 *size = (size_t)info.size; 2995 2996 return kReadAttrNativeOK; 2997 } 2998 2999 if (node->GetAttrInfo(foreignAttrName, &info) == B_OK) { 3000 if (type) 3001 *type = info.type; 3002 if (size) 3003 *size = (size_t)info.size; 3004 3005 return kReadAttrForeignOK; 3006 } 3007 return kReadAttrFailed; 3008 } 3009 3010 // launching code 3011 3012 static status_t 3013 TrackerOpenWith(const BMessage *refs) 3014 { 3015 BMessage clone(*refs); 3016 ASSERT(dynamic_cast<TTracker *>(be_app)); 3017 ASSERT(clone.what); 3018 clone.AddInt32("launchUsingSelector", 0); 3019 // runs the Open With window 3020 be_app->PostMessage(&clone); 3021 3022 return B_OK; 3023 } 3024 3025 static void 3026 AsynchLaunchBinder(void (*func)(const entry_ref *, const BMessage *, bool on), 3027 const entry_ref *entry, const BMessage *message, bool on) 3028 { 3029 Thread::Launch(NewFunctionObject(func, entry, message, on), 3030 B_NORMAL_PRIORITY, "LaunchTask"); 3031 } 3032 3033 static bool 3034 SniffIfGeneric(const entry_ref *ref) 3035 { 3036 BNode node(ref); 3037 char type[B_MIME_TYPE_LENGTH]; 3038 BNodeInfo info(&node); 3039 if (info.GetType(type) == B_OK 3040 && strcasecmp(type, B_FILE_MIME_TYPE) != 0) { 3041 // already has a type and it's not octet stream 3042 return false; 3043 } 3044 3045 BPath path(ref); 3046 if (path.Path()) { 3047 // force a mimeset 3048 node.RemoveAttr(kAttrMIMEType); 3049 update_mime_info(path.Path(), 0, 1, 1); 3050 } 3051 3052 return true; 3053 } 3054 3055 static void 3056 SniffIfGeneric(const BMessage *refs) 3057 { 3058 entry_ref ref; 3059 for (int32 index = 0; ; index++) { 3060 if (refs->FindRef("refs", index, &ref) != B_OK) 3061 break; 3062 SniffIfGeneric(&ref); 3063 } 3064 } 3065 3066 static void 3067 _TrackerLaunchAppWithDocuments(const entry_ref *appRef, const BMessage *refs, 3068 bool openWithOK) 3069 { 3070 team_id team; 3071 3072 status_t error = B_ERROR; 3073 BString alertString; 3074 3075 for (int32 mimesetIt = 0; ; mimesetIt++) { 3076 error = be_roster->Launch(appRef, refs, &team); 3077 if (error == B_ALREADY_RUNNING) 3078 // app already running, not really an error 3079 error = B_OK; 3080 3081 if (error == B_OK) 3082 break; 3083 3084 if (mimesetIt > 0) 3085 break; 3086 3087 // failed to open, try mimesetting the refs and launching again 3088 SniffIfGeneric(refs); 3089 } 3090 3091 if (error == B_OK) { 3092 // close possible parent window, if specified 3093 const node_ref *nodeToClose = 0; 3094 int32 numBytes; 3095 refs->FindData("nodeRefsToClose", B_RAW_TYPE, 3096 (const void **)&nodeToClose, &numBytes); 3097 if (nodeToClose) 3098 dynamic_cast<TTracker *>(be_app)->CloseParent(*nodeToClose); 3099 } else { 3100 alertString << "Could not open \"" << appRef->name << "\" (" 3101 << strerror(error) << "). "; 3102 if (refs && openWithOK && error != B_SHUTTING_DOWN) { 3103 alertString << kFindAlternativeStr; 3104 BAlert *alert = new BAlert("", alertString.String(), 3105 "Cancel", "Find", 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 3106 alert->SetShortcut(0, B_ESCAPE); 3107 if (alert->Go() == 1) 3108 error = TrackerOpenWith(refs); 3109 } else { 3110 BAlert *alert = new BAlert("", alertString.String(), 3111 "Cancel", 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 3112 alert->SetShortcut(0, B_ESCAPE); 3113 alert->Go(); 3114 } 3115 } 3116 } 3117 3118 extern "C" char** environ; 3119 3120 extern "C" status_t _kern_load_image(const char * const *flatArgs, 3121 size_t flatArgsSize, int32 argCount, int32 envCount, int32 priority, 3122 uint32 flags, port_id errorPort, uint32 errorToken); 3123 extern "C" status_t __flatten_process_args(const char * const *args, 3124 int32 argCount, const char * const *env, int32 envCount, char ***_flatArgs, 3125 size_t *_flatSize); 3126 3127 3128 static status_t 3129 LoaderErrorDetails(const entry_ref *app, BString &details) 3130 { 3131 BPath path; 3132 BEntry appEntry(app, true); 3133 3134 status_t result = appEntry.GetPath(&path); 3135 if (result != B_OK) 3136 return result; 3137 3138 char *argv[2] = { const_cast<char *>(path.Path()), 0}; 3139 3140 port_id errorPort = create_port(1, "Tracker loader error"); 3141 3142 // count environment variables 3143 uint32 envCount = 0; 3144 while (environ[envCount] != NULL) 3145 envCount++; 3146 3147 char** flatArgs = NULL; 3148 size_t flatArgsSize; 3149 result = __flatten_process_args((const char **)argv, 1, 3150 environ, envCount, &flatArgs, &flatArgsSize); 3151 if (result != B_OK) 3152 return result; 3153 3154 result = _kern_load_image(flatArgs, flatArgsSize, 1, envCount, 3155 B_NORMAL_PRIORITY, B_WAIT_TILL_LOADED, errorPort, 0); 3156 if (result == B_OK) { 3157 // we weren't supposed to be able to start the application... 3158 return B_ERROR; 3159 } 3160 3161 // read error message from port and construct details string 3162 3163 ssize_t bufferSize; 3164 3165 do { 3166 bufferSize = port_buffer_size_etc(errorPort, B_RELATIVE_TIMEOUT, 0); 3167 } while (bufferSize == B_INTERRUPTED); 3168 3169 if (bufferSize <= B_OK) { 3170 delete_port(errorPort); 3171 return bufferSize; 3172 } 3173 3174 uint8 *buffer = (uint8 *)malloc(bufferSize); 3175 if (buffer == NULL) { 3176 delete_port(errorPort); 3177 return B_NO_MEMORY; 3178 } 3179 3180 bufferSize = read_port_etc(errorPort, NULL, buffer, bufferSize, 3181 B_RELATIVE_TIMEOUT, 0); 3182 delete_port(errorPort); 3183 3184 if (bufferSize < B_OK) { 3185 free(buffer); 3186 return bufferSize; 3187 } 3188 3189 BMessage message; 3190 result = message.Unflatten((const char *)buffer); 3191 free(buffer); 3192 3193 if (result != B_OK) 3194 return result; 3195 3196 int32 errorCode = B_ERROR; 3197 result = message.FindInt32("error", &errorCode); 3198 if (result != B_OK) 3199 return result; 3200 3201 const char *detailName = NULL; 3202 switch (errorCode) { 3203 case B_MISSING_LIBRARY: 3204 detailName = "missing library"; 3205 break; 3206 3207 case B_MISSING_SYMBOL: 3208 detailName = "missing symbol"; 3209 break; 3210 } 3211 3212 if (detailName == NULL) 3213 return B_ERROR; 3214 3215 const char *detail; 3216 for (int32 i = 0; message.FindString(detailName, i, &detail) == B_OK; 3217 i++) { 3218 if (i > 0) 3219 details += ", "; 3220 details += detail; 3221 } 3222 3223 return B_OK; 3224 } 3225 3226 3227 static void 3228 _TrackerLaunchDocuments(const entry_ref */*doNotUse*/, const BMessage *refs, 3229 bool openWithOK) 3230 { 3231 BMessage copyOfRefs(*refs); 3232 3233 entry_ref documentRef; 3234 if (copyOfRefs.FindRef("refs", &documentRef) != B_OK) 3235 // nothing to launch, we are done 3236 return; 3237 3238 status_t error = B_ERROR; 3239 entry_ref app; 3240 BMessage *refsToPass = NULL; 3241 BString alertString; 3242 const char *alternative = 0; 3243 3244 for (int32 mimesetIt = 0; ; mimesetIt++) { 3245 alertString = ""; 3246 error = be_roster->FindApp(&documentRef, &app); 3247 3248 if (error != B_OK && mimesetIt == 0) { 3249 SniffIfGeneric(©OfRefs); 3250 continue; 3251 } 3252 3253 if (error != B_OK) { 3254 alertString << "Could not find an application to open \"" 3255 << documentRef.name << "\" (" << strerror(error) << "). "; 3256 if (openWithOK) 3257 alternative = kFindApplicationStr; 3258 3259 break; 3260 } else { 3261 BEntry appEntry(&app, true); 3262 for (int32 index = 0;;) { 3263 // remove the app itself from the refs received so we don't try 3264 // to open ourselves 3265 entry_ref ref; 3266 if (copyOfRefs.FindRef("refs", index, &ref) != B_OK) 3267 break; 3268 3269 // deal with symlinks properly 3270 BEntry documentEntry(&ref, true); 3271 if (appEntry == documentEntry) { 3272 PRINT(("stripping %s, app %s \n", ref.name, app.name)); 3273 copyOfRefs.RemoveData("refs", index); 3274 } else { 3275 PRINT(("leaving %s, app %s \n", ref.name, app.name)); 3276 index++; 3277 } 3278 } 3279 3280 refsToPass = CountRefs(©OfRefs) > 0 ? ©OfRefs: 0; 3281 team_id team; 3282 error = be_roster->Launch(&app, refsToPass, &team); 3283 if (error == B_ALREADY_RUNNING) 3284 // app already running, not really an error 3285 error = B_OK; 3286 if (error == B_OK || mimesetIt != 0) 3287 break; 3288 3289 SniffIfGeneric(©OfRefs); 3290 } 3291 } 3292 3293 if (error != B_OK && alertString.Length() == 0) { 3294 BString loaderErrorString; 3295 bool openedDocuments = true; 3296 3297 if (!refsToPass) { 3298 // we just double clicked the app itself, do not offer to 3299 // find a handling app 3300 openWithOK = false; 3301 openedDocuments = false; 3302 } 3303 3304 if (error == B_LAUNCH_FAILED_EXECUTABLE && !refsToPass) { 3305 alertString << "Could not open \"" << app.name 3306 << "\". The file is mistakenly marked as executable. "; 3307 3308 if (!openWithOK) { 3309 // offer the possibility to change the permissions 3310 3311 alertString << "\nShould this be fixed?"; 3312 BAlert *alert = new BAlert("", alertString.String(), 3313 "Cancel", "Proceed", 0, B_WIDTH_AS_USUAL, 3314 B_WARNING_ALERT); 3315 alert->SetShortcut(0, B_ESCAPE); 3316 if (alert->Go() == 1) { 3317 BEntry entry(&documentRef); 3318 mode_t permissions; 3319 3320 error = entry.GetPermissions(&permissions); 3321 if (error == B_OK) { 3322 error = entry.SetPermissions(permissions 3323 & ~(S_IXUSR | S_IXGRP | S_IXOTH)); 3324 } 3325 if (error == B_OK) { 3326 // we updated the permissions, so let's try again 3327 _TrackerLaunchDocuments(NULL, refs, false); 3328 return; 3329 } else { 3330 alertString = "Could not update permissions of " 3331 "file \""; 3332 alertString << app.name << "\". " << strerror(error); 3333 } 3334 } else 3335 return; 3336 } 3337 3338 alternative = kFindApplicationStr; 3339 } else if (error == B_LAUNCH_FAILED_APP_IN_TRASH) { 3340 alertString << "Could not open \"" << documentRef.name 3341 << "\" because application \"" << app.name << "\" is in the " 3342 "Trash. "; 3343 alternative = kFindAlternativeStr; 3344 } else if (error == B_LAUNCH_FAILED_APP_NOT_FOUND) { 3345 alertString << "Could not open \"" << documentRef.name << "\" " 3346 << "(" << strerror(error) << "). "; 3347 alternative = kFindAlternativeStr; 3348 } else if (error == B_MISSING_SYMBOL 3349 && LoaderErrorDetails(&app, loaderErrorString) == B_OK) { 3350 alertString << "Could not open \"" << documentRef.name << "\" "; 3351 if (openedDocuments) 3352 alertString << "with application \"" << app.name << "\" "; 3353 alertString << "(Missing symbol: " << loaderErrorString << "). \n"; 3354 alternative = kFindAlternativeStr; 3355 } else if (error == B_MISSING_LIBRARY 3356 && LoaderErrorDetails(&app, loaderErrorString) == B_OK) { 3357 alertString << "Could not open \"" << documentRef.name << "\" "; 3358 if (openedDocuments) 3359 alertString << "with application \"" << app.name << "\" "; 3360 alertString << "(Missing libraries: " << loaderErrorString 3361 << "). \n"; 3362 alternative = kFindAlternativeStr; 3363 } else { 3364 alertString << "Could not open \"" << documentRef.name 3365 << "\" with application \"" << app.name << "\" (" 3366 << strerror(error) << "). "; 3367 alternative = kFindAlternativeStr; 3368 } 3369 } 3370 3371 if (error != B_OK) { 3372 if (openWithOK) { 3373 ASSERT(alternative); 3374 alertString << alternative; 3375 BAlert *alert = new BAlert("", alertString.String(), 3376 "Cancel", "Find", 0, B_WIDTH_AS_USUAL, 3377 B_WARNING_ALERT); 3378 alert->SetShortcut(0, B_ESCAPE); 3379 if (alert->Go() == 1) 3380 error = TrackerOpenWith(refs); 3381 } else { 3382 BAlert *alert = new BAlert("", alertString.String(), 3383 "Cancel", 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 3384 alert->SetShortcut(0, B_ESCAPE); 3385 alert->Go(); 3386 } 3387 } 3388 } 3389 3390 // the following three calls don't return any reasonable error codes, 3391 // should fix that, making them void 3392 3393 status_t 3394 TrackerLaunch(const entry_ref *appRef, const BMessage *refs, bool async, 3395 bool openWithOK) 3396 { 3397 if (!async) 3398 _TrackerLaunchAppWithDocuments(appRef, refs, openWithOK); 3399 else { 3400 AsynchLaunchBinder(&_TrackerLaunchAppWithDocuments, appRef, refs, 3401 openWithOK); 3402 } 3403 3404 return B_OK; 3405 } 3406 3407 status_t 3408 TrackerLaunch(const entry_ref *appRef, bool async) 3409 { 3410 if (!async) 3411 _TrackerLaunchAppWithDocuments(appRef, 0, false); 3412 else 3413 AsynchLaunchBinder(&_TrackerLaunchAppWithDocuments, appRef, 0, false); 3414 3415 return B_OK; 3416 } 3417 3418 status_t 3419 TrackerLaunch(const BMessage *refs, bool async, bool openWithOK) 3420 { 3421 if (!async) 3422 _TrackerLaunchDocuments(0, refs, openWithOK); 3423 else 3424 AsynchLaunchBinder(&_TrackerLaunchDocuments, 0, refs, openWithOK); 3425 3426 return B_OK; 3427 } 3428 3429 status_t 3430 LaunchBrokenLink(const char *signature, const BMessage *refs) 3431 { 3432 // This call is to support a hacky workaround for double-clicking 3433 // broken refs for cifs 3434 be_roster->Launch(signature, const_cast<BMessage *>(refs)); 3435 return B_OK; 3436 } 3437 3438 // external launch calls; need to be robust, work if Tracker is not running 3439 3440 #if !B_BEOS_VERSION_DANO 3441 _IMPEXP_TRACKER 3442 #endif 3443 status_t 3444 FSLaunchItem(const entry_ref *application, const BMessage *refsReceived, 3445 bool async, bool openWithOK) 3446 { 3447 return TrackerLaunch(application, refsReceived, async, openWithOK); 3448 } 3449 3450 3451 #if !B_BEOS_VERSION_DANO 3452 _IMPEXP_TRACKER 3453 #endif 3454 status_t 3455 FSOpenWith(BMessage *listOfRefs) 3456 { 3457 status_t result = B_ERROR; 3458 listOfRefs->what = B_REFS_RECEIVED; 3459 3460 if (dynamic_cast<TTracker *>(be_app)) 3461 result = TrackerOpenWith(listOfRefs); 3462 else 3463 ASSERT(!"not yet implemented"); 3464 3465 return result; 3466 } 3467 3468 // legacy calls, need for compatibility 3469 3470 void 3471 FSOpenWithDocuments(const entry_ref *executable, BMessage *documents) 3472 { 3473 TrackerLaunch(executable, documents, true); 3474 delete documents; 3475 } 3476 3477 status_t 3478 FSLaunchUsing(const entry_ref *ref, BMessage *listOfRefs) 3479 { 3480 BMessage temp(B_REFS_RECEIVED); 3481 if (!listOfRefs) { 3482 ASSERT(ref); 3483 temp.AddRef("refs", ref); 3484 listOfRefs = &temp; 3485 } 3486 FSOpenWith(listOfRefs); 3487 return B_OK; 3488 } 3489 3490 status_t 3491 FSLaunchItem(const entry_ref *ref, BMessage* message, int32, bool async) 3492 { 3493 if (message) 3494 message->what = B_REFS_RECEIVED; 3495 3496 status_t result = TrackerLaunch(ref, message, async, true); 3497 delete message; 3498 return result; 3499 } 3500 3501 3502 void 3503 FSLaunchItem(const entry_ref *ref, BMessage *message, int32 workspace) 3504 { 3505 FSLaunchItem(ref, message, workspace, true); 3506 } 3507 3508 // Get the original path of an entry in the trash 3509 status_t 3510 FSGetOriginalPath(BEntry *entry, BPath *result) 3511 { 3512 status_t err; 3513 entry_ref ref; 3514 err = entry->GetRef(&ref); 3515 if (err != B_OK) 3516 return err; 3517 3518 // Only call the routine for entries in the trash 3519 if (!FSInTrashDir(&ref)) 3520 return B_ERROR; 3521 3522 BNode node(entry); 3523 BString originalPath; 3524 if (node.ReadAttrString(kAttrOriginalPath, &originalPath) == B_OK) { 3525 // We're in luck, the entry has the original path in an attribute 3526 err = result->SetTo(originalPath.String()); 3527 return err; 3528 } 3529 3530 // Iterate the parent directories to find one with 3531 // the original path attribute 3532 BEntry parent(*entry); 3533 err = parent.InitCheck(); 3534 if (err != B_OK) 3535 return err; 3536 3537 // walk up the directory structure until we find a node 3538 // with original path attribute 3539 do { 3540 // move to the parent of this node 3541 err = parent.GetParent(&parent); 3542 if (err != B_OK) 3543 return err; 3544 3545 // return if we are at the root of the trash 3546 if (FSIsTrashDir(&parent)) 3547 return B_ENTRY_NOT_FOUND; 3548 3549 // get the parent as a node 3550 err = node.SetTo(&parent); 3551 if (err != B_OK) 3552 return err; 3553 } while (node.ReadAttrString(kAttrOriginalPath, &originalPath) != B_OK); 3554 3555 // Found the attribute, figure out there this file 3556 // used to live, based on the successfully-read attribute 3557 err = result->SetTo(originalPath.String()); 3558 if (err != B_OK) 3559 return err; 3560 3561 BPath path, pathParent; 3562 err = parent.GetPath(&pathParent); 3563 if (err != B_OK) 3564 return err; 3565 err = entry->GetPath(&path); 3566 if (err != B_OK) 3567 return err; 3568 result->Append(path.Path() + strlen(pathParent.Path()) + 1); 3569 // compute the new path by appending the offset of 3570 // the item we are locating, to the original path 3571 // of the parent 3572 return B_OK; 3573 } 3574 3575 directory_which 3576 WellKnowEntryList::Match(const node_ref *node) 3577 { 3578 const WellKnownEntry *result = MatchEntry(node); 3579 if (result) 3580 return result->which; 3581 3582 return (directory_which)-1; 3583 } 3584 3585 const WellKnowEntryList::WellKnownEntry * 3586 WellKnowEntryList::MatchEntry(const node_ref *node) 3587 { 3588 if (!self) 3589 self = new WellKnowEntryList(); 3590 3591 return self->MatchEntryCommon(node); 3592 } 3593 3594 const WellKnowEntryList::WellKnownEntry * 3595 WellKnowEntryList::MatchEntryCommon(const node_ref *node) 3596 { 3597 uint32 count = entries.size(); 3598 for (uint32 index = 0; index < count; index++) 3599 if (*node == entries[index].node) 3600 return &entries[index]; 3601 3602 return NULL; 3603 } 3604 3605 3606 void 3607 WellKnowEntryList::Quit() 3608 { 3609 delete self; 3610 self = NULL; 3611 } 3612 3613 3614 void 3615 WellKnowEntryList::AddOne(directory_which which, const char *name) 3616 { 3617 BPath path; 3618 if (find_directory(which, &path, true) != B_OK) 3619 return; 3620 3621 BEntry entry(path.Path(), true); 3622 node_ref node; 3623 if (entry.GetNodeRef(&node) != B_OK) 3624 return; 3625 3626 entries.push_back(WellKnownEntry(&node, which, name)); 3627 } 3628 3629 3630 void 3631 WellKnowEntryList::AddOne(directory_which which, directory_which base, 3632 const char *extra, const char *name) 3633 { 3634 BPath path; 3635 if (find_directory(base, &path, true) != B_OK) 3636 return; 3637 3638 path.Append(extra); 3639 BEntry entry(path.Path(), true); 3640 node_ref node; 3641 if (entry.GetNodeRef(&node) != B_OK) 3642 return; 3643 3644 entries.push_back(WellKnownEntry(&node, which, name)); 3645 } 3646 3647 3648 void 3649 WellKnowEntryList::AddOne(directory_which which, const char *path, 3650 const char *name) 3651 { 3652 BEntry entry(path, true); 3653 node_ref node; 3654 if (entry.GetNodeRef(&node) != B_OK) 3655 return; 3656 3657 entries.push_back(WellKnownEntry(&node, which, name)); 3658 } 3659 3660 3661 WellKnowEntryList::WellKnowEntryList() 3662 { 3663 #ifdef __HAIKU__ 3664 AddOne(B_SYSTEM_DIRECTORY, "system"); 3665 #else 3666 AddOne(B_BEOS_DIRECTORY, "beos"); 3667 AddOne(B_BEOS_SYSTEM_DIRECTORY, "system"); 3668 #endif 3669 AddOne((directory_which)B_BOOT_DISK, "/boot", "boot"); 3670 AddOne(B_USER_DIRECTORY, "home"); 3671 3672 AddOne(B_BEOS_FONTS_DIRECTORY, "fonts"); 3673 AddOne(B_COMMON_FONTS_DIRECTORY, "fonts"); 3674 AddOne(B_USER_FONTS_DIRECTORY, "fonts"); 3675 3676 AddOne(B_BEOS_APPS_DIRECTORY, "apps"); 3677 AddOne(B_APPS_DIRECTORY, "apps"); 3678 AddOne((directory_which)B_USER_DESKBAR_APPS_DIRECTORY, 3679 B_USER_DESKBAR_DIRECTORY, "Applications", "apps"); 3680 3681 AddOne(B_BEOS_PREFERENCES_DIRECTORY, "preferences"); 3682 AddOne(B_PREFERENCES_DIRECTORY, "preferences"); 3683 AddOne((directory_which)B_USER_DESKBAR_PREFERENCES_DIRECTORY, 3684 B_USER_DESKBAR_DIRECTORY, "Preferences", "preferences"); 3685 3686 AddOne((directory_which)B_USER_MAIL_DIRECTORY, B_USER_DIRECTORY, "mail", 3687 "mail"); 3688 3689 AddOne((directory_which)B_USER_QUERIES_DIRECTORY, B_USER_DIRECTORY, 3690 "queries", "queries"); 3691 3692 3693 3694 AddOne(B_COMMON_DEVELOP_DIRECTORY, "develop"); 3695 AddOne((directory_which)B_USER_DESKBAR_DEVELOP_DIRECTORY, 3696 B_USER_DESKBAR_DIRECTORY, "Development", "develop"); 3697 3698 AddOne(B_USER_CONFIG_DIRECTORY, "config"); 3699 3700 AddOne((directory_which)B_USER_PEOPLE_DIRECTORY, B_USER_DIRECTORY, 3701 "people", "people"); 3702 3703 AddOne((directory_which)B_USER_DOWNLOADS_DIRECTORY, B_USER_DIRECTORY, 3704 "downloads", "downloads"); 3705 } 3706 3707 WellKnowEntryList *WellKnowEntryList::self = NULL; 3708 3709 } // namespace BPrivate 3710