@@ -60,6 +60,43 @@ static int otezip_mkstemp(const char *prefix) {
6060#include "../include/otezip/zstream.h"
6161#include "time.inc.c"
6262
63+ struct otezip_entry {
64+ char * name ;
65+ uint32_t local_hdr_ofs ;
66+ uint32_t comp_size ;
67+ uint32_t uncomp_size ;
68+ uint16_t method ;
69+ uint32_t crc32 ;
70+ uint16_t file_time ;
71+ uint16_t file_date ;
72+ uint32_t external_attr ;
73+ const void * pending_buf ;
74+ zip_uint64_t pending_len ;
75+ int pending_freep ;
76+ int dirty ;
77+ };
78+
79+ struct zip {
80+ FILE * fp ;
81+ struct otezip_entry * entries ;
82+ zip_uint64_t n_entries ;
83+ int mode ;
84+ zip_uint64_t next_index ;
85+ uint16_t default_method ;
86+ };
87+
88+ struct zip_file {
89+ uint8_t * data ;
90+ uint32_t size ;
91+ zip_uint64_t pos ;
92+ };
93+
94+ struct zip_source {
95+ const void * buf ;
96+ zip_uint64_t len ;
97+ int freep ;
98+ };
99+
63100#if defined(_WIN32 ) || defined(_WIN64 )
64101/* Ensure we have thread-safe fallback for localtime on Windows builds */
65102#include <time.h>
@@ -106,6 +143,10 @@ static uint32_t otezip_write_local_header(FILE *fp, const char *name, uint32_t c
106143static uint32_t otezip_write_central_header (FILE * fp , const char * name , uint32_t comp_method , uint32_t comp_size , uint32_t uncomp_size , uint32_t crc32 , uint32_t local_header_offset , uint16_t file_time , uint16_t file_date , uint32_t external_attr );
107144static void otezip_write_end_of_central_directory (FILE * fp , uint32_t num_entries , uint32_t central_dir_size , uint32_t central_dir_offset );
108145static int otezip_finalize_archive (zip_t * za );
146+ static int otezip_is_valid (const zip_t * za );
147+ static int otezip_compress_data (uint8_t * in_buf , size_t in_size , uint8_t * * out_buf , uint32_t * out_size , uint16_t * method );
148+ static void otezip_discard_pending_source (struct otezip_entry * e );
149+ static int otezip_materialize_entry (zip_t * za , struct otezip_entry * e );
109150
110151/* Helper function to get compression method ID from string name.
111152 * Returns the OTEZIP_METHOD_* value or -1 if invalid/not supported. */
@@ -683,6 +724,53 @@ static int otezip_extract_entry(zip_t *za, struct otezip_entry *e, uint8_t **out
683724 return 0 ;
684725}
685726
727+ static void otezip_discard_pending_source (struct otezip_entry * e ) {
728+ if (!e ) {
729+ return ;
730+ }
731+ if (e -> pending_freep && e -> pending_buf ) {
732+ free ((void * )e -> pending_buf );
733+ }
734+ e -> pending_buf = NULL ;
735+ e -> pending_len = 0 ;
736+ e -> pending_freep = 0 ;
737+ e -> dirty = 0 ;
738+ }
739+
740+ static int otezip_materialize_entry (zip_t * za , struct otezip_entry * e ) {
741+ if (!otezip_is_valid (za ) || !e || !e -> dirty ) {
742+ return 0 ;
743+ }
744+ if (e -> pending_len > OTEZIP_MAX_PAYLOAD || e -> pending_len > (zip_uint64_t )UINT32_MAX ) {
745+ return -1 ;
746+ }
747+
748+ long current_pos = ftell (za -> fp );
749+ if (current_pos < 0 || (uint64_t )current_pos > (uint64_t )UINT32_MAX ) {
750+ return -1 ;
751+ }
752+ e -> local_hdr_ofs = (uint32_t )current_pos ;
753+ e -> uncomp_size = (uint32_t )e -> pending_len ;
754+ e -> crc32 = otezip_crc32 (0 , e -> pending_buf , e -> pending_len );
755+
756+ uint8_t * comp_buf = NULL ;
757+ uint32_t comp_size = 0 ;
758+ if (otezip_compress_data ((uint8_t * )e -> pending_buf , e -> pending_len , & comp_buf , & comp_size , & e -> method ) != 0 ) {
759+ return -1 ;
760+ }
761+ if ((uint64_t )comp_size > OTEZIP_MAX_PAYLOAD ) {
762+ free (comp_buf );
763+ return -1 ;
764+ }
765+ e -> comp_size = comp_size ;
766+
767+ otezip_write_local_header (za -> fp , e -> name , e -> method , e -> comp_size , e -> uncomp_size , e -> crc32 );
768+ fwrite (comp_buf , 1 , comp_size , za -> fp );
769+ free (comp_buf );
770+ otezip_discard_pending_source (e );
771+ return 0 ;
772+ }
773+
686774/* Helper function to validate archive state */
687775static int otezip_is_valid (const zip_t * za ) {
688776 return (za != NULL && za -> fp != NULL );
@@ -1128,50 +1216,10 @@ zip_int64_t zip_file_add(zip_t *za, const char *name, zip_source_t *src, zip_fla
11281216
11291217 /* Set default permissions: 0644 for files */
11301218 e -> external_attr = 0100644 << 16 ; /* S_IFREG | 0644 << 16 */
1131-
1132- /* Get current position for local header offset */
1133- long current_pos = ftell (za -> fp );
1134- if (current_pos < 0 ) {
1135- free (e -> name );
1136- return -1 ;
1137- }
1138-
1139- /* Ensure local header offset fits into ZIP 32-bit field */
1140- if ((uint64_t )current_pos > (uint64_t )UINT32_MAX ) {
1141- free (e -> name );
1142- return -1 ;
1143- }
1144- e -> local_hdr_ofs = (uint32_t )current_pos ;
1145-
1146- /* Compress the data using the selected method */
1147- uint8_t * comp_buf = NULL ;
1148- uint32_t comp_size = 0 ;
1149-
1150- /* Compress the data using the selected method */
1151- if (otezip_compress_data ((uint8_t * )src -> buf , src -> len , & comp_buf , & comp_size , & e -> method ) != 0 ) {
1152- free (e -> name );
1153- return -1 ;
1154- }
1155-
1156- /* Validate compressed size too */
1157- if ((uint64_t )comp_size > OTEZIP_MAX_PAYLOAD ) {
1158- free (e -> name );
1159- free (comp_buf );
1160- return -1 ;
1161- }
1162- e -> comp_size = comp_size ;
1163-
1164- /* Write local file header */
1165- otezip_write_local_header (za -> fp , e -> name , e -> method , e -> comp_size , e -> uncomp_size , e -> crc32 );
1166-
1167- /* Write compressed data */
1168- fwrite (comp_buf , 1 , comp_size , za -> fp );
1169- free (comp_buf );
1170-
1171- /* Free source data if requested */
1172- if (src -> freep ) {
1173- free ((void * )src -> buf );
1174- }
1219+ e -> pending_buf = src -> buf ;
1220+ e -> pending_len = src -> len ;
1221+ e -> pending_freep = src -> freep ;
1222+ e -> dirty = 1 ;
11751223 free (src );
11761224
11771225 /* Increment entry count */
@@ -1231,8 +1279,19 @@ int zip_set_file_compression(zip_t *za, zip_uint64_t index, zip_int32_t comp, zi
12311279 return -1 ;
12321280 }
12331281
1234- /* Set the method for next files that will be added */
1235- za -> entries [index ].method = (uint16_t )comp ;
1282+ struct otezip_entry * e = & za -> entries [index ];
1283+ if (!e -> dirty ) {
1284+ uint8_t * buf = NULL ;
1285+ uint32_t sz = 0 ;
1286+ if (otezip_extract_entry (za , e , & buf , & sz ) != 0 ) {
1287+ return -1 ;
1288+ }
1289+ e -> pending_buf = buf ;
1290+ e -> pending_len = sz ;
1291+ e -> pending_freep = 1 ;
1292+ e -> dirty = 1 ;
1293+ }
1294+ e -> method = (uint16_t )comp ;
12361295 return 0 ;
12371296}
12381297
@@ -1241,6 +1300,14 @@ static int otezip_finalize_archive(zip_t *za) {
12411300 if (!otezip_is_valid (za ) || !za -> fp || za -> mode != 1 ) {
12421301 return -1 ;
12431302 }
1303+ if (fseek (za -> fp , 0 , SEEK_END ) != 0 ) {
1304+ return -1 ;
1305+ }
1306+ for (zip_uint64_t i = 0 ; i < za -> n_entries ; i ++ ) {
1307+ if (otezip_materialize_entry (za , & za -> entries [i ]) != 0 ) {
1308+ return -1 ;
1309+ }
1310+ }
12441311 /* Get offset for central directory */
12451312 long cd_offset = ftell (za -> fp );
12461313 if (cd_offset < 0 ) {
@@ -1276,22 +1343,24 @@ int zip_close(zip_t *za) {
12761343 return -1 ;
12771344 }
12781345 /* Finalize archive if in write mode */
1346+ int rc = 0 ;
12791347 if (za -> mode == 1 ) {
1280- otezip_finalize_archive (za );
1348+ rc = otezip_finalize_archive (za );
12811349 }
12821350
12831351 if (za -> fp ) {
12841352 fclose (za -> fp );
12851353 }
12861354 zip_uint64_t i ;
12871355 for (i = 0 ; i < za -> n_entries ; i ++ ) {
1356+ otezip_discard_pending_source (& za -> entries [i ]);
12881357 if (za -> entries [i ].name ) {
12891358 free (za -> entries [i ].name );
12901359 }
12911360 }
12921361 free (za -> entries );
12931362 free (za );
1294- return 0 ;
1363+ return rc ;
12951364}
12961365
12971366zip_uint64_t zip_get_num_files (zip_t * za ) {
@@ -1319,7 +1388,17 @@ zip_file_t *zip_fopen_index(zip_t *za, zip_uint64_t index, zip_flags_t flags) {
13191388 }
13201389 uint8_t * buf = NULL ;
13211390 uint32_t sz = 0 ;
1322- if (otezip_extract_entry (za , & za -> entries [index ], & buf , & sz ) != 0 ) {
1391+ struct otezip_entry * e = & za -> entries [index ];
1392+ if (e -> dirty ) {
1393+ sz = (uint32_t )e -> pending_len ;
1394+ buf = (uint8_t * )malloc (sz ? sz : 1u );
1395+ if (!buf ) {
1396+ return NULL ;
1397+ }
1398+ if (sz > 0 ) {
1399+ memcpy (buf , e -> pending_buf , sz );
1400+ }
1401+ } else if (otezip_extract_entry (za , e , & buf , & sz ) != 0 ) {
13231402 return NULL ;
13241403 }
13251404 zip_file_t * zf = (zip_file_t * )malloc (sizeof (zip_file_t ));
@@ -1603,6 +1682,18 @@ zip_source_t *zip_source_buffer_create(const void *data, zip_uint64_t len, int f
16031682 return zip_source_buffer (NULL , data , len , freep );
16041683}
16051684
1685+ int zip_file_get_external_attributes (zip_t * za , zip_uint64_t index , zip_flags_t flags , zip_uint8_t * opsys , zip_uint32_t * attributesp ) {
1686+ (void )flags ;
1687+ if (!otezip_is_valid (za ) || index >= za -> n_entries || !attributesp ) {
1688+ return -1 ;
1689+ }
1690+ if (opsys ) {
1691+ * opsys = 3 ;
1692+ }
1693+ * attributesp = za -> entries [index ].external_attr ;
1694+ return 0 ;
1695+ }
1696+
16061697void zip_source_free (zip_source_t * src ) {
16071698 if (!src ) {
16081699 return ;
@@ -1622,30 +1713,19 @@ static int otezip_replace_entry_data(zip_t *za, zip_uint64_t index, zip_source_t
16221713 return -1 ;
16231714 }
16241715 struct otezip_entry * e = & za -> entries [index ];
1716+ if ((uint64_t )src -> len > OTEZIP_MAX_PAYLOAD || (uint64_t )src -> len > (uint64_t )UINT32_MAX ) {
1717+ return -1 ;
1718+ }
1719+ otezip_discard_pending_source (e );
16251720 /* Update entry with new source data */
16261721 e -> uncomp_size = (uint32_t )src -> len ;
16271722 e -> crc32 = otezip_crc32 (0 , src -> buf , src -> len );
1628- /* Compress the data using the selected method */
1629- uint8_t * comp_buf = NULL ;
1630- uint32_t comp_size = 0 ;
1631- if (otezip_compress_data ((uint8_t * )src -> buf , src -> len , & comp_buf , & comp_size , & e -> method ) != 0 ) {
1632- return -1 ;
1633- }
1634- if ((uint64_t )comp_size > OTEZIP_MAX_PAYLOAD ) {
1635- free (comp_buf );
1636- return -1 ;
1637- }
1638- e -> comp_size = comp_size ;
1639- /* Write the updated entry */
1640- long current_pos = ftell (za -> fp );
1641- if (current_pos < 0 ) {
1642- free (comp_buf );
1643- return -1 ;
1644- }
1645- e -> local_hdr_ofs = (uint32_t )current_pos ;
1646- otezip_write_local_header (za -> fp , e -> name , e -> method , e -> comp_size , e -> uncomp_size , e -> crc32 );
1647- fwrite (comp_buf , 1 , comp_size , za -> fp );
1648- free (comp_buf );
1723+ otezip_get_dostime (& e -> file_time , & e -> file_date );
1724+ e -> pending_buf = src -> buf ;
1725+ e -> pending_len = src -> len ;
1726+ e -> pending_freep = src -> freep ;
1727+ e -> dirty = 1 ;
1728+ free (src );
16491729 return 0 ;
16501730}
16511731
0 commit comments