xref: /netbsd-src/external/gpl3/binutils/dist/zlib/contrib/minizip/mztools.c (revision 9573673d78c64ea1eac42d7f2e9521be89932ae5)
1*9573673dSchristos /*
2*9573673dSchristos   Additional tools for Minizip
3*9573673dSchristos   Code: Xavier Roche '2004
4*9573673dSchristos   License: Same as ZLIB (www.gzip.org)
5*9573673dSchristos */
6*9573673dSchristos 
7*9573673dSchristos /* Code */
8*9573673dSchristos #include <stdio.h>
9*9573673dSchristos #include <stdlib.h>
10*9573673dSchristos #include <string.h>
11*9573673dSchristos #include "zlib.h"
12*9573673dSchristos #include "unzip.h"
13*9573673dSchristos 
14*9573673dSchristos #define READ_8(adr)  ((unsigned char)*(adr))
15*9573673dSchristos #define READ_16(adr) ( READ_8(adr) | (READ_8(adr+1) << 8) )
16*9573673dSchristos #define READ_32(adr) ( READ_16(adr) | (READ_16((adr)+2) << 16) )
17*9573673dSchristos 
18*9573673dSchristos #define WRITE_8(buff, n) do { \
19*9573673dSchristos   *((unsigned char*)(buff)) = (unsigned char) ((n) & 0xff); \
20*9573673dSchristos } while(0)
21*9573673dSchristos #define WRITE_16(buff, n) do { \
22*9573673dSchristos   WRITE_8((unsigned char*)(buff), n); \
23*9573673dSchristos   WRITE_8(((unsigned char*)(buff)) + 1, (n) >> 8); \
24*9573673dSchristos } while(0)
25*9573673dSchristos #define WRITE_32(buff, n) do { \
26*9573673dSchristos   WRITE_16((unsigned char*)(buff), (n) & 0xffff); \
27*9573673dSchristos   WRITE_16((unsigned char*)(buff) + 2, (n) >> 16); \
28*9573673dSchristos } while(0)
29*9573673dSchristos 
30*9573673dSchristos extern int ZEXPORT unzRepair(file, fileOut, fileOutTmp, nRecovered, bytesRecovered)
31*9573673dSchristos const char* file;
32*9573673dSchristos const char* fileOut;
33*9573673dSchristos const char* fileOutTmp;
34*9573673dSchristos uLong* nRecovered;
35*9573673dSchristos uLong* bytesRecovered;
36*9573673dSchristos {
37*9573673dSchristos   int err = Z_OK;
38*9573673dSchristos   FILE* fpZip = fopen(file, "rb");
39*9573673dSchristos   FILE* fpOut = fopen(fileOut, "wb");
40*9573673dSchristos   FILE* fpOutCD = fopen(fileOutTmp, "wb");
41*9573673dSchristos   if (fpZip != NULL &&  fpOut != NULL) {
42*9573673dSchristos     int entries = 0;
43*9573673dSchristos     uLong totalBytes = 0;
44*9573673dSchristos     char header[30];
45*9573673dSchristos     char filename[1024];
46*9573673dSchristos     char extra[1024];
47*9573673dSchristos     int offset = 0;
48*9573673dSchristos     int offsetCD = 0;
49*9573673dSchristos     while ( fread(header, 1, 30, fpZip) == 30 ) {
50*9573673dSchristos       int currentOffset = offset;
51*9573673dSchristos 
52*9573673dSchristos       /* File entry */
53*9573673dSchristos       if (READ_32(header) == 0x04034b50) {
54*9573673dSchristos         unsigned int version = READ_16(header + 4);
55*9573673dSchristos         unsigned int gpflag = READ_16(header + 6);
56*9573673dSchristos         unsigned int method = READ_16(header + 8);
57*9573673dSchristos         unsigned int filetime = READ_16(header + 10);
58*9573673dSchristos         unsigned int filedate = READ_16(header + 12);
59*9573673dSchristos         unsigned int crc = READ_32(header + 14); /* crc */
60*9573673dSchristos         unsigned int cpsize = READ_32(header + 18); /* compressed size */
61*9573673dSchristos         unsigned int uncpsize = READ_32(header + 22); /* uncompressed sz */
62*9573673dSchristos         unsigned int fnsize = READ_16(header + 26); /* file name length */
63*9573673dSchristos         unsigned int extsize = READ_16(header + 28); /* extra field length */
64*9573673dSchristos         filename[0] = extra[0] = '\0';
65*9573673dSchristos 
66*9573673dSchristos         /* Header */
67*9573673dSchristos         if (fwrite(header, 1, 30, fpOut) == 30) {
68*9573673dSchristos           offset += 30;
69*9573673dSchristos         } else {
70*9573673dSchristos           err = Z_ERRNO;
71*9573673dSchristos           break;
72*9573673dSchristos         }
73*9573673dSchristos 
74*9573673dSchristos         /* Filename */
75*9573673dSchristos         if (fnsize > 0) {
76*9573673dSchristos           if (fnsize < sizeof(filename)) {
77*9573673dSchristos             if (fread(filename, 1, fnsize, fpZip) == fnsize) {
78*9573673dSchristos                 if (fwrite(filename, 1, fnsize, fpOut) == fnsize) {
79*9573673dSchristos                 offset += fnsize;
80*9573673dSchristos               } else {
81*9573673dSchristos                 err = Z_ERRNO;
82*9573673dSchristos                 break;
83*9573673dSchristos               }
84*9573673dSchristos             } else {
85*9573673dSchristos               err = Z_ERRNO;
86*9573673dSchristos               break;
87*9573673dSchristos             }
88*9573673dSchristos           } else {
89*9573673dSchristos             err = Z_ERRNO;
90*9573673dSchristos             break;
91*9573673dSchristos           }
92*9573673dSchristos         } else {
93*9573673dSchristos           err = Z_STREAM_ERROR;
94*9573673dSchristos           break;
95*9573673dSchristos         }
96*9573673dSchristos 
97*9573673dSchristos         /* Extra field */
98*9573673dSchristos         if (extsize > 0) {
99*9573673dSchristos           if (extsize < sizeof(extra)) {
100*9573673dSchristos             if (fread(extra, 1, extsize, fpZip) == extsize) {
101*9573673dSchristos               if (fwrite(extra, 1, extsize, fpOut) == extsize) {
102*9573673dSchristos                 offset += extsize;
103*9573673dSchristos                 } else {
104*9573673dSchristos                 err = Z_ERRNO;
105*9573673dSchristos                 break;
106*9573673dSchristos               }
107*9573673dSchristos             } else {
108*9573673dSchristos               err = Z_ERRNO;
109*9573673dSchristos               break;
110*9573673dSchristos             }
111*9573673dSchristos           } else {
112*9573673dSchristos             err = Z_ERRNO;
113*9573673dSchristos             break;
114*9573673dSchristos           }
115*9573673dSchristos         }
116*9573673dSchristos 
117*9573673dSchristos         /* Data */
118*9573673dSchristos         {
119*9573673dSchristos           int dataSize = cpsize;
120*9573673dSchristos           if (dataSize == 0) {
121*9573673dSchristos             dataSize = uncpsize;
122*9573673dSchristos           }
123*9573673dSchristos           if (dataSize > 0) {
124*9573673dSchristos             char* data = malloc(dataSize);
125*9573673dSchristos             if (data != NULL) {
126*9573673dSchristos               if ((int)fread(data, 1, dataSize, fpZip) == dataSize) {
127*9573673dSchristos                 if ((int)fwrite(data, 1, dataSize, fpOut) == dataSize) {
128*9573673dSchristos                   offset += dataSize;
129*9573673dSchristos                   totalBytes += dataSize;
130*9573673dSchristos                 } else {
131*9573673dSchristos                   err = Z_ERRNO;
132*9573673dSchristos                 }
133*9573673dSchristos               } else {
134*9573673dSchristos                 err = Z_ERRNO;
135*9573673dSchristos               }
136*9573673dSchristos               free(data);
137*9573673dSchristos               if (err != Z_OK) {
138*9573673dSchristos                 break;
139*9573673dSchristos               }
140*9573673dSchristos             } else {
141*9573673dSchristos               err = Z_MEM_ERROR;
142*9573673dSchristos               break;
143*9573673dSchristos             }
144*9573673dSchristos           }
145*9573673dSchristos         }
146*9573673dSchristos 
147*9573673dSchristos         /* Central directory entry */
148*9573673dSchristos         {
149*9573673dSchristos           char header[46];
150*9573673dSchristos           char* comment = "";
151*9573673dSchristos           int comsize = (int) strlen(comment);
152*9573673dSchristos           WRITE_32(header, 0x02014b50);
153*9573673dSchristos           WRITE_16(header + 4, version);
154*9573673dSchristos           WRITE_16(header + 6, version);
155*9573673dSchristos           WRITE_16(header + 8, gpflag);
156*9573673dSchristos           WRITE_16(header + 10, method);
157*9573673dSchristos           WRITE_16(header + 12, filetime);
158*9573673dSchristos           WRITE_16(header + 14, filedate);
159*9573673dSchristos           WRITE_32(header + 16, crc);
160*9573673dSchristos           WRITE_32(header + 20, cpsize);
161*9573673dSchristos           WRITE_32(header + 24, uncpsize);
162*9573673dSchristos           WRITE_16(header + 28, fnsize);
163*9573673dSchristos           WRITE_16(header + 30, extsize);
164*9573673dSchristos           WRITE_16(header + 32, comsize);
165*9573673dSchristos           WRITE_16(header + 34, 0);     /* disk # */
166*9573673dSchristos           WRITE_16(header + 36, 0);     /* int attrb */
167*9573673dSchristos           WRITE_32(header + 38, 0);     /* ext attrb */
168*9573673dSchristos           WRITE_32(header + 42, currentOffset);
169*9573673dSchristos           /* Header */
170*9573673dSchristos           if (fwrite(header, 1, 46, fpOutCD) == 46) {
171*9573673dSchristos             offsetCD += 46;
172*9573673dSchristos 
173*9573673dSchristos             /* Filename */
174*9573673dSchristos             if (fnsize > 0) {
175*9573673dSchristos               if (fwrite(filename, 1, fnsize, fpOutCD) == fnsize) {
176*9573673dSchristos                 offsetCD += fnsize;
177*9573673dSchristos               } else {
178*9573673dSchristos                 err = Z_ERRNO;
179*9573673dSchristos                 break;
180*9573673dSchristos               }
181*9573673dSchristos             } else {
182*9573673dSchristos               err = Z_STREAM_ERROR;
183*9573673dSchristos               break;
184*9573673dSchristos             }
185*9573673dSchristos 
186*9573673dSchristos             /* Extra field */
187*9573673dSchristos             if (extsize > 0) {
188*9573673dSchristos               if (fwrite(extra, 1, extsize, fpOutCD) == extsize) {
189*9573673dSchristos                 offsetCD += extsize;
190*9573673dSchristos               } else {
191*9573673dSchristos                 err = Z_ERRNO;
192*9573673dSchristos                 break;
193*9573673dSchristos               }
194*9573673dSchristos             }
195*9573673dSchristos 
196*9573673dSchristos             /* Comment field */
197*9573673dSchristos             if (comsize > 0) {
198*9573673dSchristos               if ((int)fwrite(comment, 1, comsize, fpOutCD) == comsize) {
199*9573673dSchristos                 offsetCD += comsize;
200*9573673dSchristos               } else {
201*9573673dSchristos                 err = Z_ERRNO;
202*9573673dSchristos                 break;
203*9573673dSchristos               }
204*9573673dSchristos             }
205*9573673dSchristos 
206*9573673dSchristos 
207*9573673dSchristos           } else {
208*9573673dSchristos             err = Z_ERRNO;
209*9573673dSchristos             break;
210*9573673dSchristos           }
211*9573673dSchristos         }
212*9573673dSchristos 
213*9573673dSchristos         /* Success */
214*9573673dSchristos         entries++;
215*9573673dSchristos 
216*9573673dSchristos       } else {
217*9573673dSchristos         break;
218*9573673dSchristos       }
219*9573673dSchristos     }
220*9573673dSchristos 
221*9573673dSchristos     /* Final central directory  */
222*9573673dSchristos     {
223*9573673dSchristos       int entriesZip = entries;
224*9573673dSchristos       char header[22];
225*9573673dSchristos       char* comment = ""; // "ZIP File recovered by zlib/minizip/mztools";
226*9573673dSchristos       int comsize = (int) strlen(comment);
227*9573673dSchristos       if (entriesZip > 0xffff) {
228*9573673dSchristos         entriesZip = 0xffff;
229*9573673dSchristos       }
230*9573673dSchristos       WRITE_32(header, 0x06054b50);
231*9573673dSchristos       WRITE_16(header + 4, 0);    /* disk # */
232*9573673dSchristos       WRITE_16(header + 6, 0);    /* disk # */
233*9573673dSchristos       WRITE_16(header + 8, entriesZip);   /* hack */
234*9573673dSchristos       WRITE_16(header + 10, entriesZip);  /* hack */
235*9573673dSchristos       WRITE_32(header + 12, offsetCD);    /* size of CD */
236*9573673dSchristos       WRITE_32(header + 16, offset);      /* offset to CD */
237*9573673dSchristos       WRITE_16(header + 20, comsize);     /* comment */
238*9573673dSchristos 
239*9573673dSchristos       /* Header */
240*9573673dSchristos       if (fwrite(header, 1, 22, fpOutCD) == 22) {
241*9573673dSchristos 
242*9573673dSchristos         /* Comment field */
243*9573673dSchristos         if (comsize > 0) {
244*9573673dSchristos           if ((int)fwrite(comment, 1, comsize, fpOutCD) != comsize) {
245*9573673dSchristos             err = Z_ERRNO;
246*9573673dSchristos           }
247*9573673dSchristos         }
248*9573673dSchristos 
249*9573673dSchristos       } else {
250*9573673dSchristos         err = Z_ERRNO;
251*9573673dSchristos       }
252*9573673dSchristos     }
253*9573673dSchristos 
254*9573673dSchristos     /* Final merge (file + central directory) */
255*9573673dSchristos     fclose(fpOutCD);
256*9573673dSchristos     if (err == Z_OK) {
257*9573673dSchristos       fpOutCD = fopen(fileOutTmp, "rb");
258*9573673dSchristos       if (fpOutCD != NULL) {
259*9573673dSchristos         int nRead;
260*9573673dSchristos         char buffer[8192];
261*9573673dSchristos         while ( (nRead = (int)fread(buffer, 1, sizeof(buffer), fpOutCD)) > 0) {
262*9573673dSchristos           if ((int)fwrite(buffer, 1, nRead, fpOut) != nRead) {
263*9573673dSchristos             err = Z_ERRNO;
264*9573673dSchristos             break;
265*9573673dSchristos           }
266*9573673dSchristos         }
267*9573673dSchristos         fclose(fpOutCD);
268*9573673dSchristos       }
269*9573673dSchristos     }
270*9573673dSchristos 
271*9573673dSchristos     /* Close */
272*9573673dSchristos     fclose(fpZip);
273*9573673dSchristos     fclose(fpOut);
274*9573673dSchristos 
275*9573673dSchristos     /* Wipe temporary file */
276*9573673dSchristos     (void)remove(fileOutTmp);
277*9573673dSchristos 
278*9573673dSchristos     /* Number of recovered entries */
279*9573673dSchristos     if (err == Z_OK) {
280*9573673dSchristos       if (nRecovered != NULL) {
281*9573673dSchristos         *nRecovered = entries;
282*9573673dSchristos       }
283*9573673dSchristos       if (bytesRecovered != NULL) {
284*9573673dSchristos         *bytesRecovered = totalBytes;
285*9573673dSchristos       }
286*9573673dSchristos     }
287*9573673dSchristos   } else {
288*9573673dSchristos     err = Z_STREAM_ERROR;
289*9573673dSchristos   }
290*9573673dSchristos   return err;
291*9573673dSchristos }
292