"Fossies" - the Fresh Open Source Software Archive

Member "libzip-1.5.2/lib/zip_source_filep.c" (12 Mar 2019, 15705 Bytes) of package /linux/misc/libzip-1.5.2.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 "zip_source_filep.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.5.1_vs_1.5.2.

    1 /*
    2   zip_source_filep.c -- create data source from FILE *
    3   Copyright (C) 1999-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 <stdio.h>
   35 #include <stdlib.h>
   36 #include <string.h>
   37 #include <sys/types.h>
   38 #include <sys/stat.h>
   39 
   40 #include "zipint.h"
   41 
   42 #ifdef HAVE_UNISTD_H
   43 #include <unistd.h>
   44 #endif
   45 
   46 #ifdef HAVE_CLONEFILE
   47 #include <sys/attr.h>
   48 #include <sys/clonefile.h>
   49 #define CAN_CLONE
   50 #endif
   51 #ifdef HAVE_FICLONERANGE
   52 #include <linux/fs.h>
   53 #include <sys/ioctl.h>
   54 #define CAN_CLONE
   55 #endif
   56 
   57 #ifdef _WIN32
   58 /* WIN32 needs <fcntl.h> for _O_BINARY */
   59 #include <fcntl.h>
   60 #endif
   61 
   62 /* Windows sys/types.h does not provide these */
   63 #ifndef S_ISREG
   64 #define S_ISREG(m) (((m)&S_IFMT) == S_IFREG)
   65 #endif
   66 #if defined(S_IXUSR) && defined(S_IRWXG) && defined(S_IRWXO)
   67 #define _SAFE_MASK (S_IXUSR | S_IRWXG | S_IRWXO)
   68 #elif defined(_S_IWRITE)
   69 #define _SAFE_MASK (_S_IWRITE)
   70 #else
   71 #error do not know safe values for umask, please report this
   72 #endif
   73 
   74 #ifdef _MSC_VER
   75 /* MSVC doesn't have mode_t */
   76 typedef int mode_t;
   77 #endif
   78 
   79 struct read_file {
   80     zip_error_t error; /* last error information */
   81     zip_int64_t supports;
   82 
   83     /* reading */
   84     char *fname;            /* name of file to read from */
   85     FILE *f;                /* file to read from */
   86     struct zip_stat st;     /* stat information passed in */
   87     zip_error_t stat_error; /* error returned for stat */
   88     zip_uint64_t start;     /* start offset of data to read */
   89     zip_uint64_t end;       /* end offset of data to read relative to start, 0 for up to EOF */
   90     zip_uint64_t current;   /* current offset relative to start (0 is beginning of part we read) */
   91 
   92     /* writing */
   93     char *tmpname;
   94     FILE *fout;
   95 };
   96 
   97 static zip_int64_t read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd);
   98 static int create_temp_output(struct read_file *ctx);
   99 #ifdef CAN_CLONE
  100 static zip_int64_t create_temp_output_cloning(struct read_file *ctx, zip_uint64_t offset);
  101 #endif
  102 static int _zip_fseek_u(FILE *f, zip_uint64_t offset, int whence, zip_error_t *error);
  103 static int _zip_fseek(FILE *f, zip_int64_t offset, int whence, zip_error_t *error);
  104 
  105 
  106 ZIP_EXTERN zip_source_t *
  107 zip_source_filep(zip_t *za, FILE *file, zip_uint64_t start, zip_int64_t len) {
  108     if (za == NULL)
  109     return NULL;
  110 
  111     return zip_source_filep_create(file, start, len, &za->error);
  112 }
  113 
  114 
  115 ZIP_EXTERN zip_source_t *
  116 zip_source_filep_create(FILE *file, zip_uint64_t start, zip_int64_t length, zip_error_t *error) {
  117     if (file == NULL || length < -1) {
  118     zip_error_set(error, ZIP_ER_INVAL, 0);
  119     return NULL;
  120     }
  121 
  122     return _zip_source_file_or_p(NULL, file, start, length, NULL, error);
  123 }
  124 
  125 
  126 zip_source_t *
  127 _zip_source_file_or_p(const char *fname, FILE *file, zip_uint64_t start, zip_int64_t len, const zip_stat_t *st, zip_error_t *error) {
  128     struct read_file *ctx;
  129     zip_source_t *zs;
  130     struct stat sb;
  131     bool stat_valid;
  132 
  133     if (file == NULL && fname == NULL) {
  134     zip_error_set(error, ZIP_ER_INVAL, 0);
  135     return NULL;
  136     }
  137 
  138     if (len < 0) {
  139     len = 0;
  140     }
  141 
  142     if (start > ZIP_INT64_MAX || start + (zip_uint64_t)len < start) {
  143     zip_error_set(error, ZIP_ER_INVAL, 0);
  144     return NULL;
  145     }
  146 
  147     if ((ctx = (struct read_file *)malloc(sizeof(struct read_file))) == NULL) {
  148     zip_error_set(error, ZIP_ER_MEMORY, 0);
  149     return NULL;
  150     }
  151 
  152     ctx->fname = NULL;
  153     if (fname) {
  154     if ((ctx->fname = strdup(fname)) == NULL) {
  155         zip_error_set(error, ZIP_ER_MEMORY, 0);
  156         free(ctx);
  157         return NULL;
  158     }
  159     }
  160     ctx->f = file;
  161     ctx->start = start;
  162     ctx->end = (zip_uint64_t)len;
  163     if (st) {
  164     memcpy(&ctx->st, st, sizeof(ctx->st));
  165     ctx->st.name = NULL;
  166     ctx->st.valid &= ~ZIP_STAT_NAME;
  167     }
  168     else {
  169     zip_stat_init(&ctx->st);
  170     }
  171 
  172     if (ctx->end > 0) {
  173     ctx->st.size = ctx->end;
  174     ctx->st.valid |= ZIP_STAT_SIZE;
  175     }
  176 
  177     zip_error_init(&ctx->stat_error);
  178 
  179     ctx->tmpname = NULL;
  180     ctx->fout = NULL;
  181 
  182     zip_error_init(&ctx->error);
  183 
  184     ctx->supports = ZIP_SOURCE_SUPPORTS_READABLE | zip_source_make_command_bitmap(ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, -1);
  185 
  186     if (ctx->fname) {
  187     stat_valid = stat(ctx->fname, &sb) >= 0;
  188 
  189     if (!stat_valid) {
  190         if (ctx->start == 0 && ctx->end == 0) {
  191         ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE;
  192         }
  193     }
  194     }
  195     else {
  196     stat_valid = fstat(fileno(ctx->f), &sb) >= 0;
  197     }
  198 
  199     if (!stat_valid) {
  200     zip_error_set(&ctx->stat_error, ZIP_ER_READ, errno);
  201     }
  202     else {
  203     if ((ctx->st.valid & ZIP_STAT_MTIME) == 0) {
  204         ctx->st.mtime = sb.st_mtime;
  205         ctx->st.valid |= ZIP_STAT_MTIME;
  206     }
  207     if (S_ISREG(sb.st_mode)) {
  208         ctx->supports = ZIP_SOURCE_SUPPORTS_SEEKABLE;
  209 
  210         if (ctx->start + ctx->end > (zip_uint64_t)sb.st_size) {
  211         zip_error_set(error, ZIP_ER_INVAL, 0);
  212         free(ctx->fname);
  213         free(ctx);
  214         return NULL;
  215         }
  216 
  217         if (ctx->end == 0) {
  218         ctx->st.size = (zip_uint64_t)sb.st_size - ctx->start;
  219         ctx->st.valid |= ZIP_STAT_SIZE;
  220 
  221         if (ctx->fname && start == 0) {
  222             ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE;
  223         }
  224         }
  225     }
  226     }
  227 
  228 #ifdef CAN_CLONE
  229     if (ctx->supports & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_BEGIN_WRITE)) {
  230     ctx->supports |= ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_BEGIN_WRITE_CLONING);
  231     }
  232 #endif
  233 
  234     if ((zs = zip_source_function_create(read_file, ctx, error)) == NULL) {
  235     free(ctx->fname);
  236     free(ctx);
  237     return NULL;
  238     }
  239 
  240     return zs;
  241 }
  242 
  243 
  244 static int
  245 create_temp_output(struct read_file *ctx) {
  246     char *temp;
  247     int tfd;
  248     mode_t mask;
  249     FILE *tfp;
  250 
  251     if ((temp = (char *)malloc(strlen(ctx->fname) + 8)) == NULL) {
  252     zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
  253     return -1;
  254     }
  255     sprintf(temp, "%s.XXXXXX", ctx->fname);
  256 
  257     mask = umask(_SAFE_MASK);
  258     if ((tfd = mkstemp(temp)) == -1) {
  259     zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
  260     umask(mask);
  261     free(temp);
  262     return -1;
  263     }
  264     umask(mask);
  265 
  266     if ((tfp = fdopen(tfd, "r+b")) == NULL) {
  267     zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
  268     close(tfd);
  269     (void)remove(temp);
  270     free(temp);
  271     return -1;
  272     }
  273 
  274 #ifdef _WIN32
  275     /*
  276      According to Pierre Joye, Windows in some environments per
  277      default creates text files, so force binary mode.
  278      */
  279     _setmode(_fileno(tfp), _O_BINARY);
  280 #endif
  281 
  282     ctx->fout = tfp;
  283     ctx->tmpname = temp;
  284 
  285     return 0;
  286 }
  287 
  288 #ifdef CAN_CLONE
  289 zip_int64_t static create_temp_output_cloning(struct read_file *ctx, zip_uint64_t offset) {
  290     char *temp;
  291     FILE *tfp;
  292 
  293     if (offset > ZIP_OFF_MAX) {
  294     zip_error_set(&ctx->error, ZIP_ER_SEEK, E2BIG);
  295     return -1;
  296     }
  297 
  298     if ((temp = (char *)malloc(strlen(ctx->fname) + 8)) == NULL) {
  299     zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
  300     return -1;
  301     }
  302     sprintf(temp, "%s.XXXXXX", ctx->fname);
  303 
  304 #ifdef HAVE_CLONEFILE
  305 #ifndef __clang_analyzer__
  306     /* we can't use mkstemp, since clonefile insists on creating the file */
  307     if (mktemp(temp) == NULL) {
  308     zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
  309     free(temp);
  310     return -1;
  311     }
  312 #endif
  313 
  314     if (clonefile(ctx->fname, temp, 0) < 0) {
  315     zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
  316     free(temp);
  317     return -1;
  318     }
  319     if ((tfp = fopen(temp, "r+b")) == NULL) {
  320     zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
  321     (void)remove(temp);
  322     free(temp);
  323     return -1;
  324     }
  325 #else
  326     {
  327     int fd;
  328     struct file_clone_range range;
  329     struct stat st;
  330 
  331     if (fstat(fileno(ctx->f), &st) < 0) {
  332         zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
  333         return -1;
  334     }
  335 
  336     if ((fd = mkstemp(temp)) < 0) {
  337         zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
  338         free(temp);
  339         return -1;
  340     }
  341 
  342     range.src_fd = fileno(ctx->f);
  343     range.src_offset = 0;
  344     range.src_length = ((offset + st.st_blksize - 1) / st.st_blksize) * st.st_blksize;
  345     if (range.src_length > st.st_size) {
  346         range.src_length = 0;
  347     }
  348     range.dest_offset = 0;
  349     if (ioctl(fd, FICLONERANGE, &range) < 0) {
  350         zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
  351         (void)close(fd);
  352         (void)remove(temp);
  353         free(temp);
  354         return -1;
  355     }
  356 
  357     if ((tfp = fdopen(fd, "r+b")) == NULL) {
  358         zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
  359         (void)close(fd);
  360         (void)remove(temp);
  361         free(temp);
  362         return -1;
  363     }
  364     }
  365 #endif
  366 
  367     if (ftruncate(fileno(tfp), (off_t)offset) < 0) {
  368     (void)fclose(tfp);
  369     (void)remove(temp);
  370     free(temp);
  371     return -1;
  372     }
  373     if (fseeko(tfp, (off_t)offset, SEEK_SET) < 0) {
  374     (void)fclose(tfp);
  375     (void)remove(temp);
  376     free(temp);
  377     zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
  378     }
  379 
  380     ctx->fout = tfp;
  381     ctx->tmpname = temp;
  382 
  383     return 0;
  384 }
  385 #endif
  386 
  387 
  388 static zip_int64_t
  389 read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd) {
  390     struct read_file *ctx;
  391     char *buf;
  392     zip_uint64_t n;
  393     size_t i;
  394 
  395     ctx = (struct read_file *)state;
  396     buf = (char *)data;
  397 
  398     switch (cmd) {
  399     case ZIP_SOURCE_BEGIN_WRITE:
  400     if (ctx->fname == NULL) {
  401         zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
  402         return -1;
  403     }
  404     return create_temp_output(ctx);
  405 
  406 #ifdef CAN_CLONE
  407     case ZIP_SOURCE_BEGIN_WRITE_CLONING:
  408     if (ctx->fname == NULL) {
  409         zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
  410         return -1;
  411     }
  412     return create_temp_output_cloning(ctx, len);
  413 #endif
  414 
  415     case ZIP_SOURCE_COMMIT_WRITE: {
  416     mode_t mode;
  417     struct stat st;
  418 
  419     if (fclose(ctx->fout) < 0) {
  420         ctx->fout = NULL;
  421         zip_error_set(&ctx->error, ZIP_ER_WRITE, errno);
  422     }
  423     ctx->fout = NULL;
  424     if (stat(ctx->fname, &st) == 0) {
  425         mode = st.st_mode;
  426     } else {
  427         mode_t mask = umask(022);
  428         umask(mask);
  429         mode = 0666 & ~mask;
  430     }
  431     if (rename(ctx->tmpname, ctx->fname) < 0) {
  432         zip_error_set(&ctx->error, ZIP_ER_RENAME, errno);
  433         return -1;
  434     }
  435     /* not much we can do if chmod fails except make the whole commit fail */
  436     (void)chmod(ctx->fname, mode);
  437     free(ctx->tmpname);
  438     ctx->tmpname = NULL;
  439     return 0;
  440     }
  441 
  442     case ZIP_SOURCE_CLOSE:
  443     if (ctx->fname) {
  444         fclose(ctx->f);
  445         ctx->f = NULL;
  446     }
  447     return 0;
  448 
  449     case ZIP_SOURCE_ERROR:
  450     return zip_error_to_data(&ctx->error, data, len);
  451 
  452     case ZIP_SOURCE_FREE:
  453     free(ctx->fname);
  454     free(ctx->tmpname);
  455     if (ctx->f)
  456         fclose(ctx->f);
  457     free(ctx);
  458     return 0;
  459 
  460     case ZIP_SOURCE_OPEN:
  461     if (ctx->fname) {
  462         if ((ctx->f = fopen(ctx->fname, "rb")) == NULL) {
  463         zip_error_set(&ctx->error, ZIP_ER_OPEN, errno);
  464         return -1;
  465         }
  466     }
  467 
  468     if (ctx->start > 0) {
  469         if (_zip_fseek_u(ctx->f, ctx->start, SEEK_SET, &ctx->error) < 0) {
  470         /* TODO: skip by reading */
  471         return -1;
  472         }
  473     }
  474     ctx->current = 0;
  475     return 0;
  476 
  477     case ZIP_SOURCE_READ:
  478     if (ctx->end > 0) {
  479         n = ctx->end - ctx->current;
  480         if (n > len) {
  481         n = len;
  482         }
  483     }
  484     else {
  485         n = len;
  486     }
  487 
  488     if (n > SIZE_MAX)
  489         n = SIZE_MAX;
  490 
  491     if ((i = fread(buf, 1, (size_t)n, ctx->f)) == 0) {
  492         if (ferror(ctx->f)) {
  493         zip_error_set(&ctx->error, ZIP_ER_READ, errno);
  494         return -1;
  495         }
  496     }
  497     ctx->current += i;
  498 
  499     return (zip_int64_t)i;
  500 
  501     case ZIP_SOURCE_REMOVE:
  502     if (remove(ctx->fname) < 0) {
  503         zip_error_set(&ctx->error, ZIP_ER_REMOVE, errno);
  504         return -1;
  505     }
  506     return 0;
  507 
  508     case ZIP_SOURCE_ROLLBACK_WRITE:
  509     if (ctx->fout) {
  510         fclose(ctx->fout);
  511         ctx->fout = NULL;
  512     }
  513     (void)remove(ctx->tmpname);
  514     free(ctx->tmpname);
  515     ctx->tmpname = NULL;
  516     return 0;
  517 
  518     case ZIP_SOURCE_SEEK: {
  519     zip_int64_t new_current;
  520     int need_seek;
  521     zip_source_args_seek_t *args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error);
  522 
  523     if (args == NULL)
  524         return -1;
  525 
  526     need_seek = 1;
  527 
  528     switch (args->whence) {
  529     case SEEK_SET:
  530         new_current = args->offset;
  531         break;
  532 
  533     case SEEK_END:
  534         if (ctx->end == 0) {
  535         if (_zip_fseek(ctx->f, args->offset, SEEK_END, &ctx->error) < 0) {
  536             return -1;
  537         }
  538         if ((new_current = ftello(ctx->f)) < 0) {
  539             zip_error_set(&ctx->error, ZIP_ER_SEEK, errno);
  540             return -1;
  541         }
  542         new_current -= (zip_int64_t)ctx->start;
  543         need_seek = 0;
  544         }
  545         else {
  546         new_current = (zip_int64_t)ctx->end + args->offset;
  547         }
  548         break;
  549 
  550     case SEEK_CUR:
  551         new_current = (zip_int64_t)ctx->current + args->offset;
  552         break;
  553 
  554     default:
  555         zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
  556         return -1;
  557     }
  558 
  559     if (new_current < 0 || (ctx->end != 0 && (zip_uint64_t)new_current > ctx->end) || (zip_uint64_t)new_current + ctx->start < ctx->start) {
  560         zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
  561         return -1;
  562     }
  563 
  564     ctx->current = (zip_uint64_t)new_current;
  565 
  566     if (need_seek) {
  567         if (_zip_fseek_u(ctx->f, ctx->current + ctx->start, SEEK_SET, &ctx->error) < 0) {
  568         return -1;
  569         }
  570     }
  571     return 0;
  572     }
  573 
  574     case ZIP_SOURCE_SEEK_WRITE: {
  575     zip_source_args_seek_t *args;
  576 
  577     args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error);
  578     if (args == NULL) {
  579         return -1;
  580     }
  581 
  582     if (_zip_fseek(ctx->fout, args->offset, args->whence, &ctx->error) < 0) {
  583         return -1;
  584     }
  585     return 0;
  586     }
  587 
  588     case ZIP_SOURCE_STAT: {
  589     if (len < sizeof(ctx->st))
  590         return -1;
  591 
  592     if (zip_error_code_zip(&ctx->stat_error) != 0) {
  593         zip_error_set(&ctx->error, zip_error_code_zip(&ctx->stat_error), zip_error_code_system(&ctx->stat_error));
  594         return -1;
  595     }
  596 
  597     memcpy(data, &ctx->st, sizeof(ctx->st));
  598     return sizeof(ctx->st);
  599     }
  600 
  601     case ZIP_SOURCE_SUPPORTS:
  602     return ctx->supports;
  603 
  604     case ZIP_SOURCE_TELL:
  605     return (zip_int64_t)ctx->current;
  606 
  607     case ZIP_SOURCE_TELL_WRITE: {
  608     off_t ret = ftello(ctx->fout);
  609 
  610     if (ret < 0) {
  611         zip_error_set(&ctx->error, ZIP_ER_TELL, errno);
  612         return -1;
  613     }
  614     return ret;
  615     }
  616 
  617     case ZIP_SOURCE_WRITE: {
  618     size_t ret;
  619 
  620     clearerr(ctx->fout);
  621     ret = fwrite(data, 1, len, ctx->fout);
  622     if (ret != len || ferror(ctx->fout)) {
  623         zip_error_set(&ctx->error, ZIP_ER_WRITE, errno);
  624         return -1;
  625     }
  626 
  627     return (zip_int64_t)ret;
  628     }
  629 
  630     default:
  631     zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
  632     return -1;
  633     }
  634 }
  635 
  636 
  637 static int
  638 _zip_fseek_u(FILE *f, zip_uint64_t offset, int whence, zip_error_t *error) {
  639     if (offset > ZIP_INT64_MAX) {
  640     zip_error_set(error, ZIP_ER_SEEK, EOVERFLOW);
  641     return -1;
  642     }
  643     return _zip_fseek(f, (zip_int64_t)offset, whence, error);
  644 }
  645 
  646 
  647 static int
  648 _zip_fseek(FILE *f, zip_int64_t offset, int whence, zip_error_t *error) {
  649     if (offset > ZIP_FSEEK_MAX || offset < ZIP_FSEEK_MIN) {
  650     zip_error_set(error, ZIP_ER_SEEK, EOVERFLOW);
  651     return -1;
  652     }
  653     if (fseeko(f, (off_t)offset, whence) < 0) {
  654     zip_error_set(error, ZIP_ER_SEEK, errno);
  655     return -1;
  656     }
  657     return 0;
  658 }