xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/Zip/ZipOut.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
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