2 * Copyright (c) 2011, Novell Inc.
4 * This program is licensed under the BSD license, read LICENSE.BSD
5 * for further information
19 #include "solv_xfopen.h"
22 #ifndef WITHOUT_COOKIEOPEN
24 static FILE *cookieopen(void *cookie, const char *mode,
25 ssize_t (*cread)(void *, char *, size_t),
26 ssize_t (*cwrite)(void *, const char *, size_t),
27 int (*cclose)(void *))
32 return funopen(cookie,
33 (int (*)(void *, char *, int))(*mode == 'r' ? cread : NULL), /* readfn */
34 (int (*)(void *, const char *, int))(*mode == 'w' ? cwrite : NULL), /* writefn */
35 (fpos_t (*)(void *, fpos_t, int))NULL, /* seekfn */
38 #elif defined(HAVE_FOPENCOOKIE)
39 cookie_io_functions_t cio;
43 memset(&cio, 0, sizeof(cio));
46 else if (*mode == 'w')
49 return fopencookie(cookie, *mode == 'w' ? "w" : "r", cio);
51 # error Need to implement custom I/O
56 #ifdef ENABLE_ZLIB_COMPRESSION
58 /* gzip compression */
62 static ssize_t cookie_gzread(void *cookie, char *buf, size_t nbytes)
64 return gzread((gzFile)cookie, buf, nbytes);
67 static ssize_t cookie_gzwrite(void *cookie, const char *buf, size_t nbytes)
69 return gzwrite((gzFile)cookie, buf, nbytes);
72 static int cookie_gzclose(void *cookie)
74 return gzclose((gzFile)cookie);
77 static inline FILE *mygzfopen(const char *fn, const char *mode)
79 gzFile gzf = gzopen(fn, mode);
80 return cookieopen(gzf, mode, cookie_gzread, cookie_gzwrite, cookie_gzclose);
83 static inline FILE *mygzfdopen(int fd, const char *mode)
85 gzFile gzf = gzdopen(fd, mode);
86 return cookieopen(gzf, mode, cookie_gzread, cookie_gzwrite, cookie_gzclose);
92 #ifdef ENABLE_BZIP2_COMPRESSION
94 /* bzip2 compression */
98 static ssize_t cookie_bzread(void *cookie, char *buf, size_t nbytes)
100 return BZ2_bzread((BZFILE *)cookie, buf, nbytes);
103 static ssize_t cookie_bzwrite(void *cookie, const char *buf, size_t nbytes)
105 return BZ2_bzwrite((BZFILE *)cookie, (char *)buf, nbytes);
108 static int cookie_bzclose(void *cookie)
110 BZ2_bzclose((BZFILE *)cookie);
114 static inline FILE *mybzfopen(const char *fn, const char *mode)
116 BZFILE *bzf = BZ2_bzopen(fn, mode);
117 return cookieopen(bzf, mode, cookie_bzread, cookie_bzwrite, cookie_bzclose);
120 static inline FILE *mybzfdopen(int fd, const char *mode)
122 BZFILE *bzf = BZ2_bzdopen(fd, mode);
123 return cookieopen(bzf, mode, cookie_bzread, cookie_bzwrite, cookie_bzclose);
129 #ifdef ENABLE_LZMA_COMPRESSION
131 /* lzma code written by me in 2008 for rpm's rpmio.c */
135 typedef struct lzfile {
136 unsigned char buf[1 << 15];
143 static inline lzma_ret setup_alone_encoder(lzma_stream *strm, int level)
145 lzma_options_lzma options;
146 lzma_lzma_preset(&options, level);
147 return lzma_alone_encoder(strm, &options);
150 static lzma_stream stream_init = LZMA_STREAM_INIT;
152 static LZFILE *lzopen(const char *path, const char *mode, int fd, int isxz)
162 for (; *mode; mode++)
166 else if (*mode == 'r')
168 else if (*mode >= '1' && *mode <= '9')
172 fp = fdopen(fd, encoding ? "w" : "r");
174 fp = fopen(path, encoding ? "w" : "r");
177 lzfile = calloc(1, sizeof(*lzfile));
184 lzfile->encoding = encoding;
186 lzfile->strm = stream_init;
190 ret = lzma_easy_encoder(&lzfile->strm, level, LZMA_CHECK_SHA256);
192 ret = setup_alone_encoder(&lzfile->strm, level);
195 ret = lzma_auto_decoder(&lzfile->strm, 100 << 20, 0);
205 static int lzclose(void *cookie)
207 LZFILE *lzfile = cookie;
214 if (lzfile->encoding)
218 lzfile->strm.avail_out = sizeof(lzfile->buf);
219 lzfile->strm.next_out = lzfile->buf;
220 ret = lzma_code(&lzfile->strm, LZMA_FINISH);
221 if (ret != LZMA_OK && ret != LZMA_STREAM_END)
223 n = sizeof(lzfile->buf) - lzfile->strm.avail_out;
224 if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
226 if (ret == LZMA_STREAM_END)
230 lzma_end(&lzfile->strm);
231 rc = fclose(lzfile->file);
236 static ssize_t lzread(void *cookie, char *buf, size_t len)
238 LZFILE *lzfile = cookie;
242 if (!lzfile || lzfile->encoding)
246 lzfile->strm.next_out = (unsigned char *)buf;
247 lzfile->strm.avail_out = len;
250 if (!lzfile->strm.avail_in)
252 lzfile->strm.next_in = lzfile->buf;
253 lzfile->strm.avail_in = fread(lzfile->buf, 1, sizeof(lzfile->buf), lzfile->file);
254 if (!lzfile->strm.avail_in)
257 ret = lzma_code(&lzfile->strm, LZMA_RUN);
258 if (ret == LZMA_STREAM_END)
261 return len - lzfile->strm.avail_out;
265 if (!lzfile->strm.avail_out)
272 static ssize_t lzwrite(void *cookie, const char *buf, size_t len)
274 LZFILE *lzfile = cookie;
277 if (!lzfile || !lzfile->encoding)
281 lzfile->strm.next_in = (unsigned char *)buf;
282 lzfile->strm.avail_in = len;
285 lzfile->strm.next_out = lzfile->buf;
286 lzfile->strm.avail_out = sizeof(lzfile->buf);
287 ret = lzma_code(&lzfile->strm, LZMA_RUN);
290 n = sizeof(lzfile->buf) - lzfile->strm.avail_out;
291 if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
293 if (!lzfile->strm.avail_in)
298 static inline FILE *myxzfopen(const char *fn, const char *mode)
300 LZFILE *lzf = lzopen(fn, mode, -1, 1);
301 return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
304 static inline FILE *myxzfdopen(int fd, const char *mode)
306 LZFILE *lzf = lzopen(0, mode, fd, 1);
307 return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
310 static inline FILE *mylzfopen(const char *fn, const char *mode)
312 LZFILE *lzf = lzopen(fn, mode, -1, 0);
313 return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
316 static inline FILE *mylzfdopen(int fd, const char *mode)
318 LZFILE *lzf = lzopen(0, mode, fd, 0);
319 return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
322 #endif /* ENABLE_LZMA_COMPRESSION */
324 #ifdef ENABLE_ZSTD_COMPRESSION
328 typedef struct zstdfile {
329 ZSTD_CStream *cstream;
330 ZSTD_DStream *dstream;
336 unsigned char buf[1 << 15];
339 static ZSTDFILE *zstdopen(const char *path, const char *mode, int fd)
348 for (; *mode; mode++)
352 else if (*mode == 'r')
354 else if (*mode >= '1' && *mode <= '9')
358 fp = fdopen(fd, encoding ? "w" : "r");
360 fp = fopen(path, encoding ? "w" : "r");
363 zstdfile = solv_calloc(1, sizeof(*zstdfile));
364 zstdfile->encoding = encoding;
367 zstdfile->cstream = ZSTD_createCStream();
368 zstdfile->encoding = 1;
369 if (!zstdfile->cstream)
375 if (ZSTD_isError(ZSTD_initCStream(zstdfile->cstream, level)))
377 ZSTD_freeCStream(zstdfile->cstream);
382 zstdfile->out.dst = zstdfile->buf;
383 zstdfile->out.pos = 0;
384 zstdfile->out.size = sizeof(zstdfile->buf);
388 zstdfile->dstream = ZSTD_createDStream();
389 if (ZSTD_isError(ZSTD_initDStream(zstdfile->dstream)))
391 ZSTD_freeDStream(zstdfile->dstream);
396 zstdfile->in.src = zstdfile->buf;
397 zstdfile->in.pos = 0;
398 zstdfile->in.size = 0;
404 static int zstdclose(void *cookie)
406 ZSTDFILE *zstdfile = cookie;
411 if (zstdfile->encoding)
416 zstdfile->out.pos = 0;
417 ret = ZSTD_endStream(zstdfile->cstream, &zstdfile->out);
418 if (ZSTD_isError(ret))
420 if (zstdfile->out.pos && fwrite(zstdfile->buf, 1, zstdfile->out.pos, zstdfile->file) != zstdfile->out.pos)
425 ZSTD_freeCStream(zstdfile->cstream);
429 ZSTD_freeDStream(zstdfile->dstream);
431 rc = fclose(zstdfile->file);
436 static ssize_t zstdread(void *cookie, char *buf, size_t len)
438 ZSTDFILE *zstdfile = cookie;
441 if (!zstdfile || zstdfile->encoding)
445 zstdfile->out.dst = buf;
446 zstdfile->out.pos = 0;
447 zstdfile->out.size = len;
450 if (!eof && zstdfile->in.pos == zstdfile->in.size)
452 zstdfile->in.pos = 0;
453 zstdfile->in.size = fread(zstdfile->buf, 1, sizeof(zstdfile->buf), zstdfile->file);
454 if (!zstdfile->in.size)
458 ret = ZSTD_decompressStream(zstdfile->dstream, &zstdfile->out, &zstdfile->in);
462 return zstdfile->out.pos;
464 if (ZSTD_isError(ret))
466 if (zstdfile->out.pos == len)
471 static ssize_t zstdwrite(void *cookie, const char *buf, size_t len)
473 ZSTDFILE *zstdfile = cookie;
474 if (!zstdfile || !zstdfile->encoding)
478 zstdfile->in.src = buf;
479 zstdfile->in.pos = 0;
480 zstdfile->in.size = len;
485 zstdfile->out.pos = 0;
486 ret = ZSTD_compressStream(zstdfile->cstream, &zstdfile->out, &zstdfile->in);
487 if (ZSTD_isError(ret))
489 if (zstdfile->out.pos && fwrite(zstdfile->buf, 1, zstdfile->out.pos, zstdfile->file) != zstdfile->out.pos)
491 if (zstdfile->in.pos == len)
496 static inline FILE *myzstdfopen(const char *fn, const char *mode)
498 ZSTDFILE *zstdfile = zstdopen(fn, mode, -1);
499 return cookieopen(zstdfile, mode, zstdread, zstdwrite, zstdclose);
502 static inline FILE *myzstdfdopen(int fd, const char *mode)
504 ZSTDFILE *zstdfile = zstdopen(0, mode, fd);
505 return cookieopen(zstdfile, mode, zstdread, zstdwrite, zstdclose);
510 #ifdef ENABLE_ZCHUNK_COMPRESSION
512 #ifdef WITH_SYSTEM_ZCHUNK
513 /* use the system's zchunk library that supports reading and writing of zchunk files */
517 static ssize_t cookie_zckread(void *cookie, char *buf, size_t nbytes)
519 return zck_read((zckCtx *)cookie, buf, nbytes);
522 static ssize_t cookie_zckwrite(void *cookie, const char *buf, size_t nbytes)
524 return zck_write((zckCtx *)cookie, buf, nbytes);
527 static int cookie_zckclose(void *cookie)
529 zckCtx *zck = (zckCtx *)cookie;
530 int fd = zck_get_fd(zck);
537 static void *zchunkopen(const char *path, const char *mode, int fd)
546 fd = open(path, O_RDONLY);
548 fd = open(path, O_WRONLY | O_CREAT, 0666);
560 if(!zck_init_read(f, fd))
565 if(!zck_init_write(f, fd))
568 return cookieopen(f, mode, cookie_zckread, cookie_zckwrite, cookie_zckclose);
573 #include "solv_zchunk.h"
574 /* use the libsolv's limited zchunk implementation that only supports reading of zchunk files */
576 static void *zchunkopen(const char *path, const char *mode, int fd)
583 fp = fdopen(fd, mode);
585 fp = fopen(path, mode);
588 if (strcmp(mode, "r") != 0)
590 f = solv_zchunk_open(fp, 1);
593 return cookieopen(f, mode, (ssize_t (*)(void *, char *, size_t))solv_zchunk_read, 0, (int (*)(void *))solv_zchunk_close);
598 static inline FILE *myzchunkfopen(const char *fn, const char *mode)
600 return zchunkopen(fn, mode, -1);
603 static inline FILE *myzchunkfdopen(int fd, const char *mode)
605 return zchunkopen(0, mode, fd);
608 #endif /* ENABLE_ZCHUNK_COMPRESSION */
611 /* no cookies no compression */
612 #undef ENABLE_ZLIB_COMPRESSION
613 #undef ENABLE_LZMA_COMPRESSION
614 #undef ENABLE_BZIP2_COMPRESSION
615 #undef ENABLE_ZSTD_COMPRESSION
616 #undef ENABLE_ZCHUNK_COMPRESSION
622 solv_xfopen(const char *fn, const char *mode)
630 suf = strrchr(fn, '.');
631 #ifdef ENABLE_ZLIB_COMPRESSION
632 if (suf && !strcmp(suf, ".gz"))
633 return mygzfopen(fn, mode);
635 if (suf && !strcmp(suf, ".gz"))
638 #ifdef ENABLE_LZMA_COMPRESSION
639 if (suf && !strcmp(suf, ".xz"))
640 return myxzfopen(fn, mode);
641 if (suf && !strcmp(suf, ".lzma"))
642 return mylzfopen(fn, mode);
644 if (suf && !strcmp(suf, ".xz"))
646 if (suf && !strcmp(suf, ".lzma"))
649 #ifdef ENABLE_BZIP2_COMPRESSION
650 if (suf && !strcmp(suf, ".bz2"))
651 return mybzfopen(fn, mode);
653 if (suf && !strcmp(suf, ".bz2"))
656 #ifdef ENABLE_ZSTD_COMPRESSION
657 if (suf && !strcmp(suf, ".zst"))
658 return myzstdfopen(fn, mode);
660 if (suf && !strcmp(suf, ".zst"))
663 #ifdef ENABLE_ZCHUNK_COMPRESSION
664 if (suf && !strcmp(suf, ".zck"))
665 return myzchunkfopen(fn, mode);
667 if (suf && !strcmp(suf, ".zck"))
670 return fopen(fn, mode);
674 solv_xfopen_fd(const char *fn, int fd, const char *mode)
676 const char *simplemode = mode;
679 suf = fn ? strrchr(fn, '.') : 0;
683 int fl = fcntl(fd, F_GETFL, 0);
685 HANDLE handle = (HANDLE) _get_osfhandle(fd);
686 BY_HANDLE_FILE_INFORMATION file_info;
687 if (!GetFileInformationByHandle(handle, &file_info))
689 int fl = file_info.dwFileAttributes;
693 fl &= O_RDONLY|O_WRONLY|O_RDWR;
695 mode = simplemode = "w";
696 else if (fl == O_RDWR)
702 mode = simplemode = "r";
704 #ifdef ENABLE_ZLIB_COMPRESSION
705 if (suf && !strcmp(suf, ".gz"))
706 return mygzfdopen(fd, simplemode);
708 if (suf && !strcmp(suf, ".gz"))
711 #ifdef ENABLE_LZMA_COMPRESSION
712 if (suf && !strcmp(suf, ".xz"))
713 return myxzfdopen(fd, simplemode);
714 if (suf && !strcmp(suf, ".lzma"))
715 return mylzfdopen(fd, simplemode);
717 if (suf && !strcmp(suf, ".xz"))
719 if (suf && !strcmp(suf, ".lzma"))
722 #ifdef ENABLE_BZIP2_COMPRESSION
723 if (suf && !strcmp(suf, ".bz2"))
724 return mybzfdopen(fd, simplemode);
726 if (suf && !strcmp(suf, ".bz2"))
729 #ifdef ENABLE_ZSTD_COMPRESSION
730 if (suf && !strcmp(suf, ".zst"))
731 return myzstdfdopen(fd, simplemode);
733 if (suf && !strcmp(suf, ".zst"))
736 #ifdef ENABLE_ZCHUNK_COMPRESSION
737 if (suf && !strcmp(suf, ".zck"))
738 return myzchunkfdopen(fd, simplemode);
740 if (suf && !strcmp(suf, ".zck"))
743 return fdopen(fd, mode);
747 solv_xfopen_iscompressed(const char *fn)
749 const char *suf = fn ? strrchr(fn, '.') : 0;
752 #ifdef ENABLE_ZLIB_COMPRESSION
753 if (!strcmp(suf, ".gz"))
758 if (!strcmp(suf, ".xz") || !strcmp(suf, ".lzma"))
759 #ifdef ENABLE_LZMA_COMPRESSION
764 if (!strcmp(suf, ".bz2"))
765 #ifdef ENABLE_BZIP2_COMPRESSION
770 if (!strcmp(suf, ".zst"))
771 #ifdef ENABLE_ZSTD_COMPRESSION
776 if (!strcmp(suf, ".zck"))
777 #ifdef ENABLE_ZCHUNK_COMPRESSION
786 #ifndef WITHOUT_COOKIEOPEN
795 static ssize_t cookie_bufread(void *cookie, char *buf, size_t nbytes)
797 struct bufcookie *bc = cookie;
798 size_t n = *bc->buflp > nbytes ? nbytes : *bc->buflp;
801 memcpy(buf, *bc->bufp, n);
808 static ssize_t cookie_bufwrite(void *cookie, const char *buf, size_t nbytes)
810 struct bufcookie *bc = cookie;
811 int n = nbytes > 0x40000000 ? 0x40000000 : nbytes;
814 *bc->bufp = solv_extend(*bc->bufp, *bc->buflp, n + 1, 1, 4095);
815 memcpy(*bc->bufp, buf, n);
816 (*bc->bufp)[n] = 0; /* zero-terminate */
822 static int cookie_bufclose(void *cookie)
824 struct bufcookie *bc = cookie;
826 solv_free(bc->freemem);
832 solv_xfopen_buf(const char *fn, char **bufp, size_t *buflp, const char *mode)
834 struct bufcookie *bc;
836 if (*mode != 'r' && *mode != 'w')
838 bc = solv_calloc(1, sizeof(*bc));
843 bc->bufl_int = *mode == 'w' ? 0 : strlen(*bufp);
844 buflp = &bc->bufl_int;
849 *bc->bufp = solv_extend(0, 0, 1, 1, 4095); /* always zero-terminate */
853 fp = cookieopen(bc, mode, cookie_bufread, cookie_bufwrite, cookie_bufclose);
854 if (!strcmp(mode, "rf")) /* auto-free */
859 *bc->bufp = solv_free(*bc->bufp);
868 solv_xfopen_buf(const char *fn, char **bufp, size_t *buflp, const char *mode)
874 l = buflp ? *buflp : strlen(*bufp);
875 if (!strcmp(mode, "rf"))
877 if (!(fp = fmemopen(0, l, "r+")))
879 if (l && fwrite(*bufp, l, 1, fp) != 1)
888 fp = fmemopen(*bufp, l, "r");