1 // ZipOut.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../../C/7zCrc.h"
6
7 #include "../../../Windows/TimeUtils.h"
8 #include "../../Common/OffsetStream.h"
9
10 #include "ZipOut.h"
11
12 namespace NArchive {
13 namespace NZip {
14
ClearRestriction()15 HRESULT COutArchive::ClearRestriction()
16 {
17 if (SetRestriction)
18 return SetRestriction->SetRestriction(0, 0);
19 return S_OK;
20 }
21
SetRestrictionFromCurrent()22 HRESULT COutArchive::SetRestrictionFromCurrent()
23 {
24 if (SetRestriction)
25 return SetRestriction->SetRestriction(m_Base + m_CurPos, (UInt64)(Int64)-1);
26 return S_OK;
27 }
28
Create(IOutStream * outStream)29 HRESULT COutArchive::Create(IOutStream *outStream)
30 {
31 m_CurPos = 0;
32 if (!m_OutBuffer.Create(1 << 16))
33 return E_OUTOFMEMORY;
34 m_Stream = outStream;
35 m_OutBuffer.SetStream(outStream);
36 m_OutBuffer.Init();
37
38 return m_Stream->Seek(0, STREAM_SEEK_CUR, &m_Base);
39 }
40
SeekToCurPos()41 void COutArchive::SeekToCurPos()
42 {
43 HRESULT res = m_Stream->Seek((Int64)(m_Base + m_CurPos), STREAM_SEEK_SET, NULL);
44 if (res != S_OK)
45 throw CSystemException(res);
46 }
47
48 #define DOES_NEED_ZIP64(v) (v >= (UInt32)0xFFFFFFFF)
49 // #define DOES_NEED_ZIP64(v) (v >= 0)
50
51
WriteBytes(const void * data,size_t size)52 void COutArchive::WriteBytes(const void *data, size_t size)
53 {
54 m_OutBuffer.WriteBytes(data, size);
55 m_CurPos += size;
56 }
57
Write8(Byte b)58 void COutArchive::Write8(Byte b)
59 {
60 m_OutBuffer.WriteByte(b);
61 m_CurPos++;
62 }
63
Write16(UInt16 val)64 void COutArchive::Write16(UInt16 val)
65 {
66 Write8((Byte)val);
67 Write8((Byte)(val >> 8));
68 }
69
Write32(UInt32 val)70 void COutArchive::Write32(UInt32 val)
71 {
72 for (int i = 0; i < 4; i++)
73 {
74 Write8((Byte)val);
75 val >>= 8;
76 }
77 }
78
Write64(UInt64 val)79 void COutArchive::Write64(UInt64 val)
80 {
81 for (int i = 0; i < 8; i++)
82 {
83 Write8((Byte)val);
84 val >>= 8;
85 }
86 }
87
WriteExtra(const CExtraBlock & extra)88 void COutArchive::WriteExtra(const CExtraBlock &extra)
89 {
90 FOR_VECTOR (i, extra.SubBlocks)
91 {
92 const CExtraSubBlock &subBlock = extra.SubBlocks[i];
93 Write16((UInt16)subBlock.ID);
94 Write16((UInt16)subBlock.Data.Size());
95 WriteBytes(subBlock.Data, (UInt16)subBlock.Data.Size());
96 }
97 }
98
WriteCommonItemInfo(const CLocalItem & item,bool isZip64)99 void COutArchive::WriteCommonItemInfo(const CLocalItem &item, bool isZip64)
100 {
101 {
102 Byte ver = item.ExtractVersion.Version;
103 if (isZip64 && ver < NFileHeader::NCompressionMethod::kExtractVersion_Zip64)
104 ver = NFileHeader::NCompressionMethod::kExtractVersion_Zip64;
105 Write8(ver);
106 }
107 Write8(item.ExtractVersion.HostOS);
108 Write16(item.Flags);
109 Write16(item.Method);
110 Write32(item.Time);
111 }
112
113
114 #define WRITE_32_VAL_SPEC(_v_, _isZip64_) Write32((_isZip64_) ? 0xFFFFFFFF : (UInt32)(_v_));
115
116
WriteUtfName(const CItemOut & item)117 void COutArchive::WriteUtfName(const CItemOut &item)
118 {
119 if (item.Name_Utf.Size() == 0)
120 return;
121 Write16(NFileHeader::NExtraID::kIzUnicodeName);
122 Write16((UInt16)(5 + item.Name_Utf.Size()));
123 Write8(1); // (1 = version) of that extra field
124 Write32(CrcCalc(item.Name.Ptr(), item.Name.Len()));
125 WriteBytes(item.Name_Utf, (UInt16)item.Name_Utf.Size());
126 }
127
128
129 static const unsigned k_Ntfs_ExtraSize = 4 + 2 + 2 + (3 * 8);
130 static const unsigned k_UnixTime_ExtraSize = 1 + (1 * 4);
131
WriteTimeExtra(const CItemOut & item,bool writeNtfs)132 void COutArchive::WriteTimeExtra(const CItemOut &item, bool writeNtfs)
133 {
134 if (writeNtfs)
135 {
136 // windows explorer ignores that extra
137 Write16(NFileHeader::NExtraID::kNTFS);
138 Write16(k_Ntfs_ExtraSize);
139 Write32(0); // reserved
140 Write16(NFileHeader::NNtfsExtra::kTagTime);
141 Write16(8 * 3);
142 WriteNtfsTime(item.Ntfs_MTime);
143 WriteNtfsTime(item.Ntfs_ATime);
144 WriteNtfsTime(item.Ntfs_CTime);
145 }
146
147 if (item.Write_UnixTime)
148 {
149 // windows explorer ignores that extra
150 // by specification : should we write to local header also?
151 Write16(NFileHeader::NExtraID::kUnixTime);
152 Write16(k_UnixTime_ExtraSize);
153 const Byte flags = (Byte)((unsigned)1 << NFileHeader::NUnixTime::kMTime);
154 Write8(flags);
155 UInt32 unixTime;
156 NWindows::NTime::FileTime_To_UnixTime(item.Ntfs_MTime, unixTime);
157 Write32(unixTime);
158 }
159 }
160
161
WriteLocalHeader(CItemOut & item,bool needCheck)162 void COutArchive::WriteLocalHeader(CItemOut &item, bool needCheck)
163 {
164 m_LocalHeaderPos = m_CurPos;
165 item.LocalHeaderPos = m_CurPos;
166
167 bool isZip64 =
168 DOES_NEED_ZIP64(item.PackSize) ||
169 DOES_NEED_ZIP64(item.Size);
170
171 if (needCheck && m_IsZip64)
172 isZip64 = true;
173
174 // Why don't we write NTFS timestamps to local header?
175 // Probably we want to reduce size of archive?
176 const bool writeNtfs = false; // do not write NTFS timestamp to local header
177 // const bool writeNtfs = item.Write_NtfsTime; // write NTFS time to local header
178 const UInt32 localExtraSize = (UInt32)(
179 (isZip64 ? (4 + 8 + 8): 0)
180 + (writeNtfs ? 4 + k_Ntfs_ExtraSize : 0)
181 + (item.Write_UnixTime ? 4 + k_UnixTime_ExtraSize : 0)
182 + item.Get_UtfName_ExtraSize()
183 + item.LocalExtra.GetSize());
184 if ((UInt16)localExtraSize != localExtraSize)
185 throw CSystemException(E_FAIL);
186 if (needCheck && m_ExtraSize != localExtraSize)
187 throw CSystemException(E_FAIL);
188
189 m_IsZip64 = isZip64;
190 m_ExtraSize = localExtraSize;
191
192 item.LocalExtra.IsZip64 = isZip64;
193
194 Write32(NSignature::kLocalFileHeader);
195
196 WriteCommonItemInfo(item, isZip64);
197
198 Write32(item.HasDescriptor() ? 0 : item.Crc);
199
200 UInt64 packSize = item.PackSize;
201 UInt64 size = item.Size;
202
203 if (item.HasDescriptor())
204 {
205 packSize = 0;
206 size = 0;
207 }
208
209 WRITE_32_VAL_SPEC(packSize, isZip64)
210 WRITE_32_VAL_SPEC(size, isZip64)
211
212 Write16((UInt16)item.Name.Len());
213
214 Write16((UInt16)localExtraSize);
215
216 WriteBytes((const char *)item.Name, (UInt16)item.Name.Len());
217
218 if (isZip64)
219 {
220 Write16(NFileHeader::NExtraID::kZip64);
221 Write16(8 + 8);
222 Write64(size);
223 Write64(packSize);
224 }
225
226 WriteTimeExtra(item, writeNtfs);
227
228 WriteUtfName(item);
229
230 WriteExtra(item.LocalExtra);
231
232 const UInt32 localFileHeaderSize = (UInt32)(m_CurPos - m_LocalHeaderPos);
233 if (needCheck && m_LocalFileHeaderSize != localFileHeaderSize)
234 throw CSystemException(E_FAIL);
235 m_LocalFileHeaderSize = localFileHeaderSize;
236
237 m_OutBuffer.FlushWithCheck();
238 }
239
240
WriteLocalHeader_Replace(CItemOut & item)241 void COutArchive::WriteLocalHeader_Replace(CItemOut &item)
242 {
243 m_CurPos = m_LocalHeaderPos + m_LocalFileHeaderSize + item.PackSize;
244
245 if (item.HasDescriptor())
246 {
247 WriteDescriptor(item);
248 m_OutBuffer.FlushWithCheck();
249 return;
250 // we don't replace local header, if we write Descriptor.
251 // so local header with Descriptor flag must be written to local header before.
252 }
253
254 const UInt64 nextPos = m_CurPos;
255 m_CurPos = m_LocalHeaderPos;
256 SeekToCurPos();
257 WriteLocalHeader(item, true);
258 m_CurPos = nextPos;
259 SeekToCurPos();
260 }
261
262
WriteDescriptor(const CItemOut & item)263 void COutArchive::WriteDescriptor(const CItemOut &item)
264 {
265 Byte buf[kDataDescriptorSize64];
266 SetUi32(buf, NSignature::kDataDescriptor)
267 SetUi32(buf + 4, item.Crc)
268 unsigned descriptorSize;
269 if (m_IsZip64)
270 {
271 SetUi64(buf + 8, item.PackSize)
272 SetUi64(buf + 16, item.Size)
273 descriptorSize = kDataDescriptorSize64;
274 }
275 else
276 {
277 SetUi32(buf + 8, (UInt32)item.PackSize)
278 SetUi32(buf + 12, (UInt32)item.Size)
279 descriptorSize = kDataDescriptorSize32;
280 }
281 WriteBytes(buf, descriptorSize);
282 }
283
284
285
WriteCentralHeader(const CItemOut & item)286 void COutArchive::WriteCentralHeader(const CItemOut &item)
287 {
288 const bool isUnPack64 = DOES_NEED_ZIP64(item.Size);
289 const bool isPack64 = DOES_NEED_ZIP64(item.PackSize);
290 const bool isPosition64 = DOES_NEED_ZIP64(item.LocalHeaderPos);
291 const bool isZip64 = isPack64 || isUnPack64 || isPosition64;
292
293 Write32(NSignature::kCentralFileHeader);
294 Write8(item.MadeByVersion.Version);
295 Write8(item.MadeByVersion.HostOS);
296
297 WriteCommonItemInfo(item, isZip64);
298 Write32(item.Crc);
299
300 WRITE_32_VAL_SPEC(item.PackSize, isPack64)
301 WRITE_32_VAL_SPEC(item.Size, isUnPack64)
302
303 Write16((UInt16)item.Name.Len());
304
305 const UInt16 zip64ExtraSize = (UInt16)((isUnPack64 ? 8: 0) + (isPack64 ? 8: 0) + (isPosition64 ? 8: 0));
306 const bool writeNtfs = item.Write_NtfsTime;
307 const size_t centralExtraSize =
308 (isZip64 ? 4 + zip64ExtraSize : 0)
309 + (writeNtfs ? 4 + k_Ntfs_ExtraSize : 0)
310 + (item.Write_UnixTime ? 4 + k_UnixTime_ExtraSize : 0)
311 + item.Get_UtfName_ExtraSize()
312 + item.CentralExtra.GetSize();
313
314 const UInt16 centralExtraSize16 = (UInt16)centralExtraSize;
315 if (centralExtraSize16 != centralExtraSize)
316 throw CSystemException(E_FAIL);
317
318 Write16(centralExtraSize16);
319
320 const UInt16 commentSize = (UInt16)item.Comment.Size();
321
322 Write16(commentSize);
323 Write16(0); // DiskNumberStart
324 Write16(item.InternalAttrib);
325 Write32(item.ExternalAttrib);
326 WRITE_32_VAL_SPEC(item.LocalHeaderPos, isPosition64)
327 WriteBytes((const char *)item.Name, item.Name.Len());
328
329 if (isZip64)
330 {
331 Write16(NFileHeader::NExtraID::kZip64);
332 Write16(zip64ExtraSize);
333 if (isUnPack64)
334 Write64(item.Size);
335 if (isPack64)
336 Write64(item.PackSize);
337 if (isPosition64)
338 Write64(item.LocalHeaderPos);
339 }
340
341 WriteTimeExtra(item, writeNtfs);
342 WriteUtfName(item);
343
344 WriteExtra(item.CentralExtra);
345 if (commentSize != 0)
346 WriteBytes(item.Comment, commentSize);
347 }
348
WriteCentralDir(const CObjectVector<CItemOut> & items,const CByteBuffer * comment)349 HRESULT COutArchive::WriteCentralDir(const CObjectVector<CItemOut> &items, const CByteBuffer *comment)
350 {
351 RINOK(ClearRestriction())
352
353 const UInt64 cdOffset = GetCurPos();
354 FOR_VECTOR (i, items)
355 WriteCentralHeader(items[i]);
356 const UInt64 cd64EndOffset = GetCurPos();
357 const UInt64 cdSize = cd64EndOffset - cdOffset;
358 const bool cdOffset64 = DOES_NEED_ZIP64(cdOffset);
359 const bool cdSize64 = DOES_NEED_ZIP64(cdSize);
360 const bool items64 = items.Size() >= 0xFFFF;
361 const bool isZip64 = (cdOffset64 || cdSize64 || items64);
362
363 // isZip64 = true; // to test Zip64
364
365 if (isZip64)
366 {
367 Write32(NSignature::kEcd64);
368 Write64(kEcd64_MainSize);
369
370 // to test extra block:
371 // const UInt32 extraSize = 1 << 26;
372 // Write64(kEcd64_MainSize + extraSize);
373
374 Write16(45); // made by version
375 Write16(45); // extract version
376 Write32(0); // ThisDiskNumber
377 Write32(0); // StartCentralDirectoryDiskNumber
378 Write64((UInt64)items.Size());
379 Write64((UInt64)items.Size());
380 Write64((UInt64)cdSize);
381 Write64((UInt64)cdOffset);
382
383 // for (UInt32 iii = 0; iii < extraSize; iii++) Write8(1);
384
385 Write32(NSignature::kEcd64Locator);
386 Write32(0); // number of the disk with the start of the zip64 end of central directory
387 Write64(cd64EndOffset);
388 Write32(1); // total number of disks
389 }
390
391 Write32(NSignature::kEcd);
392 Write16(0); // ThisDiskNumber
393 Write16(0); // StartCentralDirectoryDiskNumber
394 Write16((UInt16)(items64 ? 0xFFFF: items.Size()));
395 Write16((UInt16)(items64 ? 0xFFFF: items.Size()));
396
397 WRITE_32_VAL_SPEC(cdSize, cdSize64)
398 WRITE_32_VAL_SPEC(cdOffset, cdOffset64)
399
400 const UInt16 commentSize = (UInt16)(comment ? comment->Size() : 0);
401 Write16((UInt16)commentSize);
402 if (commentSize != 0)
403 WriteBytes((const Byte *)*comment, commentSize);
404 m_OutBuffer.FlushWithCheck();
405 return S_OK;
406 }
407
CreateStreamForCompressing(CMyComPtr<IOutStream> & outStream)408 void COutArchive::CreateStreamForCompressing(CMyComPtr<IOutStream> &outStream)
409 {
410 COffsetOutStream *streamSpec = new COffsetOutStream;
411 outStream = streamSpec;
412 streamSpec->Init(m_Stream, m_Base + m_CurPos);
413 }
414
CreateStreamForCopying(CMyComPtr<ISequentialOutStream> & outStream)415 void COutArchive::CreateStreamForCopying(CMyComPtr<ISequentialOutStream> &outStream)
416 {
417 outStream = m_Stream;
418 }
419
420 }}
421