1 // UpdateCallback.cpp
2 
3 #include "StdAfx.h"
4 
5 // #include <stdio.h>
6 
7 #ifndef _WIN32
8 // #include <grp.h>
9 // #include <pwd.h>
10 /*
11 inclusion of <sys/sysmacros.h> by <sys/types.h> is deprecated since glibc 2.25.
12 Since glibc 2.3.3, macros have been aliases for three GNU-specific
13 functions: gnu_dev_makedev(), gnu_dev_major(), and gnu_dev_minor()
14 */
15 // for major()/minor():
16 #include <sys/types.h>
17 #if defined(__FreeBSD__) || defined(BSD) || defined(__APPLE__)
18 #else
19 #ifndef major
20 #include <sys/sysmacros.h>
21 #endif
22 #endif
23 
24 #endif // _WIN32
25 
26 #ifndef Z7_ST
27 #include "../../../Windows/Synchronization.h"
28 #endif
29 
30 #include "../../../Common/ComTry.h"
31 #include "../../../Common/IntToString.h"
32 #include "../../../Common/StringConvert.h"
33 #include "../../../Common/Wildcard.h"
34 #include "../../../Common/UTFConvert.h"
35 
36 #include "../../../Windows/FileDir.h"
37 #include "../../../Windows/FileName.h"
38 #include "../../../Windows/PropVariant.h"
39 
40 #include "../../Common/StreamObjects.h"
41 
42 #include "UpdateCallback.h"
43 
44 #if defined(_WIN32) && !defined(UNDER_CE)
45 #define Z7_USE_SECURITY_CODE
46 #include "../../../Windows/SecurityUtils.h"
47 #endif
48 
49 using namespace NWindows;
50 using namespace NFile;
51 
52 #ifndef Z7_ST
53 static NSynchronization::CCriticalSection g_CriticalSection;
54 #define MT_LOCK NSynchronization::CCriticalSectionLock lock(g_CriticalSection);
55 #else
56 #define MT_LOCK
57 #endif
58 
59 
60 #ifdef Z7_USE_SECURITY_CODE
61 bool InitLocalPrivileges();
62 #endif
63 
CArchiveUpdateCallback()64 CArchiveUpdateCallback::CArchiveUpdateCallback():
65     PreserveATime(false),
66     ShareForWrite(false),
67     StopAfterOpenError(false),
68     StdInMode(false),
69 
70     KeepOriginalItemNames(false),
71     StoreNtSecurity(false),
72     StoreHardLinks(false),
73     StoreSymLinks(false),
74 
75    #ifndef _WIN32
76     StoreOwnerId(false),
77     StoreOwnerName(false),
78    #endif
79 
80     /*
81     , Need_ArcMTime_Report(false),
82     , ArcMTime_WasReported(false),
83     */
84     Need_LatestMTime(false),
85     LatestMTime_Defined(false),
86 
87     Callback(NULL),
88 
89     DirItems(NULL),
90     ParentDirItem(NULL),
91 
92     Arc(NULL),
93     ArcItems(NULL),
94     UpdatePairs(NULL),
95     NewNames(NULL),
96     Comment(NULL),
97     CommentIndex(-1),
98 
99     ProcessedItemsStatuses(NULL),
100     _hardIndex_From((UInt32)(Int32)-1)
101 {
102   #ifdef Z7_USE_SECURITY_CODE
103   _saclEnabled = InitLocalPrivileges();
104   #endif
105 }
106 
107 
SetTotal(UInt64 size)108 Z7_COM7F_IMF(CArchiveUpdateCallback::SetTotal(UInt64 size))
109 {
110   COM_TRY_BEGIN
111   return Callback->SetTotal(size);
112   COM_TRY_END
113 }
114 
SetCompleted(const UInt64 *completeValue)115 Z7_COM7F_IMF(CArchiveUpdateCallback::SetCompleted(const UInt64 *completeValue))
116 {
117   COM_TRY_BEGIN
118   return Callback->SetCompleted(completeValue);
119   COM_TRY_END
120 }
121 
SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)122 Z7_COM7F_IMF(CArchiveUpdateCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize))
123 {
124   COM_TRY_BEGIN
125   return Callback->SetRatioInfo(inSize, outSize);
126   COM_TRY_END
127 }
128 
129 
130 /*
131 static const CStatProp kProps[] =
132 {
133   { NULL, kpidPath, VT_BSTR},
134   { NULL, kpidIsDir, VT_BOOL},
135   { NULL, kpidSize, VT_UI8},
136   { NULL, kpidCTime, VT_FILETIME},
137   { NULL, kpidATime, VT_FILETIME},
138   { NULL, kpidMTime, VT_FILETIME},
139   { NULL, kpidAttrib, VT_UI4},
140   { NULL, kpidIsAnti, VT_BOOL}
141 };
142 
143 Z7_COM7F_IMF(CArchiveUpdateCallback::EnumProperties(IEnumSTATPROPSTG **)
144 {
145   return CStatPropEnumerator::CreateEnumerator(kProps, Z7_ARRAY_SIZE(kProps), enumerator);
146 }
147 */
148 
GetUpdateItemInfo(UInt32 index, Int32 *newData, Int32 *newProps, UInt32 *indexInArchive)149 Z7_COM7F_IMF(CArchiveUpdateCallback::GetUpdateItemInfo(UInt32 index,
150       Int32 *newData, Int32 *newProps, UInt32 *indexInArchive))
151 {
152   COM_TRY_BEGIN
153   RINOK(Callback->CheckBreak())
154   const CUpdatePair2 &up = (*UpdatePairs)[index];
155   if (newData) *newData = BoolToInt(up.NewData);
156   if (newProps) *newProps = BoolToInt(up.NewProps);
157   if (indexInArchive)
158   {
159     *indexInArchive = (UInt32)(Int32)-1;
160     if (up.ExistInArchive())
161       *indexInArchive = ArcItems ? (*ArcItems)[(unsigned)up.ArcIndex].IndexInServer : (UInt32)(Int32)up.ArcIndex;
162   }
163   return S_OK;
164   COM_TRY_END
165 }
166 
167 
GetRootProp(PROPID propID, PROPVARIANT *value)168 Z7_COM7F_IMF(CArchiveUpdateCallback::GetRootProp(PROPID propID, PROPVARIANT *value))
169 {
170   NCOM::CPropVariant prop;
171   switch (propID)
172   {
173     case kpidIsDir:  prop = true; break;
174     case kpidAttrib: if (ParentDirItem) prop = ParentDirItem->GetWinAttrib(); break;
175     case kpidCTime:  if (ParentDirItem) PropVariant_SetFrom_FiTime(prop, ParentDirItem->CTime); break;
176     case kpidATime:  if (ParentDirItem) PropVariant_SetFrom_FiTime(prop, ParentDirItem->ATime); break;
177     case kpidMTime:  if (ParentDirItem) PropVariant_SetFrom_FiTime(prop, ParentDirItem->MTime); break;
178     case kpidArcFileName:  if (!ArcFileName.IsEmpty()) prop = ArcFileName; break;
179   }
180   prop.Detach(value);
181   return S_OK;
182 }
183 
GetParent(UInt32 , UInt32 *parent, UInt32 *parentType)184 Z7_COM7F_IMF(CArchiveUpdateCallback::GetParent(UInt32 /* index */, UInt32 *parent, UInt32 *parentType))
185 {
186   *parentType = NParentType::kDir;
187   *parent = (UInt32)(Int32)-1;
188   return S_OK;
189 }
190 
GetNumRawProps(UInt32 *numProps)191 Z7_COM7F_IMF(CArchiveUpdateCallback::GetNumRawProps(UInt32 *numProps))
192 {
193   *numProps = 0;
194   if (StoreNtSecurity)
195     *numProps = 1;
196   return S_OK;
197 }
198 
GetRawPropInfo(UInt32 , BSTR *name, PROPID *propID)199 Z7_COM7F_IMF(CArchiveUpdateCallback::GetRawPropInfo(UInt32 /* index */, BSTR *name, PROPID *propID))
200 {
201   *name = NULL;
202   *propID = kpidNtSecure;
203   return S_OK;
204 }
205 
GetRootRawProp(PROPID propID , const void **data, UInt32 *dataSize, UInt32 *propType)206 Z7_COM7F_IMF(CArchiveUpdateCallback::GetRootRawProp(PROPID
207     propID
208     , const void **data, UInt32 *dataSize, UInt32 *propType))
209 {
210   #ifndef Z7_USE_SECURITY_CODE
211   UNUSED_VAR(propID)
212   #endif
213 
214   *data = NULL;
215   *dataSize = 0;
216   *propType = 0;
217   if (!StoreNtSecurity)
218     return S_OK;
219   #ifdef Z7_USE_SECURITY_CODE
220   if (propID == kpidNtSecure)
221   {
222     if (StdInMode)
223       return S_OK;
224 
225     if (ParentDirItem)
226     {
227       if (ParentDirItem->SecureIndex < 0)
228         return S_OK;
229       const CByteBuffer &buf = DirItems->SecureBlocks.Bufs[(unsigned)ParentDirItem->SecureIndex];
230       *data = buf;
231       *dataSize = (UInt32)buf.Size();
232       *propType = NPropDataType::kRaw;
233       return S_OK;
234     }
235 
236     if (Arc && Arc->GetRootProps)
237       return Arc->GetRootProps->GetRootRawProp(propID, data, dataSize, propType);
238   }
239   #endif
240   return S_OK;
241 }
242 
243 
GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType)244 Z7_COM7F_IMF(CArchiveUpdateCallback::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType))
245 {
246   *data = NULL;
247   *dataSize = 0;
248   *propType = 0;
249 
250   if (propID == kpidNtSecure ||
251       propID == kpidNtReparse)
252   {
253     if (StdInMode)
254       return S_OK;
255 
256     const CUpdatePair2 &up = (*UpdatePairs)[index];
257     if (up.UseArcProps && up.ExistInArchive() && Arc->GetRawProps)
258       return Arc->GetRawProps->GetRawProp(
259           ArcItems ? (*ArcItems)[(unsigned)up.ArcIndex].IndexInServer : (UInt32)(Int32)up.ArcIndex,
260           propID, data, dataSize, propType);
261     {
262       /*
263       if (!up.NewData)
264         return E_FAIL;
265       */
266       if (up.IsAnti)
267         return S_OK;
268 
269       #if defined(_WIN32) && !defined(UNDER_CE)
270       const CDirItem &di = DirItems->Items[(unsigned)up.DirIndex];
271       #endif
272 
273       #ifdef Z7_USE_SECURITY_CODE
274       if (propID == kpidNtSecure)
275       {
276         if (!StoreNtSecurity)
277           return S_OK;
278         if (di.SecureIndex < 0)
279           return S_OK;
280         const CByteBuffer &buf = DirItems->SecureBlocks.Bufs[(unsigned)di.SecureIndex];
281         *data = buf;
282         *dataSize = (UInt32)buf.Size();
283         *propType = NPropDataType::kRaw;
284       }
285       else
286       #endif
287       if (propID == kpidNtReparse)
288       {
289         if (!StoreSymLinks)
290           return S_OK;
291         #if defined(_WIN32) && !defined(UNDER_CE)
292         // we use ReparseData2 instead of ReparseData for WIM format
293         const CByteBuffer *buf = &di.ReparseData2;
294         if (buf->Size() == 0)
295           buf = &di.ReparseData;
296         if (buf->Size() != 0)
297         {
298           *data = *buf;
299           *dataSize = (UInt32)buf->Size();
300           *propType = NPropDataType::kRaw;
301         }
302         #endif
303       }
304 
305       return S_OK;
306     }
307   }
308 
309   return S_OK;
310 }
311 
312 #if defined(_WIN32) && !defined(UNDER_CE)
313 
GetRelativePath(const UString &to, const UString &from)314 static UString GetRelativePath(const UString &to, const UString &from)
315 {
316   UStringVector partsTo, partsFrom;
317   SplitPathToParts(to, partsTo);
318   SplitPathToParts(from, partsFrom);
319 
320   unsigned i;
321   for (i = 0;; i++)
322   {
323     if (i + 1 >= partsFrom.Size() ||
324         i + 1 >= partsTo.Size())
325       break;
326     if (CompareFileNames(partsFrom[i], partsTo[i]) != 0)
327       break;
328   }
329 
330   if (i == 0)
331   {
332     #ifdef _WIN32
333     if (NName::IsDrivePath(to) ||
334         NName::IsDrivePath(from))
335       return to;
336     #endif
337   }
338 
339   UString s;
340   unsigned k;
341 
342   for (k = i + 1; k < partsFrom.Size(); k++)
343     s += ".." STRING_PATH_SEPARATOR;
344 
345   for (k = i; k < partsTo.Size(); k++)
346   {
347     if (k != i)
348       s.Add_PathSepar();
349     s += partsTo[k];
350   }
351 
352   return s;
353 }
354 
355 #endif
356 
GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)357 Z7_COM7F_IMF(CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
358 {
359   COM_TRY_BEGIN
360   const CUpdatePair2 &up = (*UpdatePairs)[index];
361   NCOM::CPropVariant prop;
362 
363   if (up.NewData)
364   {
365     /*
366     if (propID == kpidIsHardLink)
367     {
368       prop = _isHardLink;
369       prop.Detach(value);
370       return S_OK;
371     }
372     */
373     if (propID == kpidSymLink)
374     {
375       if (index == _hardIndex_From)
376       {
377         prop.Detach(value);
378         return S_OK;
379       }
380 
381       #if !defined(UNDER_CE)
382 
383       if (up.DirIndex >= 0)
384       {
385         const CDirItem &di = DirItems->Items[(unsigned)up.DirIndex];
386 
387         #ifdef _WIN32
388         // if (di.IsDir())
389         {
390           CReparseAttr attr;
391           if (attr.Parse(di.ReparseData, di.ReparseData.Size()))
392           {
393             const UString simpleName = attr.GetPath();
394             if (!attr.IsSymLink_WSL() && attr.IsRelative_Win())
395               prop = simpleName;
396             else
397             {
398               const FString phyPath = DirItems->GetPhyPath((unsigned)up.DirIndex);
399               FString fullPath;
400               if (NDir::MyGetFullPathName(phyPath, fullPath))
401               {
402                 prop = GetRelativePath(simpleName, fs2us(fullPath));
403               }
404             }
405             prop.Detach(value);
406             return S_OK;
407           }
408         }
409 
410         #else // _WIN32
411 
412         if (di.ReparseData.Size() != 0)
413         {
414           AString utf;
415           utf.SetFrom_CalcLen((const char *)(const Byte *)di.ReparseData, (unsigned)di.ReparseData.Size());
416 
417           UString us;
418           if (ConvertUTF8ToUnicode(utf, us))
419           {
420             prop = us;
421             prop.Detach(value);
422             return S_OK;
423           }
424         }
425 
426         #endif // _WIN32
427       }
428       #endif // !defined(UNDER_CE)
429     }
430     else if (propID == kpidHardLink)
431     {
432       if (index == _hardIndex_From)
433       {
434         const CKeyKeyValPair &pair = _map[_hardIndex_To];
435         const CUpdatePair2 &up2 = (*UpdatePairs)[pair.Value];
436         prop = DirItems->GetLogPath((unsigned)up2.DirIndex);
437         prop.Detach(value);
438         return S_OK;
439       }
440       if (up.DirIndex >= 0)
441       {
442         prop.Detach(value);
443         return S_OK;
444       }
445     }
446   }
447 
448   if (up.IsAnti
449       && propID != kpidIsDir
450       && propID != kpidPath
451       && propID != kpidIsAltStream)
452   {
453     switch (propID)
454     {
455       case kpidSize:  prop = (UInt64)0; break;
456       case kpidIsAnti:  prop = true; break;
457     }
458   }
459   else if (propID == kpidPath && up.NewNameIndex >= 0)
460     prop = (*NewNames)[(unsigned)up.NewNameIndex];
461   else if (propID == kpidComment
462       && CommentIndex >= 0
463       && (unsigned)CommentIndex == index
464       && Comment)
465     prop = *Comment;
466   else if (propID == kpidShortName && up.NewNameIndex >= 0 && up.IsMainRenameItem)
467   {
468     // we can generate new ShortName here;
469   }
470   else if ((up.UseArcProps || (KeepOriginalItemNames && (propID == kpidPath || propID == kpidIsAltStream)))
471       && up.ExistInArchive() && Archive)
472     return Archive->GetProperty(ArcItems ? (*ArcItems)[(unsigned)up.ArcIndex].IndexInServer : (UInt32)(Int32)up.ArcIndex, propID, value);
473   else if (up.ExistOnDisk())
474   {
475     const CDirItem &di = DirItems->Items[(unsigned)up.DirIndex];
476     switch (propID)
477     {
478       case kpidPath:  prop = DirItems->GetLogPath((unsigned)up.DirIndex); break;
479       case kpidIsDir:  prop = di.IsDir(); break;
480       case kpidSize:  prop = (UInt64)(di.IsDir() ? (UInt64)0 : di.Size); break;
481       case kpidCTime:  PropVariant_SetFrom_FiTime(prop, di.CTime); break;
482       case kpidATime:  PropVariant_SetFrom_FiTime(prop, di.ATime); break;
483       case kpidMTime:  PropVariant_SetFrom_FiTime(prop, di.MTime); break;
484       case kpidAttrib:  prop = (UInt32)di.GetWinAttrib(); break;
485       case kpidPosixAttrib: prop = (UInt32)di.GetPosixAttrib(); break;
486 
487     #if defined(_WIN32)
488       case kpidIsAltStream:  prop = di.IsAltStream; break;
489       // case kpidShortName:  prop = di.ShortName; break;
490     #else
491 
492         #if defined(__APPLE__)
493         #pragma GCC diagnostic push
494         #pragma GCC diagnostic ignored "-Wsign-conversion"
495         #endif
496 
497       case kpidDeviceMajor:
498         /*
499         printf("\ndi.mode = %o\n", di.mode);
500         printf("\nst.st_rdev major = %d\n", (unsigned)major(di.rdev));
501         printf("\nst.st_rdev minor = %d\n", (unsigned)minor(di.rdev));
502         */
503         if (S_ISCHR(di.mode) || S_ISBLK(di.mode))
504           prop = (UInt32)major(di.rdev);
505         break;
506 
507       case kpidDeviceMinor:
508         if (S_ISCHR(di.mode) || S_ISBLK(di.mode))
509           prop = (UInt32)minor(di.rdev);
510         break;
511 
512         #if defined(__APPLE__)
513         #pragma GCC diagnostic pop
514         #endif
515 
516       // case kpidDevice: if (S_ISCHR(di.mode) || S_ISBLK(di.mode)) prop = (UInt64)(di.rdev); break;
517 
518       case kpidUserId:  if (StoreOwnerId) prop = (UInt32)di.uid; break;
519       case kpidGroupId: if (StoreOwnerId) prop = (UInt32)di.gid; break;
520       case kpidUser:
521         if (di.OwnerNameIndex >= 0)
522           prop = DirItems->OwnerNameMap.Strings[(unsigned)di.OwnerNameIndex];
523         break;
524       case kpidGroup:
525         if (di.OwnerGroupIndex >= 0)
526           prop = DirItems->OwnerGroupMap.Strings[(unsigned)di.OwnerGroupIndex];
527         break;
528      #endif
529     }
530   }
531   prop.Detach(value);
532   return S_OK;
533   COM_TRY_END
534 }
535 
536 #ifndef Z7_ST
537 static NSynchronization::CCriticalSection g_CS;
538 #endif
539 
UpdateProcessedItemStatus(unsigned dirIndex)540 void CArchiveUpdateCallback::UpdateProcessedItemStatus(unsigned dirIndex)
541 {
542   if (ProcessedItemsStatuses)
543   {
544     #ifndef Z7_ST
545     NSynchronization::CCriticalSectionLock lock(g_CS);
546     #endif
547     ProcessedItemsStatuses[dirIndex] = 1;
548   }
549 }
550 
GetStream2(UInt32 index, ISequentialInStream **inStream, UInt32 mode)551 Z7_COM7F_IMF(CArchiveUpdateCallback::GetStream2(UInt32 index, ISequentialInStream **inStream, UInt32 mode))
552 {
553   COM_TRY_BEGIN
554   *inStream = NULL;
555   const CUpdatePair2 &up = (*UpdatePairs)[index];
556   if (!up.NewData)
557     return E_FAIL;
558 
559   RINOK(Callback->CheckBreak())
560   // RINOK(Callback->Finalize());
561 
562   bool isDir = IsDir(up);
563 
564   if (up.IsAnti)
565   {
566     UString name;
567     if (up.ArcIndex >= 0)
568       name = (*ArcItems)[(unsigned)up.ArcIndex].Name;
569     else if (up.DirIndex >= 0)
570       name = DirItems->GetLogPath((unsigned)up.DirIndex);
571     RINOK(Callback->GetStream(name, isDir, true, mode))
572 
573     /* 9.33: fixed. Handlers expect real stream object for files, even for anti-file.
574        so we return empty stream */
575 
576     if (!isDir)
577     {
578       CBufInStream *inStreamSpec = new CBufInStream();
579       CMyComPtr<ISequentialInStream> inStreamLoc = inStreamSpec;
580       inStreamSpec->Init(NULL, 0);
581       *inStream = inStreamLoc.Detach();
582     }
583     return S_OK;
584   }
585 
586   RINOK(Callback->GetStream(DirItems->GetLogPath((unsigned)up.DirIndex), isDir, false, mode))
587 
588   if (isDir)
589     return S_OK;
590 
591   if (StdInMode)
592   {
593     if (mode != NUpdateNotifyOp::kAdd &&
594         mode != NUpdateNotifyOp::kUpdate)
595       return S_OK;
596 
597     CStdInFileStream *inStreamSpec = new CStdInFileStream;
598     CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
599     *inStream = inStreamLoc.Detach();
600   }
601   else
602   {
603     #if !defined(UNDER_CE)
604     const CDirItem &di = DirItems->Items[(unsigned)up.DirIndex];
605     if (di.AreReparseData())
606     {
607       /*
608       // we still need DeviceIoControlOut() instead of Read
609       if (!inStreamSpec->File.OpenReparse(path))
610       {
611         return Callback->OpenFileError(path, ::GetLastError());
612       }
613       */
614       // 20.03: we use Reparse Data instead of real data
615 
616       CBufInStream *inStreamSpec = new CBufInStream();
617       CMyComPtr<ISequentialInStream> inStreamLoc = inStreamSpec;
618       inStreamSpec->Init(di.ReparseData, di.ReparseData.Size());
619       *inStream = inStreamLoc.Detach();
620 
621       UpdateProcessedItemStatus((unsigned)up.DirIndex);
622       return S_OK;
623     }
624     #endif // !defined(UNDER_CE)
625 
626     CInFileStream *inStreamSpec = new CInFileStream;
627     CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
628 
629    /*
630    // for debug:
631    #ifdef _WIN32
632     inStreamSpec->StoreOwnerName = true;
633     inStreamSpec->OwnerName = "user_name";
634     inStreamSpec->OwnerName += di.Name;
635     inStreamSpec->OwnerName += "11111111112222222222222333333333333";
636     inStreamSpec->OwnerGroup = "gname_";
637     inStreamSpec->OwnerGroup += inStreamSpec->OwnerName;
638    #endif
639    */
640 
641    #ifndef _WIN32
642     inStreamSpec->StoreOwnerId = StoreOwnerId;
643     inStreamSpec->StoreOwnerName = StoreOwnerName;
644 
645     // if (StoreOwner)
646     {
647       inStreamSpec->_uid = di.uid;
648       inStreamSpec->_gid = di.gid;
649       if (di.OwnerNameIndex >= 0)
650         inStreamSpec->OwnerName = DirItems->OwnerNameMap.Strings[(unsigned)di.OwnerNameIndex];
651       if (di.OwnerGroupIndex >= 0)
652         inStreamSpec->OwnerGroup = DirItems->OwnerGroupMap.Strings[(unsigned)di.OwnerGroupIndex];
653     }
654    #endif
655 
656     inStreamSpec->SupportHardLinks = StoreHardLinks;
657     const bool preserveATime = (PreserveATime
658         || mode == NUpdateNotifyOp::kAnalyze);   // 22.00 : we don't change access time in Analyze pass.
659     inStreamSpec->Set_PreserveATime(preserveATime);
660 
661     const FString path = DirItems->GetPhyPath((unsigned)up.DirIndex);
662     _openFiles_Indexes.Add(index);
663     _openFiles_Paths.Add(path);
664     // _openFiles_Streams.Add(inStreamSpec);
665 
666     /* 21.02 : we set Callback/CallbackRef after _openFiles_Indexes adding
667        for correct working if exception was raised in GetPhyPath */
668     inStreamSpec->Callback = this;
669     inStreamSpec->CallbackRef = index;
670 
671     if (!inStreamSpec->OpenShared(path, ShareForWrite))
672     {
673       bool isOpen = false;
674       if (preserveATime)
675       {
676         inStreamSpec->Set_PreserveATime(false);
677         isOpen = inStreamSpec->OpenShared(path, ShareForWrite);
678       }
679       if (!isOpen)
680       {
681         const DWORD error = ::GetLastError();
682         const HRESULT hres = Callback->OpenFileError(path, error);
683         if (hres == S_OK || hres == S_FALSE)
684         if (StopAfterOpenError ||
685             // v23: we check also for some critical errors:
686             #ifdef _WIN32
687               error == ERROR_NO_SYSTEM_RESOURCES
688             #else
689               error == EMFILE
690             #endif
691             )
692         {
693           if (error == 0)
694             return E_FAIL;
695           return HRESULT_FROM_WIN32(error);
696         }
697         return hres;
698       }
699     }
700 
701     /*
702     {
703       // for debug:
704       Byte b = 0;
705       UInt32 processedSize = 0;
706       if (inStreamSpec->Read(&b, 1, &processedSize) != S_OK ||
707           processedSize != 1)
708         return E_FAIL;
709     }
710     */
711 
712     if (Need_LatestMTime)
713     {
714       inStreamSpec->ReloadProps();
715     }
716 
717     // #if defined(Z7_FILE_STREAMS_USE_WIN_FILE) || !defined(_WIN32)
718     if (StoreHardLinks)
719     {
720       CStreamFileProps props;
721       if (inStreamSpec->GetProps2(&props) == S_OK)
722       {
723         if (props.NumLinks > 1)
724         {
725           CKeyKeyValPair pair;
726           pair.Key1 = props.VolID;
727           pair.Key2 = props.FileID_Low;
728           pair.Value = index;
729           const unsigned numItems = _map.Size();
730           const unsigned pairIndex = _map.AddToUniqueSorted2(pair);
731           if (numItems == _map.Size())
732           {
733             // const CKeyKeyValPair &pair2 = _map.Pairs[pairIndex];
734             _hardIndex_From = index;
735             _hardIndex_To = pairIndex;
736             // we could return NULL as stream, but it's better to return real stream
737             // return S_OK;
738           }
739         }
740       }
741     }
742     // #endif
743 
744     UpdateProcessedItemStatus((unsigned)up.DirIndex);
745     *inStream = inStreamLoc.Detach();
746   }
747 
748   return S_OK;
749   COM_TRY_END
750 }
751 
SetOperationResult(Int32 opRes)752 Z7_COM7F_IMF(CArchiveUpdateCallback::SetOperationResult(Int32 opRes))
753 {
754   COM_TRY_BEGIN
755   return Callback->SetOperationResult(opRes);
756   COM_TRY_END
757 }
758 
GetStream(UInt32 index, ISequentialInStream **inStream)759 Z7_COM7F_IMF(CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream **inStream))
760 {
761   COM_TRY_BEGIN
762   return GetStream2(index, inStream,
763       (*UpdatePairs)[index].ArcIndex < 0 ?
764           NUpdateNotifyOp::kAdd :
765           NUpdateNotifyOp::kUpdate);
766   COM_TRY_END
767 }
768 
ReportOperation(UInt32 indexType, UInt32 index, UInt32 op)769 Z7_COM7F_IMF(CArchiveUpdateCallback::ReportOperation(UInt32 indexType, UInt32 index, UInt32 op))
770 {
771   COM_TRY_BEGIN
772 
773   // if (op == NUpdateNotifyOp::kOpFinished) return Callback->ReportFinished(indexType, index);
774 
775   bool isDir = false;
776 
777   if (indexType == NArchive::NEventIndexType::kOutArcIndex)
778   {
779     UString name;
780     if (index != (UInt32)(Int32)-1)
781     {
782       const CUpdatePair2 &up = (*UpdatePairs)[index];
783       if (up.ExistOnDisk())
784       {
785         name = DirItems->GetLogPath((unsigned)up.DirIndex);
786         isDir = DirItems->Items[(unsigned)up.DirIndex].IsDir();
787       }
788     }
789     return Callback->ReportUpdateOperation(op, name.IsEmpty() ? NULL : name.Ptr(), isDir);
790   }
791 
792   wchar_t temp[16];
793   UString s2;
794   const wchar_t *s = NULL;
795 
796   if (indexType == NArchive::NEventIndexType::kInArcIndex)
797   {
798     if (index != (UInt32)(Int32)-1)
799     {
800       if (ArcItems)
801       {
802         const CArcItem &ai = (*ArcItems)[index];
803         s = ai.Name;
804         isDir = ai.IsDir;
805       }
806       else if (Arc)
807       {
808         RINOK(Arc->GetItem_Path(index, s2))
809         s = s2;
810         RINOK(Archive_IsItem_Dir(Arc->Archive, index, isDir))
811       }
812     }
813   }
814   else if (indexType == NArchive::NEventIndexType::kBlockIndex)
815   {
816     temp[0] = '#';
817     ConvertUInt32ToString(index, temp + 1);
818     s = temp;
819   }
820 
821   if (!s)
822     s = L"";
823 
824   return Callback->ReportUpdateOperation(op, s, isDir);
825 
826   COM_TRY_END
827 }
828 
ReportExtractResult(UInt32 indexType, UInt32 index, Int32 opRes)829 Z7_COM7F_IMF(CArchiveUpdateCallback::ReportExtractResult(UInt32 indexType, UInt32 index, Int32 opRes))
830 {
831   COM_TRY_BEGIN
832 
833   bool isEncrypted = false;
834   wchar_t temp[16];
835   UString s2;
836   const wchar_t *s = NULL;
837 
838   if (indexType == NArchive::NEventIndexType::kOutArcIndex)
839   {
840     /*
841     UString name;
842     if (index != (UInt32)(Int32)-1)
843     {
844       const CUpdatePair2 &up = (*UpdatePairs)[index];
845       if (up.ExistOnDisk())
846       {
847         s2 = DirItems->GetLogPath(up.DirIndex);
848         s = s2;
849       }
850     }
851     */
852     return E_FAIL;
853   }
854 
855   if (indexType == NArchive::NEventIndexType::kInArcIndex)
856   {
857     if (index != (UInt32)(Int32)-1)
858     {
859       if (ArcItems)
860         s = (*ArcItems)[index].Name;
861       else if (Arc)
862       {
863         RINOK(Arc->GetItem_Path(index, s2))
864         s = s2;
865       }
866       if (Archive)
867       {
868         RINOK(Archive_GetItemBoolProp(Archive, index, kpidEncrypted, isEncrypted))
869       }
870     }
871   }
872   else if (indexType == NArchive::NEventIndexType::kBlockIndex)
873   {
874     temp[0] = '#';
875     ConvertUInt32ToString(index, temp + 1);
876     s = temp;
877   }
878 
879   return Callback->ReportExtractResult(opRes, BoolToInt(isEncrypted), s);
880 
881   COM_TRY_END
882 }
883 
884 
885 /*
886 Z7_COM7F_IMF(CArchiveUpdateCallback::DoNeedArcProp(PROPID propID, Int32 *answer))
887 {
888   *answer = 0;
889   if (Need_ArcMTime_Report && propID == kpidComboMTime)
890     *answer = 1;
891   return S_OK;
892 }
893 
894 Z7_COM7F_IMF(CArchiveUpdateCallback::ReportProp(UInt32 indexType, UInt32 index, PROPID propID, const PROPVARIANT *value))
895 {
896   if (indexType == NArchive::NEventIndexType::kArcProp)
897   {
898     if (propID == kpidComboMTime)
899     {
900       ArcMTime_WasReported = true;
901       if (value->vt == VT_FILETIME)
902       {
903         Reported_ArcMTime.Set_From_Prop(*value);
904         Reported_ArcMTime.Def = true;
905       }
906       else
907       {
908         Reported_ArcMTime.Clear();
909         if (value->vt != VT_EMPTY)
910           return E_FAIL; // for debug
911       }
912     }
913   }
914   return Callback->ReportProp(indexType, index, propID, value);
915 }
916 
917 Z7_COM7F_IMF(CArchiveUpdateCallback::ReportRawProp(UInt32 indexType, UInt32 index,
918     PROPID propID, const void *data, UInt32 dataSize, UInt32 propType))
919 {
920   return Callback->ReportRawProp(indexType, index, propID, data, dataSize, propType);
921 }
922 
923 Z7_COM7F_IMF(CArchiveUpdateCallback::ReportFinished(UInt32 indexType, UInt32 index, Int32 opRes))
924 {
925   return Callback->ReportFinished(indexType, index, opRes);
926 }
927 */
928 
GetVolumeSize(UInt32 index, UInt64 *size)929 Z7_COM7F_IMF(CArchiveUpdateCallback::GetVolumeSize(UInt32 index, UInt64 *size))
930 {
931   if (VolumesSizes.Size() == 0)
932     return S_FALSE;
933   if (index >= (UInt32)VolumesSizes.Size())
934     index = VolumesSizes.Size() - 1;
935   *size = VolumesSizes[index];
936   return S_OK;
937 }
938 
GetVolumeStream(UInt32 index, ISequentialOutStream **volumeStream)939 Z7_COM7F_IMF(CArchiveUpdateCallback::GetVolumeStream(UInt32 index, ISequentialOutStream **volumeStream))
940 {
941   COM_TRY_BEGIN
942   char temp[16];
943   ConvertUInt32ToString(index + 1, temp);
944   FString res (temp);
945   while (res.Len() < 2)
946     res.InsertAtFront(FTEXT('0'));
947   FString fileName = VolName;
948   fileName.Add_Dot();
949   fileName += res;
950   fileName += VolExt;
951   COutFileStream *streamSpec = new COutFileStream;
952   CMyComPtr<ISequentialOutStream> streamLoc(streamSpec);
953   if (!streamSpec->Create(fileName, false))
954     return GetLastError_noZero_HRESULT();
955   *volumeStream = streamLoc.Detach();
956   return S_OK;
957   COM_TRY_END
958 }
959 
CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password)960 Z7_COM7F_IMF(CArchiveUpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password))
961 {
962   COM_TRY_BEGIN
963   return Callback->CryptoGetTextPassword2(passwordIsDefined, password);
964   COM_TRY_END
965 }
966 
CryptoGetTextPassword(BSTR *password)967 Z7_COM7F_IMF(CArchiveUpdateCallback::CryptoGetTextPassword(BSTR *password))
968 {
969   COM_TRY_BEGIN
970   return Callback->CryptoGetTextPassword(password);
971   COM_TRY_END
972 }
973 
InFileStream_On_Error(UINT_PTR val, DWORD error)974 HRESULT CArchiveUpdateCallback::InFileStream_On_Error(UINT_PTR val, DWORD error)
975 {
976   #ifdef _WIN32 // FIX IT !!!
977   // why did we check only for ERROR_LOCK_VIOLATION ?
978   // if (error == ERROR_LOCK_VIOLATION)
979   #endif
980   {
981     MT_LOCK
982     const UInt32 index = (UInt32)val;
983     FOR_VECTOR(i, _openFiles_Indexes)
984     {
985       if (_openFiles_Indexes[i] == index)
986       {
987         RINOK(Callback->ReadingFileError(_openFiles_Paths[i], error))
988         break;
989       }
990     }
991   }
992   return HRESULT_FROM_WIN32(error);
993 }
994 
InFileStream_On_Destroy(CInFileStream *stream, UINT_PTR val)995 void CArchiveUpdateCallback::InFileStream_On_Destroy(CInFileStream *stream, UINT_PTR val)
996 {
997   MT_LOCK
998   if (Need_LatestMTime)
999   {
1000     if (stream->_info_WasLoaded)
1001     {
1002       const CFiTime &ft = ST_MTIME(stream->_info);
1003       if (!LatestMTime_Defined
1004           || Compare_FiTime(&LatestMTime, &ft) < 0)
1005         LatestMTime = ft;
1006       LatestMTime_Defined = true;
1007     }
1008   }
1009   const UInt32 index = (UInt32)val;
1010   FOR_VECTOR(i, _openFiles_Indexes)
1011   {
1012     if (_openFiles_Indexes[i] == index)
1013     {
1014       _openFiles_Indexes.Delete(i);
1015       _openFiles_Paths.Delete(i);
1016       // _openFiles_Streams.Delete(i);
1017       return;
1018     }
1019   }
1020   /* 21.02 : this function can be called in destructor.
1021      And destructor can be called after some exception.
1022      If we don't want to throw exception in desctructors or after another exceptions,
1023      we must disable the code below that raises new exception.
1024   */
1025   // throw 20141125;
1026 }
1027