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