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