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