• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // AltStreamsFolder.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/StringConvert.h"
28 #include "../../../Common/Wildcard.h"
29 
30 #include "../../../Windows/DLL.h"
31 #include "../../../Windows/ErrorMsg.h"
32 #include "../../../Windows/FileDir.h"
33 #include "../../../Windows/FileIO.h"
34 #include "../../../Windows/FileName.h"
35 #include "../../../Windows/PropVariant.h"
36 
37 #include "../Common/ExtractingFilePath.h"
38 
39 #include "../Agent/IFolderArchive.h"
40 
41 #include "AltStreamsFolder.h"
42 #include "FSDrives.h"
43 #include "FSFolder.h"
44 
45 #include "SysIconUtils.h"
46 
47 using namespace NWindows;
48 using namespace NFile;
49 using namespace NFind;
50 using namespace NDir;
51 using namespace NName;
52 
53 #ifndef USE_UNICODE_FSTRING
54 int CompareFileNames_ForFolderList(const FChar *s1, const FChar *s2);
55 #endif
56 
57 #ifndef UNDER_CE
58 
59 namespace NFsFolder
60 {
61 bool MyGetCompressedFileSizeW(CFSTR path, UInt64 &size);
62 }
63 
64 #endif
65 
66 namespace NAltStreamsFolder {
67 
68 static const Byte kProps[] =
69 {
70   kpidName,
71   kpidSize,
72   kpidPackSize
73 };
74 
GetFsParentPrefixSize(const FString & path)75 static unsigned GetFsParentPrefixSize(const FString &path)
76 {
77   if (IsNetworkShareRootPath(path))
78     return 0;
79   const unsigned prefixSize = GetRootPrefixSize(path);
80   if (prefixSize == 0 || prefixSize >= path.Len())
81     return 0;
82   FString parentPath = path;
83   int pos = parentPath.ReverseFind_PathSepar();
84   if (pos < 0)
85     return 0;
86   if (pos == (int)parentPath.Len() - 1)
87   {
88     parentPath.DeleteBack();
89     pos = parentPath.ReverseFind_PathSepar();
90     if (pos < 0)
91       return 0;
92   }
93   if ((unsigned)pos + 1 < prefixSize)
94     return 0;
95   return (unsigned)pos + 1;
96 }
97 
Init(const FString & path)98 HRESULT CAltStreamsFolder::Init(const FString &path /* , IFolderFolder *parentFolder */)
99 {
100    // _parentFolder = parentFolder;
101    if (path.Back() != ':')
102      return E_FAIL;
103 
104   _pathPrefix = path;
105   _pathBaseFile = path;
106   _pathBaseFile.DeleteBack();
107 
108   {
109     CFileInfo fi;
110     if (!fi.Find(_pathBaseFile))
111       return GetLastError_noZero_HRESULT();
112   }
113 
114   unsigned prefixSize = GetFsParentPrefixSize(_pathBaseFile);
115   if (prefixSize == 0)
116     return S_OK;
117   FString parentPath = _pathBaseFile;
118   parentPath.DeleteFrom(prefixSize);
119 
120   _findChangeNotification.FindFirst(parentPath, false,
121         FILE_NOTIFY_CHANGE_FILE_NAME
122       | FILE_NOTIFY_CHANGE_DIR_NAME
123       | FILE_NOTIFY_CHANGE_ATTRIBUTES
124       | FILE_NOTIFY_CHANGE_SIZE
125       | FILE_NOTIFY_CHANGE_LAST_WRITE
126       /*
127       | FILE_NOTIFY_CHANGE_LAST_ACCESS
128       | FILE_NOTIFY_CHANGE_CREATION
129       | FILE_NOTIFY_CHANGE_SECURITY
130       */
131       );
132   /*
133   if (_findChangeNotification.IsHandleAllocated())
134     return S_OK;
135   return GetLastError();
136   */
137   return S_OK;
138 }
139 
Z7_COM7F_IMF(CAltStreamsFolder::LoadItems ())140 Z7_COM7F_IMF(CAltStreamsFolder::LoadItems())
141 {
142   Int32 dummy;
143   WasChanged(&dummy);
144   Clear();
145 
146   CStreamEnumerator enumerator(_pathBaseFile);
147 
148   CStreamInfo si;
149   for (;;)
150   {
151     bool found;
152     if (!enumerator.Next(si, found))
153     {
154       // if (GetLastError() == ERROR_ACCESS_DENIED)
155       //   break;
156       // return E_FAIL;
157       break;
158     }
159     if (!found)
160       break;
161     if (si.IsMainStream())
162       continue;
163     CAltStream ss;
164     ss.Name = si.GetReducedName();
165     if (!ss.Name.IsEmpty() && ss.Name[0] == ':')
166       ss.Name.Delete(0);
167 
168     ss.Size = si.Size;
169     ss.PackSize_Defined = false;
170     ss.PackSize = si.Size;
171     Streams.Add(ss);
172   }
173 
174   return S_OK;
175 }
176 
Z7_COM7F_IMF(CAltStreamsFolder::GetNumberOfItems (UInt32 * numItems))177 Z7_COM7F_IMF(CAltStreamsFolder::GetNumberOfItems(UInt32 *numItems))
178 {
179   *numItems = Streams.Size();
180   return S_OK;
181 }
182 
183 #ifdef USE_UNICODE_FSTRING
184 
Z7_COM7F_IMF(CAltStreamsFolder::GetItemPrefix (UInt32,const wchar_t ** name,unsigned * len))185 Z7_COM7F_IMF(CAltStreamsFolder::GetItemPrefix(UInt32 /* index */, const wchar_t **name, unsigned *len))
186 {
187   *name = NULL;
188   *len = 0;
189   return S_OK;
190 }
191 
Z7_COM7F_IMF(CAltStreamsFolder::GetItemName (UInt32 index,const wchar_t ** name,unsigned * len))192 Z7_COM7F_IMF(CAltStreamsFolder::GetItemName(UInt32 index, const wchar_t **name, unsigned *len))
193 {
194   *name = NULL;
195   *len = 0;
196   {
197     const CAltStream &ss = Streams[index];
198     *name = ss.Name;
199     *len = ss.Name.Len();
200   }
201   return S_OK;
202 }
203 
Z7_COM7F_IMF2(UInt64,CAltStreamsFolder::GetItemSize (UInt32 index))204 Z7_COM7F_IMF2(UInt64, CAltStreamsFolder::GetItemSize(UInt32 index))
205 {
206   return Streams[index].Size;
207 }
208 
209 #endif
210 
211 
Z7_COM7F_IMF(CAltStreamsFolder::GetProperty (UInt32 index,PROPID propID,PROPVARIANT * value))212 Z7_COM7F_IMF(CAltStreamsFolder::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
213 {
214   NCOM::CPropVariant prop;
215   {
216     CAltStream &ss = Streams[index];
217     switch (propID)
218     {
219       case kpidIsDir: prop = false; break;
220       case kpidIsAltStream: prop = true; break;
221       case kpidName: prop = ss.Name; break;
222       case kpidSize: prop = ss.Size; break;
223       case kpidPackSize:
224         #ifdef UNDER_CE
225         prop = ss.Size;
226         #else
227         if (!ss.PackSize_Defined)
228         {
229           ss.PackSize_Defined = true;
230           if (!NFsFolder::MyGetCompressedFileSizeW(_pathPrefix + us2fs(ss.Name), ss.PackSize))
231             ss.PackSize = ss.Size;
232         }
233         prop = ss.PackSize;
234         #endif
235         break;
236     }
237   }
238 
239   prop.Detach(value);
240   return S_OK;
241 }
242 
243 
244 // returns Position of extension including '.'
245 
GetExtensionPtr(const UString & name)246 static inline const wchar_t *GetExtensionPtr(const UString &name)
247 {
248   const int dotPos = name.ReverseFind_Dot();
249   return name.Ptr(dotPos < 0 ? name.Len() : (unsigned)dotPos);
250 }
251 
Z7_COM7F_IMF2(Int32,CAltStreamsFolder::CompareItems (UInt32 index1,UInt32 index2,PROPID propID,Int32))252 Z7_COM7F_IMF2(Int32, CAltStreamsFolder::CompareItems(UInt32 index1, UInt32 index2, PROPID propID, Int32 /* propIsRaw */))
253 {
254   const CAltStream &ss1 = Streams[index1];
255   const CAltStream &ss2 = Streams[index2];
256 
257   switch (propID)
258   {
259     case kpidName:
260     {
261       return CompareFileNames_ForFolderList(ss1.Name, ss2.Name);
262       // return MyStringCompareNoCase(ss1.Name, ss2.Name);
263     }
264     case kpidSize:
265       return MyCompare(ss1.Size, ss2.Size);
266     case kpidPackSize:
267     {
268       #ifdef UNDER_CE
269       return MyCompare(ss1.Size, ss2.Size);
270       #else
271       // PackSize can be undefined here
272       return MyCompare(
273           ss1.PackSize,
274           ss2.PackSize);
275       #endif
276     }
277 
278     case kpidExtension:
279       return CompareFileNames_ForFolderList(
280           GetExtensionPtr(ss1.Name),
281           GetExtensionPtr(ss2.Name));
282   }
283 
284   return 0;
285 }
286 
Z7_COM7F_IMF(CAltStreamsFolder::BindToFolder (UInt32,IFolderFolder ** resultFolder))287 Z7_COM7F_IMF(CAltStreamsFolder::BindToFolder(UInt32 /* index */, IFolderFolder **resultFolder))
288 {
289   *resultFolder = NULL;
290   return E_INVALIDARG;
291 }
292 
Z7_COM7F_IMF(CAltStreamsFolder::BindToFolder (const wchar_t *,IFolderFolder ** resultFolder))293 Z7_COM7F_IMF(CAltStreamsFolder::BindToFolder(const wchar_t * /* name */, IFolderFolder **resultFolder))
294 {
295   *resultFolder = NULL;
296   return E_INVALIDARG;
297 }
298 
299 // static CFSTR const kSuperPrefix = FTEXT("\\\\?\\");
300 
Z7_COM7F_IMF(CAltStreamsFolder::BindToParentFolder (IFolderFolder ** resultFolder))301 Z7_COM7F_IMF(CAltStreamsFolder::BindToParentFolder(IFolderFolder **resultFolder))
302 {
303   *resultFolder = NULL;
304   /*
305   if (_parentFolder)
306   {
307     CMyComPtr<IFolderFolder> parentFolder = _parentFolder;
308     *resultFolder = parentFolder.Detach();
309     return S_OK;
310   }
311   */
312 
313   if (IsDriveRootPath_SuperAllowed(_pathBaseFile))
314   {
315     CFSDrives *drivesFolderSpec = new CFSDrives;
316     CMyComPtr<IFolderFolder> drivesFolder = drivesFolderSpec;
317     drivesFolderSpec->Init();
318     *resultFolder = drivesFolder.Detach();
319     return S_OK;
320   }
321 
322   /*
323   parentPath.DeleteFrom(pos + 1);
324 
325   if (parentPath == kSuperPrefix)
326   {
327     #ifdef UNDER_CE
328     *resultFolder = 0;
329     #else
330     CFSDrives *drivesFolderSpec = new CFSDrives;
331     CMyComPtr<IFolderFolder> drivesFolder = drivesFolderSpec;
332     drivesFolderSpec->Init(false, true);
333     *resultFolder = drivesFolder.Detach();
334     #endif
335     return S_OK;
336   }
337 
338   FString parentPathReduced = parentPath.Left(pos);
339 
340   #ifndef UNDER_CE
341   pos = parentPathReduced.ReverseFind_PathSepar();
342   if (pos == 1)
343   {
344     if (!IS_PATH_SEPAR_CHAR(parentPath[0]))
345       return E_FAIL;
346     CNetFolder *netFolderSpec = new CNetFolder;
347     CMyComPtr<IFolderFolder> netFolder = netFolderSpec;
348     netFolderSpec->Init(fs2us(parentPath));
349     *resultFolder = netFolder.Detach();
350     return S_OK;
351   }
352   #endif
353 
354   CFSFolder *parentFolderSpec = new CFSFolder;
355   CMyComPtr<IFolderFolder> parentFolder = parentFolderSpec;
356   RINOK(parentFolderSpec->Init(parentPath, 0));
357   *resultFolder = parentFolder.Detach();
358   */
359 
360   return S_OK;
361 }
362 
363 IMP_IFolderFolder_Props(CAltStreamsFolder)
364 
Z7_COM7F_IMF(CAltStreamsFolder::GetFolderProperty (PROPID propID,PROPVARIANT * value))365 Z7_COM7F_IMF(CAltStreamsFolder::GetFolderProperty(PROPID propID, PROPVARIANT *value))
366 {
367   COM_TRY_BEGIN
368   NWindows::NCOM::CPropVariant prop;
369   switch (propID)
370   {
371     case kpidType: prop = "AltStreamsFolder"; break;
372     case kpidPath: prop = fs2us(_pathPrefix); break;
373   }
374   prop.Detach(value);
375   return S_OK;
376   COM_TRY_END
377 }
378 
Z7_COM7F_IMF(CAltStreamsFolder::WasChanged (Int32 * wasChanged))379 Z7_COM7F_IMF(CAltStreamsFolder::WasChanged(Int32 *wasChanged))
380 {
381   bool wasChangedMain = false;
382   for (;;)
383   {
384     if (!_findChangeNotification.IsHandleAllocated())
385     {
386       *wasChanged = BoolToInt(false);
387       return S_OK;
388     }
389 
390     DWORD waitResult = ::WaitForSingleObject(_findChangeNotification, 0);
391     bool wasChangedLoc = (waitResult == WAIT_OBJECT_0);
392     if (wasChangedLoc)
393     {
394       _findChangeNotification.FindNext();
395       wasChangedMain = true;
396     }
397     else
398       break;
399   }
400   *wasChanged = BoolToInt(wasChangedMain);
401   return S_OK;
402 }
403 
Z7_COM7F_IMF(CAltStreamsFolder::Clone (IFolderFolder ** resultFolder))404 Z7_COM7F_IMF(CAltStreamsFolder::Clone(IFolderFolder **resultFolder))
405 {
406   CAltStreamsFolder *folderSpec = new CAltStreamsFolder;
407   CMyComPtr<IFolderFolder> folderNew = folderSpec;
408   folderSpec->Init(_pathPrefix);
409   *resultFolder = folderNew.Detach();
410   return S_OK;
411 }
412 
GetAbsPath(const wchar_t * name,FString & absPath)413 void CAltStreamsFolder::GetAbsPath(const wchar_t *name, FString &absPath)
414 {
415   absPath.Empty();
416   if (!IsAbsolutePath(name))
417     absPath += _pathPrefix;
418   absPath += us2fs(name);
419 }
420 
421 
422 
SendMessageError(IFolderOperationsExtractCallback * callback,const wchar_t * message,const FString & fileName)423 static HRESULT SendMessageError(IFolderOperationsExtractCallback *callback,
424     const wchar_t *message, const FString &fileName)
425 {
426   UString s = message;
427   s += " : ";
428   s += fs2us(fileName);
429   return callback->ShowMessage(s);
430 }
431 
SendMessageError(IFolderArchiveUpdateCallback * callback,const wchar_t * message,const FString & fileName)432 static HRESULT SendMessageError(IFolderArchiveUpdateCallback *callback,
433     const wchar_t *message, const FString &fileName)
434 {
435   UString s = message;
436   s += " : ";
437   s += fs2us(fileName);
438   return callback->UpdateErrorMessage(s);
439 }
440 
SendMessageError(IFolderOperationsExtractCallback * callback,const char * message,const FString & fileName)441 static HRESULT SendMessageError(IFolderOperationsExtractCallback *callback,
442     const char *message, const FString &fileName)
443 {
444   return SendMessageError(callback, MultiByteToUnicodeString(message), fileName);
445 }
446 
447 /*
448 static HRESULT SendMessageError(IFolderArchiveUpdateCallback *callback,
449     const char *message, const FString &fileName)
450 {
451   return SendMessageError(callback, MultiByteToUnicodeString(message), fileName);
452 }
453 */
454 
Z7_COM7F_IMF(CAltStreamsFolder::CreateFolder (const wchar_t *,IProgress *))455 Z7_COM7F_IMF(CAltStreamsFolder::CreateFolder(const wchar_t * /* name */, IProgress * /* progress */))
456 {
457   return E_NOTIMPL;
458 }
459 
Z7_COM7F_IMF(CAltStreamsFolder::CreateFile (const wchar_t * name,IProgress *))460 Z7_COM7F_IMF(CAltStreamsFolder::CreateFile(const wchar_t *name, IProgress * /* progress */))
461 {
462   FString absPath;
463   GetAbsPath(name, absPath);
464   NIO::COutFile outFile;
465   if (!outFile.Create_NEW(absPath))
466     return GetLastError_noZero_HRESULT();
467   return S_OK;
468 }
469 
GetLastErrorMessage()470 static UString GetLastErrorMessage()
471 {
472   return NError::MyFormatMessage(GetLastError_noZero_HRESULT());
473 }
474 
UpdateFile(NFsFolder::CCopyStateIO & state,CFSTR inPath,CFSTR outPath,IFolderArchiveUpdateCallback * callback)475 static HRESULT UpdateFile(NFsFolder::CCopyStateIO &state, CFSTR inPath, CFSTR outPath, IFolderArchiveUpdateCallback *callback)
476 {
477   if (NFind::DoesFileOrDirExist(outPath))
478   {
479     RINOK(SendMessageError(callback, NError::MyFormatMessage(ERROR_ALREADY_EXISTS), FString(outPath)))
480     CFileInfo fi;
481     if (fi.Find(inPath))
482     {
483       if (state.TotalSize >= fi.Size)
484         state.TotalSize -= fi.Size;
485     }
486     return S_OK;
487   }
488 
489   {
490     if (callback)
491       RINOK(callback->CompressOperation(fs2us(inPath)))
492     RINOK(state.MyCopyFile(inPath, outPath))
493     if (state.ErrorFileIndex >= 0)
494     {
495       if (state.ErrorMessage.IsEmpty())
496         state.ErrorMessage = GetLastErrorMessage();
497       FString errorName;
498       if (state.ErrorFileIndex == 0)
499         errorName = inPath;
500       else
501         errorName = outPath;
502       if (callback)
503         RINOK(SendMessageError(callback, state.ErrorMessage, errorName))
504     }
505     if (callback)
506       RINOK(callback->OperationResult(0))
507   }
508 
509   return S_OK;
510 }
511 
512 EXTERN_C_BEGIN
513 
514 typedef enum
515 {
516   Z7_WIN_FileRenameInformation = 10
517 }
518 Z7_WIN_FILE_INFORMATION_CLASS;
519 
520 
521 typedef struct
522 {
523   // #if (_WIN32_WINNT >= _WIN32_WINNT_WIN10_RS1)
524   union
525   {
526     BOOLEAN ReplaceIfExists;  // FileRenameInformation
527     ULONG Flags;              // FileRenameInformationEx
528   } DUMMYUNIONNAME;
529   // #else
530   // BOOLEAN ReplaceIfExists;
531   // #endif
532   HANDLE RootDirectory;
533   ULONG FileNameLength;
534   WCHAR FileName[1];
535 } Z7_WIN_FILE_RENAME_INFORMATION;
536 
537 #if (_WIN32_WINNT >= 0x0500) && !defined(_M_IA64)
538 #define Z7_WIN_NTSTATUS  NTSTATUS
539 #define Z7_WIN_IO_STATUS_BLOCK  IO_STATUS_BLOCK
540 #else
541 typedef LONG Z7_WIN_NTSTATUS;
542 typedef struct
543 {
544   union
545   {
546     Z7_WIN_NTSTATUS Status;
547     PVOID Pointer;
548   } DUMMYUNIONNAME;
549   ULONG_PTR Information;
550 } Z7_WIN_IO_STATUS_BLOCK;
551 #endif
552 
553 typedef Z7_WIN_NTSTATUS (WINAPI *Func_NtSetInformationFile)(
554     HANDLE FileHandle,
555     Z7_WIN_IO_STATUS_BLOCK *IoStatusBlock,
556     PVOID FileInformation,
557     ULONG Length,
558     Z7_WIN_FILE_INFORMATION_CLASS FileInformationClass);
559 
560 // NTAPI
561 typedef ULONG (WINAPI *Func_RtlNtStatusToDosError)(Z7_WIN_NTSTATUS Status);
562 
563 #define MY_STATUS_SUCCESS 0
564 
565 EXTERN_C_END
566 
567 // static Func_NtSetInformationFile f_NtSetInformationFile;
568 // static bool g_NtSetInformationFile_WasRequested = false;
569 Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION
570 
Z7_COM7F_IMF(CAltStreamsFolder::Rename (UInt32 index,const wchar_t * newName,IProgress * progress))571 Z7_COM7F_IMF(CAltStreamsFolder::Rename(UInt32 index, const wchar_t *newName, IProgress *progress))
572 {
573   const CAltStream &ss = Streams[index];
574   const FString srcPath = _pathPrefix + us2fs(ss.Name);
575 
576   const HMODULE ntdll = ::GetModuleHandleW(L"ntdll.dll");
577   // if (!g_NtSetInformationFile_WasRequested) {
578   // g_NtSetInformationFile_WasRequested = true;
579   const
580    Func_NtSetInformationFile
581       f_NtSetInformationFile = Z7_GET_PROC_ADDRESS(
582    Func_NtSetInformationFile, ntdll,
583        "NtSetInformationFile");
584   if (f_NtSetInformationFile)
585   {
586     NIO::CInFile inFile;
587     if (inFile.Open_for_FileRenameInformation(srcPath))
588     {
589       UString destPath (':');
590       destPath += newName;
591       const ULONG len = (ULONG)sizeof(wchar_t) * destPath.Len();
592       CByteBuffer buffer(sizeof(Z7_WIN_FILE_RENAME_INFORMATION) + len);
593       // buffer is 4 bytes larger than required.
594       Z7_WIN_FILE_RENAME_INFORMATION *fri = (Z7_WIN_FILE_RENAME_INFORMATION *)(void *)(Byte *)buffer;
595       memset(fri, 0, sizeof(Z7_WIN_FILE_RENAME_INFORMATION));
596       /* DOCS: If ReplaceIfExists is set to TRUE, the rename operation will succeed only
597          if a stream with the same name does not exist or is a zero-length data stream. */
598       fri->ReplaceIfExists = FALSE;
599       fri->RootDirectory = NULL;
600       fri->FileNameLength = len;
601       memcpy(fri->FileName, destPath.Ptr(), len);
602       Z7_WIN_IO_STATUS_BLOCK iosb;
603       const Z7_WIN_NTSTATUS status = f_NtSetInformationFile (inFile.GetHandle(),
604           &iosb, fri, (ULONG)buffer.Size(), Z7_WIN_FileRenameInformation);
605       if (status != MY_STATUS_SUCCESS)
606       {
607         const
608          Func_RtlNtStatusToDosError
609             f_RtlNtStatusToDosError = Z7_GET_PROC_ADDRESS(
610          Func_RtlNtStatusToDosError, ntdll,
611              "RtlNtStatusToDosError");
612         if (f_RtlNtStatusToDosError)
613         {
614           const ULONG res = f_RtlNtStatusToDosError(status);
615           if (res != ERROR_MR_MID_NOT_FOUND)
616             return HRESULT_FROM_WIN32(res);
617         }
618       }
619       return status;
620     }
621   }
622 
623   CMyComPtr<IFolderArchiveUpdateCallback> callback;
624   if (progress)
625   {
626     RINOK(progress->QueryInterface(IID_IFolderArchiveUpdateCallback, (void **)&callback))
627   }
628 
629   if (callback)
630   {
631     RINOK(callback->SetNumFiles(1))
632     RINOK(callback->SetTotal(ss.Size))
633   }
634 
635   NFsFolder::CCopyStateIO state;
636   state.Progress = progress;
637   state.TotalSize = 0;
638   state.DeleteSrcFile = true;
639 
640   const FString destPath = _pathPrefix + us2fs(newName);
641   return UpdateFile(state, srcPath, destPath, callback);
642 }
643 
Z7_COM7F_IMF(CAltStreamsFolder::Delete (const UInt32 * indices,UInt32 numItems,IProgress * progress))644 Z7_COM7F_IMF(CAltStreamsFolder::Delete(const UInt32 *indices, UInt32 numItems,IProgress *progress))
645 {
646   RINOK(progress->SetTotal(numItems))
647   for (UInt32 i = 0; i < numItems; i++)
648   {
649     const CAltStream &ss = Streams[indices[i]];
650     const FString fullPath = _pathPrefix + us2fs(ss.Name);
651     const bool result = DeleteFileAlways(fullPath);
652     if (!result)
653       return GetLastError_noZero_HRESULT();
654     const UInt64 completed = i;
655     RINOK(progress->SetCompleted(&completed))
656   }
657   return S_OK;
658 }
659 
Z7_COM7F_IMF(CAltStreamsFolder::SetProperty (UInt32,PROPID,const PROPVARIANT *,IProgress *))660 Z7_COM7F_IMF(CAltStreamsFolder::SetProperty(UInt32 /* index */, PROPID /* propID */,
661     const PROPVARIANT * /* value */, IProgress * /* progress */))
662 {
663   return E_NOTIMPL;
664 }
665 
Z7_COM7F_IMF(CAltStreamsFolder::GetSystemIconIndex (UInt32 index,Int32 * iconIndex))666 Z7_COM7F_IMF(CAltStreamsFolder::GetSystemIconIndex(UInt32 index, Int32 *iconIndex))
667 {
668   const CAltStream &ss = Streams[index];
669   *iconIndex = 0;
670   int iconIndexTemp;
671   if (GetRealIconIndex(_pathPrefix + us2fs(ss.Name),
672     0 // fi.Attrib
673     , iconIndexTemp) != 0)
674   {
675     *iconIndex = iconIndexTemp;
676     return S_OK;
677   }
678   return GetLastError_noZero_HRESULT();
679 }
680 
681 /*
682 Z7_CLASS_IMP_COM_1(
683   CGetProp
684   , IGetProp
685 )
686 public:
687   // const CArc *Arc;
688   // UInt32 IndexInArc;
689   UString Name; // relative path
690   UInt64 Size;
691 };
692 
693 Z7_COM7F_IMF(CGetProp::GetProp(PROPID propID, PROPVARIANT *value))
694 {
695   if (propID == kpidName)
696   {
697     COM_TRY_BEGIN
698     NCOM::CPropVariant prop;
699     prop = Name;
700     prop.Detach(value);
701     return S_OK;
702     COM_TRY_END
703   }
704   if (propID == kpidSize)
705   {
706     NCOM::CPropVariant prop = Size;
707     prop.Detach(value);
708     return S_OK;
709   }
710   NCOM::CPropVariant prop;
711   prop.Detach(value);
712   return S_OK;
713 }
714 */
715 
CopyStream(NFsFolder::CCopyStateIO & state,const FString & srcPath,const CFileInfo & srcFileInfo,const CAltStream & srcAltStream,const FString & destPathSpec,IFolderOperationsExtractCallback * callback)716 static HRESULT CopyStream(
717     NFsFolder::CCopyStateIO &state,
718     const FString &srcPath,
719     const CFileInfo &srcFileInfo,
720     const CAltStream &srcAltStream,
721     const FString &destPathSpec,
722     IFolderOperationsExtractCallback *callback)
723 {
724   FString destPath = destPathSpec;
725   if (CompareFileNames(destPath, srcPath) == 0)
726   {
727     RINOK(SendMessageError(callback, "Cannot copy file onto itself", destPath))
728     return E_ABORT;
729   }
730 
731   Int32 writeAskResult;
732   CMyComBSTR destPathResult;
733   RINOK(callback->AskWrite(
734       fs2us(srcPath),
735       BoolToInt(false),
736       &srcFileInfo.MTime, &srcAltStream.Size,
737       fs2us(destPath),
738       &destPathResult,
739       &writeAskResult))
740 
741   if (IntToBool(writeAskResult))
742   {
743     RINOK(callback->SetCurrentFilePath(fs2us(srcPath)))
744     FString destPathNew (us2fs((LPCOLESTR)destPathResult));
745     RINOK(state.MyCopyFile(srcPath, destPathNew))
746     if (state.ErrorFileIndex >= 0)
747     {
748       if (state.ErrorMessage.IsEmpty())
749         state.ErrorMessage = GetLastErrorMessage();
750       FString errorName;
751       if (state.ErrorFileIndex == 0)
752         errorName = srcPath;
753       else
754         errorName = destPathNew;
755       RINOK(SendMessageError(callback, state.ErrorMessage, errorName))
756       return E_ABORT;
757     }
758     state.StartPos += state.CurrentSize;
759   }
760   else
761   {
762     if (state.TotalSize >= srcAltStream.Size)
763     {
764       state.TotalSize -= srcAltStream.Size;
765       RINOK(state.Progress->SetTotal(state.TotalSize))
766     }
767   }
768   return S_OK;
769 }
770 
Z7_COM7F_IMF(CAltStreamsFolder::CopyTo (Int32 moveMode,const UInt32 * indices,UInt32 numItems,Int32,Int32,const wchar_t * path,IFolderOperationsExtractCallback * callback))771 Z7_COM7F_IMF(CAltStreamsFolder::CopyTo(Int32 moveMode, const UInt32 *indices, UInt32 numItems,
772     Int32 /* includeAltStreams */, Int32 /* replaceAltStreamColon */,
773     const wchar_t *path, IFolderOperationsExtractCallback *callback))
774 {
775   if (numItems == 0)
776     return S_OK;
777 
778   /*
779   Z7_DECL_CMyComPtr_QI_FROM(
780       IFolderExtractToStreamCallback,
781       ExtractToStreamCallback, callback)
782   if (ExtractToStreamCallback)
783   {
784     Int32 useStreams = 0;
785     if (ExtractToStreamCallback->UseExtractToStream(&useStreams) != S_OK)
786       useStreams = 0;
787     if (useStreams == 0)
788       ExtractToStreamCallback.Release();
789   }
790   */
791 
792   UInt64 totalSize = 0;
793   {
794     UInt32 i;
795     for (i = 0; i < numItems; i++)
796     {
797       totalSize += Streams[indices[i]].Size;
798     }
799     RINOK(callback->SetTotal(totalSize))
800     RINOK(callback->SetNumFiles(numItems))
801   }
802 
803   /*
804   if (ExtractToStreamCallback)
805   {
806     CGetProp *GetProp_Spec = new CGetProp;
807     CMyComPtr<IGetProp> GetProp= GetProp_Spec;
808 
809     for (UInt32 i = 0; i < numItems; i++)
810     {
811       UInt32 index = indices[i];
812       const CAltStream &ss = Streams[index];
813       GetProp_Spec->Name = ss.Name;
814       GetProp_Spec->Size = ss.Size;
815       CMyComPtr<ISequentialOutStream> outStream;
816       RINOK(ExtractToStreamCallback->GetStream7(GetProp_Spec->Name, BoolToInt(false), &outStream,
817         NArchive::NExtract::NAskMode::kExtract, GetProp)); // isDir
818       FString srcPath;
819       GetFullPath(ss, srcPath);
820       RINOK(ExtractToStreamCallback->PrepareOperation7(NArchive::NExtract::NAskMode::kExtract));
821       RINOK(ExtractToStreamCallback->SetOperationResult7(NArchive::NExtract::NOperationResult::kOK, BoolToInt(false))); // _encrypted
822       // RINOK(CopyStream(state, srcPath, fi, ss, destPath2, callback, completedSize));
823     }
824     return S_OK;
825   }
826   */
827 
828   FString destPath (us2fs(path));
829   if (destPath.IsEmpty() /* && !ExtractToStreamCallback */)
830     return E_INVALIDARG;
831 
832   const bool isAltDest = NName::IsAltPathPrefix(destPath);
833   const bool isDirectPath = (!isAltDest && !IsPathSepar(destPath.Back()));
834 
835   if (isDirectPath)
836   {
837     if (numItems > 1)
838       return E_INVALIDARG;
839   }
840 
841   CFileInfo fi;
842   if (!fi.Find(_pathBaseFile))
843     return GetLastError_noZero_HRESULT();
844 
845   NFsFolder::CCopyStateIO state;
846   state.Progress = callback;
847   state.DeleteSrcFile = IntToBool(moveMode);
848   state.TotalSize = totalSize;
849 
850   for (UInt32 i = 0; i < numItems; i++)
851   {
852     const UInt32 index = indices[i];
853     const CAltStream &ss = Streams[index];
854     FString destPath2 = destPath;
855     if (!isDirectPath)
856       destPath2 += us2fs(Get_Correct_FsFile_Name(ss.Name));
857     FString srcPath;
858     GetFullPath(ss, srcPath);
859     RINOK(CopyStream(state, srcPath, fi, ss, destPath2, callback))
860   }
861 
862   return S_OK;
863 }
864 
Z7_COM7F_IMF(CAltStreamsFolder::CopyFrom (Int32,const wchar_t *,const wchar_t * const *,UInt32,IProgress *))865 Z7_COM7F_IMF(CAltStreamsFolder::CopyFrom(Int32 /* moveMode */, const wchar_t * /* fromFolderPath */,
866     const wchar_t * const * /* itemsPaths */, UInt32 /* numItems */, IProgress * /* progress */))
867 {
868   /*
869   if (numItems == 0)
870     return S_OK;
871 
872   CMyComPtr<IFolderArchiveUpdateCallback> callback;
873   if (progress)
874   {
875     RINOK(progress->QueryInterface(IID_IFolderArchiveUpdateCallback, (void **)&callback));
876   }
877 
878   if (CompareFileNames(fromFolderPath, fs2us(_pathPrefix)) == 0)
879   {
880     RINOK(SendMessageError(callback, "Cannot copy file onto itself", _pathPrefix));
881     return E_ABORT;
882   }
883 
884   if (callback)
885     RINOK(callback->SetNumFiles(numItems));
886 
887   UInt64 totalSize = 0;
888 
889   UInt32 i;
890 
891   FString path;
892   for (i = 0; i < numItems; i++)
893   {
894     path = us2fs(fromFolderPath);
895     path += us2fs(itemsPaths[i]);
896 
897     CFileInfo fi;
898     if (!fi.Find(path))
899       return ::GetLastError();
900     if (fi.IsDir())
901       return E_NOTIMPL;
902     totalSize += fi.Size;
903   }
904 
905   RINOK(progress->SetTotal(totalSize));
906 
907   // UInt64 completedSize = 0;
908 
909   NFsFolder::CCopyStateIO state;
910   state.Progress = progress;
911   state.DeleteSrcFile = IntToBool(moveMode);
912   state.TotalSize = totalSize;
913 
914   // we need to clear READ-ONLY of parent before creating alt stream
915   {
916     DWORD attrib = GetFileAttrib(_pathBaseFile);
917     if (attrib != INVALID_FILE_ATTRIBUTES
918         && (attrib & FILE_ATTRIBUTE_READONLY) != 0)
919     {
920       if (!SetFileAttrib(_pathBaseFile, attrib & ~FILE_ATTRIBUTE_READONLY))
921       {
922         if (callback)
923         {
924           RINOK(SendMessageError(callback, GetLastErrorMessage(), _pathBaseFile));
925           return S_OK;
926         }
927         return Return_LastError_or_FAIL();
928       }
929     }
930   }
931 
932   for (i = 0; i < numItems; i++)
933   {
934     path = us2fs(fromFolderPath);
935     path += us2fs(itemsPaths[i]);
936 
937     FString destPath = _pathPrefix + us2fs(itemsPaths[i]);
938 
939     RINOK(UpdateFile(state, path, destPath, callback));
940   }
941 
942   return S_OK;
943   */
944   return E_NOTIMPL;
945 }
946 
Z7_COM7F_IMF(CAltStreamsFolder::CopyFromFile (UInt32,const wchar_t *,IProgress *))947 Z7_COM7F_IMF(CAltStreamsFolder::CopyFromFile(UInt32 /* index */, const wchar_t * /* fullFilePath */, IProgress * /* progress */))
948 {
949   return E_NOTIMPL;
950 }
951 
952 }
953