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 if (result < 0) 1378 throw (status_t)result; 1379 throw (status_t)B_ERROR; 1380 } 1381 1382 result = destFile.Sync(); 1383 if (result != B_OK) 1384 throw (status_t)result; 1385 1386 loopControl->UpdateStatus(NULL, ref, bytes - updateBytes, 1387 true); 1388 } else if (bytes < 0) { 1389 // read error 1390 throw (status_t)bytes; 1391 } else { 1392 // we are done 1393 break; 1394 } 1395 } 1396 1397 CopyAttributes(loopControl, &srcFile, &destFile, buffer, bufsize); 1398 } catch (...) { 1399 delete[] buffer; 1400 throw; 1401 } 1402 1403 destFile.SetPermissions(srcStat->st_mode); 1404 destFile.SetOwner(srcStat->st_uid); 1405 destFile.SetGroup(srcStat->st_gid); 1406 destFile.SetModificationTime(srcStat->st_mtime); 1407 destFile.SetCreationTime(srcStat->st_crtime); 1408 1409 delete[] buffer; 1410 1411 if (!loopControl->ChecksumFile(&ref)) { 1412 // File no good. Remove and quit. 1413 destFile.Unset(); 1414 1415 BEntry destEntry; 1416 if (destDir->FindEntry(destName, &destEntry) == B_OK) 1417 destEntry.Remove(); 1418 throw (status_t)kUserCanceled; 1419 } 1420 } 1421 1422 1423 void 1424 CopyAttributes(CopyLoopControl* control, BNode* srcNode, BNode* destNode, 1425 void* buffer, size_t bufsize) 1426 { 1427 // ToDo: 1428 // Add error checking 1429 // prior to coyping attributes, make sure indices are installed 1430 1431 // When calling CopyAttributes on files, have to make sure destNode 1432 // is a BFile opened R/W 1433 1434 srcNode->RewindAttrs(); 1435 char name[256]; 1436 while (srcNode->GetNextAttrName(name) == B_OK) { 1437 // Check to see if this attribute should be skipped. 1438 if (control->SkipAttribute(name)) 1439 continue; 1440 1441 attr_info info; 1442 if (srcNode->GetAttrInfo(name, &info) != B_OK) 1443 continue; 1444 1445 // Check to see if this attribute should be overwritten when it 1446 // already exists. 1447 if (control->PreserveAttribute(name)) { 1448 attr_info dest_info; 1449 if (destNode->GetAttrInfo(name, &dest_info) == B_OK) 1450 continue; 1451 } 1452 1453 // Special case for a size 0 attribute. It wouldn't be written at all 1454 // otherwise. 1455 if (info.size == 0) 1456 destNode->WriteAttr(name, info.type, 0, buffer, 0); 1457 1458 ssize_t bytes; 1459 ssize_t numToRead = (ssize_t)info.size; 1460 for (off_t offset = 0; numToRead > 0; offset += bytes) { 1461 size_t chunkSize = (size_t)numToRead; 1462 if (chunkSize > bufsize) 1463 chunkSize = bufsize; 1464 1465 bytes = srcNode->ReadAttr(name, info.type, offset, 1466 buffer, chunkSize); 1467 1468 if (bytes <= 0) 1469 break; 1470 1471 destNode->WriteAttr(name, info.type, offset, buffer, 1472 (size_t)bytes); 1473 1474 numToRead -= bytes; 1475 } 1476 } 1477 } 1478 1479 1480 static void 1481 CopyFolder(BEntry* srcEntry, BDirectory* destDir, 1482 CopyLoopControl* loopControl, BPoint* loc, bool makeOriginalName, 1483 Undo &undo, bool removeSource = false) 1484 { 1485 BDirectory newDir; 1486 BEntry entry; 1487 status_t err = B_OK; 1488 bool createDirectory = true; 1489 BEntry existingEntry; 1490 1491 if (loopControl->SkipEntry(srcEntry, false)) 1492 return; 1493 1494 entry_ref ref; 1495 srcEntry->GetRef(&ref); 1496 1497 char destName[B_FILE_NAME_LENGTH]; 1498 strlcpy(destName, ref.name, sizeof(destName)); 1499 1500 loopControl->UpdateStatus(ref.name, ref, 1024, true); 1501 1502 if (makeOriginalName) { 1503 BString suffix(" "); 1504 suffix << B_TRANSLATE_COMMENT("copy", "filename copy"), 1505 FSMakeOriginalName(destName, destDir, suffix.String()); 1506 undo.UpdateEntry(srcEntry, destName); 1507 } 1508 1509 if (destDir->FindEntry(destName, &existingEntry) == B_OK) { 1510 // some entry with a conflicting name is already present in destDir 1511 // decide what to do about it 1512 bool isDirectory = existingEntry.IsDirectory(); 1513 1514 switch (loopControl->OverwriteOnConflict(srcEntry, destName, destDir, 1515 true, isDirectory)) { 1516 case TrackerCopyLoopControl::kSkip: 1517 // we are about to ignore this entire directory 1518 return; 1519 1520 1521 case TrackerCopyLoopControl::kReplace: 1522 if (!isDirectory) { 1523 // conflicting with a file or symbolic link, remove entry 1524 ThrowOnError(existingEntry.Remove()); 1525 break; 1526 } 1527 // fall through if directory, do not replace. 1528 case TrackerCopyLoopControl::kMerge: 1529 ASSERT(isDirectory); 1530 // do not create a new directory, use the current one 1531 newDir.SetTo(&existingEntry); 1532 createDirectory = false; 1533 break; 1534 } 1535 } 1536 1537 // loop through everything in src folder and copy it to new folder 1538 BDirectory srcDir(srcEntry); 1539 srcDir.Rewind(); 1540 1541 // create a new folder inside of destination folder 1542 if (createDirectory) { 1543 err = destDir->CreateDirectory(destName, &newDir); 1544 #ifdef _SILENTLY_CORRECT_FILE_NAMES 1545 if (err == B_BAD_VALUE) { 1546 // check if it's an invalid name on a FAT32 file system 1547 if (CreateFileSystemCompatibleName(destDir, destName)) 1548 err = destDir->CreateDirectory(destName, &newDir); 1549 } 1550 #endif 1551 if (err != B_OK) { 1552 if (!loopControl->FileError(B_TRANSLATE_NOCOLLECT( 1553 kFolderErrorString), destName, err, true)) { 1554 throw err; 1555 } 1556 1557 // will allow rest of copy to continue 1558 return; 1559 } 1560 } 1561 1562 char* buffer; 1563 if (createDirectory && err == B_OK 1564 && (buffer = (char*)malloc(32768)) != 0) { 1565 CopyAttributes(loopControl, &srcDir, &newDir, buffer, 32768); 1566 // don't copy original pose location if new location passed 1567 free(buffer); 1568 } 1569 1570 StatStruct statbuf; 1571 srcDir.GetStat(&statbuf); 1572 dev_t sourceDeviceID = statbuf.st_dev; 1573 1574 // copy or write new pose location 1575 node_ref destNodeRef; 1576 destDir->GetNodeRef(&destNodeRef); 1577 SetUpPoseLocation(ref.directory, destNodeRef.node, &srcDir, 1578 &newDir, loc); 1579 1580 while (srcDir.GetNextEntry(&entry) == B_OK) { 1581 1582 if (loopControl->CheckUserCanceled()) 1583 throw (status_t)kUserCanceled; 1584 1585 entry.GetStat(&statbuf); 1586 1587 if (S_ISDIR(statbuf.st_mode)) { 1588 1589 // entry is a mount point, do not copy it 1590 if (statbuf.st_dev != sourceDeviceID) { 1591 PRINT(("Avoiding mount point %" B_PRIdDEV ", %" B_PRIdDEV "\n", 1592 statbuf.st_dev, sourceDeviceID)); 1593 continue; 1594 } 1595 1596 CopyFolder(&entry, &newDir, loopControl, 0, false, undo, 1597 removeSource); 1598 if (removeSource) 1599 FSDeleteFolder(&entry, loopControl, true, true, false); 1600 } else if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) { 1601 CopyFile(&entry, &statbuf, &newDir, loopControl, 0, false, undo); 1602 if (removeSource) 1603 entry.Remove(); 1604 } else { 1605 // Ignore special files 1606 } 1607 } 1608 if (removeSource) 1609 srcEntry->Remove(); 1610 else 1611 srcEntry->Unset(); 1612 } 1613 1614 1615 status_t 1616 RecursiveMove(BEntry* entry, BDirectory* destDir, 1617 CopyLoopControl* loopControl) 1618 { 1619 const char* name = entry->Name(); 1620 1621 if (destDir->Contains(name)) { 1622 BPath path (destDir, name); 1623 BDirectory subDir (path.Path()); 1624 entry_ref ref; 1625 entry->GetRef(&ref); 1626 BDirectory source(&ref); 1627 if (source.InitCheck() == B_OK) { 1628 source.Rewind(); 1629 BEntry current; 1630 while (source.GetNextEntry(¤t) == B_OK) { 1631 if (current.IsDirectory()) { 1632 RecursiveMove(¤t, &subDir, loopControl); 1633 current.Remove(); 1634 } else { 1635 name = current.Name(); 1636 if (loopControl->OverwriteOnConflict(¤t, name, 1637 &subDir, true, false) 1638 != TrackerCopyLoopControl::kSkip) { 1639 MoveError::FailOnError(current.MoveTo(&subDir, 1640 NULL, true)); 1641 } 1642 } 1643 } 1644 } 1645 entry->Remove(); 1646 } else 1647 MoveError::FailOnError(entry->MoveTo(destDir)); 1648 1649 return B_OK; 1650 } 1651 1652 status_t 1653 MoveItem(BEntry* entry, BDirectory* destDir, BPoint* loc, uint32 moveMode, 1654 const char* newName, Undo &undo, CopyLoopControl* loopControl) 1655 { 1656 entry_ref ref; 1657 try { 1658 node_ref destNode; 1659 StatStruct statbuf; 1660 MoveError::FailOnError(entry->GetStat(&statbuf)); 1661 MoveError::FailOnError(entry->GetRef(&ref)); 1662 MoveError::FailOnError(destDir->GetNodeRef(&destNode)); 1663 1664 if (moveMode == kCreateLink || moveMode == kCreateRelativeLink) { 1665 PoseInfo poseInfo; 1666 char name[B_FILE_NAME_LENGTH]; 1667 strlcpy(name, ref.name, sizeof(name)); 1668 1669 BSymLink link; 1670 BString suffix(" "); 1671 suffix << B_TRANSLATE_COMMENT("link", "filename link"), 1672 FSMakeOriginalName(name, destDir, suffix.String()); 1673 undo.UpdateEntry(entry, name); 1674 1675 BPath path; 1676 entry->GetPath(&path); 1677 if (loc && loc != (BPoint*)-1) { 1678 poseInfo.fInvisible = false; 1679 poseInfo.fInitedDirectory = destNode.node; 1680 poseInfo.fLocation = *loc; 1681 } 1682 1683 status_t err = B_ERROR; 1684 1685 if (moveMode == kCreateRelativeLink) { 1686 if (statbuf.st_dev == destNode.device) { 1687 // relative link only works on the same device 1688 char oldwd[B_PATH_NAME_LENGTH]; 1689 getcwd(oldwd, B_PATH_NAME_LENGTH); 1690 1691 BEntry destEntry; 1692 destDir -> GetEntry(&destEntry); 1693 BPath destPath; 1694 destEntry.GetPath(&destPath); 1695 1696 chdir(destPath.Path()); 1697 // change working dir to target dir 1698 1699 BString destString(destPath.Path()); 1700 destString.Append("/"); 1701 1702 BString srcString(path.Path()); 1703 srcString.RemoveLast(path.Leaf()); 1704 1705 // find index while paths are the same 1706 1707 const char* src = srcString.String(); 1708 const char* dest = destString.String(); 1709 const char* lastFolderSrc = src; 1710 const char* lastFolderDest = dest; 1711 1712 while (*src && *dest && *src == *dest) { 1713 ++src; 1714 if (*dest++ == '/') { 1715 lastFolderSrc = src; 1716 lastFolderDest = dest; 1717 } 1718 } 1719 src = lastFolderSrc; 1720 dest = lastFolderDest; 1721 1722 BString source; 1723 if (*dest == '\0' && *src != '\0') { 1724 // source is deeper in the same tree than the target 1725 source.Append(src); 1726 } else if (*dest != '\0') { 1727 // target is deeper in the same tree than the source 1728 while (*dest) { 1729 if (*dest == '/') 1730 source.Prepend("../"); 1731 ++dest; 1732 } 1733 source.Append(src); 1734 } 1735 1736 // else source and target are in the same dir 1737 1738 source.Append(path.Leaf()); 1739 err = destDir->CreateSymLink(name, source.String(), 1740 &link); 1741 1742 chdir(oldwd); 1743 // change working dir back to original 1744 } else 1745 moveMode = kCreateLink; 1746 // fall back to absolute link mode 1747 } 1748 1749 if (moveMode == kCreateLink) 1750 err = destDir->CreateSymLink(name, path.Path(), &link); 1751 1752 if (err == B_UNSUPPORTED) { 1753 throw FailWithAlert(err, 1754 B_TRANSLATE("The target disk does not support " 1755 "creating links."), NULL); 1756 } 1757 1758 FailWithAlert::FailOnError(err, 1759 B_TRANSLATE("Error creating link to \"%name\"."), 1760 ref.name); 1761 1762 if (loc && loc != (BPoint*)-1) { 1763 link.WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, &poseInfo, 1764 sizeof(PoseInfo)); 1765 } 1766 1767 BNodeInfo nodeInfo(&link); 1768 nodeInfo.SetType(B_LINK_MIMETYPE); 1769 return B_OK; 1770 } 1771 1772 // if move is on same volume don't copy 1773 if (statbuf.st_dev == destNode.device && moveMode != kCopySelectionTo 1774 && moveMode != kDuplicateSelection) { 1775 1776 // for "Move" the size for status is always 1 - since file 1777 // size is irrelevant when simply moving to a new folder 1778 loopControl->UpdateStatus(ref.name, ref, 1); 1779 if (entry->IsDirectory()) 1780 return RecursiveMove(entry, destDir, loopControl); 1781 1782 MoveError::FailOnError(entry->MoveTo(destDir, newName)); 1783 } else { 1784 bool makeOriginalName = (moveMode == kDuplicateSelection); 1785 if (S_ISDIR(statbuf.st_mode)) { 1786 CopyFolder(entry, destDir, loopControl, loc, makeOriginalName, 1787 undo, moveMode == kMoveSelectionTo); 1788 } else { 1789 CopyFile(entry, &statbuf, destDir, loopControl, loc, 1790 makeOriginalName, undo); 1791 if (moveMode == kMoveSelectionTo) 1792 entry->Remove(); 1793 } 1794 } 1795 } catch (status_t error) { 1796 // no alert, was already taken care of before 1797 return error; 1798 } catch (MoveError& error) { 1799 BString errorString(B_TRANSLATE("Error moving \"%name\"")); 1800 errorString.ReplaceFirst("%name", ref.name); 1801 BAlert* alert = new BAlert("", errorString.String(), B_TRANSLATE("OK"), 1802 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 1803 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1804 alert->Go(); 1805 return error.fError; 1806 } catch (FailWithAlert& error) { 1807 BString buffer(error.fString); 1808 if (error.fName != NULL) 1809 buffer.ReplaceFirst("%name", error.fName); 1810 else 1811 buffer << error.fString; 1812 1813 BAlert* alert = new BAlert("", buffer.String(), B_TRANSLATE("OK"), 1814 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 1815 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1816 alert->Go(); 1817 1818 return error.fError; 1819 } 1820 1821 return B_OK; 1822 } 1823 1824 1825 void 1826 FSDuplicate(BObjectList<entry_ref>* srcList, BList* pointList) 1827 { 1828 LaunchInNewThread("DupTask", B_NORMAL_PRIORITY, MoveTask, srcList, 1829 (BEntry*)NULL, pointList, kDuplicateSelection); 1830 } 1831 1832 1833 #if 0 1834 status_t 1835 FSCopyFolder(BEntry* srcEntry, BDirectory* destDir, 1836 CopyLoopControl* loopControl, BPoint* loc, bool makeOriginalName) 1837 { 1838 try 1839 CopyFolder(srcEntry, destDir, loopControl, loc, makeOriginalName); 1840 catch (status_t error) { 1841 return error; 1842 1843 return B_OK; 1844 } 1845 #endif 1846 1847 1848 status_t 1849 FSCopyAttributesAndStats(BNode* srcNode, BNode* destNode, bool copyTimes) 1850 { 1851 char* buffer = new char[1024]; 1852 1853 // copy the attributes 1854 srcNode->RewindAttrs(); 1855 char name[256]; 1856 while (srcNode->GetNextAttrName(name) == B_OK) { 1857 attr_info info; 1858 if (srcNode->GetAttrInfo(name, &info) != B_OK) 1859 continue; 1860 1861 attr_info dest_info; 1862 if (destNode->GetAttrInfo(name, &dest_info) == B_OK) 1863 continue; 1864 1865 ssize_t bytes; 1866 ssize_t numToRead = (ssize_t)info.size; 1867 for (off_t offset = 0; numToRead > 0; offset += bytes) { 1868 size_t chunkSize = (size_t)numToRead; 1869 if (chunkSize > 1024) 1870 chunkSize = 1024; 1871 1872 bytes = srcNode->ReadAttr(name, info.type, offset, buffer, 1873 chunkSize); 1874 1875 if (bytes <= 0) 1876 break; 1877 1878 destNode->WriteAttr(name, info.type, offset, buffer, 1879 (size_t)bytes); 1880 1881 numToRead -= bytes; 1882 } 1883 } 1884 delete[] buffer; 1885 1886 // copy the file stats 1887 struct stat srcStat; 1888 srcNode->GetStat(&srcStat); 1889 destNode->SetPermissions(srcStat.st_mode); 1890 destNode->SetOwner(srcStat.st_uid); 1891 destNode->SetGroup(srcStat.st_gid); 1892 if (copyTimes) { 1893 destNode->SetModificationTime(srcStat.st_mtime); 1894 destNode->SetCreationTime(srcStat.st_crtime); 1895 } 1896 1897 return B_OK; 1898 } 1899 1900 1901 #if 0 1902 status_t 1903 FSCopyFile(BEntry* srcFile, StatStruct* srcStat, BDirectory* destDir, 1904 CopyLoopControl* loopControl, BPoint* loc, bool makeOriginalName) 1905 { 1906 try { 1907 CopyFile(srcFile, srcStat, destDir, loopControl, loc, 1908 makeOriginalName); 1909 } catch (status_t error) { 1910 return error; 1911 } 1912 1913 return B_OK; 1914 } 1915 #endif 1916 1917 1918 static status_t 1919 MoveEntryToTrash(BEntry* entry, BPoint* loc, Undo &undo) 1920 { 1921 BDirectory trash_dir; 1922 entry_ref ref; 1923 status_t result = entry->GetRef(&ref); 1924 if (result != B_OK) 1925 return result; 1926 1927 node_ref nodeRef; 1928 result = entry->GetNodeRef(&nodeRef); 1929 if (result != B_OK) 1930 return result; 1931 1932 StatStruct statbuf; 1933 result = entry->GetStat(&statbuf); 1934 if (entry->GetStat(&statbuf) != B_OK) 1935 return result; 1936 1937 // if it's a directory close the window and any child dir windows 1938 if (S_ISDIR(statbuf.st_mode)) { 1939 BDirectory dir(entry); 1940 1941 // if it's a volume, try to unmount 1942 if (dir.IsRootDirectory()) { 1943 BVolume volume(nodeRef.device); 1944 BVolume boot; 1945 1946 BVolumeRoster().GetBootVolume(&boot); 1947 if (volume == boot) { 1948 char name[B_FILE_NAME_LENGTH]; 1949 volume.GetName(name); 1950 BString buffer( 1951 B_TRANSLATE("Cannot unmount the boot volume \"%name\".")); 1952 buffer.ReplaceFirst("%name", name); 1953 BAlert* alert = new BAlert("", buffer.String(), 1954 B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, 1955 B_WARNING_ALERT); 1956 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1957 alert->Go(); 1958 } else { 1959 BMessage message(kUnmountVolume); 1960 message.AddInt32("device_id", volume.Device()); 1961 be_app->PostMessage(&message); 1962 } 1963 return B_OK; 1964 } 1965 1966 // get trash directory on same volume as item being moved 1967 result = FSGetTrashDir(&trash_dir, nodeRef.device); 1968 if (result != B_OK) 1969 return result; 1970 1971 // check hierarchy before moving 1972 BEntry trashEntry; 1973 trash_dir.GetEntry(&trashEntry); 1974 1975 if (dir == trash_dir || dir.Contains(&trashEntry)) { 1976 BAlert* alert = new BAlert("", 1977 B_TRANSLATE("You cannot put the selected item(s) " 1978 "into the trash."), 1979 B_TRANSLATE("OK"), 1980 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 1981 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1982 alert->Go(); 1983 1984 // return no error so we don't get two dialogs 1985 return B_OK; 1986 } 1987 1988 BMessage message(kCloseWindowAndChildren); 1989 1990 node_ref parentNode; 1991 parentNode.device = statbuf.st_dev; 1992 parentNode.node = statbuf.st_ino; 1993 message.AddData("node_ref", B_RAW_TYPE, &parentNode, sizeof(node_ref)); 1994 be_app->PostMessage(&message); 1995 } else { 1996 // get trash directory on same volume as item being moved 1997 result = FSGetTrashDir(&trash_dir, nodeRef.device); 1998 if (result != B_OK) 1999 return result; 2000 } 2001 2002 // make sure name doesn't conflict with anything in trash already 2003 char name[B_FILE_NAME_LENGTH]; 2004 strlcpy(name, ref.name, sizeof(name)); 2005 if (trash_dir.Contains(name)) { 2006 BString suffix(" "); 2007 suffix << B_TRANSLATE_COMMENT("copy", "filename copy"), 2008 FSMakeOriginalName(name, &trash_dir, suffix.String()); 2009 undo.UpdateEntry(entry, name); 2010 } 2011 2012 BNode* src_node = 0; 2013 if (loc && loc != (BPoint*)-1 2014 && (src_node = GetWritableNode(entry, &statbuf)) != 0) { 2015 trash_dir.GetStat(&statbuf); 2016 PoseInfo poseInfo; 2017 poseInfo.fInvisible = false; 2018 poseInfo.fInitedDirectory = statbuf.st_ino; 2019 poseInfo.fLocation = *loc; 2020 src_node->WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, &poseInfo, 2021 sizeof(poseInfo)); 2022 delete src_node; 2023 } 2024 2025 BNode node(entry); 2026 BPath path; 2027 // Get path of entry before it's moved to the trash 2028 // and write it to the file as an attribute 2029 if (node.InitCheck() == B_OK && entry->GetPath(&path) == B_OK) { 2030 BString originalPath(path.Path()); 2031 node.WriteAttrString(kAttrOriginalPath, &originalPath); 2032 } 2033 2034 TrackerCopyLoopControl loopControl; 2035 MoveItem(entry, &trash_dir, loc, kMoveSelectionTo, name, undo, 2036 &loopControl); 2037 return B_OK; 2038 } 2039 2040 2041 ConflictCheckResult 2042 PreFlightNameCheck(BObjectList<entry_ref>* srcList, const BDirectory* destDir, 2043 int32* collisionCount, uint32 moveMode) 2044 { 2045 // count the number of name collisions in dest folder 2046 *collisionCount = 0; 2047 2048 int32 count = srcList->CountItems(); 2049 for (int32 i = 0; i < count; i++) { 2050 entry_ref* srcRef = srcList->ItemAt(i); 2051 BEntry entry(srcRef); 2052 BDirectory parent; 2053 entry.GetParent(&parent); 2054 2055 if (parent != *destDir && destDir->Contains(srcRef->name)) 2056 (*collisionCount)++; 2057 } 2058 2059 // prompt user only if there is more than one collision, otherwise the 2060 // single collision case will be handled as a "Prompt" case by CheckName 2061 if (*collisionCount > 1) { 2062 const char* verb = (moveMode == kMoveSelectionTo) 2063 ? B_TRANSLATE("moving") : B_TRANSLATE("copying"); 2064 BString replaceMsg(B_TRANSLATE_NOCOLLECT(kReplaceManyStr)); 2065 replaceMsg.ReplaceAll("%verb", verb); 2066 2067 BAlert* alert = new BAlert(); 2068 alert->SetText(replaceMsg.String()); 2069 alert->AddButton(B_TRANSLATE("Cancel")); 2070 alert->AddButton(B_TRANSLATE("Prompt")); 2071 alert->AddButton(B_TRANSLATE("Skip all")); 2072 alert->AddButton(B_TRANSLATE("Replace all")); 2073 alert->SetShortcut(0, B_ESCAPE); 2074 switch (alert->Go()) { 2075 case 0: 2076 return kCanceled; 2077 2078 case 1: 2079 // user selected "Prompt" 2080 return kPrompt; 2081 2082 case 2: 2083 // user selected "Skip all" 2084 return kSkipAll; 2085 2086 case 3: 2087 // user selected "Replace all" 2088 return kReplaceAll; 2089 } 2090 } 2091 2092 return kNoConflicts; 2093 } 2094 2095 2096 void 2097 FileStatToString(StatStruct* stat, char* buffer, int32 length) 2098 { 2099 tm timeData; 2100 localtime_r(&stat->st_mtime, &timeData); 2101 2102 BString size; 2103 static BStringFormat format( 2104 B_TRANSLATE("{0, plural, one{# byte} other{# bytes}}")); 2105 format.Format(size, stat->st_size); 2106 uint32 pos = snprintf(buffer, length, "\n\t(%s ", size.String()); 2107 2108 strftime(buffer + pos, length - pos, "%b %d %Y, %I:%M:%S %p)", &timeData); 2109 } 2110 2111 2112 status_t 2113 CheckName(uint32 moveMode, const BEntry* sourceEntry, 2114 const BDirectory* destDir, bool multipleCollisions, 2115 ConflictCheckResult& conflictResolution) 2116 { 2117 if (moveMode == kDuplicateSelection) { 2118 // when duplicating, we will never have a conflict 2119 return B_OK; 2120 } 2121 2122 // see if item already exists in destination dir 2123 const char* name = sourceEntry->Name(); 2124 bool sourceIsDirectory = sourceEntry->IsDirectory(); 2125 2126 BDirectory srcDirectory; 2127 if (sourceIsDirectory) { 2128 srcDirectory.SetTo(sourceEntry); 2129 BEntry destEntry; 2130 destDir->GetEntry(&destEntry); 2131 2132 if (moveMode != kCreateLink && moveMode != kCreateRelativeLink 2133 && (srcDirectory == *destDir 2134 || srcDirectory.Contains(&destEntry))) { 2135 BAlert* alert = new BAlert("", 2136 B_TRANSLATE("You can't move a folder into itself " 2137 "or any of its own sub-folders."), B_TRANSLATE("OK"), 2138 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 2139 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 2140 alert->Go(); 2141 return B_ERROR; 2142 } 2143 } 2144 2145 if (FSIsTrashDir(sourceEntry) && moveMode != kCreateLink 2146 && moveMode != kCreateRelativeLink) { 2147 BAlert* alert = new BAlert("", 2148 B_TRANSLATE("You can't move or copy the trash."), 2149 B_TRANSLATE("OK"), 0, 0, B_WIDTH_AS_USUAL, 2150 B_WARNING_ALERT); 2151 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 2152 alert->Go(); 2153 return B_ERROR; 2154 } 2155 2156 BEntry entry; 2157 if (destDir->FindEntry(name, &entry) != B_OK) { 2158 // no conflict, return 2159 return B_OK; 2160 } 2161 2162 if (moveMode == kCreateLink || moveMode == kCreateRelativeLink) { 2163 // if we are creating link in the same directory, the conflict will 2164 // be handled later by giving the link a unique name 2165 sourceEntry->GetParent(&srcDirectory); 2166 2167 if (srcDirectory == *destDir) 2168 return B_OK; 2169 } 2170 2171 bool destIsDir = entry.IsDirectory(); 2172 // be sure not to replace the parent directory of the item being moved 2173 if (destIsDir) { 2174 BDirectory targetDir(&entry); 2175 if (targetDir.Contains(sourceEntry)) { 2176 BAlert* alert = new BAlert("", 2177 B_TRANSLATE("You can't replace a folder " 2178 "with one of its sub-folders."), 2179 B_TRANSLATE("OK"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 2180 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 2181 alert->Go(); 2182 return B_ERROR; 2183 } 2184 } 2185 2186 // ensure that the user isn't trying to replace a file with folder 2187 // or vice-versa 2188 if (moveMode != kCreateLink 2189 && moveMode != kCreateRelativeLink 2190 && destIsDir != sourceIsDirectory) { 2191 BAlert* alert = new BAlert("", sourceIsDirectory 2192 ? B_TRANSLATE("You cannot replace a file with a folder or a " 2193 "symbolic link.") 2194 : B_TRANSLATE("You cannot replace a folder or a symbolic link " 2195 "with a file."), B_TRANSLATE("OK"), 0, 0, B_WIDTH_AS_USUAL, 2196 B_WARNING_ALERT); 2197 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 2198 alert->Go(); 2199 return B_ERROR; 2200 } 2201 2202 if (conflictResolution == kSkipAll) 2203 return B_ERROR; 2204 2205 if (conflictResolution != kReplaceAll) { 2206 // prompt user to determine whether to replace or not 2207 BString replaceMsg; 2208 2209 if (moveMode == kCreateLink || moveMode == kCreateRelativeLink) { 2210 replaceMsg.SetTo(B_TRANSLATE_NOCOLLECT(kSymLinkReplaceStr)); 2211 replaceMsg.ReplaceFirst("%name", name); 2212 } else if (sourceEntry->IsDirectory()) { 2213 replaceMsg.SetTo(B_TRANSLATE_NOCOLLECT(kDirectoryReplaceStr)); 2214 replaceMsg.ReplaceFirst("%name", name); 2215 replaceMsg.ReplaceFirst("%verb", 2216 moveMode == kMoveSelectionTo 2217 ? B_TRANSLATE("moving") 2218 : B_TRANSLATE("copying")); 2219 } else { 2220 char sourceBuffer[96], destBuffer[96]; 2221 StatStruct statBuffer; 2222 2223 if (!sourceEntry->IsDirectory() 2224 && sourceEntry->GetStat(&statBuffer) == B_OK) { 2225 FileStatToString(&statBuffer, sourceBuffer, 96); 2226 } else 2227 sourceBuffer[0] = '\0'; 2228 2229 if (!entry.IsDirectory() && entry.GetStat(&statBuffer) == B_OK) 2230 FileStatToString(&statBuffer, destBuffer, 96); 2231 else 2232 destBuffer[0] = '\0'; 2233 2234 replaceMsg.SetTo(B_TRANSLATE_NOCOLLECT(kReplaceStr)); 2235 replaceMsg.ReplaceAll("%name", name); 2236 replaceMsg.ReplaceFirst("%dest", destBuffer); 2237 replaceMsg.ReplaceFirst("%src", sourceBuffer); 2238 replaceMsg.ReplaceFirst("%movemode", moveMode == kMoveSelectionTo 2239 ? B_TRANSLATE("moving") : B_TRANSLATE("copying")); 2240 } 2241 2242 // special case single collision (don't need Replace All shortcut) 2243 BAlert* alert; 2244 if (multipleCollisions || sourceIsDirectory) { 2245 alert = new BAlert(); 2246 alert->SetText(replaceMsg.String()); 2247 alert->AddButton(B_TRANSLATE("Skip")); 2248 alert->AddButton(B_TRANSLATE("Skip all")); 2249 alert->AddButton(B_TRANSLATE("Replace")); 2250 alert->AddButton(B_TRANSLATE("Replace all")); 2251 switch (alert->Go()) { 2252 case 0: 2253 conflictResolution = kCanceled; 2254 return B_ERROR; 2255 case 1: 2256 conflictResolution = kSkipAll; 2257 return B_ERROR; 2258 case 2: 2259 conflictResolution = kReplace; 2260 break; 2261 case 3: 2262 conflictResolution = kReplaceAll; 2263 break; 2264 } 2265 } else { 2266 alert = new BAlert("", replaceMsg.String(), 2267 B_TRANSLATE("Cancel"), B_TRANSLATE("Replace")); 2268 alert->SetShortcut(0, B_ESCAPE); 2269 switch (alert->Go()) { 2270 case 0: 2271 conflictResolution = kCanceled; 2272 return B_ERROR; 2273 case 1: 2274 conflictResolution = kReplace; 2275 break; 2276 } 2277 } 2278 } 2279 2280 // delete destination item 2281 if (destIsDir) 2282 return B_OK; 2283 2284 status_t status = entry.Remove(); 2285 if (status != B_OK) { 2286 BString error(B_TRANSLATE("There was a problem trying to replace " 2287 "\"%name\". The item might be open or busy.")); 2288 error.ReplaceFirst("%name", name); 2289 BAlert* alert = new BAlert("", error.String(), 2290 B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 2291 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 2292 alert->Go(); 2293 } 2294 2295 return status; 2296 } 2297 2298 2299 status_t 2300 FSDeleteFolder(BEntry* dirEntry, CopyLoopControl* loopControl, 2301 bool updateStatus, bool deleteTopDir, bool upateFileNameInStatus) 2302 { 2303 BDirectory dir(dirEntry); 2304 2305 // loop through everything in folder and delete it, skipping trouble files 2306 BEntry entry; 2307 while (dir.GetNextEntry(&entry) == B_OK) { 2308 entry_ref ref; 2309 entry.GetRef(&ref); 2310 2311 if (loopControl->CheckUserCanceled()) 2312 return kTrashCanceled; 2313 2314 status_t status; 2315 2316 if (entry.IsDirectory()) 2317 status = FSDeleteFolder(&entry, loopControl, updateStatus, true, 2318 upateFileNameInStatus); 2319 else { 2320 status = entry.Remove(); 2321 if (updateStatus) { 2322 loopControl->UpdateStatus(upateFileNameInStatus ? ref.name 2323 : "", ref, 1, true); 2324 } 2325 } 2326 2327 if (status == kTrashCanceled) 2328 return kTrashCanceled; 2329 2330 if (status != B_OK) { 2331 loopControl->FileError(B_TRANSLATE_NOCOLLECT( 2332 kFileDeleteErrorString), ref.name, status, false); 2333 } 2334 } 2335 2336 if (loopControl->CheckUserCanceled()) 2337 return kTrashCanceled; 2338 2339 entry_ref ref; 2340 dirEntry->GetRef(&ref); 2341 2342 if (updateStatus && deleteTopDir) 2343 loopControl->UpdateStatus(NULL, ref, 1); 2344 2345 if (deleteTopDir) 2346 return dirEntry->Remove(); 2347 2348 return B_OK; 2349 } 2350 2351 2352 void 2353 FSMakeOriginalName(BString &string, const BDirectory* destDir, 2354 const char* suffix) 2355 { 2356 if (!destDir->Contains(string.String())) 2357 return; 2358 2359 FSMakeOriginalName(string.LockBuffer(B_FILE_NAME_LENGTH), 2360 const_cast<BDirectory*>(destDir), suffix ? suffix : " copy"); 2361 string.UnlockBuffer(); 2362 } 2363 2364 2365 void 2366 FSMakeOriginalName(char* name, BDirectory* destDir, const char* suffix) 2367 { 2368 char root[B_FILE_NAME_LENGTH]; 2369 char copybase[B_FILE_NAME_LENGTH]; 2370 char tempName[B_FILE_NAME_LENGTH + 11]; 2371 int32 fnum; 2372 2373 // is this name already original? 2374 if (!destDir->Contains(name)) 2375 return; 2376 2377 // Determine if we're copying a 'copy'. This algorithm isn't perfect. 2378 // If you're copying a file whose REAL name ends with 'copy' then 2379 // this method will return "<filename> 1", not "<filename> copy" 2380 2381 // However, it will correctly handle file that contain 'copy' 2382 // elsewhere in their name. 2383 2384 bool copycopy = false; // are we copying a copy? 2385 int32 len = (int32)strlen(name); 2386 char* p = name + len - 1; // get pointer to end os name 2387 2388 // eat up optional numbers (if were copying "<filename> copy 34") 2389 while ((p > name) && isdigit(*p)) 2390 p--; 2391 2392 // eat up optional spaces 2393 while ((p > name) && isspace(*p)) 2394 p--; 2395 2396 // now look for the phrase " copy" 2397 if (p > name) { 2398 // p points to the last char of the word. For example, 'y' in 'copy' 2399 2400 if ((p - 4 > name) && (strncmp(p - 4, suffix, 5) == 0)) { 2401 // we found 'copy' in the right place. 2402 // so truncate after 'copy' 2403 *(p + 1) = '\0'; 2404 copycopy = true; 2405 2406 // save the 'root' name of the file, for possible later use. 2407 // that is copy everything but trailing " copy". Need to 2408 // NULL terminate after copy 2409 strncpy(root, name, (uint32)((p - name) - 4)); 2410 root[(p - name) - 4] = '\0'; 2411 } 2412 } 2413 2414 if (!copycopy) { 2415 // The name can't be longer than B_FILE_NAME_LENGTH. 2416 // The algoritm adds " copy XX" to the name. That's 8 characters. 2417 // B_FILE_NAME_LENGTH already accounts for NULL termination so we 2418 // don't need to save an extra char at the end. 2419 if (strlen(name) > B_FILE_NAME_LENGTH - 8) { 2420 // name is too long - truncate it! 2421 name[B_FILE_NAME_LENGTH - 8] = '\0'; 2422 } 2423 2424 strlcpy(root, name, sizeof(root)); 2425 // save root name 2426 strlcat(name, suffix, B_FILE_NAME_LENGTH); 2427 } 2428 2429 strlcpy(copybase, name, sizeof(copybase)); 2430 2431 // if name already exists then add a number 2432 fnum = 1; 2433 strlcpy(tempName, name, sizeof(tempName)); 2434 while (destDir->Contains(tempName)) { 2435 snprintf(tempName, sizeof(tempName), "%s %" B_PRId32, copybase, 2436 ++fnum); 2437 2438 if (strlen(tempName) > (B_FILE_NAME_LENGTH - 1)) { 2439 // The name has grown too long. Maybe we just went from 2440 // "<filename> copy 9" to "<filename> copy 10" and that extra 2441 // character was too much. The solution is to further 2442 // truncate the 'root' name and continue. 2443 // ??? should we reset fnum or not ??? 2444 root[strlen(root) - 1] = '\0'; 2445 snprintf(tempName, sizeof(tempName), "%s%s %" B_PRId32, root, 2446 suffix, fnum); 2447 } 2448 } 2449 2450 strlcpy(name, tempName, B_FILE_NAME_LENGTH); 2451 } 2452 2453 2454 status_t 2455 FSRecursiveCalcSize(BInfoWindow* window, CopyLoopControl* loopControl, 2456 BDirectory* dir, off_t* _runningSize, int32* _fileCount, int32* _dirCount) 2457 { 2458 dir->Rewind(); 2459 BEntry entry; 2460 while (dir->GetNextEntry(&entry) == B_OK) { 2461 // be sure window hasn't closed 2462 if (window && window->StopCalc()) 2463 return B_OK; 2464 2465 if (loopControl->CheckUserCanceled()) 2466 return kUserCanceled; 2467 2468 StatStruct statbuf; 2469 status_t status = entry.GetStat(&statbuf); 2470 if (status != B_OK) 2471 return status; 2472 2473 (*_runningSize) += statbuf.st_blocks * 512; 2474 2475 if (S_ISDIR(statbuf.st_mode)) { 2476 BDirectory subdir(&entry); 2477 (*_dirCount)++; 2478 status = FSRecursiveCalcSize(window, loopControl, &subdir, 2479 _runningSize, _fileCount, _dirCount); 2480 if (status != B_OK) 2481 return status; 2482 } else 2483 (*_fileCount)++; 2484 } 2485 return B_OK; 2486 } 2487 2488 2489 status_t 2490 CalcItemsAndSize(CopyLoopControl* loopControl, 2491 BObjectList<entry_ref>* refList, ssize_t blockSize, int32* totalCount, 2492 off_t* totalSize) 2493 { 2494 int32 fileCount = 0; 2495 int32 dirCount = 0; 2496 2497 // check block size for sanity 2498 if (blockSize < 0) { 2499 // This would point at an error to retrieve the block size from 2500 // the target volume. The code below cannot be used, it is only 2501 // meant to get the block size when item operations happen on 2502 // the source volume. 2503 blockSize = 2048; 2504 } else if (blockSize < 1024) { 2505 blockSize = 1024; 2506 if (entry_ref* ref = refList->ItemAt(0)) { 2507 // TODO: This assumes all entries in the list share the same 2508 // volume... 2509 BVolume volume(ref->device); 2510 if (volume.InitCheck() == B_OK) 2511 blockSize = volume.BlockSize(); 2512 } 2513 } 2514 // File systems like ReiserFS may advertize a large block size, but 2515 // stuff is still packed into blocks, so clamp maximum block size. 2516 if (blockSize > 8192) 2517 blockSize = 8192; 2518 2519 int32 num_items = refList->CountItems(); 2520 for (int32 i = 0; i < num_items; i++) { 2521 entry_ref* ref = refList->ItemAt(i); 2522 BEntry entry(ref); 2523 StatStruct statbuf; 2524 entry.GetStat(&statbuf); 2525 2526 if (loopControl->CheckUserCanceled()) 2527 return kUserCanceled; 2528 2529 if (S_ISDIR(statbuf.st_mode)) { 2530 BDirectory dir(&entry); 2531 dirCount++; 2532 (*totalSize) += blockSize; 2533 status_t result = FSRecursiveCalcSize(NULL, loopControl, &dir, 2534 totalSize, &fileCount, &dirCount); 2535 if (result != B_OK) 2536 return result; 2537 } else { 2538 fileCount++; 2539 (*totalSize) += statbuf.st_size + blockSize; 2540 } 2541 } 2542 2543 *totalCount += (fileCount + dirCount); 2544 return B_OK; 2545 } 2546 2547 2548 status_t 2549 FSGetTrashDir(BDirectory* trashDir, dev_t dev) 2550 { 2551 if (trashDir == NULL) 2552 return B_BAD_VALUE; 2553 2554 BVolume volume(dev); 2555 status_t result = volume.InitCheck(); 2556 if (result != B_OK) 2557 return result; 2558 2559 BPath path; 2560 result = find_directory(B_TRASH_DIRECTORY, &path, false, &volume); 2561 if (result != B_OK) 2562 return result; 2563 2564 result = trashDir->SetTo(path.Path()); 2565 if (result != B_OK) { 2566 // Trash directory does not exist yet, create it. 2567 result = create_directory(path.Path(), 0755); 2568 if (result != B_OK) 2569 return result; 2570 2571 result = trashDir->SetTo(path.Path()); 2572 if (result != B_OK) 2573 return result; 2574 2575 // make Trash directory invisible 2576 StatStruct sbuf; 2577 trashDir->GetStat(&sbuf); 2578 2579 PoseInfo poseInfo; 2580 poseInfo.fInvisible = true; 2581 poseInfo.fInitedDirectory = sbuf.st_ino; 2582 trashDir->WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, &poseInfo, 2583 sizeof(PoseInfo)); 2584 } 2585 2586 // set Trash icons (if they haven't already been set) 2587 attr_info attrInfo; 2588 size_t size; 2589 const void* data; 2590 if (trashDir->GetAttrInfo(kAttrLargeIcon, &attrInfo) == B_ENTRY_NOT_FOUND) { 2591 data = GetTrackerResources()->LoadResource('ICON', R_TrashIcon, &size); 2592 if (data != NULL) 2593 trashDir->WriteAttr(kAttrLargeIcon, 'ICON', 0, data, size); 2594 } 2595 2596 if (trashDir->GetAttrInfo(kAttrMiniIcon, &attrInfo) == B_ENTRY_NOT_FOUND) { 2597 data = GetTrackerResources()->LoadResource('MICN', R_TrashIcon, &size); 2598 if (data != NULL) 2599 trashDir->WriteAttr(kAttrMiniIcon, 'MICN', 0, data, size); 2600 } 2601 2602 if (trashDir->GetAttrInfo(kAttrIcon, &attrInfo) == B_ENTRY_NOT_FOUND) { 2603 data = GetTrackerResources()->LoadResource(B_VECTOR_ICON_TYPE, 2604 R_TrashIcon, &size); 2605 if (data != NULL) 2606 trashDir->WriteAttr(kAttrIcon, B_VECTOR_ICON_TYPE, 0, data, size); 2607 } 2608 2609 return B_OK; 2610 } 2611 2612 2613 status_t 2614 FSGetDeskDir(BDirectory* deskDir) 2615 { 2616 if (deskDir == NULL) 2617 return B_BAD_VALUE; 2618 2619 BPath path; 2620 status_t result = find_directory(B_DESKTOP_DIRECTORY, &path, true); 2621 if (result != B_OK) 2622 return result; 2623 2624 result = deskDir->SetTo(path.Path()); 2625 if (result != B_OK) 2626 return result; 2627 2628 // set Desktop icons (if they haven't already been set) 2629 attr_info attrInfo; 2630 size_t size; 2631 const void* data; 2632 if (deskDir->GetAttrInfo(kAttrLargeIcon, &attrInfo) == B_ENTRY_NOT_FOUND) { 2633 data = GetTrackerResources()->LoadResource('ICON', R_DeskIcon, &size); 2634 if (data != NULL) 2635 deskDir->WriteAttr(kAttrLargeIcon, 'ICON', 0, data, size); 2636 } 2637 2638 if (deskDir->GetAttrInfo(kAttrMiniIcon, &attrInfo) == B_ENTRY_NOT_FOUND) { 2639 data = GetTrackerResources()->LoadResource('MICN', R_DeskIcon, &size); 2640 if (data != NULL) 2641 deskDir->WriteAttr(kAttrMiniIcon, 'MICN', 0, data, size); 2642 } 2643 2644 if (deskDir->GetAttrInfo(kAttrIcon, &attrInfo) == B_ENTRY_NOT_FOUND) { 2645 data = GetTrackerResources()->LoadResource(B_VECTOR_ICON_TYPE, 2646 R_DeskIcon, &size); 2647 if (data != NULL) 2648 deskDir->WriteAttr(kAttrIcon, B_VECTOR_ICON_TYPE, 0, data, size); 2649 } 2650 2651 return B_OK; 2652 } 2653 2654 2655 status_t 2656 FSGetBootDeskDir(BDirectory* deskDir) 2657 { 2658 BVolume bootVolume; 2659 BVolumeRoster().GetBootVolume(&bootVolume); 2660 BPath path; 2661 2662 status_t result = find_directory(B_DESKTOP_DIRECTORY, &path, true, 2663 &bootVolume); 2664 if (result != B_OK) 2665 return result; 2666 2667 return deskDir->SetTo(path.Path()); 2668 } 2669 2670 2671 static bool 2672 FSIsDirFlavor(const BEntry* entry, directory_which directoryType) 2673 { 2674 StatStruct dir_stat; 2675 StatStruct entry_stat; 2676 BVolume volume; 2677 BPath path; 2678 2679 if (entry->GetStat(&entry_stat) != B_OK) 2680 return false; 2681 2682 if (volume.SetTo(entry_stat.st_dev) != B_OK) 2683 return false; 2684 2685 if (find_directory(directoryType, &path, false, &volume) != B_OK) 2686 return false; 2687 2688 stat(path.Path(), &dir_stat); 2689 2690 return dir_stat.st_ino == entry_stat.st_ino 2691 && dir_stat.st_dev == entry_stat.st_dev; 2692 } 2693 2694 2695 bool 2696 FSIsPrintersDir(const BEntry* entry) 2697 { 2698 return FSIsDirFlavor(entry, B_USER_PRINTERS_DIRECTORY); 2699 } 2700 2701 2702 bool 2703 FSIsTrashDir(const BEntry* entry) 2704 { 2705 return FSIsDirFlavor(entry, B_TRASH_DIRECTORY); 2706 } 2707 2708 2709 bool 2710 FSIsDeskDir(const BEntry* entry) 2711 { 2712 BPath path; 2713 status_t result = find_directory(B_DESKTOP_DIRECTORY, &path, true); 2714 if (result != B_OK) 2715 return false; 2716 2717 BEntry entryToCompare(path.Path()); 2718 return entryToCompare == *entry; 2719 } 2720 2721 2722 bool 2723 FSIsHomeDir(const BEntry* entry) 2724 { 2725 return FSIsDirFlavor(entry, B_USER_DIRECTORY); 2726 } 2727 2728 2729 bool 2730 FSIsRootDir(const BEntry* entry) 2731 { 2732 BPath path(entry); 2733 return path == "/"; 2734 } 2735 2736 2737 bool 2738 DirectoryMatchesOrContains(const BEntry* entry, directory_which which) 2739 { 2740 BPath path; 2741 if (find_directory(which, &path, false, NULL) != B_OK) 2742 return false; 2743 2744 BEntry dirEntry(path.Path()); 2745 if (dirEntry.InitCheck() != B_OK) 2746 return false; 2747 2748 if (dirEntry == *entry) 2749 // root level match 2750 return true; 2751 2752 BDirectory dir(&dirEntry); 2753 return dir.Contains(entry); 2754 } 2755 2756 2757 bool 2758 DirectoryMatchesOrContains(const BEntry* entry, const char* additionalPath, 2759 directory_which which) 2760 { 2761 BPath path; 2762 if (find_directory(which, &path, false, NULL) != B_OK) 2763 return false; 2764 2765 path.Append(additionalPath); 2766 BEntry dirEntry(path.Path()); 2767 if (dirEntry.InitCheck() != B_OK) 2768 return false; 2769 2770 if (dirEntry == *entry) 2771 // root level match 2772 return true; 2773 2774 BDirectory dir(&dirEntry); 2775 return dir.Contains(entry); 2776 } 2777 2778 2779 bool 2780 DirectoryMatches(const BEntry* entry, directory_which which) 2781 { 2782 BPath path; 2783 if (find_directory(which, &path, false, NULL) != B_OK) 2784 return false; 2785 2786 BEntry dirEntry(path.Path()); 2787 if (dirEntry.InitCheck() != B_OK) 2788 return false; 2789 2790 return dirEntry == *entry; 2791 } 2792 2793 2794 bool 2795 DirectoryMatches(const BEntry* entry, const char* additionalPath, 2796 directory_which which) 2797 { 2798 BPath path; 2799 if (find_directory(which, &path, false, NULL) != B_OK) 2800 return false; 2801 2802 path.Append(additionalPath); 2803 BEntry dirEntry(path.Path()); 2804 if (dirEntry.InitCheck() != B_OK) 2805 return false; 2806 2807 return dirEntry == *entry; 2808 } 2809 2810 2811 extern status_t 2812 FSFindTrackerSettingsDir(BPath* path, bool autoCreate) 2813 { 2814 status_t result = find_directory(B_USER_SETTINGS_DIRECTORY, path, 2815 autoCreate); 2816 if (result != B_OK) 2817 return result; 2818 2819 path->Append("Tracker"); 2820 2821 return mkdir(path->Path(), 0777) ? B_OK : errno; 2822 } 2823 2824 2825 bool 2826 FSInTrashDir(const entry_ref* ref) 2827 { 2828 BEntry entry(ref); 2829 if (entry.InitCheck() != B_OK) 2830 return false; 2831 2832 BDirectory trashDir; 2833 if (FSGetTrashDir(&trashDir, ref->device) != B_OK) 2834 return false; 2835 2836 return trashDir.Contains(&entry); 2837 } 2838 2839 2840 void 2841 FSEmptyTrash() 2842 { 2843 if (find_thread("_tracker_empty_trash_") != B_OK) { 2844 resume_thread(spawn_thread(empty_trash, "_tracker_empty_trash_", 2845 B_NORMAL_PRIORITY, NULL)); 2846 } 2847 } 2848 2849 2850 status_t 2851 empty_trash(void*) 2852 { 2853 // empty trash on all mounted volumes 2854 status_t status = B_OK; 2855 2856 TrackerCopyLoopControl loopControl(kTrashState); 2857 2858 // calculate the sum total of all items on all volumes in trash 2859 BObjectList<entry_ref> srcList; 2860 int32 totalCount = 0; 2861 off_t totalSize = 0; 2862 2863 BVolumeRoster volumeRoster; 2864 BVolume volume; 2865 while (volumeRoster.GetNextVolume(&volume) == B_OK) { 2866 if (volume.IsReadOnly() || !volume.IsPersistent()) 2867 continue; 2868 2869 BDirectory trashDirectory; 2870 if (FSGetTrashDir(&trashDirectory, volume.Device()) != B_OK) 2871 continue; 2872 2873 BEntry entry; 2874 trashDirectory.GetEntry(&entry); 2875 2876 entry_ref ref; 2877 entry.GetRef(&ref); 2878 srcList.AddItem(&ref); 2879 status = CalcItemsAndSize(&loopControl, &srcList, volume.BlockSize(), 2880 &totalCount, &totalSize); 2881 if (status != B_OK) 2882 break; 2883 2884 srcList.MakeEmpty(); 2885 2886 // don't count trash directory itself 2887 totalCount--; 2888 } 2889 2890 if (status == B_OK) { 2891 loopControl.Init(totalCount, totalCount); 2892 2893 volumeRoster.Rewind(); 2894 while (volumeRoster.GetNextVolume(&volume) == B_OK) { 2895 if (volume.IsReadOnly() || !volume.IsPersistent()) 2896 continue; 2897 2898 BDirectory trashDirectory; 2899 if (FSGetTrashDir(&trashDirectory, volume.Device()) != B_OK) 2900 continue; 2901 2902 BEntry entry; 2903 trashDirectory.GetEntry(&entry); 2904 status = FSDeleteFolder(&entry, &loopControl, true, false); 2905 } 2906 } 2907 2908 if (status != B_OK && status != kTrashCanceled && status != kUserCanceled) { 2909 BAlert* alert = new BAlert("", B_TRANSLATE("Error emptying Trash"), 2910 B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 2911 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 2912 alert->Go(); 2913 } 2914 2915 return B_OK; 2916 } 2917 2918 2919 status_t 2920 _DeleteTask(BObjectList<entry_ref>* list, bool confirm) 2921 { 2922 if (confirm) { 2923 bool dontMoveToTrash = TrackerSettings().DontMoveFilesToTrash(); 2924 2925 if (!dontMoveToTrash) { 2926 BAlert* alert = new BAlert("", 2927 B_TRANSLATE_NOCOLLECT(kDeleteConfirmationStr), 2928 B_TRANSLATE("Cancel"), B_TRANSLATE("Move to Trash"), 2929 B_TRANSLATE("Delete"), B_WIDTH_AS_USUAL, B_OFFSET_SPACING, 2930 B_WARNING_ALERT); 2931 2932 alert->SetShortcut(0, B_ESCAPE); 2933 alert->SetShortcut(1, 'm'); 2934 alert->SetShortcut(2, 'd'); 2935 2936 switch (alert->Go()) { 2937 case 0: 2938 delete list; 2939 return B_OK; 2940 case 1: 2941 FSMoveToTrash(list, NULL, false); 2942 return B_OK; 2943 } 2944 } else { 2945 BAlert* alert = new BAlert("", 2946 B_TRANSLATE_NOCOLLECT(kDeleteConfirmationStr), 2947 B_TRANSLATE("Cancel"), B_TRANSLATE("Delete"), NULL, 2948 B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_WARNING_ALERT); 2949 2950 alert->SetShortcut(0, B_ESCAPE); 2951 alert->SetShortcut(1, 'd'); 2952 2953 if (!alert->Go()) { 2954 delete list; 2955 return B_OK; 2956 } 2957 } 2958 } 2959 2960 TrackerCopyLoopControl loopControl(kDeleteState); 2961 2962 // calculate the sum total of all items on all volumes in trash 2963 int32 totalItems = 0; 2964 int64 totalSize = 0; 2965 2966 status_t status = CalcItemsAndSize(&loopControl, list, 0, &totalItems, 2967 &totalSize); 2968 if (status == B_OK) { 2969 loopControl.Init(totalItems, totalItems); 2970 2971 int32 count = list->CountItems(); 2972 for (int32 index = 0; index < count; index++) { 2973 entry_ref ref(*list->ItemAt(index)); 2974 BEntry entry(&ref); 2975 loopControl.UpdateStatus(ref.name, ref, 1, true); 2976 if (entry.IsDirectory()) 2977 status = FSDeleteFolder(&entry, &loopControl, true, true, true); 2978 else 2979 status = entry.Remove(); 2980 } 2981 2982 if (status != kTrashCanceled && status != kUserCanceled 2983 && status != B_OK) { 2984 BAlert* alert = new BAlert("", B_TRANSLATE("Error deleting items"), 2985 B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, 2986 B_WARNING_ALERT); 2987 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 2988 alert->Go(); 2989 } 2990 } 2991 2992 delete list; 2993 2994 return B_OK; 2995 } 2996 2997 status_t 2998 FSRecursiveCreateFolder(BPath path) 2999 { 3000 BEntry entry(path.Path()); 3001 if (entry.InitCheck() != B_OK) { 3002 BPath parentPath; 3003 status_t err = path.GetParent(&parentPath); 3004 if (err != B_OK) 3005 return err; 3006 3007 err = FSRecursiveCreateFolder(parentPath); 3008 if (err != B_OK) 3009 return err; 3010 } 3011 3012 entry.SetTo(path.Path()); 3013 if (entry.Exists()) 3014 return B_FILE_EXISTS; 3015 3016 BDirectory parent; 3017 entry.GetParent(&parent); 3018 parent.CreateDirectory(entry.Name(), NULL); 3019 3020 return B_OK; 3021 } 3022 3023 status_t 3024 _RestoreTask(BObjectList<entry_ref>* list) 3025 { 3026 TrackerCopyLoopControl loopControl(kRestoreFromTrashState); 3027 3028 // calculate the sum total of all items that will be restored 3029 int32 totalItems = 0; 3030 int64 totalSize = 0; 3031 3032 status_t err = CalcItemsAndSize(&loopControl, list, 0, &totalItems, 3033 &totalSize); 3034 if (err == B_OK) { 3035 loopControl.Init(totalItems, totalItems); 3036 3037 int32 count = list->CountItems(); 3038 for (int32 index = 0; index < count; index++) { 3039 entry_ref ref(*list->ItemAt(index)); 3040 BEntry entry(&ref); 3041 BPath originalPath; 3042 3043 loopControl.UpdateStatus(ref.name, ref, 1, true); 3044 3045 if (FSGetOriginalPath(&entry, &originalPath) != B_OK) 3046 continue; 3047 3048 BEntry originalEntry(originalPath.Path()); 3049 BPath parentPath; 3050 err = originalPath.GetParent(&parentPath); 3051 if (err != B_OK) 3052 continue; 3053 BEntry parentEntry(parentPath.Path()); 3054 3055 if (parentEntry.InitCheck() != B_OK || !parentEntry.Exists()) { 3056 if (FSRecursiveCreateFolder(parentPath) == B_OK) { 3057 originalEntry.SetTo(originalPath.Path()); 3058 if (entry.InitCheck() != B_OK) 3059 continue; 3060 } 3061 } 3062 3063 if (!originalEntry.Exists()) { 3064 BDirectory dir(parentPath.Path()); 3065 if (dir.InitCheck() == B_OK) { 3066 const char* leafName = originalEntry.Name(); 3067 if (entry.MoveTo(&dir, leafName) == B_OK) { 3068 BNode node(&entry); 3069 if (node.InitCheck() == B_OK) 3070 node.RemoveAttr(kAttrOriginalPath); 3071 } 3072 } 3073 } 3074 3075 err = loopControl.CheckUserCanceled(); 3076 if (err != B_OK) 3077 break; 3078 } 3079 } 3080 3081 delete list; 3082 3083 return err; 3084 } 3085 3086 void 3087 FSCreateTrashDirs() 3088 { 3089 BVolume volume; 3090 BVolumeRoster roster; 3091 3092 roster.Rewind(); 3093 while (roster.GetNextVolume(&volume) == B_OK) { 3094 if (volume.IsReadOnly() || !volume.IsPersistent()) 3095 continue; 3096 3097 BDirectory trashDir; 3098 FSGetTrashDir(&trashDir, volume.Device()); 3099 } 3100 } 3101 3102 3103 status_t 3104 FSCreateNewFolder(const entry_ref* ref) 3105 { 3106 node_ref node; 3107 node.device = ref->device; 3108 node.node = ref->directory; 3109 3110 BDirectory dir(&node); 3111 status_t result = dir.InitCheck(); 3112 if (result != B_OK) 3113 return result; 3114 3115 // ToDo: is that really necessary here? 3116 BString name(ref->name); 3117 FSMakeOriginalName(name, &dir, "-"); 3118 3119 BDirectory newDir; 3120 result = dir.CreateDirectory(name.String(), &newDir); 3121 if (result != B_OK) 3122 return result; 3123 3124 BNodeInfo nodeInfo(&newDir); 3125 nodeInfo.SetType(B_DIR_MIMETYPE); 3126 3127 return result; 3128 } 3129 3130 3131 status_t 3132 FSCreateNewFolderIn(const node_ref* dirNode, entry_ref* newRef, 3133 node_ref* newNode) 3134 { 3135 BDirectory dir(dirNode); 3136 status_t result = dir.InitCheck(); 3137 if (result == B_OK) { 3138 char name[B_FILE_NAME_LENGTH]; 3139 strlcpy(name, B_TRANSLATE("New folder"), sizeof(name)); 3140 3141 int32 fnum = 1; 3142 while (dir.Contains(name)) { 3143 // if base name already exists then add a number 3144 // TODO: move this logic to FSMakeOriginalName 3145 if (++fnum > 9) { 3146 snprintf(name, sizeof(name), B_TRANSLATE("New folder%ld"), 3147 fnum); 3148 } else { 3149 snprintf(name, sizeof(name), B_TRANSLATE("New folder %ld"), 3150 fnum); 3151 } 3152 } 3153 3154 BDirectory newDir; 3155 result = dir.CreateDirectory(name, &newDir); 3156 if (result == B_OK) { 3157 BEntry entry; 3158 newDir.GetEntry(&entry); 3159 entry.GetRef(newRef); 3160 entry.GetNodeRef(newNode); 3161 3162 BNodeInfo nodeInfo(&newDir); 3163 nodeInfo.SetType(B_DIR_MIMETYPE); 3164 3165 // add undo item 3166 NewFolderUndo undo(*newRef); 3167 return B_OK; 3168 } 3169 } 3170 3171 BAlert* alert = new BAlert("", 3172 B_TRANSLATE("Sorry, could not create a new folder."), 3173 B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 3174 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 3175 alert->Go(); 3176 3177 return result; 3178 } 3179 3180 3181 ReadAttrResult 3182 ReadAttr(const BNode* node, const char* hostAttrName, 3183 const char* foreignAttrName, type_code type, off_t offset, void* buffer, 3184 size_t length, void (*swapFunc)(void*), bool isForeign) 3185 { 3186 if (!isForeign && node->ReadAttr(hostAttrName, type, offset, buffer, 3187 length) == (ssize_t)length) { 3188 return kReadAttrNativeOK; 3189 } 3190 3191 // PRINT(("trying %s\n", foreignAttrName)); 3192 // try the other endianness 3193 if (node->ReadAttr(foreignAttrName, type, offset, buffer, length) 3194 != (ssize_t)length) { 3195 return kReadAttrFailed; 3196 } 3197 3198 // PRINT(("got %s\n", foreignAttrName)); 3199 if (!swapFunc) 3200 return kReadAttrForeignOK; 3201 3202 (swapFunc)(buffer); 3203 // run the endian swapper 3204 3205 return kReadAttrForeignOK; 3206 } 3207 3208 3209 ReadAttrResult 3210 GetAttrInfo(const BNode* node, const char* hostAttrName, 3211 const char* foreignAttrName, type_code* type, size_t* size) 3212 { 3213 attr_info info; 3214 3215 if (node->GetAttrInfo(hostAttrName, &info) == B_OK) { 3216 if (type) 3217 *type = info.type; 3218 if (size) 3219 *size = (size_t)info.size; 3220 3221 return kReadAttrNativeOK; 3222 } 3223 3224 if (node->GetAttrInfo(foreignAttrName, &info) == B_OK) { 3225 if (type) 3226 *type = info.type; 3227 if (size) 3228 *size = (size_t)info.size; 3229 3230 return kReadAttrForeignOK; 3231 } 3232 return kReadAttrFailed; 3233 } 3234 3235 3236 status_t 3237 FSGetParentVirtualDirectoryAware(const BEntry& entry, entry_ref& _ref) 3238 { 3239 node_ref nodeRef; 3240 if (entry.GetNodeRef(&nodeRef) == B_OK) { 3241 if (VirtualDirectoryManager* manager 3242 = VirtualDirectoryManager::Instance()) { 3243 AutoLocker<VirtualDirectoryManager> managerLocker(manager); 3244 if (manager->GetParentDirectoryDefinitionFile(nodeRef, _ref, 3245 nodeRef)) { 3246 return B_OK; 3247 } 3248 } 3249 } 3250 3251 status_t error; 3252 BDirectory parent; 3253 BEntry parentEntry; 3254 if ((error = entry.GetParent(&parent)) != B_OK 3255 || (error = parent.GetEntry(&parentEntry)) != B_OK 3256 || (error = parentEntry.GetRef(&_ref)) != B_OK) { 3257 return error; 3258 } 3259 3260 return B_OK; 3261 } 3262 3263 3264 status_t 3265 FSGetParentVirtualDirectoryAware(const BEntry& entry, BEntry& _entry) 3266 { 3267 node_ref nodeRef; 3268 if (entry.GetNodeRef(&nodeRef) == B_OK) { 3269 if (VirtualDirectoryManager* manager 3270 = VirtualDirectoryManager::Instance()) { 3271 AutoLocker<VirtualDirectoryManager> managerLocker(manager); 3272 entry_ref parentRef; 3273 if (manager->GetParentDirectoryDefinitionFile(nodeRef, parentRef, 3274 nodeRef)) { 3275 return _entry.SetTo(&parentRef); 3276 } 3277 } 3278 } 3279 3280 return entry.GetParent(&_entry); 3281 } 3282 3283 3284 status_t 3285 FSGetParentVirtualDirectoryAware(const BEntry& entry, BNode& _node) 3286 { 3287 entry_ref ref; 3288 status_t error = FSGetParentVirtualDirectoryAware(entry, ref); 3289 if (error == B_OK) 3290 error = _node.SetTo(&ref); 3291 3292 return error; 3293 } 3294 3295 3296 // launching code 3297 3298 static status_t 3299 TrackerOpenWith(const BMessage* refs) 3300 { 3301 BMessage clone(*refs); 3302 3303 ASSERT(dynamic_cast<TTracker*>(be_app) != NULL); 3304 ASSERT(clone.what != 0); 3305 3306 clone.AddInt32("launchUsingSelector", 0); 3307 // runs the Open With window 3308 be_app->PostMessage(&clone); 3309 3310 return B_OK; 3311 } 3312 3313 3314 static void 3315 AsynchLaunchBinder(void (*func)(const entry_ref*, const BMessage*, bool on), 3316 const entry_ref* appRef, const BMessage* refs, bool openWithOK) 3317 { 3318 BMessage* task = new BMessage; 3319 task->AddPointer("function", (void*)func); 3320 task->AddMessage("refs", refs); 3321 task->AddBool("openWithOK", openWithOK); 3322 if (appRef != NULL) 3323 task->AddRef("appRef", appRef); 3324 3325 extern BLooper* gLaunchLooper; 3326 gLaunchLooper->PostMessage(task); 3327 } 3328 3329 3330 static bool 3331 SniffIfGeneric(const entry_ref* ref) 3332 { 3333 BNode node(ref); 3334 char type[B_MIME_TYPE_LENGTH]; 3335 BNodeInfo info(&node); 3336 if (info.GetType(type) == B_OK 3337 && strcasecmp(type, B_FILE_MIME_TYPE) != 0) { 3338 // already has a type and it's not octet stream 3339 return false; 3340 } 3341 3342 BPath path(ref); 3343 if (path.Path()) { 3344 // force a mimeset 3345 node.RemoveAttr(kAttrMIMEType); 3346 update_mime_info(path.Path(), 0, 1, 1); 3347 } 3348 3349 return true; 3350 } 3351 3352 3353 static void 3354 SniffIfGeneric(const BMessage* refs) 3355 { 3356 entry_ref ref; 3357 for (int32 index = 0; ; index++) { 3358 if (refs->FindRef("refs", index, &ref) != B_OK) 3359 break; 3360 SniffIfGeneric(&ref); 3361 } 3362 } 3363 3364 3365 static void 3366 _TrackerLaunchAppWithDocuments(const entry_ref* appRef, const BMessage* refs, 3367 bool openWithOK) 3368 { 3369 team_id team; 3370 3371 status_t error = B_ERROR; 3372 BString alertString; 3373 3374 for (int32 mimesetIt = 0; ; mimesetIt++) { 3375 error = be_roster->Launch(appRef, refs, &team); 3376 if (error == B_ALREADY_RUNNING) 3377 // app already running, not really an error 3378 error = B_OK; 3379 3380 if (error == B_OK) 3381 break; 3382 3383 if (mimesetIt > 0) 3384 break; 3385 3386 // failed to open, try mimesetting the refs and launching again 3387 SniffIfGeneric(refs); 3388 } 3389 3390 if (error == B_OK) { 3391 // close possible parent window, if specified 3392 const node_ref* nodeToClose = 0; 3393 ssize_t numBytes; 3394 if (refs != NULL && refs->FindData("nodeRefsToClose", B_RAW_TYPE, 3395 (const void**)&nodeToClose, &numBytes) == B_OK 3396 && nodeToClose != NULL) { 3397 TTracker* tracker = dynamic_cast<TTracker*>(be_app); 3398 if (tracker != NULL) 3399 tracker->CloseParent(*nodeToClose); 3400 } 3401 } else { 3402 alertString.SetTo(B_TRANSLATE("Could not open \"%name\" (%error). ")); 3403 alertString.ReplaceFirst("%name", appRef->name); 3404 alertString.ReplaceFirst("%error", strerror(error)); 3405 if (refs != NULL && openWithOK && error != B_SHUTTING_DOWN) { 3406 alertString << B_TRANSLATE_NOCOLLECT(kFindAlternativeStr); 3407 BAlert* alert = new BAlert("", alertString.String(), 3408 B_TRANSLATE("Cancel"), B_TRANSLATE("Find"), 0, 3409 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 3410 alert->SetShortcut(0, B_ESCAPE); 3411 if (alert->Go() == 1) 3412 error = TrackerOpenWith(refs); 3413 } else { 3414 BAlert* alert = new BAlert("", alertString.String(), 3415 B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, 3416 B_WARNING_ALERT); 3417 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 3418 alert->Go(); 3419 } 3420 } 3421 } 3422 3423 3424 extern "C" char** environ; 3425 3426 3427 static status_t 3428 LoaderErrorDetails(const entry_ref* app, BString &details) 3429 { 3430 BPath path; 3431 BEntry appEntry(app, true); 3432 3433 status_t result = appEntry.GetPath(&path); 3434 if (result != B_OK) 3435 return result; 3436 3437 char* argv[2] = { const_cast<char*>(path.Path()), 0}; 3438 3439 port_id errorPort = create_port(1, "Tracker loader error"); 3440 3441 // count environment variables 3442 int32 envCount = 0; 3443 while (environ[envCount] != NULL) 3444 envCount++; 3445 3446 char** flatArgs = NULL; 3447 size_t flatArgsSize; 3448 result = __flatten_process_args((const char**)argv, 1, 3449 environ, &envCount, argv[0], &flatArgs, &flatArgsSize); 3450 if (result != B_OK) 3451 return result; 3452 3453 result = _kern_load_image(flatArgs, flatArgsSize, 1, envCount, 3454 B_NORMAL_PRIORITY, B_WAIT_TILL_LOADED, errorPort, 0); 3455 if (result == B_OK) { 3456 // we weren't supposed to be able to start the application... 3457 return B_ERROR; 3458 } 3459 3460 // read error message from port and construct details string 3461 3462 ssize_t bufferSize; 3463 3464 do { 3465 bufferSize = port_buffer_size_etc(errorPort, B_RELATIVE_TIMEOUT, 0); 3466 } while (bufferSize == B_INTERRUPTED); 3467 3468 if (bufferSize <= B_OK) { 3469 delete_port(errorPort); 3470 return bufferSize; 3471 } 3472 3473 uint8* buffer = (uint8*)malloc(bufferSize); 3474 if (buffer == NULL) { 3475 delete_port(errorPort); 3476 return B_NO_MEMORY; 3477 } 3478 3479 bufferSize = read_port_etc(errorPort, NULL, buffer, bufferSize, 3480 B_RELATIVE_TIMEOUT, 0); 3481 delete_port(errorPort); 3482 3483 if (bufferSize < B_OK) { 3484 free(buffer); 3485 return bufferSize; 3486 } 3487 3488 BMessage message; 3489 result = message.Unflatten((const char*)buffer); 3490 free(buffer); 3491 3492 if (result != B_OK) 3493 return result; 3494 3495 int32 errorCode = B_ERROR; 3496 result = message.FindInt32("error", &errorCode); 3497 if (result != B_OK) 3498 return result; 3499 3500 const char* detailName = NULL; 3501 switch (errorCode) { 3502 case B_MISSING_LIBRARY: 3503 detailName = "missing library"; 3504 break; 3505 3506 case B_MISSING_SYMBOL: 3507 detailName = "missing symbol"; 3508 break; 3509 } 3510 3511 if (detailName == NULL) 3512 return B_ERROR; 3513 3514 const char* detail; 3515 for (int32 i = 0; message.FindString(detailName, i, &detail) == B_OK; 3516 i++) { 3517 if (i > 0) 3518 details += ", "; 3519 details += detail; 3520 } 3521 3522 return B_OK; 3523 } 3524 3525 3526 static void 3527 _TrackerLaunchDocuments(const entry_ref*, const BMessage* refs, 3528 bool openWithOK) 3529 { 3530 if (refs == NULL) 3531 return; 3532 3533 BMessage copyOfRefs(*refs); 3534 3535 entry_ref documentRef; 3536 if (copyOfRefs.FindRef("refs", &documentRef) != B_OK) { 3537 // nothing to launch, we are done 3538 return; 3539 } 3540 3541 status_t error = B_ERROR; 3542 entry_ref app; 3543 BMessage* refsToPass = NULL; 3544 BString alertString; 3545 const char* alternative = 0; 3546 3547 for (int32 mimesetIt = 0; ; mimesetIt++) { 3548 alertString = ""; 3549 error = be_roster->FindApp(&documentRef, &app); 3550 3551 if (error != B_OK && mimesetIt == 0) { 3552 SniffIfGeneric(©OfRefs); 3553 continue; 3554 } 3555 3556 if (error != B_OK) { 3557 alertString.SetTo(B_TRANSLATE("Could not find an application to " 3558 "open \"%name\" (%error). ")); 3559 alertString.ReplaceFirst("%name", documentRef.name); 3560 alertString.ReplaceFirst("%error", strerror(error)); 3561 if (openWithOK) 3562 alternative = B_TRANSLATE_NOCOLLECT(kFindApplicationStr); 3563 3564 break; 3565 } else { 3566 BEntry appEntry(&app, true); 3567 for (int32 index = 0;;) { 3568 // remove the app itself from the refs received so we don't 3569 // try to open ourselves 3570 entry_ref ref; 3571 if (copyOfRefs.FindRef("refs", index, &ref) != B_OK) 3572 break; 3573 3574 // deal with symlinks properly 3575 BEntry documentEntry(&ref, true); 3576 if (appEntry == documentEntry) { 3577 PRINT(("stripping %s, app %s \n", ref.name, app.name)); 3578 copyOfRefs.RemoveData("refs", index); 3579 } else { 3580 PRINT(("leaving %s, app %s \n", ref.name, app.name)); 3581 index++; 3582 } 3583 } 3584 3585 refsToPass = CountRefs(©OfRefs) > 0 ? ©OfRefs: 0; 3586 team_id team; 3587 error = be_roster->Launch(&app, refsToPass, &team); 3588 if (error == B_ALREADY_RUNNING) 3589 // app already running, not really an error 3590 error = B_OK; 3591 if (error == B_OK || mimesetIt != 0) 3592 break; 3593 if (error == B_LAUNCH_FAILED_EXECUTABLE) { 3594 BVolume volume(documentRef.device); 3595 if (volume.IsReadOnly()) { 3596 BMimeType type; 3597 error = BMimeType::GuessMimeType(&documentRef, &type); 3598 if (error != B_OK) 3599 break; 3600 error = be_roster->FindApp(type.Type(), &app); 3601 if (error != B_OK) 3602 break; 3603 error = be_roster->Launch(&app, refs, &team); 3604 if (error == B_ALREADY_RUNNING) 3605 // app already running, not really an error 3606 error = B_OK; 3607 if (error == B_OK || mimesetIt != 0) 3608 break; 3609 } 3610 } 3611 3612 SniffIfGeneric(©OfRefs); 3613 } 3614 } 3615 3616 if (error != B_OK && alertString.Length() == 0) { 3617 BString loaderErrorString; 3618 bool openedDocuments = true; 3619 3620 if (!refsToPass) { 3621 // we just double clicked the app itself, do not offer to 3622 // find a handling app 3623 openWithOK = false; 3624 openedDocuments = false; 3625 } 3626 if (error == B_UNKNOWN_EXECUTABLE && !refsToPass) { 3627 // We know it's an executable, but something unsupported 3628 alertString.SetTo(B_TRANSLATE("\"%name\" is an unsupported " 3629 "executable.")); 3630 alertString.ReplaceFirst("%name", app.name); 3631 } else if (error == B_LEGACY_EXECUTABLE && !refsToPass) { 3632 // For the moment, this marks an old R3 binary, we may want to 3633 // extend it to gcc2 binaries someday post R1 3634 alertString.SetTo(B_TRANSLATE("\"%name\" is a legacy executable. " 3635 "Please obtain an updated version or recompile " 3636 "the application.")); 3637 alertString.ReplaceFirst("%name", app.name); 3638 } else if (error == B_LAUNCH_FAILED_EXECUTABLE && !refsToPass) { 3639 alertString.SetTo(B_TRANSLATE("Could not open \"%name\". " 3640 "The file is mistakenly marked as executable. ")); 3641 alertString.ReplaceFirst("%name", app.name); 3642 3643 if (!openWithOK) { 3644 // offer the possibility to change the permissions 3645 3646 alertString << B_TRANSLATE("\nShould this be fixed?"); 3647 BAlert* alert = new BAlert("", alertString.String(), 3648 B_TRANSLATE("Cancel"), B_TRANSLATE("Proceed"), 0, 3649 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 3650 alert->SetShortcut(0, B_ESCAPE); 3651 if (alert->Go() == 1) { 3652 BEntry entry(&documentRef); 3653 mode_t permissions; 3654 3655 error = entry.GetPermissions(&permissions); 3656 if (error == B_OK) { 3657 error = entry.SetPermissions(permissions 3658 & ~(S_IXUSR | S_IXGRP | S_IXOTH)); 3659 } 3660 if (error == B_OK) { 3661 // we updated the permissions, so let's try again 3662 _TrackerLaunchDocuments(NULL, refs, false); 3663 return; 3664 } else { 3665 alertString.SetTo(B_TRANSLATE("Could not update " 3666 "permissions of file \"%name\". %error")); 3667 alertString.ReplaceFirst("%name", app.name); 3668 alertString.ReplaceFirst("%error", strerror(error)); 3669 } 3670 } else 3671 return; 3672 } 3673 3674 alternative = B_TRANSLATE_NOCOLLECT(kFindApplicationStr); 3675 } else if (error == B_LAUNCH_FAILED_APP_IN_TRASH) { 3676 alertString.SetTo(B_TRANSLATE("Could not open \"%document\" " 3677 "because application \"%app\" is in the Trash. ")); 3678 alertString.ReplaceFirst("%document", documentRef.name); 3679 alertString.ReplaceFirst("%app", app.name); 3680 alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr); 3681 } else if (error == B_LAUNCH_FAILED_APP_NOT_FOUND) { 3682 alertString.SetTo( 3683 B_TRANSLATE("Could not open \"%name\" (%error). ")); 3684 alertString.ReplaceFirst("%name", documentRef.name); 3685 alertString.ReplaceFirst("%error", strerror(error)); 3686 alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr); 3687 } else if (error == B_MISSING_SYMBOL 3688 && LoaderErrorDetails(&app, loaderErrorString) == B_OK) { 3689 if (openedDocuments) { 3690 alertString.SetTo(B_TRANSLATE("Could not open \"%document\" " 3691 "with application \"%app\" (Missing symbol: %symbol). " 3692 "\n")); 3693 alertString.ReplaceFirst("%document", documentRef.name); 3694 alertString.ReplaceFirst("%app", app.name); 3695 alertString.ReplaceFirst("%symbol", 3696 loaderErrorString.String()); 3697 } else { 3698 alertString.SetTo(B_TRANSLATE("Could not open \"%document\" " 3699 "(Missing symbol: %symbol). \n")); 3700 alertString.ReplaceFirst("%document", documentRef.name); 3701 alertString.ReplaceFirst("%symbol", 3702 loaderErrorString.String()); 3703 } 3704 alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr); 3705 } else if (error == B_MISSING_LIBRARY 3706 && LoaderErrorDetails(&app, loaderErrorString) == B_OK) { 3707 if (openedDocuments) { 3708 alertString.SetTo(B_TRANSLATE("Could not open \"%document\" " 3709 "with application \"%app\" (Missing libraries: %library). " 3710 "\n")); 3711 alertString.ReplaceFirst("%document", documentRef.name); 3712 alertString.ReplaceFirst("%app", app.name); 3713 alertString.ReplaceFirst("%library", 3714 loaderErrorString.String()); 3715 } else { 3716 alertString.SetTo(B_TRANSLATE("Could not open \"%document\" " 3717 "(Missing libraries: %library). \n")); 3718 alertString.ReplaceFirst("%document", documentRef.name); 3719 alertString.ReplaceFirst("%library", 3720 loaderErrorString.String()); 3721 } 3722 alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr); 3723 } else { 3724 alertString.SetTo(B_TRANSLATE("Could not open \"%document\" with " 3725 "application \"%app\" (%error). ")); 3726 alertString.ReplaceFirst("%document", documentRef.name); 3727 alertString.ReplaceFirst("%app", app.name); 3728 alertString.ReplaceFirst("%error", strerror(error)); 3729 alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr); 3730 } 3731 } 3732 3733 if (error != B_OK) { 3734 if (openWithOK) { 3735 ASSERT(alternative); 3736 alertString << alternative; 3737 BAlert* alert = new BAlert("", alertString.String(), 3738 B_TRANSLATE("Cancel"), B_TRANSLATE("Find"), 0, 3739 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 3740 alert->SetShortcut(0, B_ESCAPE); 3741 if (alert->Go() == 1) 3742 error = TrackerOpenWith(refs); 3743 } else { 3744 BAlert* alert = new BAlert("", alertString.String(), 3745 B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, 3746 B_WARNING_ALERT); 3747 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 3748 alert->Go(); 3749 } 3750 } 3751 } 3752 3753 // the following three calls don't return any reasonable error codes, 3754 // should fix that, making them void 3755 3756 status_t 3757 TrackerLaunch(const entry_ref* appRef, const BMessage* refs, bool async, 3758 bool openWithOK) 3759 { 3760 if (!async) 3761 _TrackerLaunchAppWithDocuments(appRef, refs, openWithOK); 3762 else { 3763 AsynchLaunchBinder(&_TrackerLaunchAppWithDocuments, appRef, refs, 3764 openWithOK); 3765 } 3766 3767 return B_OK; 3768 } 3769 3770 status_t 3771 TrackerLaunch(const entry_ref* appRef, bool async) 3772 { 3773 if (!async) 3774 _TrackerLaunchAppWithDocuments(appRef, NULL, false); 3775 else 3776 AsynchLaunchBinder(&_TrackerLaunchAppWithDocuments, appRef, 0, false); 3777 3778 return B_OK; 3779 } 3780 3781 status_t 3782 TrackerLaunch(const BMessage* refs, bool async, bool openWithOK) 3783 { 3784 if (!async) 3785 _TrackerLaunchDocuments(NULL, refs, openWithOK); 3786 else 3787 AsynchLaunchBinder(&_TrackerLaunchDocuments, NULL, refs, openWithOK); 3788 3789 return B_OK; 3790 } 3791 3792 3793 // external launch calls; need to be robust, work if Tracker is not running 3794 3795 3796 #if !B_BEOS_VERSION_DANO 3797 _IMPEXP_TRACKER 3798 #endif 3799 status_t 3800 FSLaunchItem(const entry_ref* application, const BMessage* refsReceived, 3801 bool async, bool openWithOK) 3802 { 3803 return TrackerLaunch(application, refsReceived, async, openWithOK); 3804 } 3805 3806 3807 #if !B_BEOS_VERSION_DANO 3808 _IMPEXP_TRACKER 3809 #endif 3810 status_t 3811 FSOpenWith(BMessage* listOfRefs) 3812 { 3813 status_t result = B_ERROR; 3814 listOfRefs->what = B_REFS_RECEIVED; 3815 3816 if (dynamic_cast<TTracker*>(be_app) != NULL) 3817 result = TrackerOpenWith(listOfRefs); 3818 else 3819 ASSERT(!"not yet implemented"); 3820 3821 return result; 3822 } 3823 3824 3825 // legacy calls, need for compatibility 3826 3827 3828 void 3829 FSOpenWithDocuments(const entry_ref* executable, BMessage* documents) 3830 { 3831 TrackerLaunch(executable, documents, true); 3832 delete documents; 3833 } 3834 3835 3836 status_t 3837 FSLaunchUsing(const entry_ref* ref, BMessage* listOfRefs) 3838 { 3839 BMessage temp(B_REFS_RECEIVED); 3840 if (listOfRefs == NULL) { 3841 ASSERT(ref != NULL); 3842 temp.AddRef("refs", ref); 3843 listOfRefs = &temp; 3844 } 3845 FSOpenWith(listOfRefs); 3846 3847 return B_OK; 3848 } 3849 3850 3851 status_t 3852 FSLaunchItem(const entry_ref* appRef, BMessage* refs, int32, bool async) 3853 { 3854 if (refs != NULL) 3855 refs->what = B_REFS_RECEIVED; 3856 3857 status_t result = TrackerLaunch(appRef, refs, async, true); 3858 delete refs; 3859 3860 return result; 3861 } 3862 3863 3864 void 3865 FSLaunchItem(const entry_ref* appRef, BMessage* refs, int32 workspace) 3866 { 3867 FSLaunchItem(appRef, refs, workspace, true); 3868 } 3869 3870 3871 // Get the original path of an entry in the trash 3872 status_t 3873 FSGetOriginalPath(BEntry* entry, BPath* result) 3874 { 3875 status_t err; 3876 entry_ref ref; 3877 err = entry->GetRef(&ref); 3878 if (err != B_OK) 3879 return err; 3880 3881 // Only call the routine for entries in the trash 3882 if (!FSInTrashDir(&ref)) 3883 return B_ERROR; 3884 3885 BNode node(entry); 3886 BString originalPath; 3887 if (node.ReadAttrString(kAttrOriginalPath, &originalPath) == B_OK) { 3888 // We're in luck, the entry has the original path in an attribute 3889 err = result->SetTo(originalPath.String()); 3890 return err; 3891 } 3892 3893 // Iterate the parent directories to find one with 3894 // the original path attribute 3895 BEntry parent(*entry); 3896 err = parent.InitCheck(); 3897 if (err != B_OK) 3898 return err; 3899 3900 // walk up the directory structure until we find a node 3901 // with original path attribute 3902 do { 3903 // move to the parent of this node 3904 err = parent.GetParent(&parent); 3905 if (err != B_OK) 3906 return err; 3907 3908 // return if we are at the root of the trash 3909 if (FSIsTrashDir(&parent)) 3910 return B_ENTRY_NOT_FOUND; 3911 3912 // get the parent as a node 3913 err = node.SetTo(&parent); 3914 if (err != B_OK) 3915 return err; 3916 } while (node.ReadAttrString(kAttrOriginalPath, &originalPath) != B_OK); 3917 3918 // Found the attribute, figure out there this file 3919 // used to live, based on the successfully-read attribute 3920 err = result->SetTo(originalPath.String()); 3921 if (err != B_OK) 3922 return err; 3923 3924 BPath path, pathParent; 3925 err = parent.GetPath(&pathParent); 3926 if (err != B_OK) 3927 return err; 3928 3929 err = entry->GetPath(&path); 3930 if (err != B_OK) 3931 return err; 3932 3933 result->Append(path.Path() + strlen(pathParent.Path()) + 1); 3934 // compute the new path by appending the offset of 3935 // the item we are locating, to the original path 3936 // of the parent 3937 3938 return B_OK; 3939 } 3940 3941 3942 directory_which 3943 WellKnowEntryList::Match(const node_ref* node) 3944 { 3945 const WellKnownEntry* result = MatchEntry(node); 3946 if (result != NULL) 3947 return result->which; 3948 3949 return (directory_which)-1; 3950 } 3951 3952 3953 const WellKnowEntryList::WellKnownEntry* 3954 WellKnowEntryList::MatchEntry(const node_ref* node) 3955 { 3956 if (self == NULL) 3957 self = new WellKnowEntryList(); 3958 3959 return self->MatchEntryCommon(node); 3960 } 3961 3962 3963 const WellKnowEntryList::WellKnownEntry* 3964 WellKnowEntryList::MatchEntryCommon(const node_ref* node) 3965 { 3966 uint32 count = entries.size(); 3967 for (uint32 index = 0; index < count; index++) { 3968 if (*node == entries[index].node) 3969 return &entries[index]; 3970 } 3971 3972 return NULL; 3973 } 3974 3975 3976 void 3977 WellKnowEntryList::Quit() 3978 { 3979 delete self; 3980 self = NULL; 3981 } 3982 3983 3984 void 3985 WellKnowEntryList::AddOne(directory_which which, const char* name) 3986 { 3987 BPath path; 3988 if (find_directory(which, &path, true) != B_OK) 3989 return; 3990 3991 BEntry entry(path.Path(), true); 3992 node_ref node; 3993 if (entry.GetNodeRef(&node) != B_OK) 3994 return; 3995 3996 entries.push_back(WellKnownEntry(&node, which, name)); 3997 } 3998 3999 4000 void 4001 WellKnowEntryList::AddOne(directory_which which, directory_which base, 4002 const char* extra, const char* name) 4003 { 4004 BPath path; 4005 if (find_directory(base, &path, true) != B_OK) 4006 return; 4007 4008 path.Append(extra); 4009 BEntry entry(path.Path(), true); 4010 node_ref node; 4011 if (entry.GetNodeRef(&node) != B_OK) 4012 return; 4013 4014 entries.push_back(WellKnownEntry(&node, which, name)); 4015 } 4016 4017 4018 void 4019 WellKnowEntryList::AddOne(directory_which which, const char* path, 4020 const char* name) 4021 { 4022 BEntry entry(path, true); 4023 node_ref node; 4024 if (entry.GetNodeRef(&node) != B_OK) 4025 return; 4026 4027 entries.push_back(WellKnownEntry(&node, which, name)); 4028 } 4029 4030 4031 WellKnowEntryList::WellKnowEntryList() 4032 { 4033 AddOne(B_SYSTEM_DIRECTORY, "system"); 4034 AddOne((directory_which)B_BOOT_DISK, "/boot", "boot"); 4035 AddOne(B_USER_DIRECTORY, "home"); 4036 4037 AddOne(B_BEOS_FONTS_DIRECTORY, "fonts"); 4038 AddOne(B_USER_FONTS_DIRECTORY, "fonts"); 4039 4040 AddOne(B_BEOS_APPS_DIRECTORY, "apps"); 4041 AddOne(B_APPS_DIRECTORY, "apps"); 4042 AddOne((directory_which)B_USER_DESKBAR_APPS_DIRECTORY, 4043 B_USER_DESKBAR_DIRECTORY, "Applications", "apps"); 4044 4045 AddOne(B_BEOS_PREFERENCES_DIRECTORY, "preferences"); 4046 AddOne(B_PREFERENCES_DIRECTORY, "preferences"); 4047 AddOne((directory_which)B_USER_DESKBAR_PREFERENCES_DIRECTORY, 4048 B_USER_DESKBAR_DIRECTORY, "Preferences", "preferences"); 4049 4050 AddOne((directory_which)B_USER_MAIL_DIRECTORY, B_USER_DIRECTORY, "mail", 4051 "mail"); 4052 4053 AddOne((directory_which)B_USER_QUERIES_DIRECTORY, B_USER_DIRECTORY, 4054 "queries", "queries"); 4055 4056 AddOne(B_SYSTEM_DEVELOP_DIRECTORY, "develop"); 4057 AddOne((directory_which)B_USER_DESKBAR_DEVELOP_DIRECTORY, 4058 B_USER_DESKBAR_DIRECTORY, "Development", "develop"); 4059 4060 AddOne(B_USER_CONFIG_DIRECTORY, "config"); 4061 4062 AddOne((directory_which)B_USER_PEOPLE_DIRECTORY, B_USER_DIRECTORY, 4063 "people", "people"); 4064 4065 AddOne((directory_which)B_USER_DOWNLOADS_DIRECTORY, B_USER_DIRECTORY, 4066 "downloads", "downloads"); 4067 } 4068 4069 WellKnowEntryList* WellKnowEntryList::self = NULL; 4070 4071 } // namespace BPrivate 4072