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