diff options
| author | Igor Pavlov <87184205+ip7z@users.noreply.github.com> | 2025-07-05 00:00:00 +0000 |
|---|---|---|
| committer | Igor Pavlov <87184205+ip7z@users.noreply.github.com> | 2025-07-05 19:27:33 +0500 |
| commit | 395149956d696e6e3099d8b76d797437f94a6942 (patch) | |
| tree | 6ed5013a637078ae2dfdc4acf1ad93bf29cea356 /C/XzIn.c | |
| parent | e5431fa6f5505e385c6f9367260717e9c47dc2ee (diff) | |
| download | 7zip-25.00.tar.gz 7zip-25.00.tar.bz2 7zip-25.00.zip | |
25.0025.00
Diffstat (limited to 'C/XzIn.c')
| -rw-r--r-- | C/XzIn.c | 265 |
1 files changed, 155 insertions, 110 deletions
| @@ -1,38 +1,39 @@ | |||
| 1 | /* XzIn.c - Xz input | 1 | /* XzIn.c - Xz input |
| 2 | 2023-09-07 : Igor Pavlov : Public domain */ | 2 | : Igor Pavlov : Public domain */ |
| 3 | 3 | ||
| 4 | #include "Precomp.h" | 4 | #include "Precomp.h" |
| 5 | 5 | ||
| 6 | #include <string.h> | 6 | #include <string.h> |
| 7 | 7 | ||
| 8 | #include "7zCrc.h" | 8 | #include "7zCrc.h" |
| 9 | #include "CpuArch.h" | ||
| 10 | #include "Xz.h" | 9 | #include "Xz.h" |
| 10 | #include "CpuArch.h" | ||
| 11 | 11 | ||
| 12 | /* | 12 | #define XZ_FOOTER_12B_ALIGNED16_SIG_CHECK(p) \ |
| 13 | #define XZ_FOOTER_SIG_CHECK(p) (memcmp((p), XZ_FOOTER_SIG, XZ_FOOTER_SIG_SIZE) == 0) | 13 | (GetUi16a((const Byte *)(const void *)(p) + 10) == \ |
| 14 | */ | 14 | (XZ_FOOTER_SIG_0 | (XZ_FOOTER_SIG_1 << 8))) |
| 15 | #define XZ_FOOTER_SIG_CHECK(p) ((p)[0] == XZ_FOOTER_SIG_0 && (p)[1] == XZ_FOOTER_SIG_1) | ||
| 16 | |||
| 17 | 15 | ||
| 18 | SRes Xz_ReadHeader(CXzStreamFlags *p, ISeqInStreamPtr inStream) | 16 | SRes Xz_ReadHeader(CXzStreamFlags *p, ISeqInStreamPtr inStream) |
| 19 | { | 17 | { |
| 20 | Byte sig[XZ_STREAM_HEADER_SIZE]; | 18 | UInt32 data32[XZ_STREAM_HEADER_SIZE / 4]; |
| 21 | size_t processedSize = XZ_STREAM_HEADER_SIZE; | 19 | size_t processedSize = XZ_STREAM_HEADER_SIZE; |
| 22 | RINOK(SeqInStream_ReadMax(inStream, sig, &processedSize)) | 20 | RINOK(SeqInStream_ReadMax(inStream, data32, &processedSize)) |
| 23 | if (processedSize != XZ_STREAM_HEADER_SIZE | 21 | if (processedSize != XZ_STREAM_HEADER_SIZE |
| 24 | || memcmp(sig, XZ_SIG, XZ_SIG_SIZE) != 0) | 22 | || memcmp(data32, XZ_SIG, XZ_SIG_SIZE) != 0) |
| 25 | return SZ_ERROR_NO_ARCHIVE; | 23 | return SZ_ERROR_NO_ARCHIVE; |
| 26 | return Xz_ParseHeader(p, sig); | 24 | return Xz_ParseHeader(p, (const Byte *)(const void *)data32); |
| 27 | } | 25 | } |
| 28 | 26 | ||
| 29 | #define READ_VARINT_AND_CHECK(buf, pos, size, res) \ | 27 | #define READ_VARINT_AND_CHECK(buf, size, res) \ |
| 30 | { const unsigned s = Xz_ReadVarInt(buf + pos, size - pos, res); \ | 28 | { const unsigned s = Xz_ReadVarInt(buf, size, res); \ |
| 31 | if (s == 0) return SZ_ERROR_ARCHIVE; \ | 29 | if (s == 0) return SZ_ERROR_ARCHIVE; \ |
| 32 | pos += s; } | 30 | size -= s; \ |
| 31 | buf += s; \ | ||
| 32 | } | ||
| 33 | 33 | ||
| 34 | SRes XzBlock_ReadHeader(CXzBlock *p, ISeqInStreamPtr inStream, BoolInt *isIndex, UInt32 *headerSizeRes) | 34 | SRes XzBlock_ReadHeader(CXzBlock *p, ISeqInStreamPtr inStream, BoolInt *isIndex, UInt32 *headerSizeRes) |
| 35 | { | 35 | { |
| 36 | MY_ALIGN(4) | ||
| 36 | Byte header[XZ_BLOCK_HEADER_SIZE_MAX]; | 37 | Byte header[XZ_BLOCK_HEADER_SIZE_MAX]; |
| 37 | unsigned headerSize; | 38 | unsigned headerSize; |
| 38 | *headerSizeRes = 0; | 39 | *headerSizeRes = 0; |
| @@ -57,8 +58,12 @@ SRes XzBlock_ReadHeader(CXzBlock *p, ISeqInStreamPtr inStream, BoolInt *isIndex, | |||
| 57 | return XzBlock_Parse(p, header); | 58 | return XzBlock_Parse(p, header); |
| 58 | } | 59 | } |
| 59 | 60 | ||
| 61 | |||
| 60 | #define ADD_SIZE_CHECK(size, val) \ | 62 | #define ADD_SIZE_CHECK(size, val) \ |
| 61 | { const UInt64 newSize = size + (val); if (newSize < size) return XZ_SIZE_OVERFLOW; size = newSize; } | 63 | { const UInt64 newSize = size + (val); \ |
| 64 | if (newSize < size) return XZ_SIZE_OVERFLOW; \ | ||
| 65 | size = newSize; \ | ||
| 66 | } | ||
| 62 | 67 | ||
| 63 | UInt64 Xz_GetUnpackSize(const CXzStream *p) | 68 | UInt64 Xz_GetUnpackSize(const CXzStream *p) |
| 64 | { | 69 | { |
| @@ -82,76 +87,85 @@ UInt64 Xz_GetPackSize(const CXzStream *p) | |||
| 82 | return size; | 87 | return size; |
| 83 | } | 88 | } |
| 84 | 89 | ||
| 85 | /* | ||
| 86 | SRes XzBlock_ReadFooter(CXzBlock *p, CXzStreamFlags f, ISeqInStreamPtr inStream) | ||
| 87 | { | ||
| 88 | return SeqInStream_Read(inStream, p->check, XzFlags_GetCheckSize(f)); | ||
| 89 | } | ||
| 90 | */ | ||
| 91 | 90 | ||
| 92 | static SRes Xz_ReadIndex2(CXzStream *p, const Byte *buf, size_t size, ISzAllocPtr alloc) | 91 | // input; |
| 92 | // CXzStream (p) is empty object. | ||
| 93 | // size != 0 | ||
| 94 | // (size & 3) == 0 | ||
| 95 | // (buf) is aligned for at least 4 bytes. | ||
| 96 | // output: | ||
| 97 | // p->numBlocks is number of allocated items in p->blocks | ||
| 98 | // p->blocks[*] values must be ignored, if function returns error. | ||
| 99 | static SRes Xz_ParseIndex(CXzStream *p, const Byte *buf, size_t size, ISzAllocPtr alloc) | ||
| 93 | { | 100 | { |
| 94 | size_t numBlocks, pos = 1; | 101 | size_t numBlocks; |
| 95 | UInt32 crc; | ||
| 96 | |||
| 97 | if (size < 5 || buf[0] != 0) | 102 | if (size < 5 || buf[0] != 0) |
| 98 | return SZ_ERROR_ARCHIVE; | 103 | return SZ_ERROR_ARCHIVE; |
| 99 | |||
| 100 | size -= 4; | 104 | size -= 4; |
| 101 | crc = CrcCalc(buf, size); | 105 | { |
| 102 | if (crc != GetUi32(buf + size)) | 106 | const UInt32 crc = CrcCalc(buf, size); |
| 103 | return SZ_ERROR_ARCHIVE; | 107 | if (crc != GetUi32a(buf + size)) |
| 104 | 108 | return SZ_ERROR_ARCHIVE; | |
| 109 | } | ||
| 110 | buf++; | ||
| 111 | size--; | ||
| 105 | { | 112 | { |
| 106 | UInt64 numBlocks64; | 113 | UInt64 numBlocks64; |
| 107 | READ_VARINT_AND_CHECK(buf, pos, size, &numBlocks64) | 114 | READ_VARINT_AND_CHECK(buf, size, &numBlocks64) |
| 108 | numBlocks = (size_t)numBlocks64; | 115 | // (numBlocks64) is 63-bit value, so we can calculate (numBlocks64 * 2): |
| 109 | if (numBlocks != numBlocks64 || numBlocks * 2 > size) | 116 | if (numBlocks64 * 2 > size) |
| 110 | return SZ_ERROR_ARCHIVE; | 117 | return SZ_ERROR_ARCHIVE; |
| 118 | if (numBlocks64 >= ((size_t)1 << (sizeof(size_t) * 8 - 1)) / sizeof(CXzBlockSizes)) | ||
| 119 | return SZ_ERROR_MEM; // SZ_ERROR_ARCHIVE | ||
| 120 | numBlocks = (size_t)numBlocks64; | ||
| 111 | } | 121 | } |
| 112 | 122 | // Xz_Free(p, alloc); // it's optional, because (p) is empty already | |
| 113 | Xz_Free(p, alloc); | 123 | if (numBlocks) |
| 114 | if (numBlocks != 0) | ||
| 115 | { | 124 | { |
| 116 | size_t i; | 125 | CXzBlockSizes *blocks = (CXzBlockSizes *)ISzAlloc_Alloc(alloc, sizeof(CXzBlockSizes) * numBlocks); |
| 117 | p->numBlocks = numBlocks; | 126 | if (!blocks) |
| 118 | p->blocks = (CXzBlockSizes *)ISzAlloc_Alloc(alloc, sizeof(CXzBlockSizes) * numBlocks); | ||
| 119 | if (!p->blocks) | ||
| 120 | return SZ_ERROR_MEM; | 127 | return SZ_ERROR_MEM; |
| 121 | for (i = 0; i < numBlocks; i++) | 128 | p->blocks = blocks; |
| 129 | p->numBlocks = numBlocks; | ||
| 130 | // the caller will call Xz_Free() in case of error | ||
| 131 | do | ||
| 122 | { | 132 | { |
| 123 | CXzBlockSizes *block = &p->blocks[i]; | 133 | READ_VARINT_AND_CHECK(buf, size, &blocks->totalSize) |
| 124 | READ_VARINT_AND_CHECK(buf, pos, size, &block->totalSize) | 134 | READ_VARINT_AND_CHECK(buf, size, &blocks->unpackSize) |
| 125 | READ_VARINT_AND_CHECK(buf, pos, size, &block->unpackSize) | 135 | if (blocks->totalSize == 0) |
| 126 | if (block->totalSize == 0) | ||
| 127 | return SZ_ERROR_ARCHIVE; | 136 | return SZ_ERROR_ARCHIVE; |
| 137 | blocks++; | ||
| 128 | } | 138 | } |
| 139 | while (--numBlocks); | ||
| 129 | } | 140 | } |
| 130 | while ((pos & 3) != 0) | 141 | if (size >= 4) |
| 131 | if (buf[pos++] != 0) | 142 | return SZ_ERROR_ARCHIVE; |
| 143 | while (size) | ||
| 144 | if (buf[--size]) | ||
| 132 | return SZ_ERROR_ARCHIVE; | 145 | return SZ_ERROR_ARCHIVE; |
| 133 | return (pos == size) ? SZ_OK : SZ_ERROR_ARCHIVE; | 146 | return SZ_OK; |
| 134 | } | 147 | } |
| 135 | 148 | ||
| 149 | |||
| 150 | /* | ||
| 136 | static SRes Xz_ReadIndex(CXzStream *p, ILookInStreamPtr stream, UInt64 indexSize, ISzAllocPtr alloc) | 151 | static SRes Xz_ReadIndex(CXzStream *p, ILookInStreamPtr stream, UInt64 indexSize, ISzAllocPtr alloc) |
| 137 | { | 152 | { |
| 138 | SRes res; | 153 | SRes res; |
| 139 | size_t size; | 154 | size_t size; |
| 140 | Byte *buf; | 155 | Byte *buf; |
| 141 | if (indexSize > ((UInt32)1 << 31)) | 156 | if (indexSize >= ((size_t)1 << (sizeof(size_t) * 8 - 1))) |
| 142 | return SZ_ERROR_UNSUPPORTED; | 157 | return SZ_ERROR_MEM; // SZ_ERROR_ARCHIVE |
| 143 | size = (size_t)indexSize; | 158 | size = (size_t)indexSize; |
| 144 | if (size != indexSize) | ||
| 145 | return SZ_ERROR_UNSUPPORTED; | ||
| 146 | buf = (Byte *)ISzAlloc_Alloc(alloc, size); | 159 | buf = (Byte *)ISzAlloc_Alloc(alloc, size); |
| 147 | if (!buf) | 160 | if (!buf) |
| 148 | return SZ_ERROR_MEM; | 161 | return SZ_ERROR_MEM; |
| 149 | res = LookInStream_Read2(stream, buf, size, SZ_ERROR_UNSUPPORTED); | 162 | res = LookInStream_Read2(stream, buf, size, SZ_ERROR_UNSUPPORTED); |
| 150 | if (res == SZ_OK) | 163 | if (res == SZ_OK) |
| 151 | res = Xz_ReadIndex2(p, buf, size, alloc); | 164 | res = Xz_ParseIndex(p, buf, size, alloc); |
| 152 | ISzAlloc_Free(alloc, buf); | 165 | ISzAlloc_Free(alloc, buf); |
| 153 | return res; | 166 | return res; |
| 154 | } | 167 | } |
| 168 | */ | ||
| 155 | 169 | ||
| 156 | static SRes LookInStream_SeekRead_ForArc(ILookInStreamPtr stream, UInt64 offset, void *buf, size_t size) | 170 | static SRes LookInStream_SeekRead_ForArc(ILookInStreamPtr stream, UInt64 offset, void *buf, size_t size) |
| 157 | { | 171 | { |
| @@ -160,84 +174,102 @@ static SRes LookInStream_SeekRead_ForArc(ILookInStreamPtr stream, UInt64 offset, | |||
| 160 | /* return LookInStream_Read2(stream, buf, size, SZ_ERROR_NO_ARCHIVE); */ | 174 | /* return LookInStream_Read2(stream, buf, size, SZ_ERROR_NO_ARCHIVE); */ |
| 161 | } | 175 | } |
| 162 | 176 | ||
| 177 | |||
| 178 | /* | ||
| 179 | in: | ||
| 180 | (*startOffset) is position in (stream) where xz_stream must be finished. | ||
| 181 | out: | ||
| 182 | if returns SZ_OK, then (*startOffset) is position in stream that shows start of xz_stream. | ||
| 183 | */ | ||
| 163 | static SRes Xz_ReadBackward(CXzStream *p, ILookInStreamPtr stream, Int64 *startOffset, ISzAllocPtr alloc) | 184 | static SRes Xz_ReadBackward(CXzStream *p, ILookInStreamPtr stream, Int64 *startOffset, ISzAllocPtr alloc) |
| 164 | { | 185 | { |
| 165 | UInt64 indexSize; | 186 | #define TEMP_BUF_SIZE (1 << 10) |
| 166 | Byte buf[XZ_STREAM_FOOTER_SIZE]; | 187 | UInt32 buf32[TEMP_BUF_SIZE / 4]; |
| 167 | UInt64 pos = (UInt64)*startOffset; | 188 | UInt64 pos = (UInt64)*startOffset; |
| 168 | 189 | ||
| 169 | if ((pos & 3) != 0 || pos < XZ_STREAM_FOOTER_SIZE) | 190 | if ((pos & 3) || pos < XZ_STREAM_FOOTER_SIZE) |
| 170 | return SZ_ERROR_NO_ARCHIVE; | 191 | return SZ_ERROR_NO_ARCHIVE; |
| 171 | |||
| 172 | pos -= XZ_STREAM_FOOTER_SIZE; | 192 | pos -= XZ_STREAM_FOOTER_SIZE; |
| 173 | RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf, XZ_STREAM_FOOTER_SIZE)) | 193 | RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf32, XZ_STREAM_FOOTER_SIZE)) |
| 174 | 194 | ||
| 175 | if (!XZ_FOOTER_SIG_CHECK(buf + 10)) | 195 | if (!XZ_FOOTER_12B_ALIGNED16_SIG_CHECK(buf32)) |
| 176 | { | 196 | { |
| 177 | UInt32 total = 0; | ||
| 178 | pos += XZ_STREAM_FOOTER_SIZE; | 197 | pos += XZ_STREAM_FOOTER_SIZE; |
| 179 | |||
| 180 | for (;;) | 198 | for (;;) |
| 181 | { | 199 | { |
| 182 | size_t i; | 200 | // pos != 0 |
| 183 | #define TEMP_BUF_SIZE (1 << 10) | 201 | // (pos & 3) == 0 |
| 184 | Byte temp[TEMP_BUF_SIZE]; | 202 | size_t i = pos >= TEMP_BUF_SIZE ? TEMP_BUF_SIZE : (size_t)pos; |
| 185 | |||
| 186 | i = (pos > TEMP_BUF_SIZE) ? TEMP_BUF_SIZE : (size_t)pos; | ||
| 187 | pos -= i; | 203 | pos -= i; |
| 188 | RINOK(LookInStream_SeekRead_ForArc(stream, pos, temp, i)) | 204 | RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf32, i)) |
| 189 | total += (UInt32)i; | 205 | i /= 4; |
| 190 | for (; i != 0; i--) | 206 | do |
| 191 | if (temp[i - 1] != 0) | 207 | if (buf32[i - 1] != 0) |
| 192 | break; | 208 | break; |
| 193 | if (i != 0) | 209 | while (--i); |
| 194 | { | 210 | |
| 195 | if ((i & 3) != 0) | 211 | pos += i * 4; |
| 196 | return SZ_ERROR_NO_ARCHIVE; | 212 | #define XZ_STREAM_BACKWARD_READING_PAD_MAX (1 << 16) |
| 197 | pos += i; | 213 | // here we don't support rare case with big padding for xz stream. |
| 198 | break; | 214 | // so we have padding limit for backward reading. |
| 199 | } | 215 | if ((UInt64)*startOffset - pos > XZ_STREAM_BACKWARD_READING_PAD_MAX) |
| 200 | if (pos < XZ_STREAM_FOOTER_SIZE || total > (1 << 16)) | ||
| 201 | return SZ_ERROR_NO_ARCHIVE; | 216 | return SZ_ERROR_NO_ARCHIVE; |
| 217 | if (i) | ||
| 218 | break; | ||
| 202 | } | 219 | } |
| 203 | 220 | // we try to open xz stream after skipping zero padding. | |
| 221 | // ((UInt64)*startOffset == pos) is possible here! | ||
| 204 | if (pos < XZ_STREAM_FOOTER_SIZE) | 222 | if (pos < XZ_STREAM_FOOTER_SIZE) |
| 205 | return SZ_ERROR_NO_ARCHIVE; | 223 | return SZ_ERROR_NO_ARCHIVE; |
| 206 | pos -= XZ_STREAM_FOOTER_SIZE; | 224 | pos -= XZ_STREAM_FOOTER_SIZE; |
| 207 | RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf, XZ_STREAM_FOOTER_SIZE)) | 225 | RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf32, XZ_STREAM_FOOTER_SIZE)) |
| 208 | if (!XZ_FOOTER_SIG_CHECK(buf + 10)) | 226 | if (!XZ_FOOTER_12B_ALIGNED16_SIG_CHECK(buf32)) |
| 209 | return SZ_ERROR_NO_ARCHIVE; | 227 | return SZ_ERROR_NO_ARCHIVE; |
| 210 | } | 228 | } |
| 211 | 229 | ||
| 212 | p->flags = (CXzStreamFlags)GetBe16(buf + 8); | 230 | p->flags = (CXzStreamFlags)GetBe16a(buf32 + 2); |
| 213 | |||
| 214 | if (!XzFlags_IsSupported(p->flags)) | 231 | if (!XzFlags_IsSupported(p->flags)) |
| 215 | return SZ_ERROR_UNSUPPORTED; | 232 | return SZ_ERROR_UNSUPPORTED; |
| 216 | |||
| 217 | { | 233 | { |
| 218 | /* to eliminate GCC 6.3 warning: | 234 | /* to eliminate GCC 6.3 warning: |
| 219 | dereferencing type-punned pointer will break strict-aliasing rules */ | 235 | dereferencing type-punned pointer will break strict-aliasing rules */ |
| 220 | const Byte *buf_ptr = buf; | 236 | const UInt32 *buf_ptr = buf32; |
| 221 | if (GetUi32(buf_ptr) != CrcCalc(buf + 4, 6)) | 237 | if (GetUi32a(buf_ptr) != CrcCalc(buf32 + 1, 6)) |
| 222 | return SZ_ERROR_ARCHIVE; | 238 | return SZ_ERROR_ARCHIVE; |
| 223 | } | 239 | } |
| 224 | |||
| 225 | indexSize = ((UInt64)GetUi32(buf + 4) + 1) << 2; | ||
| 226 | |||
| 227 | if (pos < indexSize) | ||
| 228 | return SZ_ERROR_ARCHIVE; | ||
| 229 | |||
| 230 | pos -= indexSize; | ||
| 231 | RINOK(LookInStream_SeekTo(stream, pos)) | ||
| 232 | RINOK(Xz_ReadIndex(p, stream, indexSize, alloc)) | ||
| 233 | |||
| 234 | { | 240 | { |
| 235 | UInt64 totalSize = Xz_GetPackSize(p); | 241 | const UInt64 indexSize = ((UInt64)GetUi32a(buf32 + 1) + 1) << 2; |
| 236 | if (totalSize == XZ_SIZE_OVERFLOW | 242 | if (pos < indexSize) |
| 237 | || totalSize >= ((UInt64)1 << 63) | ||
| 238 | || pos < totalSize + XZ_STREAM_HEADER_SIZE) | ||
| 239 | return SZ_ERROR_ARCHIVE; | 243 | return SZ_ERROR_ARCHIVE; |
| 240 | pos -= (totalSize + XZ_STREAM_HEADER_SIZE); | 244 | pos -= indexSize; |
| 245 | // v25.00: relaxed indexSize check. We allow big index table. | ||
| 246 | // if (indexSize > ((UInt32)1 << 31)) | ||
| 247 | if (indexSize >= ((size_t)1 << (sizeof(size_t) * 8 - 1))) | ||
| 248 | return SZ_ERROR_MEM; // SZ_ERROR_ARCHIVE | ||
| 249 | RINOK(LookInStream_SeekTo(stream, pos)) | ||
| 250 | // RINOK(Xz_ReadIndex(p, stream, indexSize, alloc)) | ||
| 251 | { | ||
| 252 | SRes res; | ||
| 253 | const size_t size = (size_t)indexSize; | ||
| 254 | // if (size != indexSize) return SZ_ERROR_UNSUPPORTED; | ||
| 255 | Byte *buf = (Byte *)ISzAlloc_Alloc(alloc, size); | ||
| 256 | if (!buf) | ||
| 257 | return SZ_ERROR_MEM; | ||
| 258 | res = LookInStream_Read2(stream, buf, size, SZ_ERROR_UNSUPPORTED); | ||
| 259 | if (res == SZ_OK) | ||
| 260 | res = Xz_ParseIndex(p, buf, size, alloc); | ||
| 261 | ISzAlloc_Free(alloc, buf); | ||
| 262 | RINOK(res) | ||
| 263 | } | ||
| 264 | } | ||
| 265 | { | ||
| 266 | UInt64 total = Xz_GetPackSize(p); | ||
| 267 | if (total == XZ_SIZE_OVERFLOW || total >= ((UInt64)1 << 63)) | ||
| 268 | return SZ_ERROR_ARCHIVE; | ||
| 269 | total += XZ_STREAM_HEADER_SIZE; | ||
| 270 | if (pos < total) | ||
| 271 | return SZ_ERROR_ARCHIVE; | ||
| 272 | pos -= total; | ||
| 241 | RINOK(LookInStream_SeekTo(stream, pos)) | 273 | RINOK(LookInStream_SeekTo(stream, pos)) |
| 242 | *startOffset = (Int64)pos; | 274 | *startOffset = (Int64)pos; |
| 243 | } | 275 | } |
| @@ -246,7 +278,6 @@ static SRes Xz_ReadBackward(CXzStream *p, ILookInStreamPtr stream, Int64 *startO | |||
| 246 | CSecToRead secToRead; | 278 | CSecToRead secToRead; |
| 247 | SecToRead_CreateVTable(&secToRead); | 279 | SecToRead_CreateVTable(&secToRead); |
| 248 | secToRead.realStream = stream; | 280 | secToRead.realStream = stream; |
| 249 | |||
| 250 | RINOK(Xz_ReadHeader(&headerFlags, &secToRead.vt)) | 281 | RINOK(Xz_ReadHeader(&headerFlags, &secToRead.vt)) |
| 251 | return (p->flags == headerFlags) ? SZ_OK : SZ_ERROR_ARCHIVE; | 282 | return (p->flags == headerFlags) ? SZ_OK : SZ_ERROR_ARCHIVE; |
| 252 | } | 283 | } |
| @@ -257,8 +288,7 @@ static SRes Xz_ReadBackward(CXzStream *p, ILookInStreamPtr stream, Int64 *startO | |||
| 257 | 288 | ||
| 258 | void Xzs_Construct(CXzs *p) | 289 | void Xzs_Construct(CXzs *p) |
| 259 | { | 290 | { |
| 260 | p->num = p->numAllocated = 0; | 291 | Xzs_CONSTRUCT(p) |
| 261 | p->streams = 0; | ||
| 262 | } | 292 | } |
| 263 | 293 | ||
| 264 | void Xzs_Free(CXzs *p, ISzAllocPtr alloc) | 294 | void Xzs_Free(CXzs *p, ISzAllocPtr alloc) |
| @@ -268,7 +298,7 @@ void Xzs_Free(CXzs *p, ISzAllocPtr alloc) | |||
| 268 | Xz_Free(&p->streams[i], alloc); | 298 | Xz_Free(&p->streams[i], alloc); |
| 269 | ISzAlloc_Free(alloc, p->streams); | 299 | ISzAlloc_Free(alloc, p->streams); |
| 270 | p->num = p->numAllocated = 0; | 300 | p->num = p->numAllocated = 0; |
| 271 | p->streams = 0; | 301 | p->streams = NULL; |
| 272 | } | 302 | } |
| 273 | 303 | ||
| 274 | UInt64 Xzs_GetNumBlocks(const CXzs *p) | 304 | UInt64 Xzs_GetNumBlocks(const CXzs *p) |
| @@ -307,34 +337,49 @@ UInt64 Xzs_GetPackSize(const CXzs *p) | |||
| 307 | SRes Xzs_ReadBackward(CXzs *p, ILookInStreamPtr stream, Int64 *startOffset, ICompressProgressPtr progress, ISzAllocPtr alloc) | 337 | SRes Xzs_ReadBackward(CXzs *p, ILookInStreamPtr stream, Int64 *startOffset, ICompressProgressPtr progress, ISzAllocPtr alloc) |
| 308 | { | 338 | { |
| 309 | Int64 endOffset = 0; | 339 | Int64 endOffset = 0; |
| 340 | // it's supposed that CXzs object is empty here. | ||
| 341 | // if CXzs object is not empty, it will add new streams to that non-empty object. | ||
| 342 | // Xzs_Free(p, alloc); // it's optional call to empty CXzs object. | ||
| 310 | RINOK(ILookInStream_Seek(stream, &endOffset, SZ_SEEK_END)) | 343 | RINOK(ILookInStream_Seek(stream, &endOffset, SZ_SEEK_END)) |
| 311 | *startOffset = endOffset; | 344 | *startOffset = endOffset; |
| 312 | for (;;) | 345 | for (;;) |
| 313 | { | 346 | { |
| 314 | CXzStream st; | 347 | CXzStream st; |
| 315 | SRes res; | 348 | SRes res; |
| 316 | Xz_Construct(&st); | 349 | Xz_CONSTRUCT(&st) |
| 317 | res = Xz_ReadBackward(&st, stream, startOffset, alloc); | 350 | res = Xz_ReadBackward(&st, stream, startOffset, alloc); |
| 351 | // if (res == SZ_OK), then (*startOffset) is start offset of new stream if | ||
| 352 | // if (res != SZ_OK), then (*startOffset) is unchend or it's expected start offset of stream with error | ||
| 318 | st.startOffset = (UInt64)*startOffset; | 353 | st.startOffset = (UInt64)*startOffset; |
| 319 | RINOK(res) | 354 | // we must store (st) object to array, or we must free (st) local object. |
| 355 | if (res != SZ_OK) | ||
| 356 | { | ||
| 357 | Xz_Free(&st, alloc); | ||
| 358 | return res; | ||
| 359 | } | ||
| 320 | if (p->num == p->numAllocated) | 360 | if (p->num == p->numAllocated) |
| 321 | { | 361 | { |
| 322 | const size_t newNum = p->num + p->num / 4 + 1; | 362 | const size_t newNum = p->num + p->num / 4 + 1; |
| 323 | void *data = ISzAlloc_Alloc(alloc, newNum * sizeof(CXzStream)); | 363 | void *data = ISzAlloc_Alloc(alloc, newNum * sizeof(CXzStream)); |
| 324 | if (!data) | 364 | if (!data) |
| 365 | { | ||
| 366 | Xz_Free(&st, alloc); | ||
| 325 | return SZ_ERROR_MEM; | 367 | return SZ_ERROR_MEM; |
| 368 | } | ||
| 326 | p->numAllocated = newNum; | 369 | p->numAllocated = newNum; |
| 327 | if (p->num != 0) | 370 | if (p->num != 0) |
| 328 | memcpy(data, p->streams, p->num * sizeof(CXzStream)); | 371 | memcpy(data, p->streams, p->num * sizeof(CXzStream)); |
| 329 | ISzAlloc_Free(alloc, p->streams); | 372 | ISzAlloc_Free(alloc, p->streams); |
| 330 | p->streams = (CXzStream *)data; | 373 | p->streams = (CXzStream *)data; |
| 331 | } | 374 | } |
| 375 | // we use direct copying of raw data from local variable (st) to object in array. | ||
| 376 | // so we don't need to call Xz_Free(&st, alloc) after copying and after p->num++ | ||
| 332 | p->streams[p->num++] = st; | 377 | p->streams[p->num++] = st; |
| 333 | if (*startOffset == 0) | 378 | if (*startOffset == 0) |
| 334 | break; | 379 | return SZ_OK; |
| 335 | RINOK(LookInStream_SeekTo(stream, (UInt64)*startOffset)) | 380 | // seek operation is optional: |
| 381 | // RINOK(LookInStream_SeekTo(stream, (UInt64)*startOffset)) | ||
| 336 | if (progress && ICompressProgress_Progress(progress, (UInt64)(endOffset - *startOffset), (UInt64)(Int64)-1) != SZ_OK) | 382 | if (progress && ICompressProgress_Progress(progress, (UInt64)(endOffset - *startOffset), (UInt64)(Int64)-1) != SZ_OK) |
| 337 | return SZ_ERROR_PROGRESS; | 383 | return SZ_ERROR_PROGRESS; |
| 338 | } | 384 | } |
| 339 | return SZ_OK; | ||
| 340 | } | 385 | } |
