1 // TarUpdate.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../Windows/TimeUtils.h"
6 
7 #include "../../Common/LimitedStreams.h"
8 #include "../../Common/ProgressUtils.h"
9 
10 #include "../../Compress/CopyCoder.h"
11 
12 #include "TarOut.h"
13 #include "TarUpdate.h"
14 
15 namespace NArchive {
16 namespace NTar {
17 
UpdateArchive(IInStream * inStream,ISequentialOutStream * outStream,const CObjectVector<NArchive::NTar::CItemEx> & inputItems,const CObjectVector<CUpdateItem> & updateItems,UINT codePage,unsigned utfFlags,IArchiveUpdateCallback * updateCallback)18 HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
19     const CObjectVector<NArchive::NTar::CItemEx> &inputItems,
20     const CObjectVector<CUpdateItem> &updateItems,
21     UINT codePage, unsigned utfFlags,
22     IArchiveUpdateCallback *updateCallback)
23 {
24   COutArchive outArchive;
25   outArchive.Create(outStream);
26   outArchive.Pos = 0;
27 
28   CMyComPtr<IOutStream> outSeekStream;
29   outStream->QueryInterface(IID_IOutStream, (void **)&outSeekStream);
30 
31   CMyComPtr<IArchiveUpdateCallbackFile> opCallback;
32   updateCallback->QueryInterface(IID_IArchiveUpdateCallbackFile, (void **)&opCallback);
33 
34   UInt64 complexity = 0;
35 
36   unsigned i;
37   for (i = 0; i < updateItems.Size(); i++)
38   {
39     const CUpdateItem &ui = updateItems[i];
40     if (ui.NewData)
41       complexity += ui.Size;
42     else
43       complexity += inputItems[(unsigned)ui.IndexInArc].GetFullSize();
44   }
45 
46   RINOK(updateCallback->SetTotal(complexity));
47 
48   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder;
49   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
50 
51   CLocalProgress *lps = new CLocalProgress;
52   CMyComPtr<ICompressProgressInfo> progress = lps;
53   lps->Init(updateCallback, true);
54 
55   CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
56   CMyComPtr<CLimitedSequentialInStream> inStreamLimited(streamSpec);
57   streamSpec->SetStream(inStream);
58 
59   complexity = 0;
60 
61   for (i = 0; i < updateItems.Size(); i++)
62   {
63     lps->InSize = lps->OutSize = complexity;
64     RINOK(lps->SetCur());
65 
66     const CUpdateItem &ui = updateItems[i];
67     CItem item;
68 
69     if (ui.NewProps)
70     {
71       item.SetDefaultWriteFields();
72       item.Mode = ui.Mode;
73       item.Name = ui.Name;
74       item.User = ui.User;
75       item.Group = ui.Group;
76 
77       if (ui.IsDir)
78       {
79         item.LinkFlag = NFileHeader::NLinkFlag::kDirectory;
80         item.PackSize = 0;
81       }
82       else
83       {
84         item.LinkFlag = NFileHeader::NLinkFlag::kNormal;
85         item.PackSize = ui.Size;
86       }
87 
88       item.MTime = ui.MTime;
89     }
90     else
91       item = inputItems[(unsigned)ui.IndexInArc];
92 
93     AString symLink;
94     if (ui.NewData || ui.NewProps)
95     {
96       RINOK(GetPropString(updateCallback, ui.IndexInClient, kpidSymLink, symLink, codePage, utfFlags, true));
97       if (!symLink.IsEmpty())
98       {
99         item.LinkFlag = NFileHeader::NLinkFlag::kSymLink;
100         item.LinkName = symLink;
101       }
102     }
103 
104     if (ui.NewData)
105     {
106       item.SparseBlocks.Clear();
107       item.PackSize = ui.Size;
108       item.Size = ui.Size;
109       if (ui.Size == (UInt64)(Int64)-1)
110         return E_INVALIDARG;
111 
112       CMyComPtr<ISequentialInStream> fileInStream;
113 
114       bool needWrite = true;
115 
116       if (!symLink.IsEmpty())
117       {
118         item.PackSize = 0;
119         item.Size = 0;
120       }
121       else
122       {
123         HRESULT res = updateCallback->GetStream(ui.IndexInClient, &fileInStream);
124 
125         if (res == S_FALSE)
126           needWrite = false;
127         else
128         {
129           RINOK(res);
130 
131           if (fileInStream)
132           {
133             CMyComPtr<IStreamGetProps> getProps;
134             fileInStream->QueryInterface(IID_IStreamGetProps, (void **)&getProps);
135             if (getProps)
136             {
137               FILETIME mTime;
138               UInt64 size2;
139               if (getProps->GetProps(&size2, NULL, NULL, &mTime, NULL) == S_OK)
140               {
141                 item.PackSize = size2;
142                 item.Size = size2;
143                 item.MTime = NWindows::NTime::FileTimeToUnixTime64(mTime);;
144               }
145             }
146           }
147           else
148           {
149             item.PackSize = 0;
150             item.Size = 0;
151           }
152 
153           {
154             AString hardLink;
155             RINOK(GetPropString(updateCallback, ui.IndexInClient, kpidHardLink, hardLink, codePage, utfFlags, true));
156             if (!hardLink.IsEmpty())
157             {
158               item.LinkFlag = NFileHeader::NLinkFlag::kHardLink;
159               item.LinkName = hardLink;
160               item.PackSize = 0;
161               item.Size = 0;
162               fileInStream.Release();
163             }
164           }
165         }
166       }
167 
168       if (needWrite)
169       {
170         UInt64 fileHeaderStartPos = outArchive.Pos;
171         RINOK(outArchive.WriteHeader(item));
172         if (fileInStream)
173         {
174           RINOK(copyCoder->Code(fileInStream, outStream, NULL, NULL, progress));
175           outArchive.Pos += copyCoderSpec->TotalSize;
176           if (copyCoderSpec->TotalSize != item.PackSize)
177           {
178             if (!outSeekStream)
179               return E_FAIL;
180             UInt64 backOffset = outArchive.Pos - fileHeaderStartPos;
181             RINOK(outSeekStream->Seek(-(Int64)backOffset, STREAM_SEEK_CUR, NULL));
182             outArchive.Pos = fileHeaderStartPos;
183             item.PackSize = copyCoderSpec->TotalSize;
184             RINOK(outArchive.WriteHeader(item));
185             RINOK(outSeekStream->Seek((Int64)item.PackSize, STREAM_SEEK_CUR, NULL));
186             outArchive.Pos += item.PackSize;
187           }
188           RINOK(outArchive.FillDataResidual(item.PackSize));
189         }
190       }
191 
192       complexity += item.PackSize;
193       RINOK(updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK));
194     }
195     else
196     {
197       const CItemEx &existItem = inputItems[(unsigned)ui.IndexInArc];
198       UInt64 size;
199 
200       if (ui.NewProps)
201       {
202         // memcpy(item.Magic, NFileHeader::NMagic::kEmpty, 8);
203 
204         if (!symLink.IsEmpty())
205         {
206           item.PackSize = 0;
207           item.Size = 0;
208         }
209         else
210         {
211           if (ui.IsDir == existItem.IsDir())
212             item.LinkFlag = existItem.LinkFlag;
213 
214           item.SparseBlocks = existItem.SparseBlocks;
215           item.Size = existItem.Size;
216           item.PackSize = existItem.PackSize;
217         }
218 
219         item.DeviceMajorDefined = existItem.DeviceMajorDefined;
220         item.DeviceMinorDefined = existItem.DeviceMinorDefined;
221         item.DeviceMajor = existItem.DeviceMajor;
222         item.DeviceMinor = existItem.DeviceMinor;
223         item.UID = existItem.UID;
224         item.GID = existItem.GID;
225 
226         RINOK(outArchive.WriteHeader(item));
227         RINOK(inStream->Seek((Int64)existItem.GetDataPosition(), STREAM_SEEK_SET, NULL));
228         size = existItem.PackSize;
229       }
230       else
231       {
232         RINOK(inStream->Seek((Int64)existItem.HeaderPos, STREAM_SEEK_SET, NULL));
233         size = existItem.GetFullSize();
234       }
235 
236       streamSpec->Init(size);
237 
238       if (opCallback)
239       {
240         RINOK(opCallback->ReportOperation(
241             NEventIndexType::kInArcIndex, (UInt32)ui.IndexInArc,
242             NUpdateNotifyOp::kReplicate))
243       }
244 
245       RINOK(copyCoder->Code(inStreamLimited, outStream, NULL, NULL, progress));
246       if (copyCoderSpec->TotalSize != size)
247         return E_FAIL;
248       outArchive.Pos += size;
249       RINOK(outArchive.FillDataResidual(existItem.PackSize));
250       complexity += size;
251     }
252   }
253 
254   lps->InSize = lps->OutSize = complexity;
255   RINOK(lps->SetCur());
256   return outArchive.WriteFinishHeader();
257 }
258 
259 }}
260