1 // Update.cpp
2
3 #include "StdAfx.h"
4
5 // #include <stdio.h>
6
7 #include "Update.h"
8
9 #include "../../../Common/StringConvert.h"
10
11 #include "../../../Windows/DLL.h"
12 #include "../../../Windows/FileDir.h"
13 #include "../../../Windows/FileFind.h"
14 #include "../../../Windows/FileName.h"
15 #include "../../../Windows/PropVariant.h"
16 #include "../../../Windows/PropVariantConv.h"
17 #include "../../../Windows/TimeUtils.h"
18
19 #include "../../Common/FileStreams.h"
20 #include "../../Common/LimitedStreams.h"
21 #include "../../Common/MultiOutStream.h"
22 #include "../../Common/StreamUtils.h"
23
24 #include "../../Compress/CopyCoder.h"
25
26 #include "../Common/DirItem.h"
27 #include "../Common/EnumDirItems.h"
28 #include "../Common/OpenArchive.h"
29 #include "../Common/UpdateProduce.h"
30
31 #include "EnumDirItems.h"
32 #include "SetProperties.h"
33 #include "TempFiles.h"
34 #include "UpdateCallback.h"
35
36 static const char * const kUpdateIsNotSupoorted =
37 "update operations are not supported for this archive";
38
39 static const char * const kUpdateIsNotSupported_MultiVol =
40 "Updating for multivolume archives is not implemented";
41
42 using namespace NWindows;
43 using namespace NCOM;
44 using namespace NFile;
45 using namespace NDir;
46 using namespace NName;
47
48 #ifdef _WIN32
49 static CFSTR const kTempFolderPrefix = FTEXT("7zE");
50 #endif
51
SetFromLastError(const char * message)52 void CUpdateErrorInfo::SetFromLastError(const char *message)
53 {
54 SystemError = ::GetLastError();
55 Message = message;
56 }
57
SetFromLastError(const char * message,const FString & fileName)58 HRESULT CUpdateErrorInfo::SetFromLastError(const char *message, const FString &fileName)
59 {
60 SetFromLastError(message);
61 FileNames.Add(fileName);
62 return Get_HRESULT_Error();
63 }
64
SetFromError_DWORD(const char * message,const FString & fileName,DWORD error)65 HRESULT CUpdateErrorInfo::SetFromError_DWORD(const char *message, const FString &fileName, DWORD error)
66 {
67 Message = message;
68 FileNames.Add(fileName);
69 SystemError = error;
70 return Get_HRESULT_Error();
71 }
72
73
74 using namespace NUpdateArchive;
75
76 struct CMultiOutStream_Rec
77 {
78 CMultiOutStream *Spec;
79 CMyComPtr<IOutStream> Ref;
80 };
81
82 struct CMultiOutStream_Bunch
83 {
84 CObjectVector<CMultiOutStream_Rec> Items;
85
DestructCMultiOutStream_Bunch86 HRESULT Destruct()
87 {
88 HRESULT hres = S_OK;
89 FOR_VECTOR (i, Items)
90 {
91 CMultiOutStream_Rec &rec = Items[i];
92 if (rec.Ref)
93 {
94 const HRESULT hres2 = rec.Spec->Destruct();
95 if (hres == S_OK)
96 hres = hres2;
97 }
98 }
99 Items.Clear();
100 return hres;
101 }
102
DisableDeletionCMultiOutStream_Bunch103 void DisableDeletion()
104 {
105 FOR_VECTOR (i, Items)
106 {
107 CMultiOutStream_Rec &rec = Items[i];
108 if (rec.Ref)
109 rec.Spec->NeedDelete = false;
110 }
111 }
112 };
113
114
ParseFromPath(const UString & path,EArcNameMode mode)115 void CArchivePath::ParseFromPath(const UString &path, EArcNameMode mode)
116 {
117 OriginalPath = path;
118
119 SplitPathToParts_2(path, Prefix, Name);
120
121 if (mode == k_ArcNameMode_Add)
122 return;
123
124 if (mode != k_ArcNameMode_Exact)
125 {
126 int dotPos = Name.ReverseFind_Dot();
127 if (dotPos < 0)
128 return;
129 if ((unsigned)dotPos == Name.Len() - 1)
130 Name.DeleteBack();
131 else
132 {
133 const UString ext = Name.Ptr((unsigned)(dotPos + 1));
134 if (BaseExtension.IsEqualTo_NoCase(ext))
135 {
136 BaseExtension = ext;
137 Name.DeleteFrom((unsigned)dotPos);
138 return;
139 }
140 }
141 }
142
143 BaseExtension.Empty();
144 }
145
GetFinalPath() const146 UString CArchivePath::GetFinalPath() const
147 {
148 UString path = GetPathWithoutExt();
149 if (!BaseExtension.IsEmpty())
150 {
151 path.Add_Dot();
152 path += BaseExtension;
153 }
154 return path;
155 }
156
GetFinalVolPath() const157 UString CArchivePath::GetFinalVolPath() const
158 {
159 UString path = GetPathWithoutExt();
160 // if BaseExtension is empty, we must ignore VolExtension also.
161 if (!BaseExtension.IsEmpty())
162 {
163 path.Add_Dot();
164 path += VolExtension;
165 }
166 return path;
167 }
168
GetTempPath() const169 FString CArchivePath::GetTempPath() const
170 {
171 FString path = TempPrefix;
172 path += us2fs(Name);
173 if (!BaseExtension.IsEmpty())
174 {
175 path.Add_Dot();
176 path += us2fs(BaseExtension);
177 }
178 path += ".tmp";
179 path += TempPostfix;
180 return path;
181 }
182
183 static const char * const kDefaultArcType = "7z";
184 static const char * const kDefaultArcExt = "7z";
185 static const char * const kSFXExtension =
186 #ifdef _WIN32
187 "exe";
188 #else
189 "";
190 #endif
191
InitFormatIndex(const CCodecs * codecs,const CObjectVector<COpenType> & types,const UString & arcPath)192 bool CUpdateOptions::InitFormatIndex(const CCodecs *codecs,
193 const CObjectVector<COpenType> &types, const UString &arcPath)
194 {
195 if (types.Size() > 1)
196 return false;
197 // int arcTypeIndex = -1;
198 if (types.Size() != 0)
199 {
200 MethodMode.Type = types[0];
201 MethodMode.Type_Defined = true;
202 }
203 if (MethodMode.Type.FormatIndex < 0)
204 {
205 // MethodMode.Type = -1;
206 MethodMode.Type = COpenType();
207 if (ArcNameMode != k_ArcNameMode_Add)
208 {
209 MethodMode.Type.FormatIndex = codecs->FindFormatForArchiveName(arcPath);
210 if (MethodMode.Type.FormatIndex >= 0)
211 MethodMode.Type_Defined = true;
212 }
213 }
214 return true;
215 }
216
SetArcPath(const CCodecs * codecs,const UString & arcPath)217 bool CUpdateOptions::SetArcPath(const CCodecs *codecs, const UString &arcPath)
218 {
219 UString typeExt;
220 int formatIndex = MethodMode.Type.FormatIndex;
221 if (formatIndex < 0)
222 {
223 typeExt = kDefaultArcExt;
224 }
225 else
226 {
227 const CArcInfoEx &arcInfo = codecs->Formats[(unsigned)formatIndex];
228 if (!arcInfo.UpdateEnabled)
229 return false;
230 typeExt = arcInfo.GetMainExt();
231 }
232 UString ext = typeExt;
233 if (SfxMode)
234 ext = kSFXExtension;
235 ArchivePath.BaseExtension = ext;
236 ArchivePath.VolExtension = typeExt;
237 ArchivePath.ParseFromPath(arcPath, ArcNameMode);
238 FOR_VECTOR (i, Commands)
239 {
240 CUpdateArchiveCommand &uc = Commands[i];
241 uc.ArchivePath.BaseExtension = ext;
242 uc.ArchivePath.VolExtension = typeExt;
243 uc.ArchivePath.ParseFromPath(uc.UserArchivePath, ArcNameMode);
244 }
245 return true;
246 }
247
248
249 struct CUpdateProduceCallbackImp Z7_final: public IUpdateProduceCallback
250 {
251 const CObjectVector<CArcItem> *_arcItems;
252 CDirItemsStat *_stat;
253 IUpdateCallbackUI *_callback;
254
CUpdateProduceCallbackImpZ7_final255 CUpdateProduceCallbackImp(
256 const CObjectVector<CArcItem> *a,
257 CDirItemsStat *stat,
258 IUpdateCallbackUI *callback):
259 _arcItems(a),
260 _stat(stat),
261 _callback(callback) {}
262
263 virtual HRESULT ShowDeleteFile(unsigned arcIndex) Z7_override;
264 };
265
266
ShowDeleteFile(unsigned arcIndex)267 HRESULT CUpdateProduceCallbackImp::ShowDeleteFile(unsigned arcIndex)
268 {
269 const CArcItem &ai = (*_arcItems)[arcIndex];
270 {
271 CDirItemsStat &stat = *_stat;
272 if (ai.IsDir)
273 stat.NumDirs++;
274 else if (ai.IsAltStream)
275 {
276 stat.NumAltStreams++;
277 stat.AltStreamsSize += ai.Size;
278 }
279 else
280 {
281 stat.NumFiles++;
282 stat.FilesSize += ai.Size;
283 }
284 }
285 return _callback->ShowDeleteFile(ai.Name, ai.IsDir);
286 }
287
Prepare()288 bool CRenamePair::Prepare()
289 {
290 if (RecursedType != NRecursedType::kNonRecursed)
291 return false;
292 if (!WildcardParsing)
293 return true;
294 return !DoesNameContainWildcard(OldName);
295 }
296
297 extern bool g_CaseSensitive;
298
CompareTwoNames(const wchar_t * s1,const wchar_t * s2)299 static unsigned CompareTwoNames(const wchar_t *s1, const wchar_t *s2)
300 {
301 for (unsigned i = 0;; i++)
302 {
303 wchar_t c1 = s1[i];
304 wchar_t c2 = s2[i];
305 if (c1 == 0 || c2 == 0)
306 return i;
307 if (c1 == c2)
308 continue;
309 if (!g_CaseSensitive && (MyCharUpper(c1) == MyCharUpper(c2)))
310 continue;
311 if (IsPathSepar(c1) && IsPathSepar(c2))
312 continue;
313 return i;
314 }
315 }
316
GetNewPath(bool isFolder,const UString & src,UString & dest) const317 bool CRenamePair::GetNewPath(bool isFolder, const UString &src, UString &dest) const
318 {
319 unsigned num = CompareTwoNames(OldName, src);
320 if (OldName[num] == 0)
321 {
322 if (src[num] != 0 && !IsPathSepar(src[num]) && num != 0 && !IsPathSepar(src[num - 1]))
323 return false;
324 }
325 else
326 {
327 // OldName[num] != 0
328 // OldName = "1\1a.txt"
329 // src = "1"
330
331 if (!isFolder
332 || src[num] != 0
333 || !IsPathSepar(OldName[num])
334 || OldName[num + 1] != 0)
335 return false;
336 }
337 dest = NewName + src.Ptr(num);
338 return true;
339 }
340
341 #ifdef SUPPORT_ALT_STREAMS
342 int FindAltStreamColon_in_Path(const wchar_t *path);
343 #endif
344
345
346
Compress(const CUpdateOptions & options,bool isUpdatingItself,CCodecs * codecs,const CActionSet & actionSet,const CArc * arc,CArchivePath & archivePath,const CObjectVector<CArcItem> & arcItems,Byte * processedItemsStatuses,const CDirItems & dirItems,const CDirItem * parentDirItem,CTempFiles & tempFiles,CMultiOutStream_Bunch & multiStreams,CUpdateErrorInfo & errorInfo,IUpdateCallbackUI * callback,CFinishArchiveStat & st)347 static HRESULT Compress(
348 const CUpdateOptions &options,
349 bool isUpdatingItself,
350 CCodecs *codecs,
351 const CActionSet &actionSet,
352 const CArc *arc,
353 CArchivePath &archivePath,
354 const CObjectVector<CArcItem> &arcItems,
355 Byte *processedItemsStatuses,
356 const CDirItems &dirItems,
357 const CDirItem *parentDirItem,
358 CTempFiles &tempFiles,
359 CMultiOutStream_Bunch &multiStreams,
360 CUpdateErrorInfo &errorInfo,
361 IUpdateCallbackUI *callback,
362 CFinishArchiveStat &st)
363 {
364 CMyComPtr<IOutArchive> outArchive;
365 int formatIndex = options.MethodMode.Type.FormatIndex;
366
367 if (arc)
368 {
369 formatIndex = arc->FormatIndex;
370 if (formatIndex < 0)
371 return E_NOTIMPL;
372 CMyComPtr<IInArchive> archive2 = arc->Archive;
373 HRESULT result = archive2.QueryInterface(IID_IOutArchive, &outArchive);
374 if (result != S_OK)
375 throw kUpdateIsNotSupoorted;
376 }
377 else
378 {
379 RINOK(codecs->CreateOutArchive((unsigned)formatIndex, outArchive))
380
381 #ifdef Z7_EXTERNAL_CODECS
382 {
383 CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
384 outArchive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
385 if (setCompressCodecsInfo)
386 {
387 RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs))
388 }
389 }
390 #endif
391 }
392
393 if (!outArchive)
394 throw kUpdateIsNotSupoorted;
395
396 // we need to set properties to get fileTimeType.
397 RINOK(SetProperties(outArchive, options.MethodMode.Properties))
398
399 NFileTimeType::EEnum fileTimeType;
400 {
401 /*
402 how we compare file_in_archive::MTime with dirItem.MTime
403 for GetUpdatePairInfoList():
404
405 if (kpidMTime is not defined), external MTime of archive is used.
406
407 before 22.00:
408 if (kpidTimeType is defined)
409 {
410 kpidTimeType is used as precision.
411 (kpidTimeType > kDOS) is not allowed.
412 }
413 else GetFileTimeType() value is used as precision.
414
415 22.00:
416 if (kpidMTime is defined)
417 {
418 if (kpidMTime::precision != 0), then kpidMTime::precision is used as precision.
419 else
420 {
421 if (kpidTimeType is defined), kpidTimeType is used as precision.
422 else GetFileTimeType() value is used as precision.
423 }
424 }
425 else external MTime of archive is used as precision.
426 */
427
428 UInt32 value;
429 RINOK(outArchive->GetFileTimeType(&value))
430
431 // we support any future fileType here.
432 fileTimeType = (NFileTimeType::EEnum)value;
433
434 /*
435 old 21.07 code:
436 switch (value)
437 {
438 case NFileTimeType::kWindows:
439 case NFileTimeType::kUnix:
440 case NFileTimeType::kDOS:
441 fileTimeType = (NFileTimeType::EEnum)value;
442 break;
443 default:
444 return E_FAIL;
445 }
446 */
447 }
448
449 // bool noTimestampExpected = false;
450 {
451 const CArcInfoEx &arcInfo = codecs->Formats[(unsigned)formatIndex];
452
453 // if (arcInfo.Flags_KeepName()) noTimestampExpected = true;
454 if (arcInfo.Is_Xz() ||
455 arcInfo.Is_BZip2())
456 {
457 /* 7-zip before 22.00 returns NFileTimeType::kUnix for xz and bzip2,
458 but we want to set timestamp without reduction to unix. */
459 // noTimestampExpected = true;
460 fileTimeType = NFileTimeType::kNotDefined; // it means not defined
461 }
462
463 if (options.AltStreams.Val && !arcInfo.Flags_AltStreams())
464 return E_NOTIMPL;
465 if (options.NtSecurity.Val && !arcInfo.Flags_NtSecurity())
466 return E_NOTIMPL;
467 if (options.DeleteAfterCompressing && arcInfo.Flags_HashHandler())
468 return E_NOTIMPL;
469 }
470
471 CRecordVector<CUpdatePair2> updatePairs2;
472
473 UStringVector newNames;
474
475 CArcToDoStat stat2;
476
477 if (options.RenamePairs.Size() != 0)
478 {
479 FOR_VECTOR (i, arcItems)
480 {
481 const CArcItem &ai = arcItems[i];
482 bool needRename = false;
483 UString dest;
484
485 if (ai.Censored)
486 {
487 FOR_VECTOR (j, options.RenamePairs)
488 {
489 const CRenamePair &rp = options.RenamePairs[j];
490 if (rp.GetNewPath(ai.IsDir, ai.Name, dest))
491 {
492 needRename = true;
493 break;
494 }
495
496 #ifdef SUPPORT_ALT_STREAMS
497 if (ai.IsAltStream)
498 {
499 int colonPos = FindAltStreamColon_in_Path(ai.Name);
500 if (colonPos >= 0)
501 {
502 UString mainName = ai.Name.Left((unsigned)colonPos);
503 /*
504 actually we must improve that code to support cases
505 with folder renaming like: rn arc dir1\ dir2\
506 */
507 if (rp.GetNewPath(false, mainName, dest))
508 {
509 needRename = true;
510 dest.Add_Colon();
511 dest += ai.Name.Ptr((unsigned)(colonPos + 1));
512 break;
513 }
514 }
515 }
516 #endif
517 }
518 }
519
520 CUpdatePair2 up2;
521 up2.SetAs_NoChangeArcItem(ai.IndexInServer);
522 if (needRename)
523 {
524 up2.NewProps = true;
525 RINOK(arc->IsItem_Anti(i, up2.IsAnti))
526 up2.NewNameIndex = (int)newNames.Add(dest);
527 }
528 updatePairs2.Add(up2);
529 }
530 }
531 else
532 {
533 CRecordVector<CUpdatePair> updatePairs;
534 GetUpdatePairInfoList(dirItems, arcItems, fileTimeType, updatePairs); // must be done only once!!!
535 CUpdateProduceCallbackImp upCallback(&arcItems, &stat2.DeleteData, callback);
536
537 UpdateProduce(updatePairs, actionSet, updatePairs2, isUpdatingItself ? &upCallback : NULL);
538 }
539
540 {
541 FOR_VECTOR (i, updatePairs2)
542 {
543 const CUpdatePair2 &up = updatePairs2[i];
544
545 // 17.01: anti-item is (up.NewData && (p.UseArcProps in most cases))
546
547 if (up.NewData && !up.UseArcProps)
548 {
549 if (up.ExistOnDisk())
550 {
551 CDirItemsStat2 &stat = stat2.NewData;
552 const CDirItem &di = dirItems.Items[(unsigned)up.DirIndex];
553 if (di.IsDir())
554 {
555 if (up.IsAnti)
556 stat.Anti_NumDirs++;
557 else
558 stat.NumDirs++;
559 }
560 #ifdef _WIN32
561 else if (di.IsAltStream)
562 {
563 if (up.IsAnti)
564 stat.Anti_NumAltStreams++;
565 else
566 {
567 stat.NumAltStreams++;
568 stat.AltStreamsSize += di.Size;
569 }
570 }
571 #endif
572 else
573 {
574 if (up.IsAnti)
575 stat.Anti_NumFiles++;
576 else
577 {
578 stat.NumFiles++;
579 stat.FilesSize += di.Size;
580 }
581 }
582 }
583 }
584 else if (up.ArcIndex >= 0)
585 {
586 CDirItemsStat2 &stat = *(up.NewData ? &stat2.NewData : &stat2.OldData);
587 const CArcItem &ai = arcItems[(unsigned)up.ArcIndex];
588 if (ai.IsDir)
589 {
590 if (up.IsAnti)
591 stat.Anti_NumDirs++;
592 else
593 stat.NumDirs++;
594 }
595 else if (ai.IsAltStream)
596 {
597 if (up.IsAnti)
598 stat.Anti_NumAltStreams++;
599 else
600 {
601 stat.NumAltStreams++;
602 stat.AltStreamsSize += ai.Size;
603 }
604 }
605 else
606 {
607 if (up.IsAnti)
608 stat.Anti_NumFiles++;
609 else
610 {
611 stat.NumFiles++;
612 stat.FilesSize += ai.Size;
613 }
614 }
615 }
616 }
617 RINOK(callback->SetNumItems(stat2))
618 }
619
620 CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback;
621 CMyComPtr<IArchiveUpdateCallback> updateCallback(updateCallbackSpec);
622
623 updateCallbackSpec->PreserveATime = options.PreserveATime;
624 updateCallbackSpec->ShareForWrite = options.OpenShareForWrite;
625 updateCallbackSpec->StopAfterOpenError = options.StopAfterOpenError;
626 updateCallbackSpec->StdInMode = options.StdInMode;
627 updateCallbackSpec->Callback = callback;
628
629 if (arc)
630 {
631 // we set Archive to allow to transfer GetProperty requests back to DLL.
632 updateCallbackSpec->Archive = arc->Archive;
633 }
634
635 updateCallbackSpec->DirItems = &dirItems;
636 updateCallbackSpec->ParentDirItem = parentDirItem;
637
638 updateCallbackSpec->StoreNtSecurity = options.NtSecurity.Val;
639 updateCallbackSpec->StoreHardLinks = options.HardLinks.Val;
640 updateCallbackSpec->StoreSymLinks = options.SymLinks.Val;
641 updateCallbackSpec->StoreOwnerName = options.StoreOwnerName.Val;
642 updateCallbackSpec->StoreOwnerId = options.StoreOwnerId.Val;
643
644 updateCallbackSpec->Arc = arc;
645 updateCallbackSpec->ArcItems = &arcItems;
646 updateCallbackSpec->UpdatePairs = &updatePairs2;
647
648 updateCallbackSpec->ProcessedItemsStatuses = processedItemsStatuses;
649
650 {
651 const UString arcPath = archivePath.GetFinalPath();
652 updateCallbackSpec->ArcFileName = ExtractFileNameFromPath(arcPath);
653 }
654
655 if (options.RenamePairs.Size() != 0)
656 updateCallbackSpec->NewNames = &newNames;
657
658 if (options.SetArcMTime)
659 {
660 // updateCallbackSpec->Need_ArcMTime_Report = true;
661 updateCallbackSpec->Need_LatestMTime = true;
662 }
663
664 CMyComPtr<IOutStream> outSeekStream;
665 CMyComPtr<ISequentialOutStream> outStream;
666
667 if (!options.StdOutMode)
668 {
669 FString dirPrefix;
670 if (!GetOnlyDirPrefix(us2fs(archivePath.GetFinalPath()), dirPrefix))
671 throw 1417161;
672 CreateComplexDir(dirPrefix);
673 }
674
675 COutFileStream *outStreamSpec = NULL;
676 CStdOutFileStream *stdOutFileStreamSpec = NULL;
677 CMultiOutStream *volStreamSpec = NULL;
678
679 if (options.VolumesSizes.Size() == 0)
680 {
681 if (options.StdOutMode)
682 {
683 stdOutFileStreamSpec = new CStdOutFileStream;
684 outStream = stdOutFileStreamSpec;
685 }
686 else
687 {
688 outStreamSpec = new COutFileStream;
689 outSeekStream = outStreamSpec;
690 outStream = outSeekStream;
691 bool isOK = false;
692 FString realPath;
693
694 for (unsigned i = 0; i < (1 << 16); i++)
695 {
696 if (archivePath.Temp)
697 {
698 if (i > 0)
699 {
700 archivePath.TempPostfix.Empty();
701 archivePath.TempPostfix.Add_UInt32(i);
702 }
703 realPath = archivePath.GetTempPath();
704 }
705 else
706 realPath = us2fs(archivePath.GetFinalPath());
707 if (outStreamSpec->Create_NEW(realPath))
708 {
709 tempFiles.Paths.Add(realPath);
710 isOK = true;
711 break;
712 }
713 if (::GetLastError() != ERROR_FILE_EXISTS)
714 break;
715 if (!archivePath.Temp)
716 break;
717 }
718
719 if (!isOK)
720 return errorInfo.SetFromLastError("cannot open file", realPath);
721 }
722 }
723 else
724 {
725 if (options.StdOutMode)
726 return E_FAIL;
727 if (arc && arc->GetGlobalOffset() > 0)
728 return E_NOTIMPL;
729
730 volStreamSpec = new CMultiOutStream();
731 outSeekStream = volStreamSpec;
732 outStream = outSeekStream;
733 volStreamSpec->Prefix = us2fs(archivePath.GetFinalVolPath());
734 volStreamSpec->Prefix.Add_Dot();
735 volStreamSpec->Init(options.VolumesSizes);
736 {
737 CMultiOutStream_Rec &rec = multiStreams.Items.AddNew();
738 rec.Spec = volStreamSpec;
739 rec.Ref = rec.Spec;
740 }
741
742 /*
743 updateCallbackSpec->VolumesSizes = volumesSizes;
744 updateCallbackSpec->VolName = archivePath.Prefix + archivePath.Name;
745 if (!archivePath.VolExtension.IsEmpty())
746 updateCallbackSpec->VolExt = UString('.') + archivePath.VolExtension;
747 */
748 }
749
750 if (options.SfxMode)
751 {
752 CInFileStream *sfxStreamSpec = new CInFileStream;
753 CMyComPtr<IInStream> sfxStream(sfxStreamSpec);
754 if (!sfxStreamSpec->Open(options.SfxModule))
755 return errorInfo.SetFromLastError("cannot open SFX module", options.SfxModule);
756
757 CMyComPtr<ISequentialOutStream> sfxOutStream;
758 COutFileStream *outStreamSpec2 = NULL;
759 if (options.VolumesSizes.Size() == 0)
760 sfxOutStream = outStream;
761 else
762 {
763 outStreamSpec2 = new COutFileStream;
764 sfxOutStream = outStreamSpec2;
765 const FString realPath = us2fs(archivePath.GetFinalPath());
766 if (!outStreamSpec2->Create_NEW(realPath))
767 return errorInfo.SetFromLastError("cannot open file", realPath);
768 }
769
770 {
771 UInt64 sfxSize;
772 RINOK(sfxStreamSpec->GetSize(&sfxSize))
773 RINOK(callback->WriteSfx(fs2us(options.SfxModule), sfxSize))
774 }
775
776 RINOK(NCompress::CopyStream(sfxStream, sfxOutStream, NULL))
777
778 if (outStreamSpec2)
779 {
780 RINOK(outStreamSpec2->Close())
781 }
782 }
783
784 CMyComPtr<ISequentialOutStream> tailStream;
785
786 if (options.SfxMode || !arc || arc->ArcStreamOffset == 0)
787 tailStream = outStream;
788 else
789 {
790 // Int64 globalOffset = arc->GetGlobalOffset();
791 RINOK(InStream_SeekToBegin(arc->InStream))
792 RINOK(NCompress::CopyStream_ExactSize(arc->InStream, outStream, arc->ArcStreamOffset, NULL))
793 if (options.StdOutMode)
794 tailStream = outStream;
795 else
796 {
797 CTailOutStream *tailStreamSpec = new CTailOutStream;
798 tailStream = tailStreamSpec;
799 tailStreamSpec->Stream = outSeekStream;
800 tailStreamSpec->Offset = arc->ArcStreamOffset;
801 tailStreamSpec->Init();
802 }
803 }
804
805 CFiTime ft;
806 FiTime_Clear(ft);
807 bool ft_Defined = false;
808 {
809 FOR_VECTOR (i, updatePairs2)
810 {
811 const CUpdatePair2 &pair2 = updatePairs2[i];
812 CFiTime ft2;
813 FiTime_Clear(ft2);
814 bool ft2_Defined = false;
815 /* we use full precision of dirItem, if dirItem is defined
816 and (dirItem will be used or dirItem is sameTime in dir and arc */
817 if (pair2.DirIndex >= 0 &&
818 (pair2.NewProps || pair2.IsSameTime))
819 {
820 ft2 = dirItems.Items[(unsigned)pair2.DirIndex].MTime;
821 ft2_Defined = true;
822 }
823 else if (pair2.UseArcProps && pair2.ArcIndex >= 0)
824 {
825 const CArcItem &arcItem = arcItems[(unsigned)pair2.ArcIndex];
826 if (arcItem.MTime.Def)
827 {
828 arcItem.MTime.Write_To_FiTime(ft2);
829 ft2_Defined = true;
830 }
831 }
832 if (ft2_Defined)
833 {
834 if (!ft_Defined || Compare_FiTime(&ft, &ft2) < 0)
835 {
836 ft = ft2;
837 ft_Defined = true;
838 }
839 }
840 }
841 /*
842 if (fileTimeType != NFileTimeType::kNotDefined)
843 FiTime_Normalize_With_Prec(ft, fileTimeType);
844 */
845 }
846
847 if (volStreamSpec && options.SetArcMTime && ft_Defined)
848 {
849 volStreamSpec->MTime = ft;
850 volStreamSpec->MTime_Defined = true;
851 }
852
853 HRESULT result = outArchive->UpdateItems(tailStream, updatePairs2.Size(), updateCallback);
854 // callback->Finalize();
855 RINOK(result)
856
857 if (!updateCallbackSpec->AreAllFilesClosed())
858 {
859 errorInfo.Message = "There are unclosed input files:";
860 errorInfo.FileNames = updateCallbackSpec->_openFiles_Paths;
861 return E_FAIL;
862 }
863
864 if (options.SetArcMTime)
865 {
866 // bool needNormalizeAfterStream;
867 // needParse;
868 /*
869 if (updateCallbackSpec->ArcMTime_WasReported)
870 {
871 isDefined = updateCallbackSpec->Reported_ArcMTime.Def;
872 if (isDefined)
873 updateCallbackSpec->Reported_ArcMTime.Write_To_FiTime(ft);
874 else
875 fileTimeType = NFileTimeType::kNotDefined;
876 }
877 if (!isDefined)
878 */
879 {
880 if (updateCallbackSpec->LatestMTime_Defined)
881 {
882 // CArcTime at = StreamCallback_ArcMTime;
883 // updateCallbackSpec->StreamCallback_ArcMTime.Write_To_FiTime(ft);
884 // we must normalize with precision from archive;
885 if (!ft_Defined || Compare_FiTime(&ft, &updateCallbackSpec->LatestMTime) < 0)
886 ft = updateCallbackSpec->LatestMTime;
887 ft_Defined = true;
888 }
889 /*
890 if (fileTimeType != NFileTimeType::kNotDefined)
891 FiTime_Normalize_With_Prec(ft, fileTimeType);
892 */
893 }
894 // if (ft.dwLowDateTime != 0 || ft.dwHighDateTime != 0)
895 if (ft_Defined)
896 {
897 // we ignore set time errors here.
898 // note that user could move some finished volumes to another folder.
899 if (outStreamSpec)
900 outStreamSpec->SetMTime(&ft);
901 else if (volStreamSpec)
902 volStreamSpec->SetMTime_Final(ft);
903 }
904 }
905
906 if (callback)
907 {
908 UInt64 size = 0;
909 if (outStreamSpec)
910 outStreamSpec->GetSize(&size);
911 else if (stdOutFileStreamSpec)
912 size = stdOutFileStreamSpec->GetSize();
913 else
914 size = volStreamSpec->GetSize();
915
916 st.OutArcFileSize = size;
917 }
918
919 if (outStreamSpec)
920 result = outStreamSpec->Close();
921 else if (volStreamSpec)
922 {
923 result = volStreamSpec->FinalFlush_and_CloseFiles(st.NumVolumes);
924 st.IsMultiVolMode = true;
925 }
926
927 RINOK(result)
928
929 if (processedItemsStatuses)
930 {
931 FOR_VECTOR (i, updatePairs2)
932 {
933 const CUpdatePair2 &up = updatePairs2[i];
934 if (up.NewData && up.DirIndex >= 0)
935 {
936 const CDirItem &di = dirItems.Items[(unsigned)up.DirIndex];
937 if (di.AreReparseData() || (!di.IsDir() && di.Size == 0))
938 processedItemsStatuses[(unsigned)up.DirIndex] = 1;
939 }
940 }
941 }
942
943 return result;
944 }
945
946
947
Censor_AreAllAllowed(const NWildcard::CCensor & censor)948 static bool Censor_AreAllAllowed(const NWildcard::CCensor &censor)
949 {
950 if (censor.Pairs.Size() != 1)
951 return false;
952 const NWildcard::CPair &pair = censor.Pairs[0];
953 /* Censor_CheckPath() ignores (CPair::Prefix).
954 So we also ignore (CPair::Prefix) here */
955 // if (!pair.Prefix.IsEmpty()) return false;
956 return pair.Head.AreAllAllowed();
957 }
958
959 bool CensorNode_CheckPath2(const NWildcard::CCensorNode &node, const CReadArcItem &item, bool &include);
960
Censor_CheckPath(const NWildcard::CCensor & censor,const CReadArcItem & item)961 static bool Censor_CheckPath(const NWildcard::CCensor &censor, const CReadArcItem &item)
962 {
963 bool finded = false;
964 FOR_VECTOR (i, censor.Pairs)
965 {
966 /* (CPair::Prefix) in not used for matching items in archive.
967 So we ignore (CPair::Prefix) here */
968 bool include;
969 if (CensorNode_CheckPath2(censor.Pairs[i].Head, item, include))
970 {
971 // Check it and FIXME !!!!
972 // here we can exclude item via some Pair, that is still allowed by another Pair
973 if (!include)
974 return false;
975 finded = true;
976 }
977 }
978 return finded;
979 }
980
EnumerateInArchiveItems(const NWildcard::CCensor & censor,const CArc & arc,CObjectVector<CArcItem> & arcItems)981 static HRESULT EnumerateInArchiveItems(
982 // bool storeStreamsMode,
983 const NWildcard::CCensor &censor,
984 const CArc &arc,
985 CObjectVector<CArcItem> &arcItems)
986 {
987 arcItems.Clear();
988 UInt32 numItems;
989 IInArchive *archive = arc.Archive;
990 RINOK(archive->GetNumberOfItems(&numItems))
991 arcItems.ClearAndReserve(numItems);
992
993 CReadArcItem item;
994
995 const bool allFilesAreAllowed = Censor_AreAllAllowed(censor);
996
997 for (UInt32 i = 0; i < numItems; i++)
998 {
999 CArcItem ai;
1000
1001 RINOK(arc.GetItem(i, item))
1002 ai.Name = item.Path;
1003 ai.IsDir = item.IsDir;
1004 ai.IsAltStream =
1005 #ifdef SUPPORT_ALT_STREAMS
1006 item.IsAltStream;
1007 #else
1008 false;
1009 #endif
1010
1011 /*
1012 if (!storeStreamsMode && ai.IsAltStream)
1013 continue;
1014 */
1015 if (allFilesAreAllowed)
1016 ai.Censored = true;
1017 else
1018 ai.Censored = Censor_CheckPath(censor, item);
1019
1020 // ai.MTime will be set to archive MTime, if not present in archive item
1021 RINOK(arc.GetItem_MTime(i, ai.MTime))
1022 RINOK(arc.GetItem_Size(i, ai.Size, ai.Size_Defined))
1023
1024 ai.IndexInServer = i;
1025 arcItems.AddInReserved(ai);
1026 }
1027 return S_OK;
1028 }
1029
1030 #if defined(_WIN32) && !defined(UNDER_CE)
1031
1032 #if defined(__MINGW32__) || defined(__MINGW64__)
1033 #include <mapi.h>
1034 #else
1035 #include <MAPI.h>
1036 #endif
1037
1038 extern "C" {
1039
1040 #ifdef MAPI_FORCE_UNICODE
1041
1042 #define Z7_WIN_LPMAPISENDMAILW LPMAPISENDMAILW
1043 #define Z7_WIN_MapiFileDescW MapiFileDescW
1044 #define Z7_WIN_MapiMessageW MapiMessageW
1045 #define Z7_WIN_MapiRecipDescW MapiRecipDescW
1046
1047 #else
1048
1049 typedef struct
1050 {
1051 ULONG ulReserved;
1052 ULONG ulRecipClass;
1053 PWSTR lpszName;
1054 PWSTR lpszAddress;
1055 ULONG ulEIDSize;
1056 PVOID lpEntryID;
1057 } Z7_WIN_MapiRecipDescW, *Z7_WIN_lpMapiRecipDescW;
1058
1059 typedef struct
1060 {
1061 ULONG ulReserved;
1062 ULONG flFlags;
1063 ULONG nPosition;
1064 PWSTR lpszPathName;
1065 PWSTR lpszFileName;
1066 PVOID lpFileType;
1067 } Z7_WIN_MapiFileDescW, *Z7_WIN_lpMapiFileDescW;
1068
1069 typedef struct
1070 {
1071 ULONG ulReserved;
1072 PWSTR lpszSubject;
1073 PWSTR lpszNoteText;
1074 PWSTR lpszMessageType;
1075 PWSTR lpszDateReceived;
1076 PWSTR lpszConversationID;
1077 FLAGS flFlags;
1078 Z7_WIN_lpMapiRecipDescW lpOriginator;
1079 ULONG nRecipCount;
1080 Z7_WIN_lpMapiRecipDescW lpRecips;
1081 ULONG nFileCount;
1082 Z7_WIN_lpMapiFileDescW lpFiles;
1083 } Z7_WIN_MapiMessageW, *Z7_WIN_lpMapiMessageW;
1084
1085 typedef ULONG (FAR PASCAL Z7_WIN_MAPISENDMAILW)(
1086 LHANDLE lhSession,
1087 ULONG_PTR ulUIParam,
1088 Z7_WIN_lpMapiMessageW lpMessage,
1089 FLAGS flFlags,
1090 ULONG ulReserved
1091 );
1092 typedef Z7_WIN_MAPISENDMAILW FAR *Z7_WIN_LPMAPISENDMAILW;
1093
1094 #endif // MAPI_FORCE_UNICODE
1095 }
1096 #endif // _WIN32
1097
1098
1099 struct C_CopyFileProgress_to_IUpdateCallbackUI2 Z7_final:
1100 public ICopyFileProgress
1101 {
1102 IUpdateCallbackUI2 *Callback;
1103 HRESULT CallbackResult;
1104 // bool Disable_Break;
1105
CopyFileProgressZ7_final1106 virtual DWORD CopyFileProgress(UInt64 total, UInt64 current) Z7_override
1107 {
1108 const HRESULT res = Callback->MoveArc_Progress(total, current);
1109 CallbackResult = res;
1110 // if (Disable_Break && res == E_ABORT) res = S_OK;
1111 return res == S_OK ? PROGRESS_CONTINUE : PROGRESS_CANCEL;
1112 }
1113
C_CopyFileProgress_to_IUpdateCallbackUI2Z7_final1114 C_CopyFileProgress_to_IUpdateCallbackUI2(
1115 IUpdateCallbackUI2 *callback) :
1116 Callback(callback),
1117 CallbackResult(S_OK)
1118 // , Disable_Break(false)
1119 {}
1120 };
1121
1122
UpdateArchive(CCodecs * codecs,const CObjectVector<COpenType> & types,const UString & cmdArcPath2,NWildcard::CCensor & censor,CUpdateOptions & options,CUpdateErrorInfo & errorInfo,IOpenCallbackUI * openCallback,IUpdateCallbackUI2 * callback,bool needSetPath)1123 HRESULT UpdateArchive(
1124 CCodecs *codecs,
1125 const CObjectVector<COpenType> &types,
1126 const UString &cmdArcPath2,
1127 NWildcard::CCensor &censor,
1128 CUpdateOptions &options,
1129 CUpdateErrorInfo &errorInfo,
1130 IOpenCallbackUI *openCallback,
1131 IUpdateCallbackUI2 *callback,
1132 bool needSetPath)
1133 {
1134 if (options.StdOutMode && options.EMailMode)
1135 return E_FAIL;
1136
1137 if (types.Size() > 1)
1138 return E_NOTIMPL;
1139
1140 bool renameMode = !options.RenamePairs.IsEmpty();
1141 if (renameMode)
1142 {
1143 if (options.Commands.Size() != 1)
1144 return E_FAIL;
1145 }
1146
1147 if (options.DeleteAfterCompressing)
1148 {
1149 if (options.Commands.Size() != 1)
1150 return E_NOTIMPL;
1151 const CActionSet &as = options.Commands[0].ActionSet;
1152 for (unsigned i = 2; i < NPairState::kNumValues; i++)
1153 if (as.StateActions[i] != NPairAction::kCompress)
1154 return E_NOTIMPL;
1155 }
1156
1157 censor.AddPathsToCensor(options.PathMode);
1158 #ifdef _WIN32
1159 ConvertToLongNames(censor);
1160 #endif
1161 censor.ExtendExclude();
1162
1163
1164 if (options.VolumesSizes.Size() > 0 && (options.EMailMode /* || options.SfxMode */))
1165 return E_NOTIMPL;
1166
1167 if (options.SfxMode)
1168 {
1169 CProperty property;
1170 property.Name = "rsfx";
1171 options.MethodMode.Properties.Add(property);
1172 if (options.SfxModule.IsEmpty())
1173 {
1174 errorInfo.Message = "SFX file is not specified";
1175 return E_FAIL;
1176 }
1177 bool found = false;
1178 if (options.SfxModule.Find(FCHAR_PATH_SEPARATOR) < 0)
1179 {
1180 const FString fullName = NDLL::GetModuleDirPrefix() + options.SfxModule;
1181 if (NFind::DoesFileExist_FollowLink(fullName))
1182 {
1183 options.SfxModule = fullName;
1184 found = true;
1185 }
1186 }
1187 if (!found)
1188 {
1189 if (!NFind::DoesFileExist_FollowLink(options.SfxModule))
1190 return errorInfo.SetFromLastError("cannot find specified SFX module", options.SfxModule);
1191 }
1192 }
1193
1194 CArchiveLink arcLink;
1195
1196
1197 if (needSetPath)
1198 {
1199 if (!options.InitFormatIndex(codecs, types, cmdArcPath2) ||
1200 !options.SetArcPath(codecs, cmdArcPath2))
1201 return E_NOTIMPL;
1202 }
1203
1204 UString arcPath = options.ArchivePath.GetFinalPath();
1205
1206 if (!options.VolumesSizes.IsEmpty())
1207 {
1208 arcPath = options.ArchivePath.GetFinalVolPath();
1209 arcPath += ".001";
1210 }
1211
1212 if (cmdArcPath2.IsEmpty())
1213 {
1214 if (options.MethodMode.Type.FormatIndex < 0)
1215 throw "type of archive is not specified";
1216 }
1217 else
1218 {
1219 NFind::CFileInfo fi;
1220 if (!fi.Find_FollowLink(us2fs(arcPath)))
1221 {
1222 if (renameMode)
1223 throw "can't find archive";
1224 if (options.MethodMode.Type.FormatIndex < 0)
1225 {
1226 if (!options.SetArcPath(codecs, cmdArcPath2))
1227 return E_NOTIMPL;
1228 }
1229 }
1230 else
1231 {
1232 if (fi.IsDir())
1233 return errorInfo.SetFromError_DWORD("There is a folder with the name of archive",
1234 us2fs(arcPath),
1235 #ifdef _WIN32
1236 ERROR_ACCESS_DENIED
1237 #else
1238 EISDIR
1239 #endif
1240 );
1241 #ifdef _WIN32
1242 if (fi.IsDevice)
1243 return E_NOTIMPL;
1244 #endif
1245
1246 if (!options.StdOutMode && options.UpdateArchiveItself)
1247 if (fi.IsReadOnly())
1248 {
1249 return errorInfo.SetFromError_DWORD("The file is read-only",
1250 us2fs(arcPath),
1251 #ifdef _WIN32
1252 ERROR_ACCESS_DENIED
1253 #else
1254 EACCES
1255 #endif
1256 );
1257 }
1258
1259 if (options.VolumesSizes.Size() > 0)
1260 {
1261 errorInfo.FileNames.Add(us2fs(arcPath));
1262 // errorInfo.SystemError = (DWORD)E_NOTIMPL;
1263 errorInfo.Message = kUpdateIsNotSupported_MultiVol;
1264 return E_NOTIMPL;
1265 }
1266 CObjectVector<COpenType> types2;
1267 // change it.
1268 if (options.MethodMode.Type_Defined)
1269 types2.Add(options.MethodMode.Type);
1270 // We need to set Properties to open archive only in some cases (WIM archives).
1271
1272 CIntVector excl;
1273 COpenOptions op;
1274 #ifndef Z7_SFX
1275 op.props = &options.MethodMode.Properties;
1276 #endif
1277 op.codecs = codecs;
1278 op.types = &types2;
1279 op.excludedFormats = !
1280 op.stdInMode = false;
1281 op.stream = NULL;
1282 op.filePath = arcPath;
1283
1284 RINOK(callback->StartOpenArchive(arcPath))
1285
1286 HRESULT result = arcLink.Open_Strict(op, openCallback);
1287
1288 if (result == E_ABORT)
1289 return result;
1290
1291 HRESULT res2 = callback->OpenResult(codecs, arcLink, arcPath, result);
1292 /*
1293 if (result == S_FALSE)
1294 return E_FAIL;
1295 */
1296 RINOK(res2)
1297 RINOK(result)
1298
1299 if (arcLink.VolumePaths.Size() > 1)
1300 {
1301 // errorInfo.SystemError = (DWORD)E_NOTIMPL;
1302 errorInfo.Message = kUpdateIsNotSupported_MultiVol;
1303 return E_NOTIMPL;
1304 }
1305
1306 CArc &arc = arcLink.Arcs.Back();
1307 arc.MTime.Def =
1308 #ifdef _WIN32
1309 !fi.IsDevice;
1310 #else
1311 true;
1312 #endif
1313 if (arc.MTime.Def)
1314 arc.MTime.Set_From_FiTime(fi.MTime);
1315
1316 if (arc.ErrorInfo.ThereIsTail)
1317 {
1318 // errorInfo.SystemError = (DWORD)E_NOTIMPL;
1319 errorInfo.Message = "There is some data block after the end of the archive";
1320 return E_NOTIMPL;
1321 }
1322 if (options.MethodMode.Type.FormatIndex < 0)
1323 {
1324 options.MethodMode.Type.FormatIndex = arcLink.GetArc()->FormatIndex;
1325 if (!options.SetArcPath(codecs, cmdArcPath2))
1326 return E_NOTIMPL;
1327 }
1328 }
1329 }
1330
1331 if (options.MethodMode.Type.FormatIndex < 0)
1332 {
1333 options.MethodMode.Type.FormatIndex = codecs->FindFormatForArchiveType((UString)kDefaultArcType);
1334 if (options.MethodMode.Type.FormatIndex < 0)
1335 return E_NOTIMPL;
1336 }
1337
1338 const bool thereIsInArchive = arcLink.IsOpen;
1339 if (!thereIsInArchive && renameMode)
1340 return E_FAIL;
1341
1342 CDirItems dirItems;
1343 dirItems.Callback = callback;
1344
1345 CDirItem parentDirItem;
1346 CDirItem *parentDirItem_Ptr = NULL;
1347
1348 /*
1349 FStringVector requestedPaths;
1350 FStringVector *requestedPaths_Ptr = NULL;
1351 if (options.DeleteAfterCompressing)
1352 requestedPaths_Ptr = &requestedPaths;
1353 */
1354
1355 if (options.StdInMode)
1356 {
1357 CDirItem di;
1358 // di.ClearBase();
1359 // di.Size = (UInt64)(Int64)-1;
1360 if (!di.SetAs_StdInFile())
1361 return GetLastError_noZero_HRESULT();
1362 di.Name = options.StdInFileName;
1363 // di.Attrib_IsDefined = false;
1364 // NTime::GetCurUtc_FiTime(di.MTime);
1365 // di.CTime = di.ATime = di.MTime;
1366 dirItems.Items.Add(di);
1367 }
1368 else
1369 {
1370 bool needScanning = false;
1371
1372 if (!renameMode)
1373 FOR_VECTOR (i, options.Commands)
1374 if (options.Commands[i].ActionSet.NeedScanning())
1375 needScanning = true;
1376
1377 if (needScanning)
1378 {
1379 RINOK(callback->StartScanning())
1380
1381 dirItems.SymLinks = options.SymLinks.Val;
1382
1383 #if defined(_WIN32) && !defined(UNDER_CE)
1384 dirItems.ReadSecure = options.NtSecurity.Val;
1385 #endif
1386
1387 dirItems.ScanAltStreams = options.AltStreams.Val;
1388 dirItems.ExcludeDirItems = censor.ExcludeDirItems;
1389 dirItems.ExcludeFileItems = censor.ExcludeFileItems;
1390
1391 dirItems.ShareForWrite = options.OpenShareForWrite;
1392
1393 #ifndef _WIN32
1394 dirItems.StoreOwnerName = options.StoreOwnerName.Val;
1395 #endif
1396
1397 const HRESULT res = EnumerateItems(censor,
1398 options.PathMode,
1399 UString(), // options.AddPathPrefix,
1400 dirItems);
1401
1402 if (res != S_OK)
1403 {
1404 if (res != E_ABORT)
1405 errorInfo.Message = "Scanning error";
1406 return res;
1407 }
1408
1409 RINOK(callback->FinishScanning(dirItems.Stat))
1410
1411 // 22.00: we don't need parent folder, if absolute path mode
1412 if (options.PathMode != NWildcard::k_AbsPath)
1413 if (censor.Pairs.Size() == 1)
1414 {
1415 NFind::CFileInfo fi;
1416 FString prefix = us2fs(censor.Pairs[0].Prefix);
1417 prefix.Add_Dot();
1418 // UString prefix = censor.Pairs[0].Prefix;
1419 /*
1420 if (prefix.Back() == WCHAR_PATH_SEPARATOR)
1421 {
1422 prefix.DeleteBack();
1423 }
1424 */
1425 if (fi.Find(prefix))
1426 if (fi.IsDir())
1427 {
1428 parentDirItem.Copy_From_FileInfoBase(fi);
1429 parentDirItem_Ptr = &parentDirItem;
1430
1431 int secureIndex = -1;
1432 #if defined(_WIN32) && !defined(UNDER_CE)
1433 if (options.NtSecurity.Val)
1434 dirItems.AddSecurityItem(prefix, secureIndex);
1435 #endif
1436 parentDirItem.SecureIndex = secureIndex;
1437 }
1438 }
1439 }
1440 }
1441
1442 FString tempDirPrefix;
1443 bool usesTempDir = false;
1444
1445 #ifdef _WIN32
1446 CTempDir tempDirectory;
1447 if (options.EMailMode && options.EMailRemoveAfter)
1448 {
1449 tempDirectory.Create(kTempFolderPrefix);
1450 tempDirPrefix = tempDirectory.GetPath();
1451 NormalizeDirPathPrefix(tempDirPrefix);
1452 usesTempDir = true;
1453 }
1454 #endif
1455
1456 CTempFiles tempFiles;
1457
1458 bool createTempFile = false;
1459
1460 if (!options.StdOutMode && options.UpdateArchiveItself)
1461 {
1462 CArchivePath &ap = options.Commands[0].ArchivePath;
1463 ap = options.ArchivePath;
1464 // if ((archive != 0 && !usesTempDir) || !options.WorkingDir.IsEmpty())
1465 if ((thereIsInArchive || !options.WorkingDir.IsEmpty()) && !usesTempDir && options.VolumesSizes.Size() == 0)
1466 {
1467 createTempFile = true;
1468 ap.Temp = true;
1469 if (!options.WorkingDir.IsEmpty())
1470 ap.TempPrefix = options.WorkingDir;
1471 else
1472 ap.TempPrefix = us2fs(ap.Prefix);
1473 NormalizeDirPathPrefix(ap.TempPrefix);
1474 }
1475 }
1476
1477 unsigned ci;
1478
1479
1480 // self including protection
1481 if (options.DeleteAfterCompressing)
1482 {
1483 for (ci = 0; ci < options.Commands.Size(); ci++)
1484 {
1485 CArchivePath &ap = options.Commands[ci].ArchivePath;
1486 const FString path = us2fs(ap.GetFinalPath());
1487 // maybe we must compare absolute paths path here
1488 FOR_VECTOR (i, dirItems.Items)
1489 {
1490 const FString phyPath = dirItems.GetPhyPath(i);
1491 if (phyPath == path)
1492 {
1493 UString s;
1494 s = "It is not allowed to include archive to itself";
1495 s.Add_LF();
1496 s += fs2us(path);
1497 throw s;
1498 }
1499 }
1500 }
1501 }
1502
1503
1504 for (ci = 0; ci < options.Commands.Size(); ci++)
1505 {
1506 CArchivePath &ap = options.Commands[ci].ArchivePath;
1507 if (usesTempDir)
1508 {
1509 // Check it
1510 ap.Prefix = fs2us(tempDirPrefix);
1511 // ap.Temp = true;
1512 // ap.TempPrefix = tempDirPrefix;
1513 }
1514 if (!options.StdOutMode &&
1515 (ci > 0 || !createTempFile))
1516 {
1517 const FString path = us2fs(ap.GetFinalPath());
1518 if (NFind::DoesFileOrDirExist(path))
1519 {
1520 errorInfo.SystemError = ERROR_FILE_EXISTS;
1521 errorInfo.Message = "The file already exists";
1522 errorInfo.FileNames.Add(path);
1523 return errorInfo.Get_HRESULT_Error();
1524 }
1525 }
1526 }
1527
1528 CObjectVector<CArcItem> arcItems;
1529 if (thereIsInArchive)
1530 {
1531 RINOK(EnumerateInArchiveItems(
1532 // options.StoreAltStreams,
1533 censor, arcLink.Arcs.Back(), arcItems))
1534 }
1535
1536 /*
1537 FStringVector processedFilePaths;
1538 FStringVector *processedFilePaths_Ptr = NULL;
1539 if (options.DeleteAfterCompressing)
1540 processedFilePaths_Ptr = &processedFilePaths;
1541 */
1542
1543 CByteBuffer processedItems;
1544 if (options.DeleteAfterCompressing)
1545 {
1546 const unsigned num = dirItems.Items.Size();
1547 processedItems.Alloc(num);
1548 for (unsigned i = 0; i < num; i++)
1549 processedItems[i] = 0;
1550 }
1551
1552 CMultiOutStream_Bunch multiStreams;
1553
1554 /*
1555 #ifndef Z7_NO_CRYPTO
1556 if (arcLink.PasswordWasAsked)
1557 {
1558 // We set password, if open have requested password
1559 RINOK(callback->SetPassword(arcLink.Password));
1560 }
1561 #endif
1562 */
1563
1564 for (ci = 0; ci < options.Commands.Size(); ci++)
1565 {
1566 const CArc *arc = thereIsInArchive ? arcLink.GetArc() : NULL;
1567 CUpdateArchiveCommand &command = options.Commands[ci];
1568 UString name;
1569 bool isUpdating;
1570
1571 if (options.StdOutMode)
1572 {
1573 name = "stdout";
1574 isUpdating = thereIsInArchive;
1575 }
1576 else
1577 {
1578 name = command.ArchivePath.GetFinalPath();
1579 isUpdating = (ci == 0 && options.UpdateArchiveItself && thereIsInArchive);
1580 }
1581
1582 RINOK(callback->StartArchive(name, isUpdating))
1583
1584 CFinishArchiveStat st;
1585
1586 RINOK(Compress(options,
1587 isUpdating,
1588 codecs,
1589 command.ActionSet,
1590 arc,
1591 command.ArchivePath,
1592 arcItems,
1593 options.DeleteAfterCompressing ? (Byte *)processedItems : NULL,
1594
1595 dirItems,
1596 parentDirItem_Ptr,
1597
1598 tempFiles,
1599 multiStreams,
1600 errorInfo, callback, st))
1601
1602 RINOK(callback->FinishArchive(st))
1603 }
1604
1605
1606 if (thereIsInArchive)
1607 {
1608 RINOK(arcLink.Close())
1609 arcLink.Release();
1610 }
1611
1612 multiStreams.DisableDeletion();
1613 RINOK(multiStreams.Destruct())
1614
1615 // here we disable deleting of temp archives.
1616 // note: archive moving can fail, or it can be interrupted,
1617 // if we move new temp update from another volume.
1618 // And we still want to keep temp archive in that case,
1619 // because we will have deleted original archive.
1620 tempFiles.NeedDeleteFiles = false;
1621 // tempFiles.Paths.Clear();
1622
1623 if (createTempFile)
1624 {
1625 try
1626 {
1627 CArchivePath &ap = options.Commands[0].ArchivePath;
1628 const FString &tempPath = ap.GetTempPath();
1629
1630 // DWORD attrib = 0;
1631 if (thereIsInArchive)
1632 {
1633 // attrib = NFind::GetFileAttrib(us2fs(arcPath));
1634 if (!DeleteFileAlways(us2fs(arcPath)))
1635 return errorInfo.SetFromLastError("cannot delete the file", us2fs(arcPath));
1636 }
1637
1638 UInt64 totalArcSize = 0;
1639 {
1640 NFind::CFileInfo fi;
1641 if (fi.Find(tempPath))
1642 totalArcSize = fi.Size;
1643 }
1644 RINOK(callback->MoveArc_Start(fs2us(tempPath), arcPath,
1645 totalArcSize, BoolToInt(thereIsInArchive)))
1646
1647 C_CopyFileProgress_to_IUpdateCallbackUI2 prox(callback);
1648 // if we update archive, we have removed original archive.
1649 // So if we break archive moving, we will have only temporary archive.
1650 // We can disable breaking here:
1651 // prox.Disable_Break = thereIsInArchive;
1652
1653 if (!MyMoveFile_with_Progress(tempPath, us2fs(arcPath), &prox))
1654 {
1655 errorInfo.SystemError = ::GetLastError();
1656 errorInfo.Message = "cannot move the file";
1657 if (errorInfo.SystemError == ERROR_INVALID_PARAMETER)
1658 {
1659 if (totalArcSize > (UInt32)(Int32)-1)
1660 {
1661 // bool isFsDetected = false;
1662 // if (NSystem::Is_File_LimitedBy_4GB(us2fs(arcPath), isFsDetected) || !isFsDetected)
1663 {
1664 errorInfo.Message.Add_LF();
1665 errorInfo.Message += "Archive file size exceeds 4 GB";
1666 }
1667 }
1668 }
1669 // if there was no input archive, and we have operation breaking.
1670 // then we can remove temporary archive, because we still have original uncompressed files.
1671 if (!thereIsInArchive
1672 && prox.CallbackResult == E_ABORT)
1673 tempFiles.NeedDeleteFiles = true;
1674 errorInfo.FileNames.Add(tempPath);
1675 errorInfo.FileNames.Add(us2fs(arcPath));
1676 RINOK(prox.CallbackResult)
1677 return errorInfo.Get_HRESULT_Error();
1678 }
1679
1680 // MoveArc_Finish() can return delayed user break (E_ABORT) status,
1681 // if callback callee ignored interruption to finish archive creation operation.
1682 RINOK(callback->MoveArc_Finish())
1683
1684 /*
1685 if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_READONLY))
1686 {
1687 DWORD attrib2 = NFind::GetFileAttrib(us2fs(arcPath));
1688 if (attrib2 != INVALID_FILE_ATTRIBUTES)
1689 NDir::SetFileAttrib(us2fs(arcPath), attrib2 | FILE_ATTRIBUTE_READONLY);
1690 }
1691 */
1692 }
1693 catch(...)
1694 {
1695 throw;
1696 }
1697 }
1698
1699
1700 #if defined(_WIN32) && !defined(UNDER_CE)
1701
1702 Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION
1703
1704 if (options.EMailMode)
1705 {
1706 NDLL::CLibrary mapiLib;
1707 if (!mapiLib.Load(FTEXT("Mapi32.dll")))
1708 {
1709 errorInfo.SetFromLastError("cannot load Mapi32.dll");
1710 return errorInfo.Get_HRESULT_Error();
1711 }
1712
1713 FStringVector fullPaths;
1714 unsigned i;
1715
1716 for (i = 0; i < options.Commands.Size(); i++)
1717 {
1718 CArchivePath &ap = options.Commands[i].ArchivePath;
1719 const FString finalPath = us2fs(ap.GetFinalPath());
1720 FString arcPath2;
1721 if (!MyGetFullPathName(finalPath, arcPath2))
1722 return errorInfo.SetFromLastError("GetFullPathName error", finalPath);
1723 fullPaths.Add(arcPath2);
1724 }
1725
1726 /*
1727 LPMAPISENDDOCUMENTS fnSend = (LPMAPISENDDOCUMENTS)mapiLib.GetProc("MAPISendDocuments");
1728 if (fnSend == 0)
1729 {
1730 errorInfo.SetFromLastError)("7-Zip cannot find MAPISendDocuments function");
1731 return errorInfo.Get_HRESULT_Error();
1732 }
1733 */
1734 const
1735 Z7_WIN_LPMAPISENDMAILW sendMailW = Z7_GET_PROC_ADDRESS(
1736 Z7_WIN_LPMAPISENDMAILW, mapiLib.Get_HMODULE(),
1737 "MAPISendMailW");
1738 if (sendMailW)
1739 {
1740
1741 CCurrentDirRestorer curDirRestorer;
1742
1743 UStringVector paths;
1744 UStringVector names;
1745
1746 for (i = 0; i < fullPaths.Size(); i++)
1747 {
1748 const UString arcPath2 = fs2us(fullPaths[i]);
1749 const UString fileName = ExtractFileNameFromPath(arcPath2);
1750 paths.Add(arcPath2);
1751 names.Add(fileName);
1752 // Warning!!! MAPISendDocuments function changes Current directory
1753 // fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0);
1754 }
1755
1756 CRecordVector<Z7_WIN_MapiFileDescW> files;
1757 files.ClearAndSetSize(paths.Size());
1758
1759 for (i = 0; i < paths.Size(); i++)
1760 {
1761 Z7_WIN_MapiFileDescW &f = files[i];
1762 memset(&f, 0, sizeof(f));
1763 f.nPosition = 0xFFFFFFFF;
1764 f.lpszPathName = paths[i].Ptr_non_const();
1765 f.lpszFileName = names[i].Ptr_non_const();
1766 }
1767
1768 {
1769 Z7_WIN_MapiMessageW m;
1770 memset(&m, 0, sizeof(m));
1771 m.nFileCount = files.Size();
1772 m.lpFiles = files.NonConstData();
1773
1774 const UString addr (options.EMailAddress);
1775 Z7_WIN_MapiRecipDescW rec;
1776 if (!addr.IsEmpty())
1777 {
1778 memset(&rec, 0, sizeof(rec));
1779 rec.ulRecipClass = MAPI_TO;
1780 rec.lpszAddress = addr.Ptr_non_const();
1781 m.nRecipCount = 1;
1782 m.lpRecips = &rec;
1783 }
1784
1785 sendMailW((LHANDLE)0, 0, &m, MAPI_DIALOG, 0);
1786 }
1787 }
1788 else
1789 {
1790 const
1791 LPMAPISENDMAIL sendMail = Z7_GET_PROC_ADDRESS(
1792 LPMAPISENDMAIL, mapiLib.Get_HMODULE(),
1793 "MAPISendMail");
1794 if (!sendMail)
1795 {
1796 errorInfo.SetFromLastError("7-Zip cannot find MAPISendMail function");
1797 return errorInfo.Get_HRESULT_Error();
1798 }
1799
1800 CCurrentDirRestorer curDirRestorer;
1801
1802 AStringVector paths;
1803 AStringVector names;
1804
1805 for (i = 0; i < fullPaths.Size(); i++)
1806 {
1807 const UString arcPath2 = fs2us(fullPaths[i]);
1808 const UString fileName = ExtractFileNameFromPath(arcPath2);
1809 paths.Add(GetAnsiString(arcPath2));
1810 names.Add(GetAnsiString(fileName));
1811 // const AString path (GetAnsiString(arcPath2));
1812 // const AString name (GetAnsiString(fileName));
1813 // Warning!!! MAPISendDocuments function changes Current directory
1814 // fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0);
1815 }
1816
1817 CRecordVector<MapiFileDesc> files;
1818 files.ClearAndSetSize(paths.Size());
1819
1820 for (i = 0; i < paths.Size(); i++)
1821 {
1822 MapiFileDesc &f = files[i];
1823 memset(&f, 0, sizeof(f));
1824 f.nPosition = 0xFFFFFFFF;
1825 f.lpszPathName = paths[i].Ptr_non_const();
1826 f.lpszFileName = names[i].Ptr_non_const();
1827 }
1828
1829 {
1830 MapiMessage m;
1831 memset(&m, 0, sizeof(m));
1832 m.nFileCount = files.Size();
1833 m.lpFiles = files.NonConstData();
1834
1835 const AString addr (GetAnsiString(options.EMailAddress));
1836 MapiRecipDesc rec;
1837 if (!addr.IsEmpty())
1838 {
1839 memset(&rec, 0, sizeof(rec));
1840 rec.ulRecipClass = MAPI_TO;
1841 rec.lpszAddress = addr.Ptr_non_const();
1842 m.nRecipCount = 1;
1843 m.lpRecips = &rec;
1844 }
1845
1846 sendMail((LHANDLE)0, 0, &m, MAPI_DIALOG, 0);
1847 }
1848 }
1849 }
1850
1851 #endif
1852
1853 if (options.DeleteAfterCompressing)
1854 {
1855 CRecordVector<CDirPathSortPair> pairs;
1856 FStringVector foldersNames;
1857
1858 unsigned i;
1859
1860 for (i = 0; i < dirItems.Items.Size(); i++)
1861 {
1862 const CDirItem &dirItem = dirItems.Items[i];
1863 const FString phyPath = dirItems.GetPhyPath(i);
1864 if (dirItem.IsDir())
1865 {
1866 CDirPathSortPair pair;
1867 pair.Index = i;
1868 pair.SetNumSlashes(phyPath);
1869 pairs.Add(pair);
1870 }
1871 else
1872 {
1873 // 21.04: we have set processedItems[*] before for all required items
1874 if (processedItems[i] != 0
1875 // || dirItem.Size == 0
1876 // || dirItem.AreReparseData()
1877 )
1878 {
1879 NFind::CFileInfo fileInfo;
1880 /* if (!SymLinks), we follow link here, similar to (dirItem) filling */
1881 if (fileInfo.Find(phyPath, !options.SymLinks.Val))
1882 {
1883 bool is_SameSize = false;
1884 if (options.SymLinks.Val && dirItem.AreReparseData())
1885 {
1886 /* (dirItem.Size = dirItem.ReparseData.Size()) was set before.
1887 So we don't compare sizes for that case here */
1888 is_SameSize = fileInfo.IsOsSymLink();
1889 }
1890 else
1891 is_SameSize = (fileInfo.Size == dirItem.Size);
1892
1893 if (is_SameSize
1894 && Compare_FiTime(&fileInfo.MTime, &dirItem.MTime) == 0
1895 && Compare_FiTime(&fileInfo.CTime, &dirItem.CTime) == 0)
1896 {
1897 RINOK(callback->DeletingAfterArchiving(phyPath, false))
1898 DeleteFileAlways(phyPath);
1899 }
1900 }
1901 }
1902 else
1903 {
1904 // file was skipped by some reason. We can throw error for debug:
1905 /*
1906 errorInfo.SystemError = 0;
1907 errorInfo.Message = "file was not processed";
1908 errorInfo.FileNames.Add(phyPath);
1909 return E_FAIL;
1910 */
1911 }
1912 }
1913 }
1914
1915 pairs.Sort2();
1916
1917 for (i = 0; i < pairs.Size(); i++)
1918 {
1919 const FString phyPath = dirItems.GetPhyPath(pairs[i].Index);
1920 if (NFind::DoesDirExist(phyPath))
1921 {
1922 RINOK(callback->DeletingAfterArchiving(phyPath, true))
1923 RemoveDir(phyPath);
1924 }
1925 }
1926
1927 RINOK(callback->FinishDeletingAfterArchiving())
1928 }
1929
1930 return S_OK;
1931 }
1932