"Fossies" - the Fresh Open Source Software Archive

Member "libzip-1.6.0/regress/source_hole.c" (24 Jan 2020, 14215 Bytes) of package /linux/misc/libzip-1.6.0.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "source_hole.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.5.2_vs_1.6.0.

    1 /*
    2  source_hole.c -- source for handling huge files that are mostly NULs
    3  Copyright (C) 2014-2019 Dieter Baron and Thomas Klausner
    4 
    5  This file is part of libzip, a library to manipulate ZIP archives.
    6  The authors can be contacted at <libzip@nih.at>
    7 
    8  Redistribution and use in source and binary forms, with or without
    9  modification, are permitted provided that the following conditions
   10  are met:
   11  1. Redistributions of source code must retain the above copyright
   12  notice, this list of conditions and the following disclaimer.
   13  2. Redistributions in binary form must reproduce the above copyright
   14  notice, this list of conditions and the following disclaimer in
   15  the documentation and/or other materials provided with the
   16  distribution.
   17  3. The names of the authors may not be used to endorse or promote
   18  products derived from this software without specific prior
   19  written permission.
   20 
   21  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
   22  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   23  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   24  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
   25  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   26  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
   27  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   28  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
   29  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
   30  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
   31  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   32  */
   33 
   34 #include <errno.h>
   35 #include <limits.h>
   36 #include <stdio.h>
   37 #include <stdlib.h>
   38 #include <string.h>
   39 
   40 #include "zip.h"
   41 
   42 /* public API */
   43 
   44 zip_source_t *source_hole_create(const char *, int flags, zip_error_t *);
   45 
   46 
   47 #ifndef EFTYPE
   48 #define EFTYPE EINVAL
   49 #endif
   50 
   51 
   52 #define MY_MIN(a, b) ((a) < (b) ? (a) : (b))
   53 
   54 #define FRAGMENT_SIZE (8 * 1024)
   55 
   56 #define MARK_BEGIN "NiH0"
   57 #define MARK_DATA "NiH1"
   58 #define MARK_NUL "NiH2"
   59 
   60 
   61 typedef struct buffer {
   62     zip_uint64_t fragment_size;
   63     zip_uint8_t **fragment;
   64     zip_uint64_t nfragments;
   65     zip_uint64_t size;
   66     zip_uint64_t offset;
   67 } buffer_t;
   68 
   69 static void buffer_free(buffer_t *buffer);
   70 static buffer_t *buffer_from_file(const char *fname, int flags, zip_error_t *error);
   71 static buffer_t *buffer_new(void);
   72 static zip_int64_t buffer_read(buffer_t *buffer, zip_uint8_t *data, zip_uint64_t length, zip_error_t *error);
   73 static int buffer_read_file(buffer_t *buffer, FILE *f, zip_error_t *error);
   74 static zip_int64_t buffer_seek(buffer_t *buffer, void *data, zip_uint64_t length, zip_error_t *error);
   75 static int buffer_to_file(buffer_t *buffer, const char *fname, zip_error_t *error);
   76 static zip_int64_t buffer_write(buffer_t *buffer, const zip_uint8_t *data, zip_uint64_t length, zip_error_t *error);
   77 static zip_uint64_t get_u64(const zip_uint8_t *b);
   78 static int only_nul(const zip_uint8_t *data, zip_uint64_t length);
   79 static int write_nuls(zip_uint64_t n, FILE *f);
   80 static int write_u64(zip_uint64_t u64, FILE *f);
   81 
   82 
   83 typedef struct hole {
   84     zip_error_t error;
   85     char *fname;
   86     buffer_t *in;
   87     buffer_t *out;
   88 } hole_t;
   89 
   90 static hole_t *hole_new(const char *fname, int flags, zip_error_t *error);
   91 static zip_int64_t source_hole_cb(void *ud, void *data, zip_uint64_t length, zip_source_cmd_t command);
   92 
   93 
   94 zip_source_t *
   95 source_hole_create(const char *fname, int flags, zip_error_t *error) {
   96     hole_t *ud = hole_new(fname, flags, error);
   97 
   98     if (ud == NULL) {
   99     return NULL;
  100     }
  101     return zip_source_function_create(source_hole_cb, ud, error);
  102 }
  103 
  104 
  105 static void
  106 buffer_free(buffer_t *buffer) {
  107     zip_uint64_t i;
  108 
  109     if (buffer == NULL) {
  110     return;
  111     }
  112 
  113     if (buffer->fragment) {
  114     for (i = 0; i < buffer->nfragments; i++) {
  115         free(buffer->fragment[i]);
  116     }
  117     free(buffer->fragment);
  118     }
  119     free(buffer);
  120 }
  121 
  122 
  123 static buffer_t *
  124 buffer_from_file(const char *fname, int flags, zip_error_t *error) {
  125     buffer_t *buffer;
  126     FILE *f;
  127 
  128     if ((buffer = buffer_new()) == NULL) {
  129     zip_error_set(error, ZIP_ER_MEMORY, 0);
  130     return NULL;
  131     }
  132 
  133     if ((flags & ZIP_TRUNCATE) == 0) {
  134     if ((f = fopen(fname, "rb")) == NULL) {
  135         if (!(errno == ENOENT && (flags & ZIP_CREATE))) {
  136         buffer_free(buffer);
  137         return NULL;
  138         }
  139     }
  140     else {
  141         if (buffer_read_file(buffer, f, error) < 0) {
  142         buffer_free(buffer);
  143         fclose(f);
  144         return NULL;
  145         }
  146         fclose(f);
  147     }
  148     }
  149 
  150     return buffer;
  151 }
  152 
  153 
  154 static buffer_t *
  155 buffer_new(void) {
  156     buffer_t *buffer;
  157 
  158     if ((buffer = (buffer_t *)malloc(sizeof(*buffer))) == NULL) {
  159     return NULL;
  160     }
  161 
  162     buffer->fragment = NULL;
  163     buffer->nfragments = 0;
  164     buffer->fragment_size = FRAGMENT_SIZE;
  165     buffer->size = 0;
  166     buffer->offset = 0;
  167 
  168     return buffer;
  169 }
  170 
  171 
  172 static zip_int64_t
  173 buffer_read(buffer_t *buffer, zip_uint8_t *data, zip_uint64_t length, zip_error_t *error) {
  174     zip_uint64_t n, i, fragment_offset;
  175 
  176     length = MY_MIN(length, buffer->size - buffer->offset);
  177 
  178     if (length == 0) {
  179     return 0;
  180     }
  181     if (length > ZIP_INT64_MAX) {
  182     return -1;
  183     }
  184 
  185     i = buffer->offset / buffer->fragment_size;
  186     fragment_offset = buffer->offset % buffer->fragment_size;
  187     n = 0;
  188     while (n < length) {
  189     zip_uint64_t left = MY_MIN(length - n, buffer->fragment_size - fragment_offset);
  190 
  191     if (buffer->fragment[i]) {
  192         memcpy(data + n, buffer->fragment[i] + fragment_offset, left);
  193     }
  194     else {
  195         memset(data + n, 0, left);
  196     }
  197 
  198     n += left;
  199     i++;
  200     fragment_offset = 0;
  201     }
  202 
  203     buffer->offset += n;
  204     return (zip_int64_t)n;
  205 }
  206 
  207 
  208 static int
  209 buffer_read_file(buffer_t *buffer, FILE *f, zip_error_t *error) {
  210     zip_uint8_t b[20];
  211     zip_uint64_t i;
  212 
  213     if (fread(b, 20, 1, f) != 1) {
  214     zip_error_set(error, ZIP_ER_READ, errno);
  215     return -1;
  216     }
  217 
  218     if (memcmp(b, MARK_BEGIN, 4) != 0) {
  219     zip_error_set(error, ZIP_ER_READ, EFTYPE);
  220     return -1;
  221     }
  222 
  223     buffer->fragment_size = get_u64(b + 4);
  224     buffer->size = get_u64(b + 12);
  225 
  226     if (buffer->size + buffer->fragment_size < buffer->size) {
  227     zip_error_set(error, ZIP_ER_MEMORY, 0);
  228     return -1;
  229     }
  230     buffer->nfragments = (buffer->size + buffer->fragment_size - 1) / buffer->fragment_size;
  231     if ((buffer->nfragments > SIZE_MAX / sizeof(buffer->fragment[0])) || ((buffer->fragment = (zip_uint8_t **)malloc(sizeof(buffer->fragment[0]) * buffer->nfragments)) == NULL)) {
  232     zip_error_set(error, ZIP_ER_MEMORY, 0);
  233     return -1;
  234     }
  235 
  236     for (i = 0; i < buffer->nfragments; i++) {
  237     buffer->fragment[i] = NULL;
  238     }
  239 
  240     i = 0;
  241     while (i < buffer->nfragments) {
  242     if (fread(b, 4, 1, f) != 1) {
  243         zip_error_set(error, ZIP_ER_READ, errno);
  244         return -1;
  245     }
  246 
  247     if (memcmp(b, MARK_DATA, 4) == 0) {
  248         if (buffer->fragment_size > SIZE_MAX) {
  249         zip_error_set(error, ZIP_ER_MEMORY, 0);
  250         return -1;
  251         }
  252         if ((buffer->fragment[i] = (zip_uint8_t *)malloc(buffer->fragment_size)) == NULL) {
  253         zip_error_set(error, ZIP_ER_MEMORY, 0);
  254         return -1;
  255         }
  256         if (fread(buffer->fragment[i], buffer->fragment_size, 1, f) != 1) {
  257         zip_error_set(error, ZIP_ER_READ, errno);
  258         return -1;
  259         }
  260         i++;
  261     }
  262     else if (memcmp(b, MARK_NUL, 4) == 0) {
  263         if (fread(b, 8, 1, f) != 1) {
  264         zip_error_set(error, ZIP_ER_READ, errno);
  265         return -1;
  266         }
  267         i += get_u64(b);
  268     }
  269     else {
  270         zip_error_set(error, ZIP_ER_READ, EFTYPE);
  271         return -1;
  272     }
  273     }
  274 
  275     return 0;
  276 }
  277 
  278 static zip_int64_t
  279 buffer_seek(buffer_t *buffer, void *data, zip_uint64_t length, zip_error_t *error) {
  280     zip_int64_t new_offset = zip_source_seek_compute_offset(buffer->offset, buffer->size, data, length, error);
  281 
  282     if (new_offset < 0) {
  283     return -1;
  284     }
  285 
  286     buffer->offset = (zip_uint64_t)new_offset;
  287     return 0;
  288 }
  289 
  290 
  291 static int
  292 buffer_to_file(buffer_t *buffer, const char *fname, zip_error_t *error) {
  293     FILE *f = fopen(fname, "wb");
  294     zip_uint64_t i;
  295     zip_uint64_t nul_run;
  296 
  297     if (f == NULL) {
  298     zip_error_set(error, ZIP_ER_OPEN, errno);
  299     return -1;
  300     }
  301 
  302     fwrite(MARK_BEGIN, 4, 1, f);
  303     write_u64(buffer->fragment_size, f);
  304     write_u64(buffer->size, f);
  305 
  306     nul_run = 0;
  307     for (i = 0; i * buffer->fragment_size < buffer->size; i++) {
  308     if (buffer->fragment[i] == NULL || only_nul(buffer->fragment[i], buffer->fragment_size)) {
  309         nul_run++;
  310     }
  311     else {
  312         if (nul_run > 0) {
  313         write_nuls(nul_run, f);
  314         nul_run = 0;
  315         }
  316         fwrite(MARK_DATA, 4, 1, f);
  317 
  318         fwrite(buffer->fragment[i], 1, buffer->fragment_size, f);
  319     }
  320     }
  321 
  322     if (nul_run > 0) {
  323     write_nuls(nul_run, f);
  324     }
  325 
  326     if (fclose(f) != 0) {
  327     zip_error_set(error, ZIP_ER_WRITE, errno);
  328     return -1;
  329     }
  330 
  331     return 0;
  332 }
  333 
  334 
  335 static zip_int64_t
  336 buffer_write(buffer_t *buffer, const zip_uint8_t *data, zip_uint64_t length, zip_error_t *error) {
  337     zip_uint8_t **fragment;
  338     if (buffer->offset + length > buffer->nfragments * buffer->fragment_size) {
  339     zip_uint64_t needed_fragments = (buffer->offset + length + buffer->fragment_size - 1) / buffer->fragment_size;
  340     zip_uint64_t new_capacity = buffer->nfragments;
  341     zip_uint64_t i;
  342 
  343     if (new_capacity == 0) {
  344         new_capacity = 4;
  345     }
  346     while (new_capacity < needed_fragments) {
  347         new_capacity *= 2;
  348     }
  349 
  350     fragment = realloc(buffer->fragment, new_capacity * sizeof(*fragment));
  351 
  352     if (fragment == NULL) {
  353         zip_error_set(error, ZIP_ER_MEMORY, 0);
  354         return -1;
  355     }
  356 
  357     for (i = buffer->nfragments; i < new_capacity; i++) {
  358         fragment[i] = NULL;
  359     }
  360 
  361     buffer->fragment = fragment;
  362     buffer->nfragments = new_capacity;
  363     }
  364 
  365     if (!only_nul(data, length)) {
  366     zip_uint64_t idx, n, fragment_offset;
  367 
  368     idx = buffer->offset / buffer->fragment_size;
  369     fragment_offset = buffer->offset % buffer->fragment_size;
  370     n = 0;
  371 
  372     while (n < length) {
  373         zip_uint64_t left = MY_MIN(length - n, buffer->fragment_size - fragment_offset);
  374 
  375         if (buffer->fragment[idx] == NULL) {
  376         if ((buffer->fragment[idx] = (zip_uint8_t *)malloc(buffer->fragment_size)) == NULL) {
  377             zip_error_set(error, ZIP_ER_MEMORY, 0);
  378             return -1;
  379         }
  380         memset(buffer->fragment[idx], 0, buffer->fragment_size);
  381         }
  382         memcpy(buffer->fragment[idx] + fragment_offset, data + n, left);
  383 
  384         n += left;
  385         idx++;
  386         fragment_offset = 0;
  387     }
  388     }
  389 
  390     buffer->offset += length;
  391     if (buffer->offset > buffer->size) {
  392     buffer->size = buffer->offset;
  393     }
  394 
  395     return (zip_int64_t)length;
  396 }
  397 
  398 
  399 static zip_uint64_t
  400 get_u64(const zip_uint8_t *b) {
  401     zip_uint64_t i;
  402 
  403     i = (zip_uint64_t)b[0] << 56 | (zip_uint64_t)b[1] << 48 | (zip_uint64_t)b[2] << 40 | (zip_uint64_t)b[3] << 32 | (zip_uint64_t)b[4] << 24 | (zip_uint64_t)b[5] << 16 | (zip_uint64_t)b[6] << 8 | (zip_uint64_t)b[7];
  404 
  405     return i;
  406 }
  407 
  408 
  409 static int
  410 only_nul(const zip_uint8_t *data, zip_uint64_t length) {
  411     zip_uint64_t i;
  412 
  413     for (i = 0; i < length; i++) {
  414     if (data[i] != '\0') {
  415         return 0;
  416     }
  417     }
  418 
  419     return 1;
  420 }
  421 
  422 
  423 static int
  424 write_nuls(zip_uint64_t n, FILE *f) {
  425     if (fwrite(MARK_NUL, 4, 1, f) != 1) {
  426     return -1;
  427     }
  428     return write_u64(n, f);
  429 }
  430 
  431 
  432 static int
  433 write_u64(zip_uint64_t u64, FILE *f) {
  434     zip_uint8_t b[8];
  435 
  436     b[0] = (zip_uint8_t)((u64 >> 56) & 0xff);
  437     b[1] = (zip_uint8_t)((u64 >> 48) & 0xff);
  438     b[2] = (zip_uint8_t)((u64 >> 40) & 0xff);
  439     b[3] = (zip_uint8_t)((u64 >> 32) & 0xff);
  440     b[4] = (zip_uint8_t)((u64 >> 24) & 0xff);
  441     b[5] = (zip_uint8_t)((u64 >> 16) & 0xff);
  442     b[6] = (zip_uint8_t)((u64 >> 8) & 0xff);
  443     b[7] = (zip_uint8_t)(u64 & 0xff);
  444 
  445     return fwrite(b, 8, 1, f) == 1 ? 0 : -1;
  446 }
  447 
  448 
  449 static void
  450 hole_free(hole_t *hole) {
  451     if (hole == NULL) {
  452     return;
  453     }
  454     zip_error_fini(&hole->error);
  455     buffer_free(hole->in);
  456     buffer_free(hole->out);
  457     free(hole->fname);
  458     free(hole);
  459 }
  460 
  461 
  462 static hole_t *
  463 hole_new(const char *fname, int flags, zip_error_t *error) {
  464     hole_t *ctx = (hole_t *)malloc(sizeof(*ctx));
  465 
  466     if (ctx == NULL) {
  467     zip_error_set(error, ZIP_ER_MEMORY, 0);
  468     return NULL;
  469     }
  470 
  471     if ((ctx->fname = strdup(fname)) == NULL) {
  472     free(ctx);
  473     zip_error_set(error, ZIP_ER_MEMORY, 0);
  474     return NULL;
  475     }
  476 
  477     if ((ctx->in = buffer_from_file(fname, flags, error)) == NULL) {
  478     free(ctx);
  479     return NULL;
  480     }
  481 
  482     zip_error_init(&ctx->error);
  483     ctx->out = NULL;
  484 
  485     return ctx;
  486 }
  487 
  488 
  489 static zip_int64_t
  490 source_hole_cb(void *ud, void *data, zip_uint64_t length, zip_source_cmd_t command) {
  491     hole_t *ctx = (hole_t *)ud;
  492 
  493     switch (command) {
  494     case ZIP_SOURCE_BEGIN_WRITE:
  495     ctx->out = buffer_new();
  496     return 0;
  497 
  498     case ZIP_SOURCE_CLOSE:
  499     return 0;
  500 
  501     case ZIP_SOURCE_COMMIT_WRITE:
  502     if (buffer_to_file(ctx->out, ctx->fname, &ctx->error) < 0) {
  503         return -1;
  504     }
  505     buffer_free(ctx->in);
  506     ctx->in = ctx->out;
  507     ctx->out = NULL;
  508     return 0;
  509 
  510     case ZIP_SOURCE_ERROR:
  511     return zip_error_to_data(&ctx->error, data, length);
  512 
  513     case ZIP_SOURCE_FREE:
  514     hole_free(ctx);
  515     return 0;
  516 
  517     case ZIP_SOURCE_OPEN:
  518     ctx->in->offset = 0;
  519     return 0;
  520 
  521     case ZIP_SOURCE_READ:
  522     return buffer_read(ctx->in, data, length, &ctx->error);
  523 
  524     case ZIP_SOURCE_REMOVE:
  525     buffer_free(ctx->in);
  526     ctx->in = buffer_new();
  527     buffer_free(ctx->out);
  528     ctx->out = NULL;
  529     (void)remove(ctx->fname);
  530     return 0;
  531 
  532     case ZIP_SOURCE_ROLLBACK_WRITE:
  533     buffer_free(ctx->out);
  534     ctx->out = NULL;
  535     return 0;
  536 
  537     case ZIP_SOURCE_SEEK:
  538     return buffer_seek(ctx->in, data, length, &ctx->error);
  539 
  540     case ZIP_SOURCE_SEEK_WRITE:
  541     return buffer_seek(ctx->out, data, length, &ctx->error);
  542 
  543     case ZIP_SOURCE_STAT: {
  544     zip_stat_t *st = ZIP_SOURCE_GET_ARGS(zip_stat_t, data, length, &ctx->error);
  545 
  546     if (st == NULL) {
  547         return -1;
  548     }
  549 
  550     /* TODO: return ENOENT if fname doesn't exist */
  551 
  552     st->valid |= ZIP_STAT_SIZE;
  553     st->size = ctx->in->size;
  554     return 0;
  555     }
  556 
  557     case ZIP_SOURCE_TELL:
  558     return (zip_int64_t)ctx->in->offset;
  559 
  560     case ZIP_SOURCE_TELL_WRITE:
  561     return (zip_int64_t)ctx->out->offset;
  562 
  563     case ZIP_SOURCE_WRITE:
  564     return buffer_write(ctx->out, data, length, &ctx->error);
  565 
  566     case ZIP_SOURCE_SUPPORTS:
  567     return zip_source_make_command_bitmap(ZIP_SOURCE_BEGIN_WRITE, ZIP_SOURCE_COMMIT_WRITE, ZIP_SOURCE_CLOSE, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_REMOVE, ZIP_SOURCE_ROLLBACK_WRITE, ZIP_SOURCE_SEEK, ZIP_SOURCE_SEEK_WRITE, ZIP_SOURCE_STAT, ZIP_SOURCE_TELL, ZIP_SOURCE_TELL_WRITE, ZIP_SOURCE_WRITE, -1);
  568 
  569     default:
  570     zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
  571     return -1;
  572     }
  573 }