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