@@ -86,13 +86,16 @@ class CompressionFile : public VirtualReadOnlyFile {
8686
8787 // offset 24, 28, 32
8888 uint32_t size = sizeof (HeaderTrailer);
89- uint32_t __padding = 0 ;
89+ // uint32_t __padding = 0;
90+ uint32_t digest = 0 ;
9091 uint64_t flags;
9192
9293 static const uint32_t FLAG_SHIFT_HEADER = 0 ; // 1:header 0:trailer
9394 static const uint32_t FLAG_SHIFT_TYPE = 1 ; // 1:data file, 0:index file
9495 static const uint32_t FLAG_SHIFT_SEALED = 2 ; // 1:YES, 0:NO
95- static const uint32_t FLAG_SHIFT_HEADER_OVERWRITE = 3 ;
96+ static const uint32_t FLAG_SHIFT_HEADER_OVERWRITE = 3 ; // overwrite trailer info to header
97+ static const uint32_t FLAG_SHIFT_CALC_DIGEST = 4 ; // caculate digest for zfile header/trailer and jumptable
98+ static const uint32_t FLAG_SHIFT_IDX_COMP = 5 ; // compress zfile index(jumptable)
9699
97100 uint32_t get_flag_bit (uint32_t shift) const {
98101 return flags & (1 << shift);
@@ -121,6 +124,17 @@ class CompressionFile : public VirtualReadOnlyFile {
121124 bool is_sealed () const {
122125 return get_flag_bit (FLAG_SHIFT_SEALED );
123126 }
127+ bool is_digest_enabled () {
128+ return get_flag_bit (FLAG_SHIFT_CALC_DIGEST );
129+ }
130+ bool is_valid () {
131+ if (!is_digest_enabled ()) return true ;
132+ auto saved_crc = this ->digest ;
133+ this ->digest = 0 ;
134+ DEFER (this ->digest = saved_crc;);
135+ auto crc = crc32::crc32c (this , CompressionFile::HeaderTrailer::SPACE );
136+ return crc == saved_crc;
137+ }
124138 void set_header () {
125139 set_flag_bit (FLAG_SHIFT_HEADER );
126140 }
@@ -143,6 +157,14 @@ class CompressionFile : public VirtualReadOnlyFile {
143157 set_flag_bit (FLAG_SHIFT_HEADER_OVERWRITE );
144158 }
145159
160+ void set_digest_enable () {
161+ set_flag_bit (FLAG_SHIFT_CALC_DIGEST );
162+ }
163+
164+ void set_compress_index () {
165+ set_flag_bit (FLAG_SHIFT_IDX_COMP );
166+ }
167+
146168 void set_compress_option (const CompressOptions &opt) {
147169 this ->opt = opt;
148170 }
@@ -151,7 +173,8 @@ class CompressionFile : public VirtualReadOnlyFile {
151173 uint64_t index_offset; // in bytes
152174 uint64_t index_size; // # of SegmentMappings
153175 uint64_t original_file_size;
154- uint64_t reserved_0;
176+ uint32_t index_crc;
177+ uint32_t reserved_0;
155178 // offset 72
156179 CompressOptions opt;
157180
@@ -181,7 +204,8 @@ class CompressionFile : public VirtualReadOnlyFile {
181204 return deltas.size ();
182205 }
183206
184- int build (const uint32_t *ibuf, size_t n, off_t offset_begin, uint32_t block_size) {
207+ int build (const uint32_t *ibuf, size_t n, off_t offset_begin, uint32_t block_size,
208+ bool enable_crc) {
185209 partial_offset.clear ();
186210 deltas.clear ();
187211 group_size = (uinttype_max + 1 ) / block_size;
@@ -190,7 +214,11 @@ class CompressionFile : public VirtualReadOnlyFile {
190214 auto raw_offset = offset_begin;
191215 partial_offset.push_back (raw_offset);
192216 deltas.push_back (0 );
217+ size_t min_blksize = (enable_crc ? sizeof (uint32_t ) : 0 );
193218 for (ssize_t i = 1 ; i < (ssize_t )n + 1 ; i++) {
219+ if (ibuf[i - 1 ] <= min_blksize) {
220+ LOG_ERRNO_RETURN (EIO , -1 , " unexpected block size(id: `):" , i - 1 , ibuf[i - 1 ]);
221+ }
194222 raw_offset += ibuf[i - 1 ];
195223 if ((i % group_size) == 0 ) {
196224 partial_offset.push_back (raw_offset);
@@ -199,7 +227,7 @@ class CompressionFile : public VirtualReadOnlyFile {
199227 }
200228 if ((uint64_t )deltas[i - 1 ] + ibuf[i - 1 ] >= (uint64_t )uinttype_max) {
201229 LOG_ERROR_RETURN (ERANGE , -1 , " build block[`] length failed `+` > ` (exceed)" ,
202- i- 1 , deltas[i- 1 ], ibuf[i- 1 ], (uint64_t )uinttype_max);
230+ i - 1 , deltas[i - 1 ], ibuf[i - 1 ], (uint64_t )uinttype_max);
203231 }
204232 deltas.push_back (deltas[i - 1 ] + ibuf[i - 1 ]);
205233 }
@@ -260,13 +288,14 @@ class CompressionFile : public VirtualReadOnlyFile {
260288 LOG_WARN (" trim and reload. (idx: `, offset: `, len: `)" , idx, begin_offset, read_size);
261289 int trim_res = m_zfile->m_file ->trim (begin_offset, read_size);
262290 if (trim_res < 0 ) {
263- LOG_ERRNO_RETURN (0 , -1 , " trim block failed. (idx: `, offset: `, len: `)" ,
264- idx, begin_offset, read_size);
291+ LOG_ERRNO_RETURN (0 , -1 , " trim block failed. (idx: `, offset: `, len: `)" , idx,
292+ begin_offset, read_size);
265293 }
266294 auto readn = m_zfile->m_file ->pread (m_buf + m_buf_offset, read_size, begin_offset);
267295 if (readn != (ssize_t )read_size) {
268- LOG_ERRNO_RETURN (0 , -1 , " read compressed blocks failed. (idx: `, offset: `, len: `)" ,
269- idx, begin_offset, read_size);
296+ LOG_ERRNO_RETURN (0 , -1 ,
297+ " read compressed blocks failed. (idx: `, offset: `, len: `)" , idx,
298+ begin_offset, read_size);
270299 }
271300 return 0 ;
272301 }
@@ -351,8 +380,9 @@ class CompressionFile : public VirtualReadOnlyFile {
351380 compressed_size = m_reader->compressed_size ();
352381 if ((size_t )(m_reader->m_buf_offset ) + compressed_size > sizeof (m_buf)) {
353382 m_reader->m_eno = ERANGE ;
354- LOG_ERRNO_RETURN (0 , -1 , " inner buffer offset (`) + compressed size (`) overflow." ,
355- m_reader->m_buf_offset , compressed_size);
383+ LOG_ERRNO_RETURN (0 , -1 ,
384+ " inner buffer offset (`) + compressed size (`) overflow." ,
385+ m_reader->m_buf_offset , compressed_size);
356386 }
357387
358388 if (blk_idx == m_reader->m_begin_idx ) {
@@ -439,15 +469,15 @@ class CompressionFile : public VirtualReadOnlyFile {
439469 if (count <= 0 )
440470 return 0 ;
441471 if (offset + count > m_ht.original_file_size ) {
442- LOG_ERRNO_RETURN (ERANGE , -1 , " pread range exceed (` > `)" ,
443- offset + count, m_ht.original_file_size );
472+ LOG_ERRNO_RETURN (ERANGE , -1 , " pread range exceed (` > `)" , offset + count,
473+ m_ht.original_file_size );
444474 }
445475 ssize_t readn = 0 ; // final will equal to count
446476 unsigned char raw[MAX_READ_SIZE ];
447477 BlockReader br (this , offset, count);
448478 for (auto &block : br) {
449479 if (buf == nullptr ) {
450- // used for prefetch; no copy, no decompress;
480+ // used for prefetch; no copy, no decompress;
451481 readn += block.cp_len ;
452482 continue ;
453483 }
@@ -506,7 +536,7 @@ static int write_header_trailer(IFile *file, bool is_header, bool is_sealed, boo
506536 CompressionFile::HeaderTrailer *pht, off_t offset = -1 );
507537
508538ssize_t compress_data (ICompressor *compressor, const unsigned char *buf, size_t count,
509- unsigned char *dest_buf, size_t dest_len, bool gen_crc) {
539+ unsigned char *dest_buf, size_t dest_len, bool gen_crc) {
510540
511541 ssize_t compressed_len = 0 ;
512542 auto ret = compressor->compress ((const unsigned char *)buf, count, dest_buf, dest_len);
@@ -584,6 +614,7 @@ class ZFileBuilder : public VirtualReadOnlyFile {
584614 LOG_ERRNO_RETURN (0 , -1 , " failed to write index." );
585615 }
586616 auto pht = (CompressionFile::HeaderTrailer *)m_ht;
617+ pht->index_crc = crc32::crc32c (&m_block_len[0 ], index_bytes);
587618 pht->index_offset = index_offset;
588619 pht->index_size = index_size;
589620 pht->original_file_size = raw_data_size;
@@ -674,7 +705,9 @@ bool load_jump_table(IFile *file, CompressionFile::HeaderTrailer *pheader_traile
674705 if (!pht->verify_magic () || !pht->is_header ()) {
675706 LOG_ERROR_RETURN (0 , false , " header magic/type don't match" );
676707 }
677-
708+ if (pht->is_valid () == false ) {
709+ LOG_ERROR_RETURN (0 , false , " digest verification failed." );
710+ }
678711 struct stat stat;
679712 ret = file->fstat (&stat);
680713 if (ret < 0 ) {
@@ -720,12 +753,20 @@ bool load_jump_table(IFile *file, CompressionFile::HeaderTrailer *pheader_traile
720753 if (ret < (ssize_t )index_bytes) {
721754 LOG_ERRNO_RETURN (0 , false , " failed to read index" );
722755 }
756+ if (pht->is_digest_enabled ()) {
757+ LOG_INFO (" check jumptable CRC32 (` expected)" , pht->index_crc );
758+ auto crc = crc32::crc32c (ibuf.get (), index_bytes);
759+ if (crc != pht->index_crc ) {
760+ LOG_ERRNO_RETURN (0 , false , " checksum of jumptable is incorrect" );
761+ }
762+ }
723763 ret = jump_table.build (ibuf.get (), pht->index_size ,
724- CompressionFile::HeaderTrailer::SPACE + pht->opt .dict_size ,
725- pht->opt .block_size );
764+ CompressionFile::HeaderTrailer::SPACE + pht->opt .dict_size ,
765+ pht->opt .block_size , pht-> opt . verify );
726766 if (ret != 0 ) {
727767 LOG_ERRNO_RETURN (0 , false , " failed to build jump table" );
728768 }
769+
729770 if (pheader_trailer)
730771 *pheader_trailer = *pht;
731772 return true ;
@@ -745,8 +786,7 @@ IFile *zfile_open_ro(IFile *file, bool verify, bool ownership) {
745786 auto res = file->fallocate (0 , 0 , -1 );
746787 LOG_ERROR (" failed to load jump table, fallocate result: `" , res);
747788 if (res < 0 ) {
748- LOG_ERRNO_RETURN (0 , nullptr ,
749- " failed to load jump table and failed to evict" );
789+ LOG_ERRNO_RETURN (0 , nullptr , " failed to load jump table and failed to evict" );
750790 }
751791 if (retry--) {
752792 LOG_INFO (" retry loading jump table" );
@@ -787,7 +827,10 @@ static int write_header_trailer(IFile *file, bool is_header, bool is_sealed, boo
787827 if (offset != -1 )
788828 pht->set_header_overwrite ();
789829
790- LOG_INFO (" pht->opt.dict_size: `" , pht->opt .dict_size );
830+ pht->set_digest_enable (); // by default
831+ pht->digest = 0 ;
832+ pht->digest = crc32::crc32c (pht, CompressionFile::HeaderTrailer::SPACE );
833+ LOG_INFO (" save header/trailer with digest: `" , pht->digest );
791834 if (offset == -1 ) {
792835 return (int )file->write (pht, CompressionFile::HeaderTrailer::SPACE );
793836 }
@@ -812,7 +855,6 @@ int zfile_compress(IFile *file, IFile *as, const CompressArgs *args) {
812855 char buf[CompressionFile::HeaderTrailer::SPACE ] = {};
813856 auto pht = new (buf) CompressionFile::HeaderTrailer;
814857 pht->set_compress_option (opt);
815-
816858 LOG_INFO (" write header." );
817859 auto ret = write_header_trailer (as, true , false , true , pht);
818860 if (ret < 0 ) {
@@ -884,6 +926,7 @@ int zfile_compress(IFile *file, IFile *as, const CompressArgs *args) {
884926 if (as->write (&block_len[0 ], index_bytes) != index_bytes) {
885927 LOG_ERRNO_RETURN (0 , -1 , " failed to write index." );
886928 }
929+ pht->index_crc = crc32::crc32c (&block_len[0 ], index_bytes);
887930 pht->index_offset = index_offset;
888931 pht->index_size = index_size;
889932 pht->original_file_size = raw_data_size;
@@ -962,6 +1005,10 @@ int is_zfile(IFile *file) {
9621005 LOG_DEBUG (" file: ` is not a zfile object" , file);
9631006 return 0 ;
9641007 }
1008+ if (!pht->is_valid ()) {
1009+ LOG_ERRNO_RETURN (0 , -1 ,
1010+ " file: ` is a zfile object but verify digest failed." , file);
1011+ }
9651012 LOG_DEBUG (" file: ` is a zfile object" , file);
9661013 return 1 ;
9671014}
0 commit comments