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 tempName[B_FILE_NAME_LENGTH + 11]; 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 strlcpy(root, name, sizeof(root)); 2418 // save root name 2419 strlcat(name, suffix, B_FILE_NAME_LENGTH); 2420 } 2421 2422 strlcpy(copybase, name, sizeof(copybase)); 2423 2424 // if name already exists then add a number 2425 fnum = 1; 2426 strlcpy(tempName, name, sizeof(tempName)); 2427 while (destDir->Contains(tempName)) { 2428 snprintf(tempName, sizeof(tempName), "%s %" B_PRId32, copybase, 2429 ++fnum); 2430 2431 if (strlen(tempName) > (B_FILE_NAME_LENGTH - 1)) { 2432 // The name has grown too long. Maybe we just went from 2433 // "<filename> copy 9" to "<filename> copy 10" and that extra 2434 // character was too much. The solution is to further 2435 // truncate the 'root' name and continue. 2436 // ??? should we reset fnum or not ??? 2437 root[strlen(root) - 1] = '\0'; 2438 snprintf(tempName, sizeof(tempName), "%s%s %" B_PRId32, root, 2439 suffix, fnum); 2440 } 2441 } 2442 2443 strlcpy(name, tempName, B_FILE_NAME_LENGTH); 2444 } 2445 2446 2447 status_t 2448 FSRecursiveCalcSize(BInfoWindow* window, CopyLoopControl* loopControl, 2449 BDirectory* dir, off_t* _runningSize, int32* _fileCount, int32* _dirCount) 2450 { 2451 dir->Rewind(); 2452 BEntry entry; 2453 while (dir->GetNextEntry(&entry) == B_OK) { 2454 // be sure window hasn't closed 2455 if (window && window->StopCalc()) 2456 return B_OK; 2457 2458 if (loopControl->CheckUserCanceled()) 2459 return kUserCanceled; 2460 2461 StatStruct statbuf; 2462 status_t status = entry.GetStat(&statbuf); 2463 if (status != B_OK) 2464 return status; 2465 2466 (*_runningSize) += statbuf.st_blocks* 512; 2467 2468 if (S_ISDIR(statbuf.st_mode)) { 2469 BDirectory subdir(&entry); 2470 (*_dirCount)++; 2471 status = FSRecursiveCalcSize(window, loopControl, &subdir, 2472 _runningSize, _fileCount, _dirCount); 2473 if (status != B_OK) 2474 return status; 2475 } else 2476 (*_fileCount)++; 2477 } 2478 return B_OK; 2479 } 2480 2481 2482 status_t 2483 CalcItemsAndSize(CopyLoopControl* loopControl, 2484 BObjectList<entry_ref>* refList, ssize_t blockSize, int32* totalCount, 2485 off_t* totalSize) 2486 { 2487 int32 fileCount = 0; 2488 int32 dirCount = 0; 2489 2490 // check block size for sanity 2491 if (blockSize < 0) { 2492 // This would point at an error to retrieve the block size from 2493 // the target volume. The code below cannot be used, it is only 2494 // meant to get the block size when item operations happen on 2495 // the source volume. 2496 blockSize = 2048; 2497 } else if (blockSize < 1024) { 2498 blockSize = 1024; 2499 if (entry_ref* ref = refList->ItemAt(0)) { 2500 // TODO: This assumes all entries in the list share the same 2501 // volume... 2502 BVolume volume(ref->device); 2503 if (volume.InitCheck() == B_OK) 2504 blockSize = volume.BlockSize(); 2505 } 2506 } 2507 // File systems like ReiserFS may advertize a large block size, but 2508 // stuff is still packed into blocks, so clamp maximum block size. 2509 if (blockSize > 8192) 2510 blockSize = 8192; 2511 2512 int32 num_items = refList->CountItems(); 2513 for (int32 i = 0; i < num_items; i++) { 2514 entry_ref* ref = refList->ItemAt(i); 2515 BEntry entry(ref); 2516 StatStruct statbuf; 2517 entry.GetStat(&statbuf); 2518 2519 if (loopControl->CheckUserCanceled()) 2520 return kUserCanceled; 2521 2522 if (S_ISDIR(statbuf.st_mode)) { 2523 BDirectory dir(&entry); 2524 dirCount++; 2525 (*totalSize) += blockSize; 2526 status_t result = FSRecursiveCalcSize(NULL, loopControl, &dir, 2527 totalSize, &fileCount, &dirCount); 2528 if (result != B_OK) 2529 return result; 2530 } else { 2531 fileCount++; 2532 (*totalSize) += statbuf.st_size + blockSize; 2533 } 2534 } 2535 2536 *totalCount += (fileCount + dirCount); 2537 return B_OK; 2538 } 2539 2540 2541 status_t 2542 FSGetTrashDir(BDirectory* trashDir, dev_t dev) 2543 { 2544 if (trashDir == NULL) 2545 return B_BAD_VALUE; 2546 2547 BVolume volume(dev); 2548 status_t result = volume.InitCheck(); 2549 if (result != B_OK) 2550 return result; 2551 2552 BPath path; 2553 result = find_directory(B_TRASH_DIRECTORY, &path, false, &volume); 2554 if (result != B_OK) 2555 return result; 2556 2557 result = trashDir->SetTo(path.Path()); 2558 if (result != B_OK) { 2559 // Trash directory does not exist yet, create it. 2560 result = create_directory(path.Path(), 0755); 2561 if (result != B_OK) 2562 return result; 2563 2564 result = trashDir->SetTo(path.Path()); 2565 if (result != B_OK) 2566 return result; 2567 2568 // make Trash directory invisible 2569 StatStruct sbuf; 2570 trashDir->GetStat(&sbuf); 2571 2572 PoseInfo poseInfo; 2573 poseInfo.fInvisible = true; 2574 poseInfo.fInitedDirectory = sbuf.st_ino; 2575 trashDir->WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, &poseInfo, 2576 sizeof(PoseInfo)); 2577 } 2578 2579 // set Trash icons (if they haven't already been set) 2580 attr_info attrInfo; 2581 size_t size; 2582 const void* data; 2583 if (trashDir->GetAttrInfo(kAttrLargeIcon, &attrInfo) == B_ENTRY_NOT_FOUND) { 2584 data = GetTrackerResources()->LoadResource('ICON', R_TrashIcon, &size); 2585 if (data != NULL) 2586 trashDir->WriteAttr(kAttrLargeIcon, 'ICON', 0, data, size); 2587 } 2588 2589 if (trashDir->GetAttrInfo(kAttrMiniIcon, &attrInfo) == B_ENTRY_NOT_FOUND) { 2590 data = GetTrackerResources()->LoadResource('MICN', R_TrashIcon, &size); 2591 if (data != NULL) 2592 trashDir->WriteAttr(kAttrMiniIcon, 'MICN', 0, data, size); 2593 } 2594 2595 if (trashDir->GetAttrInfo(kAttrIcon, &attrInfo) == B_ENTRY_NOT_FOUND) { 2596 data = GetTrackerResources()->LoadResource(B_VECTOR_ICON_TYPE, 2597 R_TrashIcon, &size); 2598 if (data != NULL) 2599 trashDir->WriteAttr(kAttrIcon, B_VECTOR_ICON_TYPE, 0, data, size); 2600 } 2601 2602 return B_OK; 2603 } 2604 2605 2606 status_t 2607 FSGetDeskDir(BDirectory* deskDir) 2608 { 2609 if (deskDir == NULL) 2610 return B_BAD_VALUE; 2611 2612 BPath path; 2613 status_t result = find_directory(B_DESKTOP_DIRECTORY, &path, true); 2614 if (result != B_OK) 2615 return result; 2616 2617 result = deskDir->SetTo(path.Path()); 2618 if (result != B_OK) 2619 return result; 2620 2621 // set Desktop icons (if they haven't already been set) 2622 attr_info attrInfo; 2623 size_t size; 2624 const void* data; 2625 if (deskDir->GetAttrInfo(kAttrLargeIcon, &attrInfo) == B_ENTRY_NOT_FOUND) { 2626 data = GetTrackerResources()->LoadResource('ICON', R_DeskIcon, &size); 2627 if (data != NULL) 2628 deskDir->WriteAttr(kAttrLargeIcon, 'ICON', 0, data, size); 2629 } 2630 2631 if (deskDir->GetAttrInfo(kAttrMiniIcon, &attrInfo) == B_ENTRY_NOT_FOUND) { 2632 data = GetTrackerResources()->LoadResource('MICN', R_DeskIcon, &size); 2633 if (data != NULL) 2634 deskDir->WriteAttr(kAttrMiniIcon, 'MICN', 0, data, size); 2635 } 2636 2637 if (deskDir->GetAttrInfo(kAttrIcon, &attrInfo) == B_ENTRY_NOT_FOUND) { 2638 data = GetTrackerResources()->LoadResource(B_VECTOR_ICON_TYPE, 2639 R_DeskIcon, &size); 2640 if (data != NULL) 2641 deskDir->WriteAttr(kAttrIcon, B_VECTOR_ICON_TYPE, 0, data, size); 2642 } 2643 2644 return B_OK; 2645 } 2646 2647 2648 status_t 2649 FSGetBootDeskDir(BDirectory* deskDir) 2650 { 2651 BVolume bootVolume; 2652 BVolumeRoster().GetBootVolume(&bootVolume); 2653 BPath path; 2654 2655 status_t result = find_directory(B_DESKTOP_DIRECTORY, &path, true, 2656 &bootVolume); 2657 if (result != B_OK) 2658 return result; 2659 2660 return deskDir->SetTo(path.Path()); 2661 } 2662 2663 2664 static bool 2665 FSIsDirFlavor(const BEntry* entry, directory_which directoryType) 2666 { 2667 StatStruct dir_stat; 2668 StatStruct entry_stat; 2669 BVolume volume; 2670 BPath path; 2671 2672 if (entry->GetStat(&entry_stat) != B_OK) 2673 return false; 2674 2675 if (volume.SetTo(entry_stat.st_dev) != B_OK) 2676 return false; 2677 2678 if (find_directory(directoryType, &path, false, &volume) != B_OK) 2679 return false; 2680 2681 stat(path.Path(), &dir_stat); 2682 2683 return dir_stat.st_ino == entry_stat.st_ino 2684 && dir_stat.st_dev == entry_stat.st_dev; 2685 } 2686 2687 2688 bool 2689 FSIsPrintersDir(const BEntry* entry) 2690 { 2691 return FSIsDirFlavor(entry, B_USER_PRINTERS_DIRECTORY); 2692 } 2693 2694 2695 bool 2696 FSIsTrashDir(const BEntry* entry) 2697 { 2698 return FSIsDirFlavor(entry, B_TRASH_DIRECTORY); 2699 } 2700 2701 2702 bool 2703 FSIsDeskDir(const BEntry* entry) 2704 { 2705 BPath path; 2706 status_t result = find_directory(B_DESKTOP_DIRECTORY, &path, true); 2707 if (result != B_OK) 2708 return false; 2709 2710 BEntry entryToCompare(path.Path()); 2711 return entryToCompare == *entry; 2712 } 2713 2714 2715 bool 2716 FSIsHomeDir(const BEntry* entry) 2717 { 2718 return FSIsDirFlavor(entry, B_USER_DIRECTORY); 2719 } 2720 2721 2722 bool 2723 FSIsRootDir(const BEntry* entry) 2724 { 2725 BPath path(entry); 2726 return path == "/"; 2727 } 2728 2729 2730 bool 2731 DirectoryMatchesOrContains(const BEntry* entry, directory_which which) 2732 { 2733 BPath path; 2734 if (find_directory(which, &path, false, NULL) != B_OK) 2735 return false; 2736 2737 BEntry dirEntry(path.Path()); 2738 if (dirEntry.InitCheck() != B_OK) 2739 return false; 2740 2741 if (dirEntry == *entry) 2742 // root level match 2743 return true; 2744 2745 BDirectory dir(&dirEntry); 2746 return dir.Contains(entry); 2747 } 2748 2749 2750 bool 2751 DirectoryMatchesOrContains(const BEntry* entry, const char* additionalPath, 2752 directory_which which) 2753 { 2754 BPath path; 2755 if (find_directory(which, &path, false, NULL) != B_OK) 2756 return false; 2757 2758 path.Append(additionalPath); 2759 BEntry dirEntry(path.Path()); 2760 if (dirEntry.InitCheck() != B_OK) 2761 return false; 2762 2763 if (dirEntry == *entry) 2764 // root level match 2765 return true; 2766 2767 BDirectory dir(&dirEntry); 2768 return dir.Contains(entry); 2769 } 2770 2771 2772 bool 2773 DirectoryMatches(const BEntry* entry, directory_which which) 2774 { 2775 BPath path; 2776 if (find_directory(which, &path, false, NULL) != B_OK) 2777 return false; 2778 2779 BEntry dirEntry(path.Path()); 2780 if (dirEntry.InitCheck() != B_OK) 2781 return false; 2782 2783 return dirEntry == *entry; 2784 } 2785 2786 2787 bool 2788 DirectoryMatches(const BEntry* entry, const char* additionalPath, 2789 directory_which which) 2790 { 2791 BPath path; 2792 if (find_directory(which, &path, false, NULL) != B_OK) 2793 return false; 2794 2795 path.Append(additionalPath); 2796 BEntry dirEntry(path.Path()); 2797 if (dirEntry.InitCheck() != B_OK) 2798 return false; 2799 2800 return dirEntry == *entry; 2801 } 2802 2803 2804 extern status_t 2805 FSFindTrackerSettingsDir(BPath* path, bool autoCreate) 2806 { 2807 status_t result = find_directory(B_USER_SETTINGS_DIRECTORY, path, 2808 autoCreate); 2809 if (result != B_OK) 2810 return result; 2811 2812 path->Append("Tracker"); 2813 2814 return mkdir(path->Path(), 0777) ? B_OK : errno; 2815 } 2816 2817 2818 bool 2819 FSInTrashDir(const entry_ref* ref) 2820 { 2821 BEntry entry(ref); 2822 if (entry.InitCheck() != B_OK) 2823 return false; 2824 2825 BDirectory trashDir; 2826 if (FSGetTrashDir(&trashDir, ref->device) != B_OK) 2827 return false; 2828 2829 return trashDir.Contains(&entry); 2830 } 2831 2832 2833 void 2834 FSEmptyTrash() 2835 { 2836 if (find_thread("_tracker_empty_trash_") != B_OK) { 2837 resume_thread(spawn_thread(empty_trash, "_tracker_empty_trash_", 2838 B_NORMAL_PRIORITY, NULL)); 2839 } 2840 } 2841 2842 2843 status_t 2844 empty_trash(void*) 2845 { 2846 // empty trash on all mounted volumes 2847 status_t status = B_OK; 2848 2849 TrackerCopyLoopControl loopControl(kTrashState); 2850 2851 // calculate the sum total of all items on all volumes in trash 2852 BObjectList<entry_ref> srcList; 2853 int32 totalCount = 0; 2854 off_t totalSize = 0; 2855 2856 BVolumeRoster volumeRoster; 2857 BVolume volume; 2858 while (volumeRoster.GetNextVolume(&volume) == B_OK) { 2859 if (volume.IsReadOnly() || !volume.IsPersistent()) 2860 continue; 2861 2862 BDirectory trashDirectory; 2863 if (FSGetTrashDir(&trashDirectory, volume.Device()) != B_OK) 2864 continue; 2865 2866 BEntry entry; 2867 trashDirectory.GetEntry(&entry); 2868 2869 entry_ref ref; 2870 entry.GetRef(&ref); 2871 srcList.AddItem(&ref); 2872 status = CalcItemsAndSize(&loopControl, &srcList, volume.BlockSize(), 2873 &totalCount, &totalSize); 2874 if (status != B_OK) 2875 break; 2876 2877 srcList.MakeEmpty(); 2878 2879 // don't count trash directory itself 2880 totalCount--; 2881 } 2882 2883 if (status == B_OK) { 2884 loopControl.Init(totalCount, totalCount); 2885 2886 volumeRoster.Rewind(); 2887 while (volumeRoster.GetNextVolume(&volume) == B_OK) { 2888 if (volume.IsReadOnly() || !volume.IsPersistent()) 2889 continue; 2890 2891 BDirectory trashDirectory; 2892 if (FSGetTrashDir(&trashDirectory, volume.Device()) != B_OK) 2893 continue; 2894 2895 BEntry entry; 2896 trashDirectory.GetEntry(&entry); 2897 status = FSDeleteFolder(&entry, &loopControl, true, false); 2898 } 2899 } 2900 2901 if (status != B_OK && status != kTrashCanceled && status != kUserCanceled) { 2902 BAlert* alert = new BAlert("", B_TRANSLATE("Error emptying Trash"), 2903 B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 2904 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 2905 alert->Go(); 2906 } 2907 2908 return B_OK; 2909 } 2910 2911 2912 status_t 2913 _DeleteTask(BObjectList<entry_ref>* list, bool confirm) 2914 { 2915 if (confirm) { 2916 bool dontMoveToTrash = TrackerSettings().DontMoveFilesToTrash(); 2917 2918 if (!dontMoveToTrash) { 2919 BAlert* alert = new BAlert("", 2920 B_TRANSLATE_NOCOLLECT(kDeleteConfirmationStr), 2921 B_TRANSLATE("Cancel"), B_TRANSLATE("Move to Trash"), 2922 B_TRANSLATE("Delete"), B_WIDTH_AS_USUAL, B_OFFSET_SPACING, 2923 B_WARNING_ALERT); 2924 2925 alert->SetShortcut(0, B_ESCAPE); 2926 alert->SetShortcut(1, 'm'); 2927 alert->SetShortcut(2, 'd'); 2928 2929 switch (alert->Go()) { 2930 case 0: 2931 delete list; 2932 return B_OK; 2933 case 1: 2934 FSMoveToTrash(list, NULL, false); 2935 return B_OK; 2936 } 2937 } else { 2938 BAlert* alert = new BAlert("", 2939 B_TRANSLATE_NOCOLLECT(kDeleteConfirmationStr), 2940 B_TRANSLATE("Cancel"), B_TRANSLATE("Delete"), NULL, 2941 B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_WARNING_ALERT); 2942 2943 alert->SetShortcut(0, B_ESCAPE); 2944 alert->SetShortcut(1, 'd'); 2945 2946 if (!alert->Go()) { 2947 delete list; 2948 return B_OK; 2949 } 2950 } 2951 } 2952 2953 TrackerCopyLoopControl loopControl(kDeleteState); 2954 2955 // calculate the sum total of all items on all volumes in trash 2956 int32 totalItems = 0; 2957 int64 totalSize = 0; 2958 2959 status_t status = CalcItemsAndSize(&loopControl, list, 0, &totalItems, 2960 &totalSize); 2961 if (status == B_OK) { 2962 loopControl.Init(totalItems, totalItems); 2963 2964 int32 count = list->CountItems(); 2965 for (int32 index = 0; index < count; index++) { 2966 entry_ref ref(*list->ItemAt(index)); 2967 BEntry entry(&ref); 2968 loopControl.UpdateStatus(ref.name, ref, 1, true); 2969 if (entry.IsDirectory()) 2970 status = FSDeleteFolder(&entry, &loopControl, true, true, true); 2971 else 2972 status = entry.Remove(); 2973 } 2974 2975 if (status != kTrashCanceled && status != kUserCanceled 2976 && status != B_OK) { 2977 BAlert* alert = new BAlert("", B_TRANSLATE("Error deleting items"), 2978 B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, 2979 B_WARNING_ALERT); 2980 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 2981 alert->Go(); 2982 } 2983 } 2984 2985 delete list; 2986 2987 return B_OK; 2988 } 2989 2990 status_t 2991 FSRecursiveCreateFolder(BPath path) 2992 { 2993 BEntry entry(path.Path()); 2994 if (entry.InitCheck() != B_OK) { 2995 BPath parentPath; 2996 status_t err = path.GetParent(&parentPath); 2997 if (err != B_OK) 2998 return err; 2999 3000 err = FSRecursiveCreateFolder(parentPath); 3001 if (err != B_OK) 3002 return err; 3003 } 3004 3005 entry.SetTo(path.Path()); 3006 if (entry.Exists()) 3007 return B_FILE_EXISTS; 3008 3009 BDirectory parent; 3010 entry.GetParent(&parent); 3011 parent.CreateDirectory(entry.Name(), NULL); 3012 3013 return B_OK; 3014 } 3015 3016 status_t 3017 _RestoreTask(BObjectList<entry_ref>* list) 3018 { 3019 TrackerCopyLoopControl loopControl(kRestoreFromTrashState); 3020 3021 // calculate the sum total of all items that will be restored 3022 int32 totalItems = 0; 3023 int64 totalSize = 0; 3024 3025 status_t err = CalcItemsAndSize(&loopControl, list, 0, &totalItems, 3026 &totalSize); 3027 if (err == B_OK) { 3028 loopControl.Init(totalItems, totalItems); 3029 3030 int32 count = list->CountItems(); 3031 for (int32 index = 0; index < count; index++) { 3032 entry_ref ref(*list->ItemAt(index)); 3033 BEntry entry(&ref); 3034 BPath originalPath; 3035 3036 loopControl.UpdateStatus(ref.name, ref, 1, true); 3037 3038 if (FSGetOriginalPath(&entry, &originalPath) != B_OK) 3039 continue; 3040 3041 BEntry originalEntry(originalPath.Path()); 3042 BPath parentPath; 3043 err = originalPath.GetParent(&parentPath); 3044 if (err != B_OK) 3045 continue; 3046 BEntry parentEntry(parentPath.Path()); 3047 3048 if (parentEntry.InitCheck() != B_OK || !parentEntry.Exists()) { 3049 if (FSRecursiveCreateFolder(parentPath) == B_OK) { 3050 originalEntry.SetTo(originalPath.Path()); 3051 if (entry.InitCheck() != B_OK) 3052 continue; 3053 } 3054 } 3055 3056 if (!originalEntry.Exists()) { 3057 BDirectory dir(parentPath.Path()); 3058 if (dir.InitCheck() == B_OK) { 3059 const char* leafName = originalEntry.Name(); 3060 if (entry.MoveTo(&dir, leafName) == B_OK) { 3061 BNode node(&entry); 3062 if (node.InitCheck() == B_OK) 3063 node.RemoveAttr(kAttrOriginalPath); 3064 } 3065 } 3066 } 3067 3068 err = loopControl.CheckUserCanceled(); 3069 if (err != B_OK) 3070 break; 3071 } 3072 } 3073 3074 delete list; 3075 3076 return err; 3077 } 3078 3079 void 3080 FSCreateTrashDirs() 3081 { 3082 BVolume volume; 3083 BVolumeRoster roster; 3084 3085 roster.Rewind(); 3086 while (roster.GetNextVolume(&volume) == B_OK) { 3087 if (volume.IsReadOnly() || !volume.IsPersistent()) 3088 continue; 3089 3090 BDirectory trashDir; 3091 FSGetTrashDir(&trashDir, volume.Device()); 3092 } 3093 } 3094 3095 3096 status_t 3097 FSCreateNewFolder(const entry_ref* ref) 3098 { 3099 node_ref node; 3100 node.device = ref->device; 3101 node.node = ref->directory; 3102 3103 BDirectory dir(&node); 3104 status_t result = dir.InitCheck(); 3105 if (result != B_OK) 3106 return result; 3107 3108 // ToDo: is that really necessary here? 3109 BString name(ref->name); 3110 FSMakeOriginalName(name, &dir, "-"); 3111 3112 BDirectory newDir; 3113 result = dir.CreateDirectory(name.String(), &newDir); 3114 if (result != B_OK) 3115 return result; 3116 3117 BNodeInfo nodeInfo(&newDir); 3118 nodeInfo.SetType(B_DIR_MIMETYPE); 3119 3120 return result; 3121 } 3122 3123 3124 status_t 3125 FSCreateNewFolderIn(const node_ref* dirNode, entry_ref* newRef, 3126 node_ref* newNode) 3127 { 3128 BDirectory dir(dirNode); 3129 status_t result = dir.InitCheck(); 3130 if (result == B_OK) { 3131 char name[B_FILE_NAME_LENGTH]; 3132 strlcpy(name, B_TRANSLATE("New folder"), sizeof(name)); 3133 3134 int32 fnum = 1; 3135 while (dir.Contains(name)) { 3136 // if base name already exists then add a number 3137 // TODO: move this logic to FSMakeOriginalName 3138 if (++fnum > 9) { 3139 snprintf(name, sizeof(name), B_TRANSLATE("New folder%ld"), 3140 fnum); 3141 } else { 3142 snprintf(name, sizeof(name), B_TRANSLATE("New folder %ld"), 3143 fnum); 3144 } 3145 } 3146 3147 BDirectory newDir; 3148 result = dir.CreateDirectory(name, &newDir); 3149 if (result == B_OK) { 3150 BEntry entry; 3151 newDir.GetEntry(&entry); 3152 entry.GetRef(newRef); 3153 entry.GetNodeRef(newNode); 3154 3155 BNodeInfo nodeInfo(&newDir); 3156 nodeInfo.SetType(B_DIR_MIMETYPE); 3157 3158 // add undo item 3159 NewFolderUndo undo(*newRef); 3160 return B_OK; 3161 } 3162 } 3163 3164 BAlert* alert = new BAlert("", 3165 B_TRANSLATE("Sorry, could not create a new folder."), 3166 B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 3167 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 3168 alert->Go(); 3169 3170 return result; 3171 } 3172 3173 3174 ReadAttrResult 3175 ReadAttr(const BNode* node, const char* hostAttrName, 3176 const char* foreignAttrName, type_code type, off_t offset, void* buffer, 3177 size_t length, void (*swapFunc)(void*), bool isForeign) 3178 { 3179 if (!isForeign && node->ReadAttr(hostAttrName, type, offset, buffer, 3180 length) == (ssize_t)length) { 3181 return kReadAttrNativeOK; 3182 } 3183 3184 // PRINT(("trying %s\n", foreignAttrName)); 3185 // try the other endianness 3186 if (node->ReadAttr(foreignAttrName, type, offset, buffer, length) 3187 != (ssize_t)length) { 3188 return kReadAttrFailed; 3189 } 3190 3191 // PRINT(("got %s\n", foreignAttrName)); 3192 if (!swapFunc) 3193 return kReadAttrForeignOK; 3194 3195 (swapFunc)(buffer); 3196 // run the endian swapper 3197 3198 return kReadAttrForeignOK; 3199 } 3200 3201 3202 ReadAttrResult 3203 GetAttrInfo(const BNode* node, const char* hostAttrName, 3204 const char* foreignAttrName, type_code* type, size_t* size) 3205 { 3206 attr_info info; 3207 3208 if (node->GetAttrInfo(hostAttrName, &info) == B_OK) { 3209 if (type) 3210 *type = info.type; 3211 if (size) 3212 *size = (size_t)info.size; 3213 3214 return kReadAttrNativeOK; 3215 } 3216 3217 if (node->GetAttrInfo(foreignAttrName, &info) == B_OK) { 3218 if (type) 3219 *type = info.type; 3220 if (size) 3221 *size = (size_t)info.size; 3222 3223 return kReadAttrForeignOK; 3224 } 3225 return kReadAttrFailed; 3226 } 3227 3228 3229 status_t 3230 FSGetParentVirtualDirectoryAware(const BEntry& entry, entry_ref& _ref) 3231 { 3232 node_ref nodeRef; 3233 if (entry.GetNodeRef(&nodeRef) == B_OK) { 3234 if (VirtualDirectoryManager* manager 3235 = VirtualDirectoryManager::Instance()) { 3236 AutoLocker<VirtualDirectoryManager> managerLocker(manager); 3237 if (manager->GetParentDirectoryDefinitionFile(nodeRef, _ref, 3238 nodeRef)) { 3239 return B_OK; 3240 } 3241 } 3242 } 3243 3244 status_t error; 3245 BDirectory parent; 3246 BEntry parentEntry; 3247 if ((error = entry.GetParent(&parent)) != B_OK 3248 || (error = parent.GetEntry(&parentEntry)) != B_OK 3249 || (error = parentEntry.GetRef(&_ref)) != B_OK) { 3250 return error; 3251 } 3252 3253 return B_OK; 3254 } 3255 3256 3257 status_t 3258 FSGetParentVirtualDirectoryAware(const BEntry& entry, BEntry& _entry) 3259 { 3260 node_ref nodeRef; 3261 if (entry.GetNodeRef(&nodeRef) == B_OK) { 3262 if (VirtualDirectoryManager* manager 3263 = VirtualDirectoryManager::Instance()) { 3264 AutoLocker<VirtualDirectoryManager> managerLocker(manager); 3265 entry_ref parentRef; 3266 if (manager->GetParentDirectoryDefinitionFile(nodeRef, parentRef, 3267 nodeRef)) { 3268 return _entry.SetTo(&parentRef); 3269 } 3270 } 3271 } 3272 3273 return entry.GetParent(&_entry); 3274 } 3275 3276 3277 status_t 3278 FSGetParentVirtualDirectoryAware(const BEntry& entry, BNode& _node) 3279 { 3280 entry_ref ref; 3281 status_t error = FSGetParentVirtualDirectoryAware(entry, ref); 3282 if (error == B_OK) 3283 error = _node.SetTo(&ref); 3284 3285 return error; 3286 } 3287 3288 3289 // launching code 3290 3291 static status_t 3292 TrackerOpenWith(const BMessage* refs) 3293 { 3294 BMessage clone(*refs); 3295 3296 ASSERT(dynamic_cast<TTracker*>(be_app) != NULL); 3297 ASSERT(clone.what != 0); 3298 3299 clone.AddInt32("launchUsingSelector", 0); 3300 // runs the Open With window 3301 be_app->PostMessage(&clone); 3302 3303 return B_OK; 3304 } 3305 3306 3307 static void 3308 AsynchLaunchBinder(void (*func)(const entry_ref*, const BMessage*, bool on), 3309 const entry_ref* appRef, const BMessage* refs, bool openWithOK) 3310 { 3311 BMessage* task = new BMessage; 3312 task->AddPointer("function", (void*)func); 3313 task->AddMessage("refs", refs); 3314 task->AddBool("openWithOK", openWithOK); 3315 if (appRef != NULL) 3316 task->AddRef("appRef", appRef); 3317 3318 extern BLooper* gLaunchLooper; 3319 gLaunchLooper->PostMessage(task); 3320 } 3321 3322 3323 static bool 3324 SniffIfGeneric(const entry_ref* ref) 3325 { 3326 BNode node(ref); 3327 char type[B_MIME_TYPE_LENGTH]; 3328 BNodeInfo info(&node); 3329 if (info.GetType(type) == B_OK 3330 && strcasecmp(type, B_FILE_MIME_TYPE) != 0) { 3331 // already has a type and it's not octet stream 3332 return false; 3333 } 3334 3335 BPath path(ref); 3336 if (path.Path()) { 3337 // force a mimeset 3338 node.RemoveAttr(kAttrMIMEType); 3339 update_mime_info(path.Path(), 0, 1, 1); 3340 } 3341 3342 return true; 3343 } 3344 3345 3346 static void 3347 SniffIfGeneric(const BMessage* refs) 3348 { 3349 entry_ref ref; 3350 for (int32 index = 0; ; index++) { 3351 if (refs->FindRef("refs", index, &ref) != B_OK) 3352 break; 3353 SniffIfGeneric(&ref); 3354 } 3355 } 3356 3357 3358 static void 3359 _TrackerLaunchAppWithDocuments(const entry_ref* appRef, const BMessage* refs, 3360 bool openWithOK) 3361 { 3362 team_id team; 3363 3364 status_t error = B_ERROR; 3365 BString alertString; 3366 3367 for (int32 mimesetIt = 0; ; mimesetIt++) { 3368 error = be_roster->Launch(appRef, refs, &team); 3369 if (error == B_ALREADY_RUNNING) 3370 // app already running, not really an error 3371 error = B_OK; 3372 3373 if (error == B_OK) 3374 break; 3375 3376 if (mimesetIt > 0) 3377 break; 3378 3379 // failed to open, try mimesetting the refs and launching again 3380 SniffIfGeneric(refs); 3381 } 3382 3383 if (error == B_OK) { 3384 // close possible parent window, if specified 3385 const node_ref* nodeToClose = 0; 3386 ssize_t numBytes; 3387 if (refs != NULL && refs->FindData("nodeRefsToClose", B_RAW_TYPE, 3388 (const void**)&nodeToClose, &numBytes) == B_OK 3389 && nodeToClose != NULL) { 3390 TTracker* tracker = dynamic_cast<TTracker*>(be_app); 3391 if (tracker != NULL) 3392 tracker->CloseParent(*nodeToClose); 3393 } 3394 } else { 3395 alertString.SetTo(B_TRANSLATE("Could not open \"%name\" (%error). ")); 3396 alertString.ReplaceFirst("%name", appRef->name); 3397 alertString.ReplaceFirst("%error", strerror(error)); 3398 if (refs != NULL && openWithOK && error != B_SHUTTING_DOWN) { 3399 alertString << B_TRANSLATE_NOCOLLECT(kFindAlternativeStr); 3400 BAlert* alert = new BAlert("", alertString.String(), 3401 B_TRANSLATE("Cancel"), B_TRANSLATE("Find"), 0, 3402 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 3403 alert->SetShortcut(0, B_ESCAPE); 3404 if (alert->Go() == 1) 3405 error = TrackerOpenWith(refs); 3406 } else { 3407 BAlert* alert = new BAlert("", alertString.String(), 3408 B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, 3409 B_WARNING_ALERT); 3410 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 3411 alert->Go(); 3412 } 3413 } 3414 } 3415 3416 3417 extern "C" char** environ; 3418 3419 3420 static status_t 3421 LoaderErrorDetails(const entry_ref* app, BString &details) 3422 { 3423 BPath path; 3424 BEntry appEntry(app, true); 3425 3426 status_t result = appEntry.GetPath(&path); 3427 if (result != B_OK) 3428 return result; 3429 3430 char* argv[2] = { const_cast<char*>(path.Path()), 0}; 3431 3432 port_id errorPort = create_port(1, "Tracker loader error"); 3433 3434 // count environment variables 3435 int32 envCount = 0; 3436 while (environ[envCount] != NULL) 3437 envCount++; 3438 3439 char** flatArgs = NULL; 3440 size_t flatArgsSize; 3441 result = __flatten_process_args((const char**)argv, 1, 3442 environ, &envCount, argv[0], &flatArgs, &flatArgsSize); 3443 if (result != B_OK) 3444 return result; 3445 3446 result = _kern_load_image(flatArgs, flatArgsSize, 1, envCount, 3447 B_NORMAL_PRIORITY, B_WAIT_TILL_LOADED, errorPort, 0); 3448 if (result == B_OK) { 3449 // we weren't supposed to be able to start the application... 3450 return B_ERROR; 3451 } 3452 3453 // read error message from port and construct details string 3454 3455 ssize_t bufferSize; 3456 3457 do { 3458 bufferSize = port_buffer_size_etc(errorPort, B_RELATIVE_TIMEOUT, 0); 3459 } while (bufferSize == B_INTERRUPTED); 3460 3461 if (bufferSize <= B_OK) { 3462 delete_port(errorPort); 3463 return bufferSize; 3464 } 3465 3466 uint8* buffer = (uint8*)malloc(bufferSize); 3467 if (buffer == NULL) { 3468 delete_port(errorPort); 3469 return B_NO_MEMORY; 3470 } 3471 3472 bufferSize = read_port_etc(errorPort, NULL, buffer, bufferSize, 3473 B_RELATIVE_TIMEOUT, 0); 3474 delete_port(errorPort); 3475 3476 if (bufferSize < B_OK) { 3477 free(buffer); 3478 return bufferSize; 3479 } 3480 3481 BMessage message; 3482 result = message.Unflatten((const char*)buffer); 3483 free(buffer); 3484 3485 if (result != B_OK) 3486 return result; 3487 3488 int32 errorCode = B_ERROR; 3489 result = message.FindInt32("error", &errorCode); 3490 if (result != B_OK) 3491 return result; 3492 3493 const char* detailName = NULL; 3494 switch (errorCode) { 3495 case B_MISSING_LIBRARY: 3496 detailName = "missing library"; 3497 break; 3498 3499 case B_MISSING_SYMBOL: 3500 detailName = "missing symbol"; 3501 break; 3502 } 3503 3504 if (detailName == NULL) 3505 return B_ERROR; 3506 3507 const char* detail; 3508 for (int32 i = 0; message.FindString(detailName, i, &detail) == B_OK; 3509 i++) { 3510 if (i > 0) 3511 details += ", "; 3512 details += detail; 3513 } 3514 3515 return B_OK; 3516 } 3517 3518 3519 static void 3520 _TrackerLaunchDocuments(const entry_ref*, const BMessage* refs, 3521 bool openWithOK) 3522 { 3523 if (refs == NULL) 3524 return; 3525 3526 BMessage copyOfRefs(*refs); 3527 3528 entry_ref documentRef; 3529 if (copyOfRefs.FindRef("refs", &documentRef) != B_OK) { 3530 // nothing to launch, we are done 3531 return; 3532 } 3533 3534 status_t error = B_ERROR; 3535 entry_ref app; 3536 BMessage* refsToPass = NULL; 3537 BString alertString; 3538 const char* alternative = 0; 3539 3540 for (int32 mimesetIt = 0; ; mimesetIt++) { 3541 alertString = ""; 3542 error = be_roster->FindApp(&documentRef, &app); 3543 3544 if (error != B_OK && mimesetIt == 0) { 3545 SniffIfGeneric(©OfRefs); 3546 continue; 3547 } 3548 3549 if (error != B_OK) { 3550 alertString.SetTo(B_TRANSLATE("Could not find an application to " 3551 "open \"%name\" (%error). ")); 3552 alertString.ReplaceFirst("%name", documentRef.name); 3553 alertString.ReplaceFirst("%error", strerror(error)); 3554 if (openWithOK) 3555 alternative = B_TRANSLATE_NOCOLLECT(kFindApplicationStr); 3556 3557 break; 3558 } else { 3559 BEntry appEntry(&app, true); 3560 for (int32 index = 0;;) { 3561 // remove the app itself from the refs received so we don't 3562 // try to open ourselves 3563 entry_ref ref; 3564 if (copyOfRefs.FindRef("refs", index, &ref) != B_OK) 3565 break; 3566 3567 // deal with symlinks properly 3568 BEntry documentEntry(&ref, true); 3569 if (appEntry == documentEntry) { 3570 PRINT(("stripping %s, app %s \n", ref.name, app.name)); 3571 copyOfRefs.RemoveData("refs", index); 3572 } else { 3573 PRINT(("leaving %s, app %s \n", ref.name, app.name)); 3574 index++; 3575 } 3576 } 3577 3578 refsToPass = CountRefs(©OfRefs) > 0 ? ©OfRefs: 0; 3579 team_id team; 3580 error = be_roster->Launch(&app, refsToPass, &team); 3581 if (error == B_ALREADY_RUNNING) 3582 // app already running, not really an error 3583 error = B_OK; 3584 if (error == B_OK || mimesetIt != 0) 3585 break; 3586 3587 SniffIfGeneric(©OfRefs); 3588 } 3589 } 3590 3591 if (error != B_OK && alertString.Length() == 0) { 3592 BString loaderErrorString; 3593 bool openedDocuments = true; 3594 3595 if (!refsToPass) { 3596 // we just double clicked the app itself, do not offer to 3597 // find a handling app 3598 openWithOK = false; 3599 openedDocuments = false; 3600 } 3601 if (error == B_UNKNOWN_EXECUTABLE && !refsToPass) { 3602 // We know it's an executable, but something unsupported 3603 alertString.SetTo(B_TRANSLATE("\"%name\" is an unsupported " 3604 "executable.")); 3605 alertString.ReplaceFirst("%name", app.name); 3606 } else if (error == B_LEGACY_EXECUTABLE && !refsToPass) { 3607 // For the moment, this marks an old R3 binary, we may want to 3608 // extend it to gcc2 binaries someday post R1 3609 alertString.SetTo(B_TRANSLATE("\"%name\" is a legacy executable. " 3610 "Please obtain an updated version or recompile " 3611 "the application.")); 3612 alertString.ReplaceFirst("%name", app.name); 3613 } else if (error == B_LAUNCH_FAILED_EXECUTABLE && !refsToPass) { 3614 alertString.SetTo(B_TRANSLATE("Could not open \"%name\". " 3615 "The file is mistakenly marked as executable. ")); 3616 alertString.ReplaceFirst("%name", app.name); 3617 3618 if (!openWithOK) { 3619 // offer the possibility to change the permissions 3620 3621 alertString << B_TRANSLATE("\nShould this be fixed?"); 3622 BAlert* alert = new BAlert("", alertString.String(), 3623 B_TRANSLATE("Cancel"), B_TRANSLATE("Proceed"), 0, 3624 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 3625 alert->SetShortcut(0, B_ESCAPE); 3626 if (alert->Go() == 1) { 3627 BEntry entry(&documentRef); 3628 mode_t permissions; 3629 3630 error = entry.GetPermissions(&permissions); 3631 if (error == B_OK) { 3632 error = entry.SetPermissions(permissions 3633 & ~(S_IXUSR | S_IXGRP | S_IXOTH)); 3634 } 3635 if (error == B_OK) { 3636 // we updated the permissions, so let's try again 3637 _TrackerLaunchDocuments(NULL, refs, false); 3638 return; 3639 } else { 3640 alertString.SetTo(B_TRANSLATE("Could not update " 3641 "permissions of file \"%name\". %error")); 3642 alertString.ReplaceFirst("%name", app.name); 3643 alertString.ReplaceFirst("%error", strerror(error)); 3644 } 3645 } else 3646 return; 3647 } 3648 3649 alternative = B_TRANSLATE_NOCOLLECT(kFindApplicationStr); 3650 } else if (error == B_LAUNCH_FAILED_APP_IN_TRASH) { 3651 alertString.SetTo(B_TRANSLATE("Could not open \"%document\" " 3652 "because application \"%app\" is in the Trash. ")); 3653 alertString.ReplaceFirst("%document", documentRef.name); 3654 alertString.ReplaceFirst("%app", app.name); 3655 alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr); 3656 } else if (error == B_LAUNCH_FAILED_APP_NOT_FOUND) { 3657 alertString.SetTo( 3658 B_TRANSLATE("Could not open \"%name\" (%error). ")); 3659 alertString.ReplaceFirst("%name", documentRef.name); 3660 alertString.ReplaceFirst("%error", strerror(error)); 3661 alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr); 3662 } else if (error == B_MISSING_SYMBOL 3663 && LoaderErrorDetails(&app, loaderErrorString) == B_OK) { 3664 if (openedDocuments) { 3665 alertString.SetTo(B_TRANSLATE("Could not open \"%document\" " 3666 "with application \"%app\" (Missing symbol: %symbol). " 3667 "\n")); 3668 alertString.ReplaceFirst("%document", documentRef.name); 3669 alertString.ReplaceFirst("%app", app.name); 3670 alertString.ReplaceFirst("%symbol", 3671 loaderErrorString.String()); 3672 } else { 3673 alertString.SetTo(B_TRANSLATE("Could not open \"%document\" " 3674 "(Missing symbol: %symbol). \n")); 3675 alertString.ReplaceFirst("%document", documentRef.name); 3676 alertString.ReplaceFirst("%symbol", 3677 loaderErrorString.String()); 3678 } 3679 alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr); 3680 } else if (error == B_MISSING_LIBRARY 3681 && LoaderErrorDetails(&app, loaderErrorString) == B_OK) { 3682 if (openedDocuments) { 3683 alertString.SetTo(B_TRANSLATE("Could not open \"%document\" " 3684 "with application \"%app\" (Missing libraries: %library). " 3685 "\n")); 3686 alertString.ReplaceFirst("%document", documentRef.name); 3687 alertString.ReplaceFirst("%app", app.name); 3688 alertString.ReplaceFirst("%library", 3689 loaderErrorString.String()); 3690 } else { 3691 alertString.SetTo(B_TRANSLATE("Could not open \"%document\" " 3692 "(Missing libraries: %library). \n")); 3693 alertString.ReplaceFirst("%document", documentRef.name); 3694 alertString.ReplaceFirst("%library", 3695 loaderErrorString.String()); 3696 } 3697 alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr); 3698 } else { 3699 alertString.SetTo(B_TRANSLATE("Could not open \"%document\" with " 3700 "application \"%app\" (%error). ")); 3701 alertString.ReplaceFirst("%document", documentRef.name); 3702 alertString.ReplaceFirst("%app", app.name); 3703 alertString.ReplaceFirst("%error", strerror(error)); 3704 alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr); 3705 } 3706 } 3707 3708 if (error != B_OK) { 3709 if (openWithOK) { 3710 ASSERT(alternative); 3711 alertString << alternative; 3712 BAlert* alert = new BAlert("", alertString.String(), 3713 B_TRANSLATE("Cancel"), B_TRANSLATE("Find"), 0, 3714 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 3715 alert->SetShortcut(0, B_ESCAPE); 3716 if (alert->Go() == 1) 3717 error = TrackerOpenWith(refs); 3718 } else { 3719 BAlert* alert = new BAlert("", alertString.String(), 3720 B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, 3721 B_WARNING_ALERT); 3722 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 3723 alert->Go(); 3724 } 3725 } 3726 } 3727 3728 // the following three calls don't return any reasonable error codes, 3729 // should fix that, making them void 3730 3731 status_t 3732 TrackerLaunch(const entry_ref* appRef, const BMessage* refs, bool async, 3733 bool openWithOK) 3734 { 3735 if (!async) 3736 _TrackerLaunchAppWithDocuments(appRef, refs, openWithOK); 3737 else { 3738 AsynchLaunchBinder(&_TrackerLaunchAppWithDocuments, appRef, refs, 3739 openWithOK); 3740 } 3741 3742 return B_OK; 3743 } 3744 3745 status_t 3746 TrackerLaunch(const entry_ref* appRef, bool async) 3747 { 3748 if (!async) 3749 _TrackerLaunchAppWithDocuments(appRef, NULL, false); 3750 else 3751 AsynchLaunchBinder(&_TrackerLaunchAppWithDocuments, appRef, 0, false); 3752 3753 return B_OK; 3754 } 3755 3756 status_t 3757 TrackerLaunch(const BMessage* refs, bool async, bool openWithOK) 3758 { 3759 if (!async) 3760 _TrackerLaunchDocuments(NULL, refs, openWithOK); 3761 else 3762 AsynchLaunchBinder(&_TrackerLaunchDocuments, NULL, refs, openWithOK); 3763 3764 return B_OK; 3765 } 3766 3767 3768 // external launch calls; need to be robust, work if Tracker is not running 3769 3770 3771 #if !B_BEOS_VERSION_DANO 3772 _IMPEXP_TRACKER 3773 #endif 3774 status_t 3775 FSLaunchItem(const entry_ref* application, const BMessage* refsReceived, 3776 bool async, bool openWithOK) 3777 { 3778 return TrackerLaunch(application, refsReceived, async, openWithOK); 3779 } 3780 3781 3782 #if !B_BEOS_VERSION_DANO 3783 _IMPEXP_TRACKER 3784 #endif 3785 status_t 3786 FSOpenWith(BMessage* listOfRefs) 3787 { 3788 status_t result = B_ERROR; 3789 listOfRefs->what = B_REFS_RECEIVED; 3790 3791 if (dynamic_cast<TTracker*>(be_app) != NULL) 3792 result = TrackerOpenWith(listOfRefs); 3793 else 3794 ASSERT(!"not yet implemented"); 3795 3796 return result; 3797 } 3798 3799 3800 // legacy calls, need for compatibility 3801 3802 3803 void 3804 FSOpenWithDocuments(const entry_ref* executable, BMessage* documents) 3805 { 3806 TrackerLaunch(executable, documents, true); 3807 delete documents; 3808 } 3809 3810 3811 status_t 3812 FSLaunchUsing(const entry_ref* ref, BMessage* listOfRefs) 3813 { 3814 BMessage temp(B_REFS_RECEIVED); 3815 if (listOfRefs == NULL) { 3816 ASSERT(ref != NULL); 3817 temp.AddRef("refs", ref); 3818 listOfRefs = &temp; 3819 } 3820 FSOpenWith(listOfRefs); 3821 3822 return B_OK; 3823 } 3824 3825 3826 status_t 3827 FSLaunchItem(const entry_ref* appRef, BMessage* refs, int32, bool async) 3828 { 3829 if (refs != NULL) 3830 refs->what = B_REFS_RECEIVED; 3831 3832 status_t result = TrackerLaunch(appRef, refs, async, true); 3833 delete refs; 3834 3835 return result; 3836 } 3837 3838 3839 void 3840 FSLaunchItem(const entry_ref* appRef, BMessage* refs, int32 workspace) 3841 { 3842 FSLaunchItem(appRef, refs, workspace, true); 3843 } 3844 3845 3846 // Get the original path of an entry in the trash 3847 status_t 3848 FSGetOriginalPath(BEntry* entry, BPath* result) 3849 { 3850 status_t err; 3851 entry_ref ref; 3852 err = entry->GetRef(&ref); 3853 if (err != B_OK) 3854 return err; 3855 3856 // Only call the routine for entries in the trash 3857 if (!FSInTrashDir(&ref)) 3858 return B_ERROR; 3859 3860 BNode node(entry); 3861 BString originalPath; 3862 if (node.ReadAttrString(kAttrOriginalPath, &originalPath) == B_OK) { 3863 // We're in luck, the entry has the original path in an attribute 3864 err = result->SetTo(originalPath.String()); 3865 return err; 3866 } 3867 3868 // Iterate the parent directories to find one with 3869 // the original path attribute 3870 BEntry parent(*entry); 3871 err = parent.InitCheck(); 3872 if (err != B_OK) 3873 return err; 3874 3875 // walk up the directory structure until we find a node 3876 // with original path attribute 3877 do { 3878 // move to the parent of this node 3879 err = parent.GetParent(&parent); 3880 if (err != B_OK) 3881 return err; 3882 3883 // return if we are at the root of the trash 3884 if (FSIsTrashDir(&parent)) 3885 return B_ENTRY_NOT_FOUND; 3886 3887 // get the parent as a node 3888 err = node.SetTo(&parent); 3889 if (err != B_OK) 3890 return err; 3891 } while (node.ReadAttrString(kAttrOriginalPath, &originalPath) != B_OK); 3892 3893 // Found the attribute, figure out there this file 3894 // used to live, based on the successfully-read attribute 3895 err = result->SetTo(originalPath.String()); 3896 if (err != B_OK) 3897 return err; 3898 3899 BPath path, pathParent; 3900 err = parent.GetPath(&pathParent); 3901 if (err != B_OK) 3902 return err; 3903 3904 err = entry->GetPath(&path); 3905 if (err != B_OK) 3906 return err; 3907 3908 result->Append(path.Path() + strlen(pathParent.Path()) + 1); 3909 // compute the new path by appending the offset of 3910 // the item we are locating, to the original path 3911 // of the parent 3912 3913 return B_OK; 3914 } 3915 3916 3917 directory_which 3918 WellKnowEntryList::Match(const node_ref* node) 3919 { 3920 const WellKnownEntry* result = MatchEntry(node); 3921 if (result != NULL) 3922 return result->which; 3923 3924 return (directory_which)-1; 3925 } 3926 3927 3928 const WellKnowEntryList::WellKnownEntry* 3929 WellKnowEntryList::MatchEntry(const node_ref* node) 3930 { 3931 if (self == NULL) 3932 self = new WellKnowEntryList(); 3933 3934 return self->MatchEntryCommon(node); 3935 } 3936 3937 3938 const WellKnowEntryList::WellKnownEntry* 3939 WellKnowEntryList::MatchEntryCommon(const node_ref* node) 3940 { 3941 uint32 count = entries.size(); 3942 for (uint32 index = 0; index < count; index++) { 3943 if (*node == entries[index].node) 3944 return &entries[index]; 3945 } 3946 3947 return NULL; 3948 } 3949 3950 3951 void 3952 WellKnowEntryList::Quit() 3953 { 3954 delete self; 3955 self = NULL; 3956 } 3957 3958 3959 void 3960 WellKnowEntryList::AddOne(directory_which which, const char* name) 3961 { 3962 BPath path; 3963 if (find_directory(which, &path, true) != B_OK) 3964 return; 3965 3966 BEntry entry(path.Path(), true); 3967 node_ref node; 3968 if (entry.GetNodeRef(&node) != B_OK) 3969 return; 3970 3971 entries.push_back(WellKnownEntry(&node, which, name)); 3972 } 3973 3974 3975 void 3976 WellKnowEntryList::AddOne(directory_which which, directory_which base, 3977 const char* extra, const char* name) 3978 { 3979 BPath path; 3980 if (find_directory(base, &path, true) != B_OK) 3981 return; 3982 3983 path.Append(extra); 3984 BEntry entry(path.Path(), true); 3985 node_ref node; 3986 if (entry.GetNodeRef(&node) != B_OK) 3987 return; 3988 3989 entries.push_back(WellKnownEntry(&node, which, name)); 3990 } 3991 3992 3993 void 3994 WellKnowEntryList::AddOne(directory_which which, const char* path, 3995 const char* name) 3996 { 3997 BEntry entry(path, true); 3998 node_ref node; 3999 if (entry.GetNodeRef(&node) != B_OK) 4000 return; 4001 4002 entries.push_back(WellKnownEntry(&node, which, name)); 4003 } 4004 4005 4006 WellKnowEntryList::WellKnowEntryList() 4007 { 4008 AddOne(B_SYSTEM_DIRECTORY, "system"); 4009 AddOne((directory_which)B_BOOT_DISK, "/boot", "boot"); 4010 AddOne(B_USER_DIRECTORY, "home"); 4011 4012 AddOne(B_BEOS_FONTS_DIRECTORY, "fonts"); 4013 AddOne(B_USER_FONTS_DIRECTORY, "fonts"); 4014 4015 AddOne(B_BEOS_APPS_DIRECTORY, "apps"); 4016 AddOne(B_APPS_DIRECTORY, "apps"); 4017 AddOne((directory_which)B_USER_DESKBAR_APPS_DIRECTORY, 4018 B_USER_DESKBAR_DIRECTORY, "Applications", "apps"); 4019 4020 AddOne(B_BEOS_PREFERENCES_DIRECTORY, "preferences"); 4021 AddOne(B_PREFERENCES_DIRECTORY, "preferences"); 4022 AddOne((directory_which)B_USER_DESKBAR_PREFERENCES_DIRECTORY, 4023 B_USER_DESKBAR_DIRECTORY, "Preferences", "preferences"); 4024 4025 AddOne((directory_which)B_USER_MAIL_DIRECTORY, B_USER_DIRECTORY, "mail", 4026 "mail"); 4027 4028 AddOne((directory_which)B_USER_QUERIES_DIRECTORY, B_USER_DIRECTORY, 4029 "queries", "queries"); 4030 4031 AddOne(B_SYSTEM_DEVELOP_DIRECTORY, "develop"); 4032 AddOne((directory_which)B_USER_DESKBAR_DEVELOP_DIRECTORY, 4033 B_USER_DESKBAR_DIRECTORY, "Development", "develop"); 4034 4035 AddOne(B_USER_CONFIG_DIRECTORY, "config"); 4036 4037 AddOne((directory_which)B_USER_PEOPLE_DIRECTORY, B_USER_DIRECTORY, 4038 "people", "people"); 4039 4040 AddOne((directory_which)B_USER_DOWNLOADS_DIRECTORY, B_USER_DIRECTORY, 4041 "downloads", "downloads"); 4042 } 4043 4044 WellKnowEntryList* WellKnowEntryList::self = NULL; 4045 4046 } // namespace BPrivate 4047