OLD | NEW |
(Empty) | |
| 1 /* XzIn.c - Xz input |
| 2 2009-06-19 : Igor Pavlov : Public domain */ |
| 3 |
| 4 #include <string.h> |
| 5 |
| 6 #include "7zCrc.h" |
| 7 #include "CpuArch.h" |
| 8 #include "Xz.h" |
| 9 |
| 10 SRes Xz_ReadHeader(CXzStreamFlags *p, ISeqInStream *inStream) |
| 11 { |
| 12 Byte sig[XZ_STREAM_HEADER_SIZE]; |
| 13 RINOK(SeqInStream_Read2(inStream, sig, XZ_STREAM_HEADER_SIZE, SZ_ERROR_NO_ARCH
IVE)); |
| 14 if (memcmp(sig, XZ_SIG, XZ_SIG_SIZE) != 0) |
| 15 return SZ_ERROR_NO_ARCHIVE; |
| 16 return Xz_ParseHeader(p, sig); |
| 17 } |
| 18 |
| 19 #define READ_VARINT_AND_CHECK(buf, pos, size, res) \ |
| 20 { unsigned s = Xz_ReadVarInt(buf + pos, size - pos, res); \ |
| 21 if (s == 0) return SZ_ERROR_ARCHIVE; pos += s; } |
| 22 |
| 23 SRes XzBlock_ReadHeader(CXzBlock *p, ISeqInStream *inStream, Bool *isIndex, UInt
32 *headerSizeRes) |
| 24 { |
| 25 Byte header[XZ_BLOCK_HEADER_SIZE_MAX]; |
| 26 unsigned headerSize; |
| 27 *headerSizeRes = 0; |
| 28 RINOK(SeqInStream_ReadByte(inStream, &header[0])); |
| 29 headerSize = ((unsigned)header[0] << 2) + 4; |
| 30 if (headerSize == 0) |
| 31 { |
| 32 *headerSizeRes = 1; |
| 33 *isIndex = True; |
| 34 return SZ_OK; |
| 35 } |
| 36 |
| 37 *isIndex = False; |
| 38 *headerSizeRes = headerSize; |
| 39 RINOK(SeqInStream_Read(inStream, header + 1, headerSize - 1)); |
| 40 return XzBlock_Parse(p, header); |
| 41 } |
| 42 |
| 43 #define ADD_SIZE_CHECH(size, val) \ |
| 44 { UInt64 newSize = size + (val); if (newSize < size) return XZ_SIZE_OVERFLOW;
size = newSize; } |
| 45 |
| 46 UInt64 Xz_GetUnpackSize(const CXzStream *p) |
| 47 { |
| 48 UInt64 size = 0; |
| 49 size_t i; |
| 50 for (i = 0; i < p->numBlocks; i++) |
| 51 ADD_SIZE_CHECH(size, p->blocks[i].unpackSize); |
| 52 return size; |
| 53 } |
| 54 |
| 55 UInt64 Xz_GetPackSize(const CXzStream *p) |
| 56 { |
| 57 UInt64 size = 0; |
| 58 size_t i; |
| 59 for (i = 0; i < p->numBlocks; i++) |
| 60 ADD_SIZE_CHECH(size, (p->blocks[i].totalSize + 3) & ~(UInt64)3); |
| 61 return size; |
| 62 } |
| 63 |
| 64 /* |
| 65 SRes XzBlock_ReadFooter(CXzBlock *p, CXzStreamFlags f, ISeqInStream *inStream) |
| 66 { |
| 67 return SeqInStream_Read(inStream, p->check, XzFlags_GetCheckSize(f)); |
| 68 } |
| 69 */ |
| 70 |
| 71 static SRes Xz_ReadIndex2(CXzStream *p, const Byte *buf, size_t size, ISzAlloc *
alloc) |
| 72 { |
| 73 size_t i, numBlocks, crcStartPos, pos = 1; |
| 74 UInt32 crc; |
| 75 |
| 76 if (size < 5 || buf[0] != 0) |
| 77 return SZ_ERROR_ARCHIVE; |
| 78 |
| 79 size -= 4; |
| 80 crc = CrcCalc(buf, size); |
| 81 if (crc != GetUi32(buf + size)) |
| 82 return SZ_ERROR_ARCHIVE; |
| 83 |
| 84 { |
| 85 UInt64 numBlocks64; |
| 86 READ_VARINT_AND_CHECK(buf, pos, size, &numBlocks64); |
| 87 numBlocks = (size_t)numBlocks64; |
| 88 if (numBlocks != numBlocks64 || numBlocks * 2 > size) |
| 89 return SZ_ERROR_ARCHIVE; |
| 90 } |
| 91 |
| 92 crcStartPos = pos; |
| 93 Xz_Free(p, alloc); |
| 94 if (numBlocks != 0) |
| 95 { |
| 96 p->numBlocks = numBlocks; |
| 97 p->numBlocksAllocated = numBlocks; |
| 98 p->blocks = alloc->Alloc(alloc, sizeof(CXzBlockSizes) * numBlocks); |
| 99 if (p->blocks == 0) |
| 100 return SZ_ERROR_MEM; |
| 101 for (i = 0; i < numBlocks; i++) |
| 102 { |
| 103 CXzBlockSizes *block = &p->blocks[i]; |
| 104 READ_VARINT_AND_CHECK(buf, pos, size, &block->totalSize); |
| 105 READ_VARINT_AND_CHECK(buf, pos, size, &block->unpackSize); |
| 106 if (block->totalSize == 0) |
| 107 return SZ_ERROR_ARCHIVE; |
| 108 } |
| 109 } |
| 110 while ((pos & 3) != 0) |
| 111 if (buf[pos++] != 0) |
| 112 return SZ_ERROR_ARCHIVE; |
| 113 return (pos == size) ? SZ_OK : SZ_ERROR_ARCHIVE; |
| 114 } |
| 115 |
| 116 static SRes Xz_ReadIndex(CXzStream *p, ILookInStream *stream, UInt64 indexSize,
ISzAlloc *alloc) |
| 117 { |
| 118 SRes res; |
| 119 size_t size; |
| 120 Byte *buf; |
| 121 if (indexSize > ((UInt32)1 << 31)) |
| 122 return SZ_ERROR_UNSUPPORTED; |
| 123 size = (size_t)indexSize; |
| 124 if (size != indexSize) |
| 125 return SZ_ERROR_UNSUPPORTED; |
| 126 buf = alloc->Alloc(alloc, size); |
| 127 if (buf == 0) |
| 128 return SZ_ERROR_MEM; |
| 129 res = LookInStream_Read2(stream, buf, size, SZ_ERROR_UNSUPPORTED); |
| 130 if (res == SZ_OK) |
| 131 res = Xz_ReadIndex2(p, buf, size, alloc); |
| 132 alloc->Free(alloc, buf); |
| 133 return res; |
| 134 } |
| 135 |
| 136 static SRes SeekFromCur(ILookInStream *inStream, Int64 *res) |
| 137 { |
| 138 return inStream->Seek(inStream, res, SZ_SEEK_CUR); |
| 139 } |
| 140 |
| 141 static SRes Xz_ReadBackward(CXzStream *p, ILookInStream *stream, Int64 *startOff
set, ISzAlloc *alloc) |
| 142 { |
| 143 UInt64 indexSize; |
| 144 Byte buf[XZ_STREAM_FOOTER_SIZE]; |
| 145 |
| 146 if ((*startOffset & 3) != 0 || *startOffset < XZ_STREAM_FOOTER_SIZE) |
| 147 return SZ_ERROR_NO_ARCHIVE; |
| 148 *startOffset = -XZ_STREAM_FOOTER_SIZE; |
| 149 RINOK(SeekFromCur(stream, startOffset)); |
| 150 |
| 151 RINOK(LookInStream_Read2(stream, buf, XZ_STREAM_FOOTER_SIZE, SZ_ERROR_NO_ARCHI
VE)); |
| 152 |
| 153 if (memcmp(buf + 10, XZ_FOOTER_SIG, XZ_FOOTER_SIG_SIZE) != 0) |
| 154 { |
| 155 Int64 i = 0; |
| 156 *startOffset += XZ_STREAM_FOOTER_SIZE; |
| 157 for (;;) |
| 158 { |
| 159 int j; |
| 160 size_t processedSize; |
| 161 #define TEMP_BUF_SIZE (1 << 10) |
| 162 Byte tempBuf[TEMP_BUF_SIZE]; |
| 163 if (*startOffset < XZ_STREAM_FOOTER_SIZE || i > (1 << 16)) |
| 164 return SZ_ERROR_NO_ARCHIVE; |
| 165 processedSize = (*startOffset > TEMP_BUF_SIZE) ? TEMP_BUF_SIZE : (size_t)*
startOffset; |
| 166 i += processedSize; |
| 167 *startOffset = -(Int64)processedSize; |
| 168 RINOK(SeekFromCur(stream, startOffset)); |
| 169 RINOK(LookInStream_Read2(stream, tempBuf, processedSize, SZ_ERROR_NO_ARCHI
VE)); |
| 170 for (j = (int)processedSize; j >= 0; j--) |
| 171 if (tempBuf[j -1] != 0) |
| 172 break; |
| 173 if (j != 0) |
| 174 { |
| 175 if ((j & 3) != 0) |
| 176 return SZ_ERROR_NO_ARCHIVE; |
| 177 *startOffset += j; |
| 178 if (*startOffset < XZ_STREAM_FOOTER_SIZE) |
| 179 return SZ_ERROR_NO_ARCHIVE; |
| 180 *startOffset -= XZ_STREAM_FOOTER_SIZE; |
| 181 RINOK(stream->Seek(stream, startOffset, SZ_SEEK_SET)); |
| 182 RINOK(LookInStream_Read2(stream, buf, XZ_STREAM_FOOTER_SIZE, SZ_ERROR_NO
_ARCHIVE)); |
| 183 if (memcmp(buf + 10, XZ_FOOTER_SIG, XZ_FOOTER_SIG_SIZE) != 0) |
| 184 return SZ_ERROR_NO_ARCHIVE; |
| 185 break; |
| 186 } |
| 187 } |
| 188 } |
| 189 |
| 190 p->flags = (CXzStreamFlags)GetBe16(buf + 8); |
| 191 |
| 192 if (!XzFlags_IsSupported(p->flags)) |
| 193 return SZ_ERROR_UNSUPPORTED; |
| 194 |
| 195 if (GetUi32(buf) != CrcCalc(buf + 4, 6)) |
| 196 return SZ_ERROR_ARCHIVE; |
| 197 |
| 198 indexSize = ((UInt64)GetUi32(buf + 4) + 1) << 2; |
| 199 |
| 200 *startOffset = -(Int64)(indexSize + XZ_STREAM_FOOTER_SIZE); |
| 201 RINOK(SeekFromCur(stream, startOffset)); |
| 202 |
| 203 RINOK(Xz_ReadIndex(p, stream, indexSize, alloc)); |
| 204 |
| 205 { |
| 206 UInt64 totalSize = Xz_GetPackSize(p); |
| 207 UInt64 sum = XZ_STREAM_HEADER_SIZE + totalSize + indexSize; |
| 208 if (totalSize == XZ_SIZE_OVERFLOW || |
| 209 sum >= ((UInt64)1 << 63) || |
| 210 totalSize >= ((UInt64)1 << 63)) |
| 211 return SZ_ERROR_ARCHIVE; |
| 212 *startOffset = -(Int64)sum; |
| 213 RINOK(SeekFromCur(stream, startOffset)); |
| 214 } |
| 215 { |
| 216 CXzStreamFlags headerFlags; |
| 217 CSecToRead secToRead; |
| 218 SecToRead_CreateVTable(&secToRead); |
| 219 secToRead.realStream = stream; |
| 220 |
| 221 RINOK(Xz_ReadHeader(&headerFlags, &secToRead.s)); |
| 222 return (p->flags == headerFlags) ? SZ_OK : SZ_ERROR_ARCHIVE; |
| 223 } |
| 224 } |
| 225 |
| 226 |
| 227 /* ---------- Xz Streams ---------- */ |
| 228 |
| 229 void Xzs_Construct(CXzs *p) |
| 230 { |
| 231 p->num = p->numAllocated = 0; |
| 232 p->streams = 0; |
| 233 } |
| 234 |
| 235 void Xzs_Free(CXzs *p, ISzAlloc *alloc) |
| 236 { |
| 237 size_t i; |
| 238 for (i = 0; i < p->num; i++) |
| 239 Xz_Free(&p->streams[i], alloc); |
| 240 alloc->Free(alloc, p->streams); |
| 241 p->num = p->numAllocated = 0; |
| 242 p->streams = 0; |
| 243 } |
| 244 |
| 245 UInt64 Xzs_GetNumBlocks(const CXzs *p) |
| 246 { |
| 247 UInt64 num = 0; |
| 248 size_t i; |
| 249 for (i = 0; i < p->num; i++) |
| 250 num += p->streams[i].numBlocks; |
| 251 return num; |
| 252 } |
| 253 |
| 254 UInt64 Xzs_GetUnpackSize(const CXzs *p) |
| 255 { |
| 256 UInt64 size = 0; |
| 257 size_t i; |
| 258 for (i = 0; i < p->num; i++) |
| 259 ADD_SIZE_CHECH(size, Xz_GetUnpackSize(&p->streams[i])); |
| 260 return size; |
| 261 } |
| 262 |
| 263 /* |
| 264 UInt64 Xzs_GetPackSize(const CXzs *p) |
| 265 { |
| 266 UInt64 size = 0; |
| 267 size_t i; |
| 268 for (i = 0; i < p->num; i++) |
| 269 ADD_SIZE_CHECH(size, Xz_GetTotalSize(&p->streams[i])); |
| 270 return size; |
| 271 } |
| 272 */ |
| 273 |
| 274 SRes Xzs_ReadBackward(CXzs *p, ILookInStream *stream, Int64 *startOffset, ICompr
essProgress *progress, ISzAlloc *alloc) |
| 275 { |
| 276 Int64 endOffset = 0; |
| 277 RINOK(stream->Seek(stream, &endOffset, SZ_SEEK_END)); |
| 278 *startOffset = endOffset; |
| 279 for (;;) |
| 280 { |
| 281 CXzStream st; |
| 282 SRes res; |
| 283 Xz_Construct(&st); |
| 284 res = Xz_ReadBackward(&st, stream, startOffset, alloc); |
| 285 st.startOffset = *startOffset; |
| 286 RINOK(res); |
| 287 if (p->num == p->numAllocated) |
| 288 { |
| 289 size_t newNum = p->num + p->num / 4 + 1; |
| 290 Byte *data = (Byte *)alloc->Alloc(alloc, newNum * sizeof(CXzStream)); |
| 291 if (data == 0) |
| 292 return SZ_ERROR_MEM; |
| 293 p->numAllocated = newNum; |
| 294 memcpy(data, p->streams, p->num * sizeof(CXzStream)); |
| 295 alloc->Free(alloc, p->streams); |
| 296 p->streams = (CXzStream *)data; |
| 297 } |
| 298 p->streams[p->num++] = st; |
| 299 if (*startOffset == 0) |
| 300 break; |
| 301 RINOK(stream->Seek(stream, startOffset, SZ_SEEK_SET)); |
| 302 if (progress && progress->Progress(progress, endOffset - *startOffset, (UInt
64)(Int64)-1) != SZ_OK) |
| 303 return SZ_ERROR_PROGRESS; |
| 304 } |
| 305 return SZ_OK; |
| 306 } |
OLD | NEW |