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