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