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