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
25 using namespace NWindows;
26 using namespace NFile;
27
28 #ifdef _USE_SECURITY_CODE
29 bool InitLocalPrivileges();
30 #endif
31
CArchiveUpdateCallback()32 CArchiveUpdateCallback::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
SetTotal(UInt64 size)54 STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 size)
55 {
56 COM_TRY_BEGIN
57 return Callback->SetTotal(size);
58 COM_TRY_END
59 }
60
SetCompleted(const UInt64 * completeValue)61 STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UInt64 *completeValue)
62 {
63 COM_TRY_BEGIN
64 return Callback->SetCompleted(completeValue);
65 COM_TRY_END
66 }
67
SetRatioInfo(const UInt64 * inSize,const UInt64 * outSize)68 STDMETHODIMP 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 /*
77 static 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
89 STDMETHODIMP CArchiveUpdateCallback::EnumProperties(IEnumSTATPROPSTG **)
90 {
91 return CStatPropEnumerator::CreateEnumerator(kProps, ARRAY_SIZE(kProps), enumerator);
92 }
93 */
94
GetUpdateItemInfo(UInt32 index,Int32 * newData,Int32 * newProps,UInt32 * indexInArchive)95 STDMETHODIMP 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
GetRootProp(PROPID propID,PROPVARIANT * value)113 STDMETHODIMP 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
GetParent(UInt32,UInt32 * parent,UInt32 * parentType)128 STDMETHODIMP CArchiveUpdateCallback::GetParent(UInt32 /* index */, UInt32 *parent, UInt32 *parentType)
129 {
130 *parentType = NParentType::kDir;
131 *parent = (UInt32)(Int32)-1;
132 return S_OK;
133 }
134
GetNumRawProps(UInt32 * numProps)135 STDMETHODIMP CArchiveUpdateCallback::GetNumRawProps(UInt32 *numProps)
136 {
137 *numProps = 0;
138 if (StoreNtSecurity)
139 *numProps = 1;
140 return S_OK;
141 }
142
GetRawPropInfo(UInt32,BSTR * name,PROPID * propID)143 STDMETHODIMP CArchiveUpdateCallback::GetRawPropInfo(UInt32 /* index */, BSTR *name, PROPID *propID)
144 {
145 *name = NULL;
146 *propID = kpidNtSecure;
147 return S_OK;
148 }
149
GetRootRawProp(PROPID propID,const void ** data,UInt32 * dataSize,UInt32 * propType)150 STDMETHODIMP 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
GetRawProp(UInt32 index,PROPID propID,const void ** data,UInt32 * dataSize,UInt32 * propType)188 STDMETHODIMP 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
GetRelativePath(const UString & to,const UString & from)259 static 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
GetProperty(UInt32 index,PROPID propID,PROPVARIANT * value)302 STDMETHODIMP 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
415 static NSynchronization::CCriticalSection CS;
416
GetStream(UInt32 index,ISequentialInStream ** inStream)417 STDMETHODIMP 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
SetOperationResult(Int32 operationResult)524 STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 operationResult)
525 {
526 COM_TRY_BEGIN
527 return Callback->SetOperationResult(operationResult);
528 COM_TRY_END
529 }
530
GetVolumeSize(UInt32 index,UInt64 * size)531 STDMETHODIMP 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
GetVolumeStream(UInt32 index,ISequentialOutStream ** volumeStream)541 STDMETHODIMP 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
CryptoGetTextPassword2(Int32 * passwordIsDefined,BSTR * password)562 STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password)
563 {
564 COM_TRY_BEGIN
565 return Callback->CryptoGetTextPassword2(passwordIsDefined, password);
566 COM_TRY_END
567 }
568
CryptoGetTextPassword(BSTR * password)569 STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword(BSTR *password)
570 {
571 COM_TRY_BEGIN
572 return Callback->CryptoGetTextPassword(password);
573 COM_TRY_END
574 }
575