1 // FSFolder.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../Common/ComTry.h"
6 #include "../../../Common/Defs.h"
7 #include "../../../Common/StringConvert.h"
8 #include "../../../Common/UTFConvert.h"
9
10 #include "../../../Windows/FileDir.h"
11 #include "../../../Windows/FileIO.h"
12 #include "../../../Windows/FileName.h"
13 #include "../../../Windows/PropVariant.h"
14
15 #include "../../PropID.h"
16
17 #include "FSDrives.h"
18 #include "FSFolder.h"
19
20 #ifndef UNDER_CE
21 #include "NetFolder.h"
22 #endif
23
24 #include "SysIconUtils.h"
25
26 #if _WIN32_WINNT < 0x0501
27 #ifdef _APISETFILE_
28 // Windows SDK 8.1 defines in fileapi.h the function GetCompressedFileSizeW only if _WIN32_WINNT >= 0x0501
29 // But real support version for that function is NT 3.1 (probably)
30 // So we must define GetCompressedFileSizeW
31 EXTERN_C_BEGIN
32 WINBASEAPI DWORD WINAPI GetCompressedFileSizeW(LPCWSTR lpFileName, LPDWORD lpFileSizeHigh);
33 EXTERN_C_END
34 #endif
35 #endif
36
37 using namespace NWindows;
38 using namespace NFile;
39 using namespace NFind;
40 using namespace NDir;
41 using namespace NName;
42
43 #ifndef USE_UNICODE_FSTRING
CompareFileNames_ForFolderList(const FChar * s1,const FChar * s2)44 int CompareFileNames_ForFolderList(const FChar *s1, const FChar *s2)
45 {
46 return CompareFileNames_ForFolderList(fs2us(s1), fs2us(s2));
47 }
48 #endif
49
50 namespace NFsFolder {
51
52 static const Byte kProps[] =
53 {
54 kpidName,
55 kpidSize,
56 kpidMTime,
57 kpidCTime,
58 kpidATime,
59 kpidAttrib,
60 kpidPackSize,
61 #ifdef FS_SHOW_LINKS_INFO
62 kpidINode,
63 kpidLinks,
64 #endif
65 kpidComment,
66 kpidNumSubDirs,
67 kpidNumSubFiles,
68 kpidPrefix
69 };
70
Init(const FString & path)71 HRESULT CFSFolder::Init(const FString &path /* , IFolderFolder *parentFolder */)
72 {
73 // _parentFolder = parentFolder;
74 _path = path;
75
76 #ifdef _WIN32
77
78 _findChangeNotification.FindFirst(_path, false,
79 FILE_NOTIFY_CHANGE_FILE_NAME
80 | FILE_NOTIFY_CHANGE_DIR_NAME
81 | FILE_NOTIFY_CHANGE_ATTRIBUTES
82 | FILE_NOTIFY_CHANGE_SIZE
83 | FILE_NOTIFY_CHANGE_LAST_WRITE
84 /*
85 | FILE_NOTIFY_CHANGE_LAST_ACCESS
86 | FILE_NOTIFY_CHANGE_CREATION
87 | FILE_NOTIFY_CHANGE_SECURITY
88 */
89 );
90
91 if (!_findChangeNotification.IsHandleAllocated())
92 {
93 DWORD lastError = GetLastError();
94 CFindFile findFile;
95 CFileInfo fi;
96 FString path2 = _path;
97 path2 += '*'; // CHAR_ANY_MASK;
98 if (!findFile.FindFirst(path2, fi))
99 return lastError;
100 }
101
102 #endif
103
104 return S_OK;
105 }
106
107
Enumerate()108 HRESULT CFsFolderStat::Enumerate()
109 {
110 if (Progress)
111 {
112 RINOK(Progress->SetCompleted(NULL));
113 }
114 Path.Add_PathSepar();
115 const unsigned len = Path.Len();
116 CEnumerator enumerator;
117 enumerator.SetDirPrefix(Path);
118 CDirEntry fi;
119 while (enumerator.Next(fi))
120 {
121 if (fi.IsDir())
122 {
123 NumFolders++;
124 Path.DeleteFrom(len);
125 Path += fi.Name;
126 RINOK(Enumerate());
127 }
128 else
129 {
130 NumFiles++;
131 Size += fi.Size;
132 }
133 }
134 return S_OK;
135 }
136
137 #ifndef UNDER_CE
138
139 bool MyGetCompressedFileSizeW(CFSTR path, UInt64 &size);
MyGetCompressedFileSizeW(CFSTR path,UInt64 & size)140 bool MyGetCompressedFileSizeW(CFSTR path, UInt64 &size)
141 {
142 DWORD highPart;
143 DWORD lowPart = INVALID_FILE_SIZE;
144 IF_USE_MAIN_PATH
145 {
146 lowPart = ::GetCompressedFileSizeW(fs2us(path), &highPart);
147 if (lowPart != INVALID_FILE_SIZE || ::GetLastError() == NO_ERROR)
148 {
149 size = ((UInt64)highPart << 32) | lowPart;
150 return true;
151 }
152 }
153 #ifdef WIN_LONG_PATH
154 if (USE_SUPER_PATH)
155 {
156 UString superPath;
157 if (GetSuperPath(path, superPath, USE_MAIN_PATH))
158 {
159 lowPart = ::GetCompressedFileSizeW(superPath, &highPart);
160 if (lowPart != INVALID_FILE_SIZE || ::GetLastError() == NO_ERROR)
161 {
162 size = ((UInt64)highPart << 32) | lowPart;
163 return true;
164 }
165 }
166 }
167 #endif
168 return false;
169 }
170
171 #endif
172
LoadSubItems(int dirItem,const FString & relPrefix)173 HRESULT CFSFolder::LoadSubItems(int dirItem, const FString &relPrefix)
174 {
175 const unsigned startIndex = Folders.Size();
176 {
177 CEnumerator enumerator;
178 enumerator.SetDirPrefix(_path + relPrefix);
179 CDirItem fi;
180 fi.FolderStat_Defined = false;
181 fi.NumFolders = 0;
182 fi.NumFiles = 0;
183 fi.Parent = dirItem;
184
185 while (enumerator.Next(fi))
186 {
187 if (fi.IsDir())
188 {
189 fi.Size = 0;
190 if (_flatMode)
191 Folders.Add(relPrefix + fi.Name + FCHAR_PATH_SEPARATOR);
192 }
193 else
194 {
195 /*
196 fi.PackSize_Defined = true;
197 if (!MyGetCompressedFileSizeW(_path + relPrefix + fi.Name, fi.PackSize))
198 fi.PackSize = fi.Size;
199 */
200 }
201
202 #ifndef UNDER_CE
203
204 fi.Reparse.Free();
205 fi.PackSize_Defined = false;
206
207 #ifdef FS_SHOW_LINKS_INFO
208 fi.FileInfo_Defined = false;
209 fi.FileInfo_WasRequested = false;
210 fi.FileIndex = 0;
211 fi.NumLinks = 0;
212 #endif
213
214 fi.PackSize = fi.Size;
215 if (fi.HasReparsePoint())
216 {
217 fi.FileInfo_WasRequested = true;
218 BY_HANDLE_FILE_INFORMATION info;
219 NIO::GetReparseData(_path + relPrefix + fi.Name, fi.Reparse, &info);
220 fi.NumLinks = info.nNumberOfLinks;
221 fi.FileIndex = (((UInt64)info.nFileIndexHigh) << 32) + info.nFileIndexLow;
222 fi.FileInfo_Defined = true;
223 }
224
225 #endif
226
227 /* unsigned fileIndex = */ Files.Add(fi);
228
229 #if defined(_WIN32) && !defined(UNDER_CE)
230 /*
231 if (_scanAltStreams)
232 {
233 CStreamEnumerator enumerator(_path + relPrefix + fi.Name);
234 CStreamInfo si;
235 for (;;)
236 {
237 bool found;
238 if (!enumerator.Next(si, found))
239 {
240 // if (GetLastError() == ERROR_ACCESS_DENIED)
241 // break;
242 // return E_FAIL;
243 break;
244 }
245 if (!found)
246 break;
247 if (si.IsMainStream())
248 continue;
249 CAltStream ss;
250 ss.Parent = fileIndex;
251 ss.Name = si.GetReducedName();
252 ss.Size = si.Size;
253 ss.PackSize_Defined = false;
254 ss.PackSize = si.Size;
255 Streams.Add(ss);
256 }
257 }
258 */
259 #endif
260 }
261 }
262 if (!_flatMode)
263 return S_OK;
264
265 const unsigned endIndex = Folders.Size();
266 for (unsigned i = startIndex; i < endIndex; i++)
267 LoadSubItems(i, Folders[i]);
268 return S_OK;
269 }
270
LoadItems()271 STDMETHODIMP CFSFolder::LoadItems()
272 {
273 Int32 dummy;
274 WasChanged(&dummy);
275 Clear();
276 RINOK(LoadSubItems(-1, FString()));
277 _commentsAreLoaded = false;
278 return S_OK;
279 }
280
281 static CFSTR const kDescriptionFileName = FTEXT("descript.ion");
282
LoadComments()283 bool CFSFolder::LoadComments()
284 {
285 _comments.Clear();
286 _commentsAreLoaded = true;
287 NIO::CInFile file;
288 if (!file.Open(_path + kDescriptionFileName))
289 return false;
290 UInt64 len;
291 if (!file.GetLength(len))
292 return false;
293 if (len >= (1 << 28))
294 return false;
295 AString s;
296 char *p = s.GetBuf((unsigned)(size_t)len);
297 size_t processedSize;
298 if (!file.ReadFull(p, (unsigned)(size_t)len, processedSize))
299 return false;
300 s.ReleaseBuf_CalcLen((unsigned)(size_t)len);
301 if (processedSize != len)
302 return false;
303 file.Close();
304 UString unicodeString;
305 if (!ConvertUTF8ToUnicode(s, unicodeString))
306 return false;
307 return _comments.ReadFromString(unicodeString);
308 }
309
SaveComments()310 bool CFSFolder::SaveComments()
311 {
312 AString utf;
313 {
314 UString unicode;
315 _comments.SaveToString(unicode);
316 ConvertUnicodeToUTF8(unicode, utf);
317 }
318 if (!utf.IsAscii())
319 utf.Insert(0, "\xEF\xBB\xBF" "\r\n");
320
321 FString path = _path + kDescriptionFileName;
322 // We must set same attrib. COutFile::CreateAlways can fail, if file has another attrib.
323 DWORD attrib = FILE_ATTRIBUTE_NORMAL;
324 {
325 CFileInfo fi;
326 if (fi.Find(path))
327 attrib = fi.Attrib;
328 }
329 NIO::COutFile file;
330 if (!file.CreateAlways(path, attrib))
331 return false;
332 UInt32 processed;
333 file.Write(utf, utf.Len(), processed);
334 _commentsAreLoaded = false;
335 return true;
336 }
337
GetNumberOfItems(UInt32 * numItems)338 STDMETHODIMP CFSFolder::GetNumberOfItems(UInt32 *numItems)
339 {
340 *numItems = Files.Size() /* + Streams.Size() */;
341 return S_OK;
342 }
343
344 #ifdef USE_UNICODE_FSTRING
345
GetItemPrefix(UInt32 index,const wchar_t ** name,unsigned * len)346 STDMETHODIMP CFSFolder::GetItemPrefix(UInt32 index, const wchar_t **name, unsigned *len)
347 {
348 *name = 0;
349 *len = 0;
350 /*
351 if (index >= Files.Size())
352 index = Streams[index - Files.Size()].Parent;
353 */
354 CDirItem &fi = Files[index];
355 if (fi.Parent >= 0)
356 {
357 const FString &fo = Folders[fi.Parent];
358 USE_UNICODE_FSTRING
359 *name = fo;
360 *len = fo.Len();
361 }
362 return S_OK;
363 }
364
GetItemName(UInt32 index,const wchar_t ** name,unsigned * len)365 STDMETHODIMP CFSFolder::GetItemName(UInt32 index, const wchar_t **name, unsigned *len)
366 {
367 *name = 0;
368 *len = 0;
369 if (index < Files.Size())
370 {
371 CDirItem &fi = Files[index];
372 *name = fi.Name;
373 *len = fi.Name.Len();
374 return S_OK;
375 }
376 else
377 {
378 // const CAltStream &ss = Streams[index - Files.Size()];
379 // *name = ss.Name;
380 // *len = ss.Name.Len();
381 //
382 // change it;
383 }
384 return S_OK;
385 }
386
STDMETHODIMP_(UInt64)387 STDMETHODIMP_(UInt64) CFSFolder::GetItemSize(UInt32 index)
388 {
389 /*
390 if (index >= Files.Size())
391 return Streams[index - Files.Size()].Size;
392 */
393 CDirItem &fi = Files[index];
394 return fi.IsDir() ? 0 : fi.Size;
395 }
396
397 #endif
398
399 #ifdef FS_SHOW_LINKS_INFO
ReadFileInfo(CDirItem & di)400 bool CFSFolder::ReadFileInfo(CDirItem &di)
401 {
402 di.FileInfo_WasRequested = true;
403 BY_HANDLE_FILE_INFORMATION info;
404 memset(&info, 0, sizeof(info)); // for vc6-O2
405 if (!NIO::CFileBase::GetFileInformation(_path + GetRelPath(di), &info))
406 return false;
407 di.NumLinks = info.nNumberOfLinks;
408 di.FileIndex = (((UInt64)info.nFileIndexHigh) << 32) + info.nFileIndexLow;
409 di.FileInfo_Defined = true;
410 return true;
411 }
412 #endif
413
GetProperty(UInt32 index,PROPID propID,PROPVARIANT * value)414 STDMETHODIMP CFSFolder::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
415 {
416 NCOM::CPropVariant prop;
417 /*
418 if (index >= (UInt32)Files.Size())
419 {
420 CAltStream &ss = Streams[index - Files.Size()];
421 CDirItem &fi = Files[ss.Parent];
422 switch (propID)
423 {
424 case kpidIsDir: prop = false; break;
425 case kpidIsAltStream: prop = true; break;
426 case kpidName: prop = fs2us(fi.Name) + ss.Name; break;
427 case kpidSize: prop = ss.Size; break;
428 case kpidPackSize:
429 #ifdef UNDER_CE
430 prop = ss.Size;
431 #else
432 if (!ss.PackSize_Defined)
433 {
434 ss.PackSize_Defined = true;
435 if (!MyGetCompressedFileSizeW(_path + GetRelPath(fi) + us2fs(ss.Name), ss.PackSize))
436 ss.PackSize = ss.Size;
437 }
438 prop = ss.PackSize;
439 #endif
440 break;
441 case kpidComment: break;
442 default: index = ss.Parent;
443 }
444 if (index >= (UInt32)Files.Size())
445 {
446 prop.Detach(value);
447 return S_OK;
448 }
449 }
450 */
451 CDirItem &fi = Files[index];
452 switch (propID)
453 {
454 case kpidIsDir: prop = fi.IsDir(); break;
455 case kpidIsAltStream: prop = false; break;
456 case kpidName: prop = fs2us(fi.Name); break;
457 case kpidSize: if (!fi.IsDir() || fi.FolderStat_Defined) prop = fi.Size; break;
458 case kpidPackSize:
459 #ifdef UNDER_CE
460 prop = fi.Size;
461 #else
462 if (!fi.PackSize_Defined)
463 {
464 fi.PackSize_Defined = true;
465 if (fi.IsDir () || !MyGetCompressedFileSizeW(_path + GetRelPath(fi), fi.PackSize))
466 fi.PackSize = fi.Size;
467 }
468 prop = fi.PackSize;
469 #endif
470 break;
471
472 #ifdef FS_SHOW_LINKS_INFO
473
474 case kpidLinks:
475 #ifdef UNDER_CE
476 // prop = fi.NumLinks;
477 #else
478 if (!fi.FileInfo_WasRequested)
479 ReadFileInfo(fi);
480 if (fi.FileInfo_Defined)
481 prop = fi.NumLinks;
482 #endif
483 break;
484
485 case kpidINode:
486 #ifdef UNDER_CE
487 // prop = fi.FileIndex;
488 #else
489 if (!fi.FileInfo_WasRequested)
490 ReadFileInfo(fi);
491 if (fi.FileInfo_Defined)
492 prop = fi.FileIndex;
493 #endif
494 break;
495
496 #endif
497
498 case kpidAttrib: prop = (UInt32)fi.Attrib; break;
499 case kpidCTime: prop = fi.CTime; break;
500 case kpidATime: prop = fi.ATime; break;
501 case kpidMTime: prop = fi.MTime; break;
502 case kpidComment:
503 {
504 if (!_commentsAreLoaded)
505 LoadComments();
506 UString comment;
507 if (_comments.GetValue(fs2us(GetRelPath(fi)), comment))
508 {
509 int pos = comment.Find((wchar_t)4);
510 if (pos >= 0)
511 comment.DeleteFrom((unsigned)pos);
512 prop = comment;
513 }
514 break;
515 }
516 case kpidPrefix:
517 if (fi.Parent >= 0)
518 prop = fs2us(Folders[fi.Parent]);
519 break;
520 case kpidNumSubDirs: if (fi.IsDir() && fi.FolderStat_Defined) prop = fi.NumFolders; break;
521 case kpidNumSubFiles: if (fi.IsDir() && fi.FolderStat_Defined) prop = fi.NumFiles; break;
522 }
523 prop.Detach(value);
524 return S_OK;
525 }
526
527
528 // ---------- IArchiveGetRawProps ----------
529
530
GetNumRawProps(UInt32 * numProps)531 STDMETHODIMP CFSFolder::GetNumRawProps(UInt32 *numProps)
532 {
533 *numProps = 1;
534 return S_OK;
535 }
536
GetRawPropInfo(UInt32,BSTR * name,PROPID * propID)537 STDMETHODIMP CFSFolder::GetRawPropInfo(UInt32 /* index */, BSTR *name, PROPID *propID)
538 {
539 *name = NULL;
540 *propID = kpidNtReparse;
541 return S_OK;
542 }
543
GetParent(UInt32,UInt32 *,UInt32 *)544 STDMETHODIMP CFSFolder::GetParent(UInt32 /* index */, UInt32 * /* parent */, UInt32 * /* parentType */)
545 {
546 return E_FAIL;
547 }
548
GetRawProp(UInt32 index,PROPID propID,const void ** data,UInt32 * dataSize,UInt32 * propType)549 STDMETHODIMP CFSFolder::GetRawProp(UInt32
550 #ifndef UNDER_CE
551 index
552 #endif
553 , PROPID
554 #ifndef UNDER_CE
555 propID
556 #endif
557 , const void **data, UInt32 *dataSize, UInt32 *propType)
558 {
559 *data = NULL;
560 *dataSize = 0;
561 *propType = 0;
562
563 #ifndef UNDER_CE
564 if (propID == kpidNtReparse)
565 {
566 const CDirItem &fi = Files[index];
567 const CByteBuffer &buf = fi.Reparse;
568 if (buf.Size() == 0)
569 return S_OK;
570 *data = buf;
571 *dataSize = (UInt32)buf.Size();
572 *propType = NPropDataType::kRaw;
573 return S_OK;
574 }
575 #endif
576
577 return S_OK;
578 }
579
580
581 // returns Position of extension including '.'
582
GetExtensionPtr(const FString & name)583 static inline CFSTR GetExtensionPtr(const FString &name)
584 {
585 int dotPos = name.ReverseFind_Dot();
586 return name.Ptr((dotPos < 0) ? name.Len() : dotPos);
587 }
588
STDMETHODIMP_(Int32)589 STDMETHODIMP_(Int32) CFSFolder::CompareItems(UInt32 index1, UInt32 index2, PROPID propID, Int32 /* propIsRaw */)
590 {
591 /*
592 const CAltStream *ss1 = NULL;
593 const CAltStream *ss2 = NULL;
594 if (index1 >= (UInt32)Files.Size()) { ss1 = &Streams[index1 - Files.Size()]; index1 = ss1->Parent; }
595 if (index2 >= (UInt32)Files.Size()) { ss2 = &Streams[index2 - Files.Size()]; index2 = ss2->Parent; }
596 */
597 CDirItem &fi1 = Files[index1];
598 CDirItem &fi2 = Files[index2];
599
600 switch (propID)
601 {
602 case kpidName:
603 {
604 int comp = CompareFileNames_ForFolderList(fi1.Name, fi2.Name);
605 /*
606 if (comp != 0)
607 return comp;
608 if (!ss1)
609 return ss2 ? -1 : 0;
610 if (!ss2)
611 return 1;
612 return MyStringCompareNoCase(ss1->Name, ss2->Name);
613 */
614 return comp;
615 }
616 case kpidSize:
617 return MyCompare(
618 /* ss1 ? ss1->Size : */ fi1.Size,
619 /* ss2 ? ss2->Size : */ fi2.Size);
620 case kpidAttrib: return MyCompare(fi1.Attrib, fi2.Attrib);
621 case kpidCTime: return CompareFileTime(&fi1.CTime, &fi2.CTime);
622 case kpidATime: return CompareFileTime(&fi1.ATime, &fi2.ATime);
623 case kpidMTime: return CompareFileTime(&fi1.MTime, &fi2.MTime);
624 case kpidIsDir:
625 {
626 bool isDir1 = /* ss1 ? false : */ fi1.IsDir();
627 bool isDir2 = /* ss2 ? false : */ fi2.IsDir();
628 if (isDir1 == isDir2)
629 return 0;
630 return isDir1 ? -1 : 1;
631 }
632 case kpidPackSize:
633 {
634 #ifdef UNDER_CE
635 return MyCompare(fi1.Size, fi2.Size);
636 #else
637 // PackSize can be undefined here
638 return MyCompare(
639 /* ss1 ? ss1->PackSize : */ fi1.PackSize,
640 /* ss2 ? ss2->PackSize : */ fi2.PackSize);
641 #endif
642 }
643
644 #ifdef FS_SHOW_LINKS_INFO
645 case kpidINode:
646 {
647 #ifndef UNDER_CE
648 if (!fi1.FileInfo_WasRequested) ReadFileInfo(fi1);
649 if (!fi2.FileInfo_WasRequested) ReadFileInfo(fi2);
650 return MyCompare(
651 fi1.FileIndex,
652 fi2.FileIndex);
653 #endif
654 }
655 case kpidLinks:
656 {
657 #ifndef UNDER_CE
658 if (!fi1.FileInfo_WasRequested) ReadFileInfo(fi1);
659 if (!fi2.FileInfo_WasRequested) ReadFileInfo(fi2);
660 return MyCompare(
661 fi1.NumLinks,
662 fi2.NumLinks);
663 #endif
664 }
665 #endif
666
667 case kpidComment:
668 {
669 // change it !
670 UString comment1, comment2;
671 _comments.GetValue(fs2us(GetRelPath(fi1)), comment1);
672 _comments.GetValue(fs2us(GetRelPath(fi2)), comment2);
673 return MyStringCompareNoCase(comment1, comment2);
674 }
675 case kpidPrefix:
676 if (fi1.Parent < 0) return (fi2.Parent < 0) ? 0 : -1;
677 if (fi2.Parent < 0) return 1;
678 return CompareFileNames_ForFolderList(
679 Folders[fi1.Parent],
680 Folders[fi2.Parent]);
681 case kpidExtension:
682 return CompareFileNames_ForFolderList(
683 GetExtensionPtr(fi1.Name),
684 GetExtensionPtr(fi2.Name));
685 }
686
687 return 0;
688 }
689
BindToFolderSpec(CFSTR name,IFolderFolder ** resultFolder)690 HRESULT CFSFolder::BindToFolderSpec(CFSTR name, IFolderFolder **resultFolder)
691 {
692 *resultFolder = 0;
693 CFSFolder *folderSpec = new CFSFolder;
694 CMyComPtr<IFolderFolder> subFolder = folderSpec;
695 RINOK(folderSpec->Init(_path + name + FCHAR_PATH_SEPARATOR));
696 *resultFolder = subFolder.Detach();
697 return S_OK;
698 }
699
700 /*
701 void CFSFolder::GetPrefix(const CDirItem &item, FString &prefix) const
702 {
703 if (item.Parent >= 0)
704 prefix = Folders[item.Parent];
705 else
706 prefix.Empty();
707 }
708 */
709
710 /*
711 void CFSFolder::GetPrefix(const CDirItem &item, FString &prefix) const
712 {
713 int parent = item.Parent;
714
715 unsigned len = 0;
716
717 while (parent >= 0)
718 {
719 const CDirItem &cur = Files[parent];
720 len += cur.Name.Len() + 1;
721 parent = cur.Parent;
722 }
723
724 wchar_t *p = prefix.GetBuf_SetEnd(len) + len;
725 parent = item.Parent;
726
727 while (parent >= 0)
728 {
729 const CDirItem &cur = Files[parent];
730 *(--p) = FCHAR_PATH_SEPARATOR;
731 p -= cur.Name.Len();
732 wmemcpy(p, cur.Name, cur.Name.Len());
733 parent = cur.Parent;
734 }
735 }
736 */
737
GetRelPath(const CDirItem & item) const738 FString CFSFolder::GetRelPath(const CDirItem &item) const
739 {
740 if (item.Parent < 0)
741 return item.Name;
742 return Folders[item.Parent] + item.Name;
743 }
744
BindToFolder(UInt32 index,IFolderFolder ** resultFolder)745 STDMETHODIMP CFSFolder::BindToFolder(UInt32 index, IFolderFolder **resultFolder)
746 {
747 *resultFolder = 0;
748 const CDirItem &fi = Files[index];
749 if (!fi.IsDir())
750 return E_INVALIDARG;
751 return BindToFolderSpec(GetRelPath(fi), resultFolder);
752 }
753
BindToFolder(const wchar_t * name,IFolderFolder ** resultFolder)754 STDMETHODIMP CFSFolder::BindToFolder(const wchar_t *name, IFolderFolder **resultFolder)
755 {
756 return BindToFolderSpec(us2fs(name), resultFolder);
757 }
758
BindToParentFolder(IFolderFolder ** resultFolder)759 STDMETHODIMP CFSFolder::BindToParentFolder(IFolderFolder **resultFolder)
760 {
761 *resultFolder = 0;
762 /*
763 if (_parentFolder)
764 {
765 CMyComPtr<IFolderFolder> parentFolder = _parentFolder;
766 *resultFolder = parentFolder.Detach();
767 return S_OK;
768 }
769 */
770 if (_path.IsEmpty())
771 return E_INVALIDARG;
772
773 #ifndef UNDER_CE
774
775 if (IsDriveRootPath_SuperAllowed(_path))
776 {
777 CFSDrives *drivesFolderSpec = new CFSDrives;
778 CMyComPtr<IFolderFolder> drivesFolder = drivesFolderSpec;
779 drivesFolderSpec->Init(false, IsSuperPath(_path));
780 *resultFolder = drivesFolder.Detach();
781 return S_OK;
782 }
783
784 int pos = _path.ReverseFind_PathSepar();
785 if (pos < 0 || pos != (int)_path.Len() - 1)
786 return E_FAIL;
787 FString parentPath = _path.Left(pos);
788 pos = parentPath.ReverseFind_PathSepar();
789 parentPath.DeleteFrom((unsigned)(pos + 1));
790
791 if (NName::IsDrivePath_SuperAllowed(parentPath))
792 {
793 CFSFolder *parentFolderSpec = new CFSFolder;
794 CMyComPtr<IFolderFolder> parentFolder = parentFolderSpec;
795 if (parentFolderSpec->Init(parentPath) == S_OK)
796 {
797 *resultFolder = parentFolder.Detach();
798 return S_OK;
799 }
800 }
801
802 /*
803 FString parentPathReduced = parentPath.Left(pos);
804
805 pos = parentPathReduced.ReverseFind_PathSepar();
806 if (pos == 1)
807 {
808 if (!IS_PATH_SEPAR_CHAR(parentPath[0]))
809 return E_FAIL;
810 CNetFolder *netFolderSpec = new CNetFolder;
811 CMyComPtr<IFolderFolder> netFolder = netFolderSpec;
812 netFolderSpec->Init(fs2us(parentPath));
813 *resultFolder = netFolder.Detach();
814 return S_OK;
815 }
816 */
817
818 #endif
819
820 return S_OK;
821 }
822
GetNumberOfProperties(UInt32 * numProperties)823 STDMETHODIMP CFSFolder::GetNumberOfProperties(UInt32 *numProperties)
824 {
825 *numProperties = ARRAY_SIZE(kProps);
826 if (!_flatMode)
827 (*numProperties)--;
828 return S_OK;
829 }
830
IMP_IFolderFolder_GetProp(kProps)831 STDMETHODIMP CFSFolder::GetPropertyInfo IMP_IFolderFolder_GetProp(kProps)
832
833 STDMETHODIMP CFSFolder::GetFolderProperty(PROPID propID, PROPVARIANT *value)
834 {
835 COM_TRY_BEGIN
836 NWindows::NCOM::CPropVariant prop;
837 switch (propID)
838 {
839 case kpidType: prop = "FSFolder"; break;
840 case kpidPath: prop = fs2us(_path); break;
841 }
842 prop.Detach(value);
843 return S_OK;
844 COM_TRY_END
845 }
846
WasChanged(Int32 * wasChanged)847 STDMETHODIMP CFSFolder::WasChanged(Int32 *wasChanged)
848 {
849 bool wasChangedMain = false;
850
851 #ifdef _WIN32
852
853 for (;;)
854 {
855 if (!_findChangeNotification.IsHandleAllocated())
856 break;
857 DWORD waitResult = ::WaitForSingleObject(_findChangeNotification, 0);
858 if (waitResult != WAIT_OBJECT_0)
859 break;
860 _findChangeNotification.FindNext();
861 wasChangedMain = true;
862 }
863
864 #endif
865
866 *wasChanged = BoolToInt(wasChangedMain);
867 return S_OK;
868 }
869
Clone(IFolderFolder ** resultFolder)870 STDMETHODIMP CFSFolder::Clone(IFolderFolder **resultFolder)
871 {
872 CFSFolder *fsFolderSpec = new CFSFolder;
873 CMyComPtr<IFolderFolder> folderNew = fsFolderSpec;
874 fsFolderSpec->Init(_path);
875 *resultFolder = folderNew.Detach();
876 return S_OK;
877 }
878
GetItemsFullSize(const UInt32 * indices,UInt32 numItems,CFsFolderStat & stat)879 HRESULT CFSFolder::GetItemsFullSize(const UInt32 *indices, UInt32 numItems, CFsFolderStat &stat)
880 {
881 for (UInt32 i = 0; i < numItems; i++)
882 {
883 UInt32 index = indices[i];
884 /*
885 if (index >= Files.Size())
886 {
887 size += Streams[index - Files.Size()].Size;
888 // numFiles++;
889 continue;
890 }
891 */
892 const CDirItem &fi = Files[index];
893 if (fi.IsDir())
894 {
895 stat.Path = _path;
896 stat.Path += GetRelPath(fi);
897 RINOK(stat.Enumerate());
898 stat.NumFolders++;
899 }
900 else
901 {
902 stat.NumFiles++;
903 stat.Size += fi.Size;
904 }
905 }
906 return S_OK;
907 }
908
909 /*
910 HRESULT CFSFolder::GetItemFullSize(unsigned index, UInt64 &size, IProgress *progress)
911 {
912 if (index >= Files.Size())
913 {
914 size = Streams[index - Files.Size()].Size;
915 return S_OK;
916 }
917 const CDirItem &fi = Files[index];
918 if (fi.IsDir())
919 {
920 UInt64 numFolders = 0, numFiles = 0;
921 size = 0;
922 return GetFolderSize(_path + GetRelPath(fi), numFolders, numFiles, size, progress);
923 }
924 size = fi.Size;
925 return S_OK;
926 }
927
928 STDMETHODIMP CFSFolder::GetItemFullSize(UInt32 index, PROPVARIANT *value, IProgress *progress)
929 {
930 NCOM::CPropVariant prop;
931 UInt64 size = 0;
932 HRESULT result = GetItemFullSize(index, size, progress);
933 prop = size;
934 prop.Detach(value);
935 return result;
936 }
937 */
938
CalcItemFullSize(UInt32 index,IProgress * progress)939 STDMETHODIMP CFSFolder::CalcItemFullSize(UInt32 index, IProgress *progress)
940 {
941 if (index >= (UInt32)Files.Size())
942 return S_OK;
943 CDirItem &fi = Files[index];
944 if (!fi.IsDir())
945 return S_OK;
946 CFsFolderStat stat(_path + GetRelPath(fi), progress);
947 RINOK(stat.Enumerate());
948 fi.Size = stat.Size;
949 fi.NumFolders = stat.NumFolders;
950 fi.NumFiles = stat.NumFiles;
951 fi.FolderStat_Defined = true;
952 return S_OK;
953 }
954
GetAbsPath(const wchar_t * name,FString & absPath)955 void CFSFolder::GetAbsPath(const wchar_t *name, FString &absPath)
956 {
957 absPath.Empty();
958 if (!IsAbsolutePath(name))
959 absPath += _path;
960 absPath += us2fs(name);
961 }
962
CreateFolder(const wchar_t * name,IProgress *)963 STDMETHODIMP CFSFolder::CreateFolder(const wchar_t *name, IProgress * /* progress */)
964 {
965 FString absPath;
966 GetAbsPath(name, absPath);
967 if (CreateDir(absPath))
968 return S_OK;
969 if (::GetLastError() == ERROR_ALREADY_EXISTS)
970 return ::GetLastError();
971 if (!CreateComplexDir(absPath))
972 return ::GetLastError();
973 return S_OK;
974 }
975
CreateFile(const wchar_t * name,IProgress *)976 STDMETHODIMP CFSFolder::CreateFile(const wchar_t *name, IProgress * /* progress */)
977 {
978 FString absPath;
979 GetAbsPath(name, absPath);
980 NIO::COutFile outFile;
981 if (!outFile.Create(absPath, false))
982 return ::GetLastError();
983 return S_OK;
984 }
985
Rename(UInt32 index,const wchar_t * newName,IProgress *)986 STDMETHODIMP CFSFolder::Rename(UInt32 index, const wchar_t *newName, IProgress * /* progress */)
987 {
988 if (index >= (UInt32)Files.Size())
989 return E_NOTIMPL;
990 const CDirItem &fi = Files[index];
991 // FString prefix;
992 // GetPrefix(fi, prefix);
993 FString fullPrefix = _path;
994 if (fi.Parent >= 0)
995 fullPrefix += Folders[fi.Parent];
996 if (!MyMoveFile(fullPrefix + fi.Name, fullPrefix + us2fs(newName)))
997 return GetLastError();
998 return S_OK;
999 }
1000
Delete(const UInt32 * indices,UInt32 numItems,IProgress * progress)1001 STDMETHODIMP CFSFolder::Delete(const UInt32 *indices, UInt32 numItems,IProgress *progress)
1002 {
1003 RINOK(progress->SetTotal(numItems));
1004 // int prevDeletedFileIndex = -1;
1005 for (UInt32 i = 0; i < numItems; i++)
1006 {
1007 // Sleep(200);
1008 UInt32 index = indices[i];
1009 bool result = true;
1010 /*
1011 if (index >= (UInt32)Files.Size())
1012 {
1013 const CAltStream &ss = Streams[index - (UInt32)Files.Size()];
1014 if (prevDeletedFileIndex != ss.Parent)
1015 {
1016 const CDirItem &fi = Files[ss.Parent];
1017 result = DeleteFileAlways(_path + GetRelPath(fi) + us2fs(ss.Name));
1018 }
1019 }
1020 else
1021 */
1022 {
1023 const CDirItem &fi = Files[index];
1024 const FString fullPath = _path + GetRelPath(fi);
1025 // prevDeletedFileIndex = index;
1026 if (fi.IsDir())
1027 result = RemoveDirWithSubItems(fullPath);
1028 else
1029 result = DeleteFileAlways(fullPath);
1030 }
1031 if (!result)
1032 return GetLastError();
1033 UInt64 completed = i;
1034 RINOK(progress->SetCompleted(&completed));
1035 }
1036 return S_OK;
1037 }
1038
SetProperty(UInt32 index,PROPID propID,const PROPVARIANT * value,IProgress *)1039 STDMETHODIMP CFSFolder::SetProperty(UInt32 index, PROPID propID,
1040 const PROPVARIANT *value, IProgress * /* progress */)
1041 {
1042 if (index >= (UInt32)Files.Size())
1043 return E_INVALIDARG;
1044 CDirItem &fi = Files[index];
1045 if (fi.Parent >= 0)
1046 return E_NOTIMPL;
1047 switch (propID)
1048 {
1049 case kpidComment:
1050 {
1051 UString filename = fs2us(fi.Name);
1052 filename.Trim();
1053 if (value->vt == VT_EMPTY)
1054 _comments.DeletePair(filename);
1055 else if (value->vt == VT_BSTR)
1056 {
1057 CTextPair pair;
1058 pair.ID = filename;
1059 pair.ID.Trim();
1060 pair.Value.SetFromBstr(value->bstrVal);
1061 pair.Value.Trim();
1062 if (pair.Value.IsEmpty())
1063 _comments.DeletePair(filename);
1064 else
1065 _comments.AddPair(pair);
1066 }
1067 else
1068 return E_INVALIDARG;
1069 SaveComments();
1070 break;
1071 }
1072 default:
1073 return E_NOTIMPL;
1074 }
1075 return S_OK;
1076 }
1077
GetSystemIconIndex(UInt32 index,Int32 * iconIndex)1078 STDMETHODIMP CFSFolder::GetSystemIconIndex(UInt32 index, Int32 *iconIndex)
1079 {
1080 if (index >= (UInt32)Files.Size())
1081 return E_INVALIDARG;
1082 const CDirItem &fi = Files[index];
1083 *iconIndex = 0;
1084 int iconIndexTemp;
1085 if (GetRealIconIndex(_path + GetRelPath(fi), fi.Attrib, iconIndexTemp) != 0)
1086 {
1087 *iconIndex = iconIndexTemp;
1088 return S_OK;
1089 }
1090 return GetLastError();
1091 }
1092
SetFlatMode(Int32 flatMode)1093 STDMETHODIMP CFSFolder::SetFlatMode(Int32 flatMode)
1094 {
1095 _flatMode = IntToBool(flatMode);
1096 return S_OK;
1097 }
1098
1099 /*
1100 STDMETHODIMP CFSFolder::SetShowNtfsStreamsMode(Int32 showStreamsMode)
1101 {
1102 _scanAltStreams = IntToBool(showStreamsMode);
1103 return S_OK;
1104 }
1105 */
1106
1107 }
1108