6b853b85cc903cdc4bdaf98e171b8aa97bbd7070
[platform/upstream/libzip.git] / regress / ziptool_regress.c
1 #include "zip.h"
2
3 #include <sys/stat.h>
4
5 #define ZIP_MIN(a, b) ((a) < (b) ? (a) : (b))
6
7 #define FOR_REGRESS
8
9 typedef enum { SOURCE_TYPE_NONE, SOURCE_TYPE_IN_MEMORY, SOURCE_TYPE_HOLE } source_type_t;
10
11 source_type_t source_type = SOURCE_TYPE_NONE;
12 zip_uint64_t fragment_size = 0;
13
14 static int add_nul(char *argv[]);
15 static int cancel(char *argv[]);
16 static int unchange_one(char *argv[]);
17 static int unchange_all(char *argv[]);
18 static int zin_close(char *argv[]);
19
20 #define OPTIONS_REGRESS "F:Hm"
21
22 #define USAGE_REGRESS " [-Hm] [-F fragment-size]"
23
24 #define GETOPT_REGRESS                              \
25     case 'H':                                       \
26         source_type = SOURCE_TYPE_HOLE;             \
27         break;                                      \
28     case 'm':                                       \
29         source_type = SOURCE_TYPE_IN_MEMORY;        \
30         break;                                      \
31     case 'F':                                       \
32         fragment_size = strtoull(optarg, NULL, 10); \
33         break;
34
35 /* clang-format off */
36
37 #define DISPATCH_REGRESS \
38     {"add_nul", 2, "name length", "add NUL bytes", add_nul}, \
39     {"cancel", 1, "limit", "cancel writing archive when limit% have been written (calls print_progress)", cancel}, \
40     {"unchange", 1, "index", "revert changes for entry", unchange_one}, \
41     {"unchange_all", 0, "", "revert all changes", unchange_all}, \
42     { "zin_close", 1, "index", "close input zip_source (for internal tests)", zin_close }
43
44 /* clang-format on */
45
46
47 zip_t *ziptool_open(const char *archive, int flags, zip_error_t *error, zip_uint64_t offset, zip_uint64_t len);
48
49
50 #include "ziptool.c"
51
52
53 zip_source_t *memory_src = NULL;
54
55 zip_source_t *source_hole_create(const char *, int flags, zip_error_t *);
56
57 static zip_t *read_to_memory(const char *archive, int flags, zip_error_t *error, zip_source_t **srcp);
58 static zip_source_t *source_nul(zip_t *za, zip_uint64_t length);
59
60
61 static int
62 add_nul(char *argv[]) {
63     zip_source_t *zs;
64     zip_uint64_t length = strtoull(argv[1], NULL, 10);
65
66     if ((zs = source_nul(za, length)) == NULL) {
67         fprintf(stderr, "can't create zip_source for length: %s\n", zip_strerror(za));
68         return -1;
69     }
70
71     if (zip_add(za, argv[0], zs) == -1) {
72         zip_source_free(zs);
73         fprintf(stderr, "can't add file '%s': %s\n", argv[0], zip_strerror(za));
74         return -1;
75     }
76     return 0;
77 }
78
79 static int
80 unchange_all(char *argv[]) {
81     if (zip_unchange_all(za) < 0) {
82         fprintf(stderr, "can't revert changes to archive: %s\n", zip_strerror(za));
83         return -1;
84     }
85     return 0;
86 }
87
88
89 static int
90 unchange_one(char *argv[]) {
91     zip_uint64_t idx;
92
93     idx = strtoull(argv[0], NULL, 10);
94
95     if (zip_unchange(za, idx) < 0) {
96         fprintf(stderr, "can't revert changes for entry %" PRIu64 ": %s", idx, zip_strerror(za));
97         return -1;
98     }
99
100     return 0;
101 }
102
103
104 static int
105 cancel_callback(zip_t *archive, void *ud) {
106     if (progress_userdata.percentage >= progress_userdata.limit) {
107         return -1;
108     }
109     return 0;
110 }
111
112 static int
113 cancel(char *argv[]) {
114     zip_int64_t percent;
115     percent = strtoll(argv[0], NULL, 10);
116     if (percent > 100 || percent < 0) {
117         fprintf(stderr, "invalid percentage '%" PRId64 "' for cancel (valid: 0 <= x <= 100)\n", percent);
118         return -1;
119     }
120     progress_userdata.limit = ((double)percent) / 100;
121
122     zip_register_cancel_callback_with_state(za, cancel_callback, NULL, NULL);
123
124     /* needs the percentage updates from print_progress */
125     print_progress(argv);
126     return 0;
127 }
128
129 static int
130 zin_close(char *argv[]) {
131     zip_uint64_t idx;
132
133     idx = strtoull(argv[0], NULL, 10);
134     if (idx >= z_in_count) {
135         fprintf(stderr, "invalid argument '%" PRIu64 "', only %u zip sources open\n", idx, z_in_count);
136         return -1;
137     }
138     if (zip_close(z_in[idx]) < 0) {
139         fprintf(stderr, "can't close source archive: %s\n", zip_strerror(z_in[idx]));
140         return -1;
141     }
142     z_in[idx] = z_in[z_in_count];
143     z_in_count--;
144
145     return 0;
146 }
147
148
149 static zip_t *
150 read_hole(const char *archive, int flags, zip_error_t *error) {
151     zip_source_t *src = NULL;
152     zip_t *zs = NULL;
153
154     if (strcmp(archive, "/dev/stdin") == 0) {
155         zip_error_set(error, ZIP_ER_OPNOTSUPP, 0);
156         return NULL;
157     }
158
159     if ((src = source_hole_create(archive, flags, error)) == NULL || (zs = zip_open_from_source(src, flags, error)) == NULL) {
160         zip_source_free(src);
161     }
162
163     return zs;
164 }
165
166
167 static zip_t *
168 read_to_memory(const char *archive, int flags, zip_error_t *error, zip_source_t **srcp) {
169     zip_source_t *src;
170     zip_t *zb;
171     FILE *fp;
172
173     if (strcmp(archive, "/dev/stdin") == 0) {
174         zip_error_set(error, ZIP_ER_OPNOTSUPP, 0);
175         return NULL;
176     }
177
178     if ((fp = fopen(archive, "rb")) == NULL) {
179         if (errno == ENOENT) {
180             src = zip_source_buffer_create(NULL, 0, 0, error);
181         }
182         else {
183             zip_error_set(error, ZIP_ER_OPEN, errno);
184             return NULL;
185         }
186     }
187     else {
188         struct stat st;
189
190         if (fstat(fileno(fp), &st) < 0) {
191             fclose(fp);
192             zip_error_set(error, ZIP_ER_OPEN, errno);
193             return NULL;
194         }
195         if (fragment_size == 0) {
196             char *buf;
197             if ((buf = malloc((size_t)st.st_size)) == NULL) {
198                 fclose(fp);
199                 zip_error_set(error, ZIP_ER_MEMORY, 0);
200                 return NULL;
201             }
202             if (fread(buf, (size_t)st.st_size, 1, fp) < 1) {
203                 free(buf);
204                 fclose(fp);
205                 zip_error_set(error, ZIP_ER_READ, errno);
206                 return NULL;
207             }
208             src = zip_source_buffer_create(buf, (zip_uint64_t)st.st_size, 1, error);
209             if (src == NULL) {
210                 free(buf);
211             }
212         }
213         else {
214             zip_uint64_t nfragments, i, left;
215             zip_buffer_fragment_t *fragments;
216
217             nfragments = ((size_t)st.st_size + fragment_size - 1) / fragment_size;
218             if ((fragments = malloc(sizeof(fragments[0]) * nfragments)) == NULL) {
219                 fclose(fp);
220                 zip_error_set(error, ZIP_ER_MEMORY, 0);
221                 return NULL;
222             }
223             for (i = 0; i < nfragments; i++) {
224                 left = ZIP_MIN(fragment_size, (size_t)st.st_size - i * fragment_size);
225                 if ((fragments[i].data = malloc(left)) == NULL) {
226 #ifndef __clang_analyzer__
227                     /* fragments is initialized up to i - 1*/
228                     while (--i > 0) {
229                         free(fragments[i].data);
230                     }
231 #endif
232                     free(fragments);
233                     fclose(fp);
234                     zip_error_set(error, ZIP_ER_MEMORY, 0);
235                     return NULL;
236                 }
237                 fragments[i].length = left;
238                 if (fread(fragments[i].data, left, 1, fp) < 1) {
239 #ifndef __clang_analyzer__
240                     /* fragments is initialized up to i - 1*/
241                     while (--i > 0) {
242                         free(fragments[i].data);
243                     }
244 #endif
245                     free(fragments);
246                     fclose(fp);
247                     zip_error_set(error, ZIP_ER_READ, errno);
248                     return NULL;
249                 }
250             }
251             src = zip_source_buffer_fragment_create(fragments, nfragments, 1, error);
252             if (src == NULL) {
253                 for (i = 0; i < nfragments; i++) {
254                     free(fragments[i].data);
255                 }
256                 free(fragments);
257                 fclose(fp);
258                 return NULL;
259             }
260             free(fragments);
261         }
262         fclose(fp);
263     }
264     if (src == NULL) {
265         return NULL;
266     }
267     zb = zip_open_from_source(src, flags, error);
268     if (zb == NULL) {
269         zip_source_free(src);
270         return NULL;
271     }
272     zip_source_keep(src);
273     *srcp = src;
274     return zb;
275 }
276
277
278 typedef struct source_nul {
279     zip_error_t error;
280     zip_uint64_t length;
281     zip_uint64_t offset;
282 } source_nul_t;
283
284 static zip_int64_t
285 source_nul_cb(void *ud, void *data, zip_uint64_t length, zip_source_cmd_t command) {
286     source_nul_t *ctx = (source_nul_t *)ud;
287
288     switch (command) {
289     case ZIP_SOURCE_CLOSE:
290         return 0;
291
292     case ZIP_SOURCE_ERROR:
293         return zip_error_to_data(&ctx->error, data, length);
294
295     case ZIP_SOURCE_FREE:
296         free(ctx);
297         return 0;
298
299     case ZIP_SOURCE_OPEN:
300         ctx->offset = 0;
301         return 0;
302
303     case ZIP_SOURCE_READ:
304         if (length > ZIP_INT64_MAX) {
305             zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
306             return -1;
307         }
308
309         if (length > ctx->length - ctx->offset) {
310             length = ctx->length - ctx->offset;
311         }
312
313         memset(data, 0, length);
314         ctx->offset += length;
315         return (zip_int64_t)length;
316
317     case ZIP_SOURCE_STAT: {
318         zip_stat_t *st = ZIP_SOURCE_GET_ARGS(zip_stat_t, data, length, &ctx->error);
319
320         if (st == NULL) {
321             return -1;
322         }
323
324         st->valid |= ZIP_STAT_SIZE;
325         st->size = ctx->length;
326
327         return 0;
328     }
329
330     case ZIP_SOURCE_SUPPORTS:
331         return zip_source_make_command_bitmap(ZIP_SOURCE_CLOSE, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_STAT, -1);
332
333     default:
334         zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
335         return -1;
336     }
337 }
338
339 static zip_source_t *
340 source_nul(zip_t *zs, zip_uint64_t length) {
341     source_nul_t *ctx;
342     zip_source_t *src;
343
344     if ((ctx = (source_nul_t *)malloc(sizeof(*ctx))) == NULL) {
345         zip_error_set(zip_get_error(zs), ZIP_ER_MEMORY, 0);
346         return NULL;
347     }
348
349     zip_error_init(&ctx->error);
350     ctx->length = length;
351     ctx->offset = 0;
352
353     if ((src = zip_source_function(zs, source_nul_cb, ctx)) == NULL) {
354         free(ctx);
355         return NULL;
356     }
357
358     return src;
359 }
360
361
362 static int
363 write_memory_src_to_file(const char *archive, zip_source_t *src) {
364     zip_stat_t zst;
365     char *buf;
366     FILE *fp;
367
368     if (zip_source_stat(src, &zst) < 0) {
369         fprintf(stderr, "zip_source_stat on buffer failed: %s\n", zip_error_strerror(zip_source_error(src)));
370         return -1;
371     }
372     if (zip_source_open(src) < 0) {
373         if (zip_error_code_zip(zip_source_error(src)) == ZIP_ER_DELETED) {
374             if (unlink(archive) < 0 && errno != ENOENT) {
375                 fprintf(stderr, "unlink failed: %s\n", strerror(errno));
376                 return -1;
377             }
378             return 0;
379         }
380         fprintf(stderr, "zip_source_open on buffer failed: %s\n", zip_error_strerror(zip_source_error(src)));
381         return -1;
382     }
383     if ((buf = malloc(zst.size)) == NULL) {
384         fprintf(stderr, "malloc failed: %s\n", strerror(errno));
385         zip_source_close(src);
386         return -1;
387     }
388     if (zip_source_read(src, buf, zst.size) < (zip_int64_t)zst.size) {
389         fprintf(stderr, "zip_source_read on buffer failed: %s\n", zip_error_strerror(zip_source_error(src)));
390         zip_source_close(src);
391         free(buf);
392         return -1;
393     }
394     zip_source_close(src);
395     if ((fp = fopen(archive, "wb")) == NULL) {
396         fprintf(stderr, "fopen failed: %s\n", strerror(errno));
397         free(buf);
398         return -1;
399     }
400     if (fwrite(buf, zst.size, 1, fp) < 1) {
401         fprintf(stderr, "fwrite failed: %s\n", strerror(errno));
402         free(buf);
403         fclose(fp);
404         return -1;
405     }
406     free(buf);
407     if (fclose(fp) != 0) {
408         fprintf(stderr, "fclose failed: %s\n", strerror(errno));
409         return -1;
410     }
411     return 0;
412 }
413
414
415 zip_t *
416 ziptool_open(const char *archive, int flags, zip_error_t *error, zip_uint64_t offset, zip_uint64_t len) {
417     switch (source_type) {
418     case SOURCE_TYPE_NONE:
419         za = read_from_file(archive, flags, error, offset, len);
420         break;
421
422     case SOURCE_TYPE_IN_MEMORY:
423         za = read_to_memory(archive, flags, error, &memory_src);
424         break;
425
426     case SOURCE_TYPE_HOLE:
427         za = read_hole(archive, flags, error);
428         break;
429     }
430
431     return za;
432 }
433
434
435 int
436 ziptool_post_close(const char *archive) {
437     if (source_type == SOURCE_TYPE_IN_MEMORY) {
438         if (write_memory_src_to_file(archive, memory_src) < 0) {
439             return -1;
440         }
441         zip_source_free(memory_src);
442     }
443
444     return 0;
445 }