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/*
11inclusion of <sys/sysmacros.h> by <sys/types.h> is deprecated since glibc 2.25.
12Since glibc 2.3.3, macros have been aliases for three GNU-specific
13functions: 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
49using namespace NWindows;
50using namespace NFile;
51
52#ifndef Z7_ST
53static 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
61bool InitLocalPrivileges();
62#endif
63
64CArchiveUpdateCallback::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
108Z7_COM7F_IMF(CArchiveUpdateCallback::SetTotal(UInt64 size))
109{
110  COM_TRY_BEGIN
111  return Callback->SetTotal(size);
112  COM_TRY_END
113}
114
115Z7_COM7F_IMF(CArchiveUpdateCallback::SetCompleted(const UInt64 *completeValue))
116{
117  COM_TRY_BEGIN
118  return Callback->SetCompleted(completeValue);
119  COM_TRY_END
120}
121
122Z7_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/*
131static 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
143Z7_COM7F_IMF(CArchiveUpdateCallback::EnumProperties(IEnumSTATPROPSTG **)
144{
145  return CStatPropEnumerator::CreateEnumerator(kProps, Z7_ARRAY_SIZE(kProps), enumerator);
146}
147*/
148
149Z7_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
168Z7_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
184Z7_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
191Z7_COM7F_IMF(CArchiveUpdateCallback::GetNumRawProps(UInt32 *numProps))
192{
193  *numProps = 0;
194  if (StoreNtSecurity)
195    *numProps = 1;
196  return S_OK;
197}
198
199Z7_COM7F_IMF(CArchiveUpdateCallback::GetRawPropInfo(UInt32 /* index */, BSTR *name, PROPID *propID))
200{
201  *name = NULL;
202  *propID = kpidNtSecure;
203  return S_OK;
204}
205
206Z7_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
244Z7_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
314static 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
357Z7_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
537static NSynchronization::CCriticalSection g_CS;
538#endif
539
540void 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
551Z7_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
752Z7_COM7F_IMF(CArchiveUpdateCallback::SetOperationResult(Int32 opRes))
753{
754  COM_TRY_BEGIN
755  return Callback->SetOperationResult(opRes);
756  COM_TRY_END
757}
758
759Z7_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
769Z7_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
829Z7_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/*
886Z7_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
894Z7_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
917Z7_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
923Z7_COM7F_IMF(CArchiveUpdateCallback::ReportFinished(UInt32 indexType, UInt32 index, Int32 opRes))
924{
925  return Callback->ReportFinished(indexType, index, opRes);
926}
927*/
928
929Z7_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
939Z7_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
960Z7_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
967Z7_COM7F_IMF(CArchiveUpdateCallback::CryptoGetTextPassword(BSTR *password))
968{
969  COM_TRY_BEGIN
970  return Callback->CryptoGetTextPassword(password);
971  COM_TRY_END
972}
973
974HRESULT 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
995void 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