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