1// UpdateCallback.cpp
2
3#include "StdAfx.h"
4
5#include "../../../Common/ComTry.h"
6#include "../../../Common/IntToString.h"
7#include "../../../Common/StringConvert.h"
8#include "../../../Common/Wildcard.h"
9
10#include "../../../Windows/FileDir.h"
11#include "../../../Windows/FileName.h"
12#include "../../../Windows/PropVariant.h"
13#include "../../../Windows/Synchronization.h"
14
15#include "../../Common/FileStreams.h"
16#include "../../Common/StreamObjects.h"
17
18#include "UpdateCallback.h"
19
20#if defined(_WIN32) && !defined(UNDER_CE)
21#define _USE_SECURITY_CODE
22#include "../../../Windows/SecurityUtils.h"
23#endif
24
25using namespace NWindows;
26using namespace NFile;
27
28#ifdef _USE_SECURITY_CODE
29bool InitLocalPrivileges();
30#endif
31
32CArchiveUpdateCallback::CArchiveUpdateCallback():
33  Callback(0),
34  ShareForWrite(false),
35  StdInMode(false),
36  DirItems(0),
37  ArcItems(0),
38  UpdatePairs(0),
39  NewNames(0),
40  KeepOriginalItemNames(false),
41  ProcessedItemsStatuses(NULL),
42  ParentDirItem(NULL),
43  StoreNtSecurity(false),
44  StoreHardLinks(false),
45  StoreSymLinks(false),
46  _hardIndex_From((UInt32)(Int32)-1)
47{
48  #ifdef _USE_SECURITY_CODE
49  _saclEnabled = InitLocalPrivileges();
50  #endif
51}
52
53
54STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 size)
55{
56  COM_TRY_BEGIN
57  return Callback->SetTotal(size);
58  COM_TRY_END
59}
60
61STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UInt64 *completeValue)
62{
63  COM_TRY_BEGIN
64  return Callback->SetCompleted(completeValue);
65  COM_TRY_END
66}
67
68STDMETHODIMP CArchiveUpdateCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
69{
70  COM_TRY_BEGIN
71  return Callback->SetRatioInfo(inSize, outSize);
72  COM_TRY_END
73}
74
75
76/*
77static const STATPROPSTG kProps[] =
78{
79  { NULL, kpidPath, VT_BSTR},
80  { NULL, kpidIsDir, VT_BOOL},
81  { NULL, kpidSize, VT_UI8},
82  { NULL, kpidCTime, VT_FILETIME},
83  { NULL, kpidATime, VT_FILETIME},
84  { NULL, kpidMTime, VT_FILETIME},
85  { NULL, kpidAttrib, VT_UI4},
86  { NULL, kpidIsAnti, VT_BOOL}
87};
88
89STDMETHODIMP CArchiveUpdateCallback::EnumProperties(IEnumSTATPROPSTG **)
90{
91  return CStatPropEnumerator::CreateEnumerator(kProps, ARRAY_SIZE(kProps), enumerator);
92}
93*/
94
95STDMETHODIMP CArchiveUpdateCallback::GetUpdateItemInfo(UInt32 index,
96      Int32 *newData, Int32 *newProps, UInt32 *indexInArchive)
97{
98  COM_TRY_BEGIN
99  RINOK(Callback->CheckBreak());
100  const CUpdatePair2 &up = (*UpdatePairs)[index];
101  if (newData) *newData = BoolToInt(up.NewData);
102  if (newProps) *newProps = BoolToInt(up.NewProps);
103  if (indexInArchive)
104  {
105    *indexInArchive = (UInt32)(Int32)-1;
106    if (up.ExistInArchive())
107      *indexInArchive = (ArcItems == 0) ? up.ArcIndex : (*ArcItems)[up.ArcIndex].IndexInServer;
108  }
109  return S_OK;
110  COM_TRY_END
111}
112
113STDMETHODIMP CArchiveUpdateCallback::GetRootProp(PROPID propID, PROPVARIANT *value)
114{
115  NCOM::CPropVariant prop;
116  switch (propID)
117  {
118    case kpidIsDir:  prop = true; break;
119    case kpidAttrib: if (ParentDirItem) prop = ParentDirItem->Attrib; break;
120    case kpidCTime:  if (ParentDirItem) prop = ParentDirItem->CTime; break;
121    case kpidATime:  if (ParentDirItem) prop = ParentDirItem->ATime; break;
122    case kpidMTime:  if (ParentDirItem) prop = ParentDirItem->MTime; break;
123  }
124  prop.Detach(value);
125  return S_OK;
126}
127
128STDMETHODIMP CArchiveUpdateCallback::GetParent(UInt32 /* index */, UInt32 *parent, UInt32 *parentType)
129{
130  *parentType = NParentType::kDir;
131  *parent = (UInt32)(Int32)-1;
132  return S_OK;
133}
134
135STDMETHODIMP CArchiveUpdateCallback::GetNumRawProps(UInt32 *numProps)
136{
137  *numProps = 0;
138  if (StoreNtSecurity)
139    *numProps = 1;
140  return S_OK;
141}
142
143STDMETHODIMP CArchiveUpdateCallback::GetRawPropInfo(UInt32 /* index */, BSTR *name, PROPID *propID)
144{
145  *name = NULL;
146  *propID = kpidNtSecure;
147  return S_OK;
148}
149
150STDMETHODIMP CArchiveUpdateCallback::GetRootRawProp(PROPID
151    #ifdef _USE_SECURITY_CODE
152    propID
153    #endif
154    , const void **data, UInt32 *dataSize, UInt32 *propType)
155{
156  *data = 0;
157  *dataSize = 0;
158  *propType = 0;
159  if (!StoreNtSecurity)
160    return S_OK;
161  #ifdef _USE_SECURITY_CODE
162  if (propID == kpidNtSecure)
163  {
164    if (StdInMode)
165      return S_OK;
166
167    if (ParentDirItem)
168    {
169      if (ParentDirItem->SecureIndex < 0)
170        return S_OK;
171      const CByteBuffer &buf = DirItems->SecureBlocks.Bufs[ParentDirItem->SecureIndex];
172      *data = buf;
173      *dataSize = (UInt32)buf.Size();
174      *propType = NPropDataType::kRaw;
175      return S_OK;
176    }
177
178    if (GetRootProps)
179      return GetRootProps->GetRootRawProp(propID, data, dataSize, propType);
180  }
181  #endif
182  return S_OK;
183}
184
185//    #ifdef _USE_SECURITY_CODE
186//    #endif
187
188STDMETHODIMP CArchiveUpdateCallback::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType)
189{
190  *data = 0;
191  *dataSize = 0;
192  *propType = 0;
193
194  if (propID == kpidNtSecure ||
195      propID == kpidNtReparse)
196  {
197    if (StdInMode)
198      return S_OK;
199
200    const CUpdatePair2 &up = (*UpdatePairs)[index];
201    if (up.UseArcProps && up.ExistInArchive() && GetRawProps)
202      return GetRawProps->GetRawProp(
203          ArcItems ? (*ArcItems)[up.ArcIndex].IndexInServer : up.ArcIndex,
204          propID, data, dataSize, propType);
205
206    {
207      const CUpdatePair2 &up = (*UpdatePairs)[index];
208      /*
209      if (!up.NewData)
210        return E_FAIL;
211      */
212      if (up.IsAnti)
213        return S_OK;
214
215      #ifndef UNDER_CE
216      const CDirItem &di = DirItems->Items[up.DirIndex];
217      #endif
218
219      #ifdef _USE_SECURITY_CODE
220      if (propID == kpidNtSecure)
221      {
222        if (!StoreNtSecurity)
223          return S_OK;
224        if (di.SecureIndex < 0)
225          return S_OK;
226        const CByteBuffer &buf = DirItems->SecureBlocks.Bufs[di.SecureIndex];
227        *data = buf;
228        *dataSize = (UInt32)buf.Size();
229        *propType = NPropDataType::kRaw;
230      }
231      else
232      #endif
233      {
234        // propID == kpidNtReparse
235        if (!StoreSymLinks)
236          return S_OK;
237        #ifndef UNDER_CE
238        const CByteBuffer *buf = &di.ReparseData2;
239        if (buf->Size() == 0)
240          buf = &di.ReparseData;
241        if (buf->Size() != 0)
242        {
243          *data = *buf;
244          *dataSize = (UInt32)buf->Size();
245          *propType = NPropDataType::kRaw;
246        }
247        #endif
248      }
249
250      return S_OK;
251    }
252  }
253
254  return S_OK;
255}
256
257#ifndef UNDER_CE
258
259static UString GetRelativePath(const UString &to, const UString &from)
260{
261  UStringVector partsTo, partsFrom;
262  SplitPathToParts(to, partsTo);
263  SplitPathToParts(from, partsFrom);
264
265  unsigned i;
266  for (i = 0;; i++)
267  {
268    if (i + 1 >= partsFrom.Size() ||
269        i + 1 >= partsTo.Size())
270      break;
271    if (CompareFileNames(partsFrom[i], partsTo[i]) != 0)
272      break;
273  }
274
275  if (i == 0)
276  {
277    #ifdef _WIN32
278    if (NName::IsDrivePath(to) ||
279        NName::IsDrivePath(from))
280      return to;
281    #endif
282  }
283
284  UString s;
285  unsigned k;
286
287  for (k = i + 1; k < partsFrom.Size(); k++)
288    s += L".." WSTRING_PATH_SEPARATOR;
289
290  for (k = i; k < partsTo.Size(); k++)
291  {
292    if (k != i)
293      s += WCHAR_PATH_SEPARATOR;
294    s += partsTo[k];
295  }
296
297  return s;
298}
299
300#endif
301
302STDMETHODIMP CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
303{
304  COM_TRY_BEGIN
305  const CUpdatePair2 &up = (*UpdatePairs)[index];
306  NCOM::CPropVariant prop;
307
308  if (up.NewData)
309  {
310    /*
311    if (propID == kpidIsHardLink)
312    {
313      prop = _isHardLink;
314      prop.Detach(value);
315      return S_OK;
316    }
317    */
318    if (propID == kpidSymLink)
319    {
320      if (index == _hardIndex_From)
321      {
322        prop.Detach(value);
323        return S_OK;
324      }
325      if (up.DirIndex >= 0)
326      {
327        #ifndef UNDER_CE
328        const CDirItem &di = DirItems->Items[up.DirIndex];
329        // if (di.IsDir())
330        {
331          CReparseAttr attr;
332          if (attr.Parse(di.ReparseData, di.ReparseData.Size()))
333          {
334            UString simpleName = attr.GetPath();
335            if (attr.IsRelative())
336              prop = simpleName;
337            else
338            {
339              const UString phyPath = DirItems->GetPhyPath(up.DirIndex);
340              FString fullPath;
341              if (NDir::MyGetFullPathName(us2fs(phyPath), fullPath))
342              {
343                prop = GetRelativePath(simpleName, fs2us(fullPath));
344              }
345            }
346            prop.Detach(value);
347            return S_OK;
348          }
349        }
350        #endif
351      }
352    }
353    else if (propID == kpidHardLink)
354    {
355      if (index == _hardIndex_From)
356      {
357        const CKeyKeyValPair &pair = _map[_hardIndex_To];
358        const CUpdatePair2 &up2 = (*UpdatePairs)[pair.Value];
359        prop = DirItems->GetLogPath(up2.DirIndex);
360        prop.Detach(value);
361        return S_OK;
362      }
363      if (up.DirIndex >= 0)
364      {
365        prop.Detach(value);
366        return S_OK;
367      }
368    }
369  }
370
371  if (up.IsAnti
372      && propID != kpidIsDir
373      && propID != kpidPath
374      && propID != kpidIsAltStream)
375  {
376    switch (propID)
377    {
378      case kpidSize:  prop = (UInt64)0; break;
379      case kpidIsAnti:  prop = true; break;
380    }
381  }
382  else if (propID == kpidPath && up.NewNameIndex >= 0)
383    prop = (*NewNames)[up.NewNameIndex];
384  else if (propID == kpidShortName && up.NewNameIndex >= 0 && up.IsMainRenameItem)
385  {
386    // we can generate new ShortName here;
387  }
388  else if ((up.UseArcProps
389      || (KeepOriginalItemNames && (propID == kpidPath || propID == kpidIsAltStream)))
390      && up.ExistInArchive() && Archive)
391    return Archive->GetProperty(ArcItems ? (*ArcItems)[up.ArcIndex].IndexInServer : up.ArcIndex, propID, value);
392  else if (up.ExistOnDisk())
393  {
394    const CDirItem &di = DirItems->Items[up.DirIndex];
395    switch (propID)
396    {
397      case kpidPath:  prop = DirItems->GetLogPath(up.DirIndex); break;
398      case kpidIsDir:  prop = di.IsDir(); break;
399      case kpidSize:  prop = di.Size; break;
400      case kpidAttrib:  prop = di.Attrib; break;
401      case kpidCTime:  prop = di.CTime; break;
402      case kpidATime:  prop = di.ATime; break;
403      case kpidMTime:  prop = di.MTime; break;
404      case kpidIsAltStream:  prop = di.IsAltStream; break;
405      #if defined(_WIN32) && !defined(UNDER_CE)
406      // case kpidShortName:  prop = di.ShortName; break;
407      #endif
408    }
409  }
410  prop.Detach(value);
411  return S_OK;
412  COM_TRY_END
413}
414
415static NSynchronization::CCriticalSection CS;
416
417STDMETHODIMP CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream **inStream)
418{
419  COM_TRY_BEGIN
420  *inStream = NULL;
421  const CUpdatePair2 &up = (*UpdatePairs)[index];
422  if (!up.NewData)
423    return E_FAIL;
424
425  RINOK(Callback->CheckBreak());
426  RINOK(Callback->Finilize());
427
428  bool isDir = IsDir(up);
429
430  if (up.IsAnti)
431  {
432    UString name;
433    if (up.ArcIndex >= 0)
434      name = (*ArcItems)[up.ArcIndex].Name;
435    else if (up.DirIndex >= 0)
436      name = DirItems->GetLogPath(up.DirIndex);
437    RINOK(Callback->GetStream(name, true));
438
439    /* 9.33: fixed. Handlers expect real stream object for files, even for anti-file.
440       so we return empty stream */
441
442    if (!isDir)
443    {
444      CBufInStream *inStreamSpec = new CBufInStream();
445      CMyComPtr<ISequentialInStream> inStreamLoc = inStreamSpec;
446      inStreamSpec->Init(NULL, 0);
447      *inStream = inStreamLoc.Detach();
448    }
449    return S_OK;
450  }
451
452  RINOK(Callback->GetStream(DirItems->GetLogPath(up.DirIndex), false));
453
454  if (isDir)
455    return S_OK;
456
457  if (StdInMode)
458  {
459    CStdInFileStream *inStreamSpec = new CStdInFileStream;
460    CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
461    *inStream = inStreamLoc.Detach();
462  }
463  else
464  {
465    CInFileStream *inStreamSpec = new CInFileStream;
466    CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
467
468    inStreamSpec->SupportHardLinks = StoreHardLinks;
469
470    const UString path = DirItems->GetPhyPath(up.DirIndex);
471
472    #if defined(_WIN32) && !defined(UNDER_CE)
473    if (DirItems->Items[up.DirIndex].AreReparseData())
474    {
475      if (!inStreamSpec->File.OpenReparse(us2fs(path)))
476      {
477        return Callback->OpenFileError(path, ::GetLastError());
478      }
479    }
480    else
481    #endif
482    if (!inStreamSpec->OpenShared(us2fs(path), ShareForWrite))
483    {
484      return Callback->OpenFileError(path, ::GetLastError());
485    }
486
487    if (StoreHardLinks)
488    {
489      CStreamFileProps props;
490      if (inStreamSpec->GetProps2(&props) == S_OK)
491      {
492        if (props.NumLinks > 1)
493        {
494          CKeyKeyValPair pair;
495          pair.Key1 = props.VolID;
496          pair.Key2 = props.FileID_Low;
497          pair.Value = index;
498          unsigned numItems = _map.Size();
499          unsigned pairIndex = _map.AddToUniqueSorted2(pair);
500          if (numItems == _map.Size())
501          {
502            // const CKeyKeyValPair &pair2 = _map.Pairs[pairIndex];
503            _hardIndex_From = index;
504            _hardIndex_To = pairIndex;
505            // we could return NULL as stream, but it's better to return real stream
506            // return S_OK;
507          }
508        }
509      }
510    }
511
512    if (ProcessedItemsStatuses)
513    {
514      NSynchronization::CCriticalSectionLock lock(CS);
515      ProcessedItemsStatuses[up.DirIndex] = 1;
516    }
517    *inStream = inStreamLoc.Detach();
518  }
519
520  return S_OK;
521  COM_TRY_END
522}
523
524STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 operationResult)
525{
526  COM_TRY_BEGIN
527  return Callback->SetOperationResult(operationResult);
528  COM_TRY_END
529}
530
531STDMETHODIMP CArchiveUpdateCallback::GetVolumeSize(UInt32 index, UInt64 *size)
532{
533  if (VolumesSizes.Size() == 0)
534    return S_FALSE;
535  if (index >= (UInt32)VolumesSizes.Size())
536    index = VolumesSizes.Size() - 1;
537  *size = VolumesSizes[index];
538  return S_OK;
539}
540
541STDMETHODIMP CArchiveUpdateCallback::GetVolumeStream(UInt32 index, ISequentialOutStream **volumeStream)
542{
543  COM_TRY_BEGIN
544  FChar temp[16];
545  ConvertUInt32ToString(index + 1, temp);
546  FString res = temp;
547  while (res.Len() < 2)
548    res.InsertAtFront(FTEXT('0'));
549  FString fileName = VolName;
550  fileName += L'.';
551  fileName += res;
552  fileName += VolExt;
553  COutFileStream *streamSpec = new COutFileStream;
554  CMyComPtr<ISequentialOutStream> streamLoc(streamSpec);
555  if (!streamSpec->Create(fileName, false))
556    return ::GetLastError();
557  *volumeStream = streamLoc.Detach();
558  return S_OK;
559  COM_TRY_END
560}
561
562STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password)
563{
564  COM_TRY_BEGIN
565  return Callback->CryptoGetTextPassword2(passwordIsDefined, password);
566  COM_TRY_END
567}
568
569STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword(BSTR *password)
570{
571  COM_TRY_BEGIN
572  return Callback->CryptoGetTextPassword(password);
573  COM_TRY_END
574}
575