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