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