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 76 #include "Attributes.h" 77 #include "Bitmaps.h" 78 #include "Commands.h" 79 #include "FSUndoRedo.h" 80 #include "FSUtils.h" 81 #include "InfoWindow.h" 82 #include "MimeTypes.h" 83 #include "Model.h" 84 #include "OverrideAlert.h" 85 #include "StatusWindow.h" 86 #include "Thread.h" 87 #include "Tracker.h" 88 #include "TrackerSettings.h" 89 #include "Utilities.h" 90 #include "VirtualDirectoryManager.h" 91 92 93 enum { 94 kUserCanceled = B_ERRORS_END + 1, 95 kCopyCanceled = kUserCanceled, 96 kTrashCanceled 97 }; 98 99 enum ConflictCheckResult { 100 kCanceled = kUserCanceled, 101 kPrompt, 102 kReplace, 103 kReplaceAll, 104 kNoConflicts 105 }; 106 107 namespace BPrivate { 108 109 #undef B_TRANSLATION_CONTEXT 110 #define B_TRANSLATION_CONTEXT "FSUtils" 111 112 static status_t FSDeleteFolder(BEntry*, CopyLoopControl*, bool updateStatus, 113 bool deleteTopDir = true, bool upateFileNameInStatus = false); 114 static status_t MoveEntryToTrash(BEntry*, BPoint*, Undo &undo); 115 static void LowLevelCopy(BEntry*, StatStruct*, BDirectory*, char* destName, 116 CopyLoopControl*, BPoint*); 117 status_t DuplicateTask(BObjectList<entry_ref>* srcList); 118 static status_t MoveTask(BObjectList<entry_ref>*, BEntry*, BList*, uint32); 119 static status_t _DeleteTask(BObjectList<entry_ref>*, bool); 120 static status_t _RestoreTask(BObjectList<entry_ref>*); 121 status_t CalcItemsAndSize(CopyLoopControl* loopControl, 122 BObjectList<entry_ref>* refList, ssize_t blockSize, int32* totalCount, 123 off_t* totalSize); 124 status_t MoveItem(BEntry* entry, BDirectory* destDir, BPoint* loc, 125 uint32 moveMode, const char* newName, Undo &undo, 126 CopyLoopControl* loopControl); 127 ConflictCheckResult PreFlightNameCheck(BObjectList<entry_ref>* srcList, 128 const BDirectory* destDir, int32* collisionCount, uint32 moveMode); 129 status_t CheckName(uint32 moveMode, const BEntry* srcEntry, 130 const BDirectory* destDir, bool multipleCollisions, 131 ConflictCheckResult &); 132 void CopyAttributes(CopyLoopControl* control, BNode* srcNode, 133 BNode* destNode, void* buffer, size_t bufsize); 134 void CopyPoseLocation(BNode* src, BNode* dest); 135 bool DirectoryMatchesOrContains(const BEntry*, directory_which); 136 bool DirectoryMatchesOrContains(const BEntry*, const char* additionalPath, 137 directory_which); 138 bool DirectoryMatches(const BEntry*, directory_which); 139 bool DirectoryMatches(const BEntry*, const char* additionalPath, 140 directory_which); 141 142 status_t empty_trash(void*); 143 144 145 static const char* kDeleteConfirmationStr = 146 B_TRANSLATE_MARK("Are you sure you want to delete the " 147 "selected item(s)? This operation cannot be reverted."); 148 149 static const char* kReplaceStr = 150 B_TRANSLATE_MARK("You are trying to replace the item:\n" 151 "\t%name%dest\n" 152 "with:\n" 153 "\t%name%src\n\n" 154 "Would you like to replace it with the one you are %movemode?"); 155 156 static const char* kDirectoryReplaceStr = 157 B_TRANSLATE_MARK("An item named \"%name\" already exists in " 158 "this folder, and may contain\nitems with the same names. Would you like " 159 "to replace them with those contained in the folder you are %verb?"); 160 161 static const char* kSymLinkReplaceStr = 162 B_TRANSLATE_MARK("An item named \"%name\" already exists in this " 163 "folder. Would you like to replace it with the symbolic link you are " 164 "creating?"); 165 166 static const char* kNoFreeSpace = 167 B_TRANSLATE_MARK("Sorry, there is not enough free space on the " 168 "destination volume to copy the selection."); 169 170 static const char* kFileErrorString = 171 B_TRANSLATE_MARK("Error copying file \"%name\":\n\t%error\n\n" 172 "Would you like to continue?"); 173 174 static const char* kFolderErrorString = 175 B_TRANSLATE_MARK("Error copying folder \"%name\":\n\t%error\n\n" 176 "Would you like to continue?"); 177 178 static const char* kFileDeleteErrorString = 179 B_TRANSLATE_MARK("There was an error deleting \"%name\"" 180 ":\n\t%error"); 181 182 static const char* kReplaceManyStr = 183 B_TRANSLATE_MARK("Some items already exist in this folder with " 184 "the same names as the items you are %verb.\n \nWould you like to " 185 "replace them with the ones you are %verb or be prompted for each " 186 "one?"); 187 188 static const char* kFindAlternativeStr = 189 B_TRANSLATE_MARK("Would you like to find some other suitable " 190 "application?"); 191 192 static const char* kFindApplicationStr = 193 B_TRANSLATE_MARK("Would you like to find a suitable application " 194 "to open the file?"); 195 196 // Skip these attributes when copying in Tracker 197 const char* kSkipAttributes[] = { 198 kAttrPoseInfo, 199 NULL 200 }; 201 202 203 // #pragma mark - 204 205 206 CopyLoopControl::~CopyLoopControl() 207 { 208 } 209 210 211 void 212 CopyLoopControl::Init(uint32 jobKind) 213 { 214 } 215 216 217 void 218 CopyLoopControl::Init(int32 totalItems, off_t totalSize, 219 const entry_ref* destDir, bool showCount) 220 { 221 } 222 223 224 bool 225 CopyLoopControl::FileError(const char* message, const char* name, 226 status_t error, bool allowContinue) 227 { 228 return false; 229 } 230 231 232 void 233 CopyLoopControl::UpdateStatus(const char* name, const entry_ref& ref, 234 int32 count, bool optional) 235 { 236 } 237 238 239 bool 240 CopyLoopControl::CheckUserCanceled() 241 { 242 return false; 243 } 244 245 246 CopyLoopControl::OverwriteMode 247 CopyLoopControl::OverwriteOnConflict(const BEntry* srcEntry, 248 const char* destName, const BDirectory* destDir, bool srcIsDir, 249 bool dstIsDir) 250 { 251 return kReplace; 252 } 253 254 255 bool 256 CopyLoopControl::SkipEntry(const BEntry*, bool) 257 { 258 // Tracker makes no exceptions 259 return false; 260 } 261 262 263 void 264 CopyLoopControl::ChecksumChunk(const char*, size_t) 265 { 266 } 267 268 269 bool 270 CopyLoopControl::ChecksumFile(const entry_ref*) 271 { 272 return true; 273 } 274 275 276 bool 277 CopyLoopControl::SkipAttribute(const char*) 278 { 279 return false; 280 } 281 282 283 bool 284 CopyLoopControl::PreserveAttribute(const char*) 285 { 286 return false; 287 } 288 289 290 // #pragma mark - 291 292 293 TrackerCopyLoopControl::TrackerCopyLoopControl() 294 : 295 fThread(find_thread(NULL)), 296 fSourceList(NULL) 297 { 298 } 299 300 301 TrackerCopyLoopControl::TrackerCopyLoopControl(uint32 jobKind) 302 : 303 fThread(find_thread(NULL)), 304 fSourceList(NULL) 305 { 306 Init(jobKind); 307 } 308 309 310 TrackerCopyLoopControl::TrackerCopyLoopControl(int32 totalItems, 311 off_t totalSize) 312 : 313 fThread(find_thread(NULL)), 314 fSourceList(NULL) 315 { 316 Init(totalItems, totalSize); 317 } 318 319 320 TrackerCopyLoopControl::~TrackerCopyLoopControl() 321 { 322 if (gStatusWindow != NULL) 323 gStatusWindow->RemoveStatusItem(fThread); 324 } 325 326 327 void 328 TrackerCopyLoopControl::Init(uint32 jobKind) 329 { 330 if (gStatusWindow != NULL) 331 gStatusWindow->CreateStatusItem(fThread, (StatusWindowState)jobKind); 332 } 333 334 335 void 336 TrackerCopyLoopControl::Init(int32 totalItems, off_t totalSize, 337 const entry_ref* destDir, bool showCount) 338 { 339 if (gStatusWindow != NULL) { 340 gStatusWindow->InitStatusItem(fThread, totalItems, totalSize, 341 destDir, showCount); 342 } 343 } 344 345 346 bool 347 TrackerCopyLoopControl::FileError(const char* message, const char* name, 348 status_t error, bool allowContinue) 349 { 350 BString buffer(message); 351 buffer.ReplaceFirst("%name", name); 352 buffer.ReplaceFirst("%error", strerror(error)); 353 354 if (allowContinue) { 355 BAlert* alert = new BAlert("", buffer.String(), B_TRANSLATE("Cancel"), 356 B_TRANSLATE("OK"), 0, B_WIDTH_AS_USUAL, B_STOP_ALERT); 357 alert->SetShortcut(0, B_ESCAPE); 358 return alert->Go() != 0; 359 } 360 361 BAlert* alert = new BAlert("", buffer.String(), B_TRANSLATE("Cancel"), 0, 0, 362 B_WIDTH_AS_USUAL, B_STOP_ALERT); 363 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 364 alert->Go(); 365 return false; 366 } 367 368 369 void 370 TrackerCopyLoopControl::UpdateStatus(const char* name, const entry_ref&, 371 int32 count, bool optional) 372 { 373 if (gStatusWindow != NULL) 374 gStatusWindow->UpdateStatus(fThread, name, count, optional); 375 } 376 377 378 bool 379 TrackerCopyLoopControl::CheckUserCanceled() 380 { 381 if (gStatusWindow == NULL) 382 return false; 383 384 if (gStatusWindow->CheckCanceledOrPaused(fThread)) 385 return true; 386 387 if (fSourceList != NULL) { 388 // TODO: Check if the user dropped additional files onto this job. 389 // printf("%p->CheckUserCanceled()\n", this); 390 } 391 392 return false; 393 } 394 395 396 bool 397 TrackerCopyLoopControl::SkipAttribute(const char* attributeName) 398 { 399 for (const char** skipAttribute = kSkipAttributes; *skipAttribute; 400 skipAttribute++) { 401 if (strcmp(*skipAttribute, attributeName) == 0) 402 return true; 403 } 404 405 return false; 406 } 407 408 409 void 410 TrackerCopyLoopControl::SetSourceList(EntryList* list) 411 { 412 fSourceList = list; 413 } 414 415 416 // #pragma mark - 417 418 419 static BNode* 420 GetWritableNode(BEntry* entry, StatStruct* statBuf = 0) 421 { 422 // utility call that works around the problem with BNodes not being 423 // universally writeable 424 // BNodes created on files will fail to WriteAttr because they do not 425 // have the right r/w permissions 426 427 StatStruct localStatbuf; 428 429 if (!statBuf) { 430 statBuf = &localStatbuf; 431 if (entry->GetStat(statBuf) != B_OK) 432 return 0; 433 } 434 435 if (S_ISREG(statBuf->st_mode)) 436 return new BFile(entry, O_RDWR); 437 438 return new BNode(entry); 439 } 440 441 442 bool 443 CheckDevicesEqual(const entry_ref* srcRef, const Model* targetModel) 444 { 445 BDirectory destDir (targetModel->EntryRef()); 446 struct stat deststat; 447 destDir.GetStat(&deststat); 448 449 return srcRef->device == deststat.st_dev; 450 } 451 452 453 status_t 454 FSSetPoseLocation(ino_t destDirInode, BNode* destNode, BPoint point) 455 { 456 PoseInfo poseInfo; 457 poseInfo.fInvisible = false; 458 poseInfo.fInitedDirectory = destDirInode; 459 poseInfo.fLocation = point; 460 461 status_t result = destNode->WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, 462 &poseInfo, sizeof(poseInfo)); 463 464 if (result == sizeof(poseInfo)) 465 return B_OK; 466 467 return result; 468 } 469 470 471 status_t 472 FSSetPoseLocation(BEntry* entry, BPoint point) 473 { 474 BNode node(entry); 475 status_t result = node.InitCheck(); 476 if (result != B_OK) 477 return result; 478 479 BDirectory parent; 480 result = entry->GetParent(&parent); 481 if (result != B_OK) 482 return result; 483 484 node_ref destNodeRef; 485 result = parent.GetNodeRef(&destNodeRef); 486 if (result != B_OK) 487 return result; 488 489 return FSSetPoseLocation(destNodeRef.node, &node, point); 490 } 491 492 493 bool 494 FSGetPoseLocation(const BNode* node, BPoint* point) 495 { 496 PoseInfo poseInfo; 497 if (ReadAttr(node, kAttrPoseInfo, kAttrPoseInfoForeign, 498 B_RAW_TYPE, 0, &poseInfo, sizeof(poseInfo), &PoseInfo::EndianSwap) 499 == kReadAttrFailed) 500 return false; 501 502 if (poseInfo.fInitedDirectory == -1LL) 503 return false; 504 505 *point = poseInfo.fLocation; 506 507 return true; 508 } 509 510 511 static void 512 SetUpPoseLocation(ino_t sourceParentIno, ino_t destParentIno, 513 const BNode* sourceNode, BNode* destNode, BPoint* loc) 514 { 515 BPoint point; 516 if (!loc 517 // we don't have a position yet 518 && sourceParentIno != destParentIno 519 // we aren't copying into the same directory 520 && FSGetPoseLocation(sourceNode, &point)) 521 // the original has a valid inited location 522 loc = &point; 523 // copy the originals location 524 525 if (loc && loc != (BPoint*)-1) { 526 // loc of -1 is used when copying/moving into a window in list mode 527 // where copying positions would not work 528 // ToSo: 529 // should push all this logic to upper levels 530 FSSetPoseLocation(destParentIno, destNode, *loc); 531 } 532 } 533 534 535 void 536 FSMoveToFolder(BObjectList<entry_ref>* srcList, BEntry* destEntry, 537 uint32 moveMode, BList* pointList) 538 { 539 if (srcList->IsEmpty()) { 540 delete srcList; 541 delete pointList; 542 delete destEntry; 543 return; 544 } 545 546 LaunchInNewThread("MoveTask", B_NORMAL_PRIORITY, MoveTask, srcList, 547 destEntry, pointList, moveMode); 548 } 549 550 551 void 552 FSDelete(entry_ref* ref, bool async, bool confirm) 553 { 554 BObjectList<entry_ref>* list = new BObjectList<entry_ref>(1, true); 555 list->AddItem(ref); 556 FSDeleteRefList(list, async, confirm); 557 } 558 559 560 void 561 FSDeleteRefList(BObjectList<entry_ref>* list, bool async, bool confirm) 562 { 563 if (async) { 564 LaunchInNewThread("DeleteTask", B_NORMAL_PRIORITY, _DeleteTask, list, 565 confirm); 566 } else 567 _DeleteTask(list, confirm); 568 } 569 570 571 void 572 FSRestoreRefList(BObjectList<entry_ref>* list, bool async) 573 { 574 if (async) { 575 LaunchInNewThread("RestoreTask", B_NORMAL_PRIORITY, _RestoreTask, 576 list); 577 } else 578 _RestoreTask(list); 579 } 580 581 582 void 583 FSMoveToTrash(BObjectList<entry_ref>* srcList, BList* pointList, bool async) 584 { 585 if (srcList->IsEmpty()) { 586 delete srcList; 587 delete pointList; 588 return; 589 } 590 591 if (async) 592 LaunchInNewThread("MoveTask", B_NORMAL_PRIORITY, MoveTask, srcList, 593 (BEntry*)0, pointList, kMoveSelectionTo); 594 else 595 MoveTask(srcList, 0, pointList, kMoveSelectionTo); 596 } 597 598 599 static bool 600 IsDisksWindowIcon(BEntry* entry) 601 { 602 BPath path; 603 if (entry->InitCheck() != B_OK || entry->GetPath(&path) != B_OK) 604 return false; 605 606 return strcmp(path.Path(), "/") == 0; 607 } 608 609 enum { 610 kNotConfirmed, 611 kConfirmedHomeMove, 612 kConfirmedAll 613 }; 614 615 616 bool 617 ConfirmChangeIfWellKnownDirectory(const BEntry* entry, 618 const char* ifYouDoAction, const char* toDoAction, 619 const char* toConfirmAction, bool dontAsk, int32* confirmedAlready) 620 { 621 // Don't let the user casually move/change important files/folders 622 // 623 // This is a cheap replacement for having a real UID support turned 624 // on and not running as root all the time 625 626 if (confirmedAlready && *confirmedAlready == kConfirmedAll) 627 return true; 628 629 if (FSIsDeskDir(entry) || FSIsTrashDir(entry) || FSIsRootDir(entry)) 630 return false; 631 632 if (!DirectoryMatchesOrContains(entry, B_SYSTEM_DIRECTORY) 633 && !DirectoryMatchesOrContains(entry, B_COMMON_DIRECTORY) 634 && !DirectoryMatchesOrContains(entry, B_USER_DIRECTORY)) 635 // quick way out 636 return true; 637 638 BString warning; 639 bool requireOverride = true; 640 641 if (DirectoryMatchesOrContains(entry, B_SYSTEM_DIRECTORY)) { 642 warning.SetTo( 643 B_TRANSLATE("If you %ifYouDoAction the system folder or its " 644 "contents, you won't be able to boot %osName!\n\nAre you sure " 645 "you want to do this?\n\nTo %toDoAction the system folder or its " 646 "contents anyway, hold down the Shift key and click " 647 "\"%toConfirmAction\".")); 648 } else if (DirectoryMatches(entry, B_COMMON_DIRECTORY)) { 649 warning.SetTo( 650 B_TRANSLATE("If you %ifYouDoAction the common folder, %osName " 651 "may not behave properly!\n\nAre you sure you want to do this?" 652 "\n\nTo %toDoAction the common folder anyway, hold down the " 653 "Shift key and click \"%toConfirmAction\".")); 654 } else if (DirectoryMatches(entry, B_USER_DIRECTORY)) { 655 warning .SetTo( 656 B_TRANSLATE("If you %ifYouDoAction the home folder, %osName " 657 "may not behave properly!\n\nAre you sure you want to do this?" 658 "\n\nTo %toDoAction the home folder anyway, hold down the " 659 "Shift key and click \"%toConfirmAction\".")); 660 } else if (DirectoryMatchesOrContains(entry, B_USER_CONFIG_DIRECTORY) 661 || DirectoryMatchesOrContains(entry, B_SYSTEM_SETTINGS_DIRECTORY)) { 662 663 if (DirectoryMatchesOrContains(entry, "beos_mime", 664 B_USER_SETTINGS_DIRECTORY) 665 || DirectoryMatchesOrContains(entry, "beos_mime", 666 B_SYSTEM_SETTINGS_DIRECTORY)) { 667 warning.SetTo( 668 B_TRANSLATE("If you %ifYouDoAction the mime settings, " 669 "%osName may not behave properly!\n\nAre you sure you want " 670 "to do this?")); 671 requireOverride = false; 672 } else if (DirectoryMatches(entry, B_USER_CONFIG_DIRECTORY)) { 673 warning.SetTo( 674 B_TRANSLATE("If you %ifYouDoAction the config folder, " 675 "%osName may not behave properly!\n\nAre you sure you want " 676 "to do this?")); 677 requireOverride = false; 678 } else if (DirectoryMatches(entry, B_USER_SETTINGS_DIRECTORY) 679 || DirectoryMatches(entry, B_SYSTEM_SETTINGS_DIRECTORY)) { 680 warning.SetTo( 681 B_TRANSLATE("If you %ifYouDoAction the settings folder, " 682 "%osName may not behave properly!\n\nAre you sure you want " 683 "to do this?")); 684 requireOverride = false; 685 } 686 } 687 688 if (!warning.Length()) 689 return true; 690 691 if (dontAsk) 692 return false; 693 694 if (confirmedAlready && *confirmedAlready == kConfirmedHomeMove 695 && !requireOverride) 696 // we already warned about moving home this time around 697 return true; 698 699 struct utsname name; 700 if (uname(&name) == -1) 701 warning.ReplaceFirst("%osName", "Haiku"); 702 else 703 warning.ReplaceFirst("%osName", name.sysname); 704 705 warning.ReplaceFirst("%ifYouDoAction", ifYouDoAction); 706 warning.ReplaceFirst("%toDoAction", toDoAction); 707 warning.ReplaceFirst("%toConfirmAction", toConfirmAction); 708 709 BString buttonLabel(toConfirmAction); 710 711 OverrideAlert* alert = new OverrideAlert("", warning.String(), 712 buttonLabel.String(), (requireOverride ? B_SHIFT_KEY : 0), 713 B_TRANSLATE("Cancel"), 0, NULL, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 714 alert->SetShortcut(1, B_ESCAPE); 715 if (alert->Go() == 1) { 716 if (confirmedAlready) 717 *confirmedAlready = kNotConfirmed; 718 return false; 719 } 720 721 if (confirmedAlready) { 722 if (!requireOverride) 723 *confirmedAlready = kConfirmedHomeMove; 724 else 725 *confirmedAlready = kConfirmedAll; 726 } 727 728 return true; 729 } 730 731 732 static status_t 733 InitCopy(CopyLoopControl* loopControl, uint32 moveMode, 734 BObjectList<entry_ref>* srcList, BVolume* dstVol, BDirectory* destDir, 735 entry_ref* destRef, bool preflightNameCheck, bool needSizeCalculation, 736 int32* collisionCount, ConflictCheckResult* preflightResult) 737 { 738 if (dstVol->IsReadOnly()) { 739 BAlert* alert = new BAlert("", 740 B_TRANSLATE("You can't move or copy items to read-only volumes."), 741 B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 742 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 743 alert->Go(); 744 return B_ERROR; 745 } 746 747 int32 numItems = srcList->CountItems(); 748 int32 askOnceOnly = kNotConfirmed; 749 for (int32 index = 0; index < numItems; index++) { 750 // we could check for this while iterating through items in each of 751 // the copy loops, except it takes forever to call CalcItemsAndSize 752 BEntry entry((entry_ref*)srcList->ItemAt(index)); 753 if (IsDisksWindowIcon(&entry)) { 754 BString errorStr; 755 if (moveMode == kCreateLink) { 756 errorStr.SetTo( 757 B_TRANSLATE("You cannot create a link to the root " 758 "directory.")); 759 } else { 760 errorStr.SetTo( 761 B_TRANSLATE("You cannot copy or move the root " 762 "directory.")); 763 } 764 765 BAlert* alert = new BAlert("", errorStr.String(), 766 B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, 767 B_WARNING_ALERT); 768 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 769 alert->Go(); 770 return B_ERROR; 771 } 772 if (moveMode == kMoveSelectionTo 773 && !ConfirmChangeIfWellKnownDirectory(&entry, 774 B_TRANSLATE_COMMENT("move", 775 "As in 'if you move this folder...' (en) " 776 "'Wird dieser Ordner verschoben...' (de)"), 777 B_TRANSLATE_COMMENT("move", 778 "As in 'to move this folder...' (en) " 779 "Um diesen Ordner zu verschieben...' (de)"), 780 B_TRANSLATE_COMMENT("Move", 781 "Button label, 'Move' (en), 'Verschieben' (de)"), 782 false, &askOnceOnly)) { 783 return B_ERROR; 784 } 785 } 786 787 if (preflightNameCheck) { 788 ASSERT(collisionCount); 789 ASSERT(preflightResult); 790 791 *preflightResult = kPrompt; 792 *collisionCount = 0; 793 794 *preflightResult = PreFlightNameCheck(srcList, destDir, 795 collisionCount, moveMode); 796 if (*preflightResult == kCanceled) { 797 // user canceled 798 return B_ERROR; 799 } 800 } 801 802 // set up the status display 803 switch (moveMode) { 804 case kCopySelectionTo: 805 case kDuplicateSelection: 806 case kMoveSelectionTo: 807 { 808 loopControl->Init(moveMode == kMoveSelectionTo ? kMoveState 809 : kCopyState); 810 811 int32 totalItems = 0; 812 off_t totalSize = 0; 813 if (needSizeCalculation) { 814 if (CalcItemsAndSize(loopControl, srcList, 815 dstVol->BlockSize(), &totalItems, &totalSize) 816 != B_OK) { 817 return B_ERROR; 818 } 819 820 // check for free space before starting copy 821 if ((totalSize + (4* kKBSize)) >= dstVol->FreeBytes()) { 822 BAlert* alert = new BAlert("", 823 B_TRANSLATE_NOCOLLECT(kNoFreeSpace), 824 B_TRANSLATE("Cancel"), 825 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 826 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 827 alert->Go(); 828 return B_ERROR; 829 } 830 } 831 832 loopControl->Init(totalItems, totalSize, destRef); 833 break; 834 } 835 836 case kCreateLink: 837 if (numItems > 10) { 838 // this will be fast, only put up status if lots of items 839 // moved, links created 840 loopControl->Init(kCreateLinkState); 841 loopControl->Init(numItems, numItems, destRef); 842 } 843 break; 844 } 845 return B_OK; 846 } 847 848 849 // ToDo: 850 // get rid of this cruft 851 bool 852 delete_ref(void* ref) 853 { 854 delete (entry_ref*)ref; 855 return false; 856 } 857 858 859 bool 860 delete_point(void* point) 861 { 862 delete (BPoint*)point; 863 return false; 864 } 865 866 867 static status_t 868 MoveTask(BObjectList<entry_ref>* srcList, BEntry* destEntry, BList* pointList, 869 uint32 moveMode) 870 { 871 ASSERT(!srcList->IsEmpty()); 872 873 // extract information from src, dest models 874 // ## note that we're assuming all items come from the same volume 875 // ## by looking only at FirstItem here which is not a good idea 876 dev_t srcVolumeDevice = srcList->FirstItem()->device; 877 dev_t destVolumeDevice = srcVolumeDevice; 878 879 StatStruct deststat; 880 BVolume volume(srcVolumeDevice); 881 entry_ref destRef; 882 883 bool destIsTrash = false; 884 BDirectory destDir; 885 BDirectory* destDirToCheck = NULL; 886 bool needPreflightNameCheck = false; 887 bool sourceIsReadOnly = volume.IsReadOnly(); 888 volume.Unset(); 889 890 bool fromUndo = FSIsUndoMoveMode(moveMode); 891 moveMode = FSMoveMode(moveMode); 892 893 // if we're not passed a destEntry then we are supposed to move to trash 894 if (destEntry != NULL) { 895 destEntry->GetRef(&destRef); 896 897 destDir.SetTo(destEntry); 898 destDir.GetStat(&deststat); 899 destDirToCheck = &destDir; 900 901 destVolumeDevice = deststat.st_dev; 902 destIsTrash = FSIsTrashDir(destEntry); 903 volume.SetTo(destVolumeDevice); 904 905 needPreflightNameCheck = true; 906 } else if (moveMode == kDuplicateSelection) { 907 BEntry entry; 908 entry.SetTo(srcList->FirstItem()); 909 entry.GetParent(&destDir); 910 volume.SetTo(srcVolumeDevice); 911 } else { 912 // move is to trash 913 destIsTrash = true; 914 915 FSGetTrashDir(&destDir, srcVolumeDevice); 916 volume.SetTo(srcVolumeDevice); 917 918 BEntry entry; 919 destDir.GetEntry(&entry); 920 destDirToCheck = &destDir; 921 922 entry.GetRef(&destRef); 923 } 924 925 // change the move mode if needed 926 if (moveMode == kCopySelectionTo && destIsTrash) { 927 // cannot copy to trash 928 moveMode = kMoveSelectionTo; 929 } 930 931 if (moveMode == kMoveSelectionTo && sourceIsReadOnly) 932 moveMode = kCopySelectionTo; 933 934 bool needSizeCalculation = true; 935 if ((moveMode == kMoveSelectionTo && srcVolumeDevice == destVolumeDevice) 936 || destIsTrash) { 937 needSizeCalculation = false; 938 } 939 940 // we need the undo object later on, so we create it no matter 941 // if we really need it or not (it's very lightweight) 942 MoveCopyUndo undo(srcList, destDir, pointList, moveMode); 943 if (fromUndo) 944 undo.Remove(); 945 946 TrackerCopyLoopControl loopControl; 947 948 ConflictCheckResult conflictCheckResult = kPrompt; 949 int32 collisionCount = 0; 950 // TODO: Status item is created in InitCopy(), but it would be kind of 951 // neat to move all that into TrackerCopyLoopControl 952 status_t result = InitCopy(&loopControl, moveMode, srcList, 953 &volume, destDirToCheck, &destRef, needPreflightNameCheck, 954 needSizeCalculation, &collisionCount, &conflictCheckResult); 955 956 loopControl.SetSourceList(srcList); 957 958 if (result == B_OK) { 959 for (int32 i = 0; i < srcList->CountItems(); i++) { 960 BPoint* loc = (BPoint*)-1; 961 // a loc of -1 forces autoplacement, rather than copying the 962 // position of the original node 963 // TODO: 964 // Clean this mess up! 965 // What could be a cleaner design is to pass along some kind 966 // "filter" object that post-processes poses, i.e. adds the 967 // location or other stuff. It should not be a job of the 968 // copy-engine. 969 970 entry_ref* srcRef = srcList->ItemAt(i); 971 972 if (moveMode == kDuplicateSelection) { 973 BEntry entry(srcRef); 974 entry.GetParent(&destDir); 975 destDir.GetStat(&deststat); 976 volume.SetTo(srcRef->device); 977 } 978 979 // handle case where item is dropped into folder it already lives 980 // in which could happen if dragging from a query window 981 if (moveMode != kCreateLink 982 && moveMode != kCreateRelativeLink 983 && moveMode != kDuplicateSelection 984 && !destIsTrash 985 && (srcRef->device == destRef.device 986 && srcRef->directory == deststat.st_ino)) 987 continue; 988 989 if (loopControl.CheckUserCanceled()) 990 break; 991 992 BEntry sourceEntry(srcRef); 993 if (sourceEntry.InitCheck() != B_OK) { 994 BString error(B_TRANSLATE("Error moving \"%name\".")); 995 error.ReplaceFirst("%name", srcRef->name); 996 BAlert* alert = new BAlert("", error.String(), 997 B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, 998 B_WARNING_ALERT); 999 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1000 alert->Go(); 1001 break; 1002 } 1003 1004 // are we moving item to trash? 1005 if (destIsTrash) { 1006 if (pointList) 1007 loc = (BPoint*)pointList->ItemAt(i); 1008 1009 result = MoveEntryToTrash(&sourceEntry, loc, undo); 1010 if (result != B_OK) { 1011 BString error(B_TRANSLATE("Error moving \"%name\" to Trash. " 1012 "(%error)")); 1013 error.ReplaceFirst("%name", srcRef->name); 1014 error.ReplaceFirst("%error", strerror(result)); 1015 BAlert* alert = new BAlert("", error.String(), 1016 B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, 1017 B_WARNING_ALERT); 1018 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1019 alert->Go(); 1020 break; 1021 } 1022 continue; 1023 } 1024 1025 // resolve name collisions and hierarchy problems 1026 if (CheckName(moveMode, &sourceEntry, &destDir, 1027 collisionCount > 1, conflictCheckResult) != B_OK) { 1028 // we will skip the current item, because we got a conflict 1029 // and were asked to or because there was some conflict 1030 1031 // update the status because item got skipped and the status 1032 // will not get updated by the move call 1033 loopControl.UpdateStatus(srcRef->name, *srcRef, 1); 1034 1035 continue; 1036 } 1037 1038 // get location to place this item 1039 if (pointList && moveMode != kCopySelectionTo) { 1040 loc = (BPoint*)pointList->ItemAt(i); 1041 1042 BNode* src_node = GetWritableNode(&sourceEntry); 1043 if (src_node && src_node->InitCheck() == B_OK) { 1044 PoseInfo poseInfo; 1045 poseInfo.fInvisible = false; 1046 poseInfo.fInitedDirectory = deststat.st_ino; 1047 poseInfo.fLocation = *loc; 1048 src_node->WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, 1049 &poseInfo, sizeof(poseInfo)); 1050 } 1051 delete src_node; 1052 } 1053 1054 if (pointList) 1055 loc = (BPoint*)pointList->ItemAt(i); 1056 1057 result = MoveItem(&sourceEntry, &destDir, loc, moveMode, NULL, 1058 undo, &loopControl); 1059 if (result != B_OK) 1060 break; 1061 } 1062 } 1063 1064 // duplicates of srcList, destFolder were created - dispose them 1065 delete srcList; 1066 delete destEntry; 1067 1068 // delete file location list and all Points within 1069 if (pointList) { 1070 pointList->DoForEach(delete_point); 1071 delete pointList; 1072 } 1073 1074 return B_OK; 1075 } 1076 1077 class FailWithAlert { 1078 public: 1079 static void FailOnError(status_t error, const char* string, 1080 const char* name = NULL) 1081 { 1082 if (error != B_OK) 1083 throw FailWithAlert(error, string, name); 1084 } 1085 1086 FailWithAlert(status_t error, const char* string, const char* name) 1087 : fString(string), 1088 fName(name), 1089 fError(error) 1090 { 1091 } 1092 1093 const char* fString; 1094 const char* fName; 1095 status_t fError; 1096 }; 1097 1098 class MoveError { 1099 public: 1100 static void FailOnError(status_t error) 1101 { 1102 if (error != B_OK) 1103 throw MoveError(error); 1104 } 1105 1106 MoveError(status_t error) 1107 : fError(error) 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 extern "C" status_t _kern_load_image(const char* const* flatArgs, 3345 size_t flatArgsSize, int32 argCount, int32 envCount, int32 priority, 3346 uint32 flags, port_id errorPort, uint32 errorToken); 3347 extern "C" status_t __flatten_process_args(const char* const* args, 3348 int32 argCount, const char* const* env, int32 envCount, char***_flatArgs, 3349 size_t* _flatSize); 3350 3351 3352 static status_t 3353 LoaderErrorDetails(const entry_ref* app, BString &details) 3354 { 3355 BPath path; 3356 BEntry appEntry(app, true); 3357 3358 status_t result = appEntry.GetPath(&path); 3359 if (result != B_OK) 3360 return result; 3361 3362 char* argv[2] = { const_cast<char*>(path.Path()), 0}; 3363 3364 port_id errorPort = create_port(1, "Tracker loader error"); 3365 3366 // count environment variables 3367 uint32 envCount = 0; 3368 while (environ[envCount] != NULL) 3369 envCount++; 3370 3371 char** flatArgs = NULL; 3372 size_t flatArgsSize; 3373 result = __flatten_process_args((const char**)argv, 1, 3374 environ, envCount, &flatArgs, &flatArgsSize); 3375 if (result != B_OK) 3376 return result; 3377 3378 result = _kern_load_image(flatArgs, flatArgsSize, 1, envCount, 3379 B_NORMAL_PRIORITY, B_WAIT_TILL_LOADED, errorPort, 0); 3380 if (result == B_OK) { 3381 // we weren't supposed to be able to start the application... 3382 return B_ERROR; 3383 } 3384 3385 // read error message from port and construct details string 3386 3387 ssize_t bufferSize; 3388 3389 do { 3390 bufferSize = port_buffer_size_etc(errorPort, B_RELATIVE_TIMEOUT, 0); 3391 } while (bufferSize == B_INTERRUPTED); 3392 3393 if (bufferSize <= B_OK) { 3394 delete_port(errorPort); 3395 return bufferSize; 3396 } 3397 3398 uint8* buffer = (uint8*)malloc(bufferSize); 3399 if (buffer == NULL) { 3400 delete_port(errorPort); 3401 return B_NO_MEMORY; 3402 } 3403 3404 bufferSize = read_port_etc(errorPort, NULL, buffer, bufferSize, 3405 B_RELATIVE_TIMEOUT, 0); 3406 delete_port(errorPort); 3407 3408 if (bufferSize < B_OK) { 3409 free(buffer); 3410 return bufferSize; 3411 } 3412 3413 BMessage message; 3414 result = message.Unflatten((const char*)buffer); 3415 free(buffer); 3416 3417 if (result != B_OK) 3418 return result; 3419 3420 int32 errorCode = B_ERROR; 3421 result = message.FindInt32("error", &errorCode); 3422 if (result != B_OK) 3423 return result; 3424 3425 const char* detailName = NULL; 3426 switch (errorCode) { 3427 case B_MISSING_LIBRARY: 3428 detailName = "missing library"; 3429 break; 3430 3431 case B_MISSING_SYMBOL: 3432 detailName = "missing symbol"; 3433 break; 3434 } 3435 3436 if (detailName == NULL) 3437 return B_ERROR; 3438 3439 const char* detail; 3440 for (int32 i = 0; message.FindString(detailName, i, &detail) == B_OK; 3441 i++) { 3442 if (i > 0) 3443 details += ", "; 3444 details += detail; 3445 } 3446 3447 return B_OK; 3448 } 3449 3450 3451 static void 3452 _TrackerLaunchDocuments(const entry_ref* /*doNotUse*/, const BMessage* refs, 3453 bool openWithOK) 3454 { 3455 BMessage copyOfRefs(*refs); 3456 3457 entry_ref documentRef; 3458 if (copyOfRefs.FindRef("refs", &documentRef) != B_OK) 3459 // nothing to launch, we are done 3460 return; 3461 3462 status_t error = B_ERROR; 3463 entry_ref app; 3464 BMessage* refsToPass = NULL; 3465 BString alertString; 3466 const char* alternative = 0; 3467 3468 for (int32 mimesetIt = 0; ; mimesetIt++) { 3469 alertString = ""; 3470 error = be_roster->FindApp(&documentRef, &app); 3471 3472 if (error != B_OK && mimesetIt == 0) { 3473 SniffIfGeneric(©OfRefs); 3474 continue; 3475 } 3476 3477 if (error != B_OK) { 3478 alertString.SetTo(B_TRANSLATE("Could not find an application to " 3479 "open \"%name\" (%error). ")); 3480 alertString.ReplaceFirst("%name", documentRef.name); 3481 alertString.ReplaceFirst("%error", strerror(error)); 3482 if (openWithOK) 3483 alternative = B_TRANSLATE_NOCOLLECT(kFindApplicationStr); 3484 3485 break; 3486 } else { 3487 BEntry appEntry(&app, true); 3488 for (int32 index = 0;;) { 3489 // remove the app itself from the refs received so we don't 3490 // try to open ourselves 3491 entry_ref ref; 3492 if (copyOfRefs.FindRef("refs", index, &ref) != B_OK) 3493 break; 3494 3495 // deal with symlinks properly 3496 BEntry documentEntry(&ref, true); 3497 if (appEntry == documentEntry) { 3498 PRINT(("stripping %s, app %s \n", ref.name, app.name)); 3499 copyOfRefs.RemoveData("refs", index); 3500 } else { 3501 PRINT(("leaving %s, app %s \n", ref.name, app.name)); 3502 index++; 3503 } 3504 } 3505 3506 refsToPass = CountRefs(©OfRefs) > 0 ? ©OfRefs: 0; 3507 team_id team; 3508 error = be_roster->Launch(&app, refsToPass, &team); 3509 if (error == B_ALREADY_RUNNING) 3510 // app already running, not really an error 3511 error = B_OK; 3512 if (error == B_OK || mimesetIt != 0) 3513 break; 3514 3515 SniffIfGeneric(©OfRefs); 3516 } 3517 } 3518 3519 if (error != B_OK && alertString.Length() == 0) { 3520 BString loaderErrorString; 3521 bool openedDocuments = true; 3522 3523 if (!refsToPass) { 3524 // we just double clicked the app itself, do not offer to 3525 // find a handling app 3526 openWithOK = false; 3527 openedDocuments = false; 3528 } 3529 3530 if (error == B_LAUNCH_FAILED_EXECUTABLE && !refsToPass) { 3531 alertString.SetTo(B_TRANSLATE("Could not open \"%name\". " 3532 "The file is mistakenly marked as executable. ")); 3533 alertString.ReplaceFirst("%name", app.name); 3534 3535 if (!openWithOK) { 3536 // offer the possibility to change the permissions 3537 3538 alertString << B_TRANSLATE("\nShould this be fixed?"); 3539 BAlert* alert = new BAlert("", alertString.String(), 3540 B_TRANSLATE("Cancel"), B_TRANSLATE("Proceed"), 0, 3541 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 3542 alert->SetShortcut(0, B_ESCAPE); 3543 if (alert->Go() == 1) { 3544 BEntry entry(&documentRef); 3545 mode_t permissions; 3546 3547 error = entry.GetPermissions(&permissions); 3548 if (error == B_OK) { 3549 error = entry.SetPermissions(permissions 3550 & ~(S_IXUSR | S_IXGRP | S_IXOTH)); 3551 } 3552 if (error == B_OK) { 3553 // we updated the permissions, so let's try again 3554 _TrackerLaunchDocuments(NULL, refs, false); 3555 return; 3556 } else { 3557 alertString.SetTo(B_TRANSLATE("Could not update " 3558 "permissions of file \"%name\". %error")); 3559 alertString.ReplaceFirst("%name", app.name); 3560 alertString.ReplaceFirst("%error", strerror(error)); 3561 } 3562 } else 3563 return; 3564 } 3565 3566 alternative = B_TRANSLATE_NOCOLLECT(kFindApplicationStr); 3567 } else if (error == B_LAUNCH_FAILED_APP_IN_TRASH) { 3568 alertString.SetTo(B_TRANSLATE("Could not open \"%document\" " 3569 "because application \"%app\" is in the Trash. ")); 3570 alertString.ReplaceFirst("%document", documentRef.name); 3571 alertString.ReplaceFirst("%app", app.name); 3572 alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr); 3573 } else if (error == B_LAUNCH_FAILED_APP_NOT_FOUND) { 3574 alertString.SetTo( 3575 B_TRANSLATE("Could not open \"%name\" (%error). ")); 3576 alertString.ReplaceFirst("%name", documentRef.name); 3577 alertString.ReplaceFirst("%error", strerror(error)); 3578 alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr); 3579 } else if (error == B_MISSING_SYMBOL 3580 && LoaderErrorDetails(&app, loaderErrorString) == B_OK) { 3581 if (openedDocuments) { 3582 alertString.SetTo(B_TRANSLATE("Could not open \"%document\" " 3583 "with application \"%app\" (Missing symbol: %symbol). " 3584 "\n")); 3585 alertString.ReplaceFirst("%document", documentRef.name); 3586 alertString.ReplaceFirst("%app", app.name); 3587 alertString.ReplaceFirst("%symbol", 3588 loaderErrorString.String()); 3589 } else { 3590 alertString.SetTo(B_TRANSLATE("Could not open \"%document\" " 3591 "(Missing symbol: %symbol). \n")); 3592 alertString.ReplaceFirst("%document", documentRef.name); 3593 alertString.ReplaceFirst("%symbol", 3594 loaderErrorString.String()); 3595 } 3596 alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr); 3597 } else if (error == B_MISSING_LIBRARY 3598 && LoaderErrorDetails(&app, loaderErrorString) == B_OK) { 3599 if (openedDocuments) { 3600 alertString.SetTo(B_TRANSLATE("Could not open \"%document\" " 3601 "with application \"%app\" (Missing libraries: %library). " 3602 "\n")); 3603 alertString.ReplaceFirst("%document", documentRef.name); 3604 alertString.ReplaceFirst("%app", app.name); 3605 alertString.ReplaceFirst("%library", 3606 loaderErrorString.String()); 3607 } else { 3608 alertString.SetTo(B_TRANSLATE("Could not open \"%document\" " 3609 "(Missing libraries: %library). \n")); 3610 alertString.ReplaceFirst("%document", documentRef.name); 3611 alertString.ReplaceFirst("%library", 3612 loaderErrorString.String()); 3613 } 3614 alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr); 3615 } else { 3616 alertString.SetTo(B_TRANSLATE("Could not open \"%document\" with " 3617 "application \"%app\" (%error). ")); 3618 alertString.ReplaceFirst("%document", documentRef.name); 3619 alertString.ReplaceFirst("%app", app.name); 3620 alertString.ReplaceFirst("%error", strerror(error)); 3621 alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr); 3622 } 3623 } 3624 3625 if (error != B_OK) { 3626 if (openWithOK) { 3627 ASSERT(alternative); 3628 alertString << alternative; 3629 BAlert* alert = new BAlert("", alertString.String(), 3630 B_TRANSLATE("Cancel"), B_TRANSLATE("Find"), 0, 3631 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 3632 alert->SetShortcut(0, B_ESCAPE); 3633 if (alert->Go() == 1) 3634 error = TrackerOpenWith(refs); 3635 } else { 3636 BAlert* alert = new BAlert("", alertString.String(), 3637 B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, 3638 B_WARNING_ALERT); 3639 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 3640 alert->Go(); 3641 } 3642 } 3643 } 3644 3645 // the following three calls don't return any reasonable error codes, 3646 // should fix that, making them void 3647 3648 status_t 3649 TrackerLaunch(const entry_ref* appRef, const BMessage* refs, bool async, 3650 bool openWithOK) 3651 { 3652 if (!async) 3653 _TrackerLaunchAppWithDocuments(appRef, refs, openWithOK); 3654 else { 3655 AsynchLaunchBinder(&_TrackerLaunchAppWithDocuments, appRef, refs, 3656 openWithOK); 3657 } 3658 3659 return B_OK; 3660 } 3661 3662 status_t 3663 TrackerLaunch(const entry_ref* appRef, bool async) 3664 { 3665 if (!async) 3666 _TrackerLaunchAppWithDocuments(appRef, 0, false); 3667 else 3668 AsynchLaunchBinder(&_TrackerLaunchAppWithDocuments, appRef, 0, false); 3669 3670 return B_OK; 3671 } 3672 3673 status_t 3674 TrackerLaunch(const BMessage* refs, bool async, bool openWithOK) 3675 { 3676 if (!async) 3677 _TrackerLaunchDocuments(0, refs, openWithOK); 3678 else 3679 AsynchLaunchBinder(&_TrackerLaunchDocuments, 0, refs, openWithOK); 3680 3681 return B_OK; 3682 } 3683 3684 status_t 3685 LaunchBrokenLink(const char* signature, const BMessage* refs) 3686 { 3687 // This call is to support a hacky workaround for double-clicking 3688 // broken refs for cifs 3689 be_roster->Launch(signature, const_cast<BMessage*>(refs)); 3690 return B_OK; 3691 } 3692 3693 // external launch calls; need to be robust, work if Tracker is not running 3694 3695 #if !B_BEOS_VERSION_DANO 3696 _IMPEXP_TRACKER 3697 #endif 3698 status_t 3699 FSLaunchItem(const entry_ref* application, const BMessage* refsReceived, 3700 bool async, bool openWithOK) 3701 { 3702 return TrackerLaunch(application, refsReceived, async, openWithOK); 3703 } 3704 3705 3706 #if !B_BEOS_VERSION_DANO 3707 _IMPEXP_TRACKER 3708 #endif 3709 status_t 3710 FSOpenWith(BMessage* listOfRefs) 3711 { 3712 status_t result = B_ERROR; 3713 listOfRefs->what = B_REFS_RECEIVED; 3714 3715 if (dynamic_cast<TTracker*>(be_app)) 3716 result = TrackerOpenWith(listOfRefs); 3717 else 3718 ASSERT(!"not yet implemented"); 3719 3720 return result; 3721 } 3722 3723 // legacy calls, need for compatibility 3724 3725 void 3726 FSOpenWithDocuments(const entry_ref* executable, BMessage* documents) 3727 { 3728 TrackerLaunch(executable, documents, true); 3729 delete documents; 3730 } 3731 3732 status_t 3733 FSLaunchUsing(const entry_ref* ref, BMessage* listOfRefs) 3734 { 3735 BMessage temp(B_REFS_RECEIVED); 3736 if (!listOfRefs) { 3737 ASSERT(ref); 3738 temp.AddRef("refs", ref); 3739 listOfRefs = &temp; 3740 } 3741 FSOpenWith(listOfRefs); 3742 return B_OK; 3743 } 3744 3745 status_t 3746 FSLaunchItem(const entry_ref* ref, BMessage* message, int32, bool async) 3747 { 3748 if (message) 3749 message->what = B_REFS_RECEIVED; 3750 3751 status_t result = TrackerLaunch(ref, message, async, true); 3752 delete message; 3753 return result; 3754 } 3755 3756 3757 void 3758 FSLaunchItem(const entry_ref* ref, BMessage* message, int32 workspace) 3759 { 3760 FSLaunchItem(ref, message, workspace, true); 3761 } 3762 3763 // Get the original path of an entry in the trash 3764 status_t 3765 FSGetOriginalPath(BEntry* entry, BPath* result) 3766 { 3767 status_t err; 3768 entry_ref ref; 3769 err = entry->GetRef(&ref); 3770 if (err != B_OK) 3771 return err; 3772 3773 // Only call the routine for entries in the trash 3774 if (!FSInTrashDir(&ref)) 3775 return B_ERROR; 3776 3777 BNode node(entry); 3778 BString originalPath; 3779 if (node.ReadAttrString(kAttrOriginalPath, &originalPath) == B_OK) { 3780 // We're in luck, the entry has the original path in an attribute 3781 err = result->SetTo(originalPath.String()); 3782 return err; 3783 } 3784 3785 // Iterate the parent directories to find one with 3786 // the original path attribute 3787 BEntry parent(*entry); 3788 err = parent.InitCheck(); 3789 if (err != B_OK) 3790 return err; 3791 3792 // walk up the directory structure until we find a node 3793 // with original path attribute 3794 do { 3795 // move to the parent of this node 3796 err = parent.GetParent(&parent); 3797 if (err != B_OK) 3798 return err; 3799 3800 // return if we are at the root of the trash 3801 if (FSIsTrashDir(&parent)) 3802 return B_ENTRY_NOT_FOUND; 3803 3804 // get the parent as a node 3805 err = node.SetTo(&parent); 3806 if (err != B_OK) 3807 return err; 3808 } while (node.ReadAttrString(kAttrOriginalPath, &originalPath) != B_OK); 3809 3810 // Found the attribute, figure out there this file 3811 // used to live, based on the successfully-read attribute 3812 err = result->SetTo(originalPath.String()); 3813 if (err != B_OK) 3814 return err; 3815 3816 BPath path, pathParent; 3817 err = parent.GetPath(&pathParent); 3818 if (err != B_OK) 3819 return err; 3820 err = entry->GetPath(&path); 3821 if (err != B_OK) 3822 return err; 3823 result->Append(path.Path() + strlen(pathParent.Path()) + 1); 3824 // compute the new path by appending the offset of 3825 // the item we are locating, to the original path 3826 // of the parent 3827 return B_OK; 3828 } 3829 3830 directory_which 3831 WellKnowEntryList::Match(const node_ref* node) 3832 { 3833 const WellKnownEntry* result = MatchEntry(node); 3834 if (result) 3835 return result->which; 3836 3837 return (directory_which)-1; 3838 } 3839 3840 const WellKnowEntryList::WellKnownEntry* 3841 WellKnowEntryList::MatchEntry(const node_ref* node) 3842 { 3843 if (!self) 3844 self = new WellKnowEntryList(); 3845 3846 return self->MatchEntryCommon(node); 3847 } 3848 3849 const WellKnowEntryList::WellKnownEntry* 3850 WellKnowEntryList::MatchEntryCommon(const node_ref* node) 3851 { 3852 uint32 count = entries.size(); 3853 for (uint32 index = 0; index < count; index++) 3854 if (*node == entries[index].node) 3855 return &entries[index]; 3856 3857 return NULL; 3858 } 3859 3860 3861 void 3862 WellKnowEntryList::Quit() 3863 { 3864 delete self; 3865 self = NULL; 3866 } 3867 3868 3869 void 3870 WellKnowEntryList::AddOne(directory_which which, const char* name) 3871 { 3872 BPath path; 3873 if (find_directory(which, &path, true) != B_OK) 3874 return; 3875 3876 BEntry entry(path.Path(), true); 3877 node_ref node; 3878 if (entry.GetNodeRef(&node) != B_OK) 3879 return; 3880 3881 entries.push_back(WellKnownEntry(&node, which, name)); 3882 } 3883 3884 3885 void 3886 WellKnowEntryList::AddOne(directory_which which, directory_which base, 3887 const char* extra, const char* name) 3888 { 3889 BPath path; 3890 if (find_directory(base, &path, true) != B_OK) 3891 return; 3892 3893 path.Append(extra); 3894 BEntry entry(path.Path(), true); 3895 node_ref node; 3896 if (entry.GetNodeRef(&node) != B_OK) 3897 return; 3898 3899 entries.push_back(WellKnownEntry(&node, which, name)); 3900 } 3901 3902 3903 void 3904 WellKnowEntryList::AddOne(directory_which which, const char* path, 3905 const char* name) 3906 { 3907 BEntry entry(path, true); 3908 node_ref node; 3909 if (entry.GetNodeRef(&node) != B_OK) 3910 return; 3911 3912 entries.push_back(WellKnownEntry(&node, which, name)); 3913 } 3914 3915 3916 WellKnowEntryList::WellKnowEntryList() 3917 { 3918 #ifdef __HAIKU__ 3919 AddOne(B_SYSTEM_DIRECTORY, "system"); 3920 #else 3921 AddOne(B_BEOS_DIRECTORY, "beos"); 3922 AddOne(B_BEOS_SYSTEM_DIRECTORY, "system"); 3923 #endif 3924 AddOne((directory_which)B_BOOT_DISK, "/boot", "boot"); 3925 AddOne(B_USER_DIRECTORY, "home"); 3926 3927 AddOne(B_BEOS_FONTS_DIRECTORY, "fonts"); 3928 AddOne(B_COMMON_FONTS_DIRECTORY, "fonts"); 3929 AddOne(B_USER_FONTS_DIRECTORY, "fonts"); 3930 3931 AddOne(B_BEOS_APPS_DIRECTORY, "apps"); 3932 AddOne(B_APPS_DIRECTORY, "apps"); 3933 AddOne((directory_which)B_USER_DESKBAR_APPS_DIRECTORY, 3934 B_USER_DESKBAR_DIRECTORY, "Applications", "apps"); 3935 3936 AddOne(B_BEOS_PREFERENCES_DIRECTORY, "preferences"); 3937 AddOne(B_PREFERENCES_DIRECTORY, "preferences"); 3938 AddOne((directory_which)B_USER_DESKBAR_PREFERENCES_DIRECTORY, 3939 B_USER_DESKBAR_DIRECTORY, "Preferences", "preferences"); 3940 3941 AddOne((directory_which)B_USER_MAIL_DIRECTORY, B_USER_DIRECTORY, "mail", 3942 "mail"); 3943 3944 AddOne((directory_which)B_USER_QUERIES_DIRECTORY, B_USER_DIRECTORY, 3945 "queries", "queries"); 3946 3947 AddOne(B_COMMON_DEVELOP_DIRECTORY, "develop"); 3948 AddOne((directory_which)B_USER_DESKBAR_DEVELOP_DIRECTORY, 3949 B_USER_DESKBAR_DIRECTORY, "Development", "develop"); 3950 3951 AddOne(B_USER_CONFIG_DIRECTORY, "config"); 3952 3953 AddOne((directory_which)B_USER_PEOPLE_DIRECTORY, B_USER_DIRECTORY, 3954 "people", "people"); 3955 3956 AddOne((directory_which)B_USER_DOWNLOADS_DIRECTORY, B_USER_DIRECTORY, 3957 "downloads", "downloads"); 3958 } 3959 3960 WellKnowEntryList* WellKnowEntryList::self = NULL; 3961 3962 } // namespace BPrivate 3963