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