1 /*
2 * Copyright (c) 2011, Novell Inc.
3 *
4 * This program is licensed under the BSD license, read LICENSE.BSD
5 * for further information
6 */
7
8 #define _DEFAULT_SOURCE
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <zlib.h>
14 #include <fcntl.h>
15
16 #include "solv_xfopen.h"
17 #include "util.h"
18
19
cookieopen(void * cookie,const char * mode,ssize_t (* cread)(void *,char *,size_t),ssize_t (* cwrite)(void *,const char *,size_t),int (* cclose)(void *))20 static FILE *cookieopen(void *cookie, const char *mode,
21 ssize_t (*cread)(void *, char *, size_t),
22 ssize_t (*cwrite)(void *, const char *, size_t),
23 int (*cclose)(void *))
24 {
25 #ifdef HAVE_FUNOPEN
26 if (!cookie)
27 return 0;
28 return funopen(cookie,
29 (int (*)(void *, char *, int))(*mode == 'r' ? cread : NULL), /* readfn */
30 (int (*)(void *, const char *, int))(*mode == 'w' ? cwrite : NULL), /* writefn */
31 (fpos_t (*)(void *, fpos_t, int))NULL, /* seekfn */
32 cclose
33 );
34 #elif defined(HAVE_FOPENCOOKIE)
35 cookie_io_functions_t cio;
36
37 if (!cookie)
38 return 0;
39 memset(&cio, 0, sizeof(cio));
40 if (*mode == 'r')
41 cio.read = cread;
42 else if (*mode == 'w')
43 cio.write = cwrite;
44 cio.close = cclose;
45 return fopencookie(cookie, *mode == 'w' ? "w" : "r", cio);
46 #else
47 # error Need to implement custom I/O
48 #endif
49 }
50
51
52 /* gzip compression */
53
cookie_gzread(void * cookie,char * buf,size_t nbytes)54 static ssize_t cookie_gzread(void *cookie, char *buf, size_t nbytes)
55 {
56 return gzread((gzFile)cookie, buf, nbytes);
57 }
58
cookie_gzwrite(void * cookie,const char * buf,size_t nbytes)59 static ssize_t cookie_gzwrite(void *cookie, const char *buf, size_t nbytes)
60 {
61 return gzwrite((gzFile)cookie, buf, nbytes);
62 }
63
cookie_gzclose(void * cookie)64 static int cookie_gzclose(void *cookie)
65 {
66 return gzclose((gzFile)cookie);
67 }
68
mygzfopen(const char * fn,const char * mode)69 static inline FILE *mygzfopen(const char *fn, const char *mode)
70 {
71 gzFile gzf = gzopen(fn, mode);
72 return cookieopen(gzf, mode, cookie_gzread, cookie_gzwrite, cookie_gzclose);
73 }
74
mygzfdopen(int fd,const char * mode)75 static inline FILE *mygzfdopen(int fd, const char *mode)
76 {
77 gzFile gzf = gzdopen(fd, mode);
78 return cookieopen(gzf, mode, cookie_gzread, cookie_gzwrite, cookie_gzclose);
79 }
80
81 #ifdef ENABLE_BZIP2_COMPRESSION
82
83 #include <bzlib.h>
84
85 /* bzip2 compression */
86
cookie_bzread(void * cookie,char * buf,size_t nbytes)87 static ssize_t cookie_bzread(void *cookie, char *buf, size_t nbytes)
88 {
89 return BZ2_bzread((BZFILE *)cookie, buf, nbytes);
90 }
91
cookie_bzwrite(void * cookie,const char * buf,size_t nbytes)92 static ssize_t cookie_bzwrite(void *cookie, const char *buf, size_t nbytes)
93 {
94 return BZ2_bzwrite((BZFILE *)cookie, (char *)buf, nbytes);
95 }
96
cookie_bzclose(void * cookie)97 static int cookie_bzclose(void *cookie)
98 {
99 BZ2_bzclose((BZFILE *)cookie);
100 return 0;
101 }
102
mybzfopen(const char * fn,const char * mode)103 static inline FILE *mybzfopen(const char *fn, const char *mode)
104 {
105 BZFILE *bzf = BZ2_bzopen(fn, mode);
106 return cookieopen(bzf, mode, cookie_bzread, cookie_bzwrite, cookie_bzclose);
107 }
108
mybzfdopen(int fd,const char * mode)109 static inline FILE *mybzfdopen(int fd, const char *mode)
110 {
111 BZFILE *bzf = BZ2_bzdopen(fd, mode);
112 return cookieopen(bzf, mode, cookie_bzread, cookie_bzwrite, cookie_bzclose);
113 }
114
115 #endif
116
117
118 #ifdef ENABLE_LZMA_COMPRESSION
119
120 #include <lzma.h>
121
122 /* lzma code written by me in 2008 for rpm's rpmio.c */
123
124 typedef struct lzfile {
125 unsigned char buf[1 << 15];
126 lzma_stream strm;
127 FILE *file;
128 int encoding;
129 int eof;
130 } LZFILE;
131
setup_alone_encoder(lzma_stream * strm,int level)132 static inline lzma_ret setup_alone_encoder(lzma_stream *strm, int level)
133 {
134 lzma_options_lzma options;
135 lzma_lzma_preset(&options, level);
136 return lzma_alone_encoder(strm, &options);
137 }
138
139 static lzma_stream stream_init = LZMA_STREAM_INIT;
140
lzopen(const char * path,const char * mode,int fd,int isxz)141 static LZFILE *lzopen(const char *path, const char *mode, int fd, int isxz)
142 {
143 int level = 7;
144 int encoding = 0;
145 FILE *fp;
146 LZFILE *lzfile;
147 lzma_ret ret;
148
149 if (!path && fd < 0)
150 return 0;
151 for (; *mode; mode++)
152 {
153 if (*mode == 'w')
154 encoding = 1;
155 else if (*mode == 'r')
156 encoding = 0;
157 else if (*mode >= '1' && *mode <= '9')
158 level = *mode - '0';
159 }
160 if (fd != -1)
161 fp = fdopen(fd, encoding ? "w" : "r");
162 else
163 fp = fopen(path, encoding ? "w" : "r");
164 if (!fp)
165 return 0;
166 lzfile = calloc(1, sizeof(*lzfile));
167 if (!lzfile)
168 {
169 fclose(fp);
170 return 0;
171 }
172 lzfile->file = fp;
173 lzfile->encoding = encoding;
174 lzfile->eof = 0;
175 lzfile->strm = stream_init;
176 if (encoding)
177 {
178 if (isxz)
179 ret = lzma_easy_encoder(&lzfile->strm, level, LZMA_CHECK_SHA256);
180 else
181 ret = setup_alone_encoder(&lzfile->strm, level);
182 }
183 else
184 ret = lzma_auto_decoder(&lzfile->strm, 100 << 20, 0);
185 if (ret != LZMA_OK)
186 {
187 fclose(fp);
188 free(lzfile);
189 return 0;
190 }
191 return lzfile;
192 }
193
lzclose(void * cookie)194 static int lzclose(void *cookie)
195 {
196 LZFILE *lzfile = cookie;
197 lzma_ret ret;
198 size_t n;
199 int rc;
200
201 if (!lzfile)
202 return -1;
203 if (lzfile->encoding)
204 {
205 for (;;)
206 {
207 lzfile->strm.avail_out = sizeof(lzfile->buf);
208 lzfile->strm.next_out = lzfile->buf;
209 ret = lzma_code(&lzfile->strm, LZMA_FINISH);
210 if (ret != LZMA_OK && ret != LZMA_STREAM_END)
211 return -1;
212 n = sizeof(lzfile->buf) - lzfile->strm.avail_out;
213 if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
214 return -1;
215 if (ret == LZMA_STREAM_END)
216 break;
217 }
218 }
219 lzma_end(&lzfile->strm);
220 rc = fclose(lzfile->file);
221 free(lzfile);
222 return rc;
223 }
224
lzread(void * cookie,char * buf,size_t len)225 static ssize_t lzread(void *cookie, char *buf, size_t len)
226 {
227 LZFILE *lzfile = cookie;
228 lzma_ret ret;
229 int eof = 0;
230
231 if (!lzfile || lzfile->encoding)
232 return -1;
233 if (lzfile->eof)
234 return 0;
235 lzfile->strm.next_out = (unsigned char *)buf;
236 lzfile->strm.avail_out = len;
237 for (;;)
238 {
239 if (!lzfile->strm.avail_in)
240 {
241 lzfile->strm.next_in = lzfile->buf;
242 lzfile->strm.avail_in = fread(lzfile->buf, 1, sizeof(lzfile->buf), lzfile->file);
243 if (!lzfile->strm.avail_in)
244 eof = 1;
245 }
246 ret = lzma_code(&lzfile->strm, LZMA_RUN);
247 if (ret == LZMA_STREAM_END)
248 {
249 lzfile->eof = 1;
250 return len - lzfile->strm.avail_out;
251 }
252 if (ret != LZMA_OK)
253 return -1;
254 if (!lzfile->strm.avail_out)
255 return len;
256 if (eof)
257 return -1;
258 }
259 }
260
lzwrite(void * cookie,const char * buf,size_t len)261 static ssize_t lzwrite(void *cookie, const char *buf, size_t len)
262 {
263 LZFILE *lzfile = cookie;
264 lzma_ret ret;
265 size_t n;
266 if (!lzfile || !lzfile->encoding)
267 return -1;
268 if (!len)
269 return 0;
270 lzfile->strm.next_in = (unsigned char *)buf;
271 lzfile->strm.avail_in = len;
272 for (;;)
273 {
274 lzfile->strm.next_out = lzfile->buf;
275 lzfile->strm.avail_out = sizeof(lzfile->buf);
276 ret = lzma_code(&lzfile->strm, LZMA_RUN);
277 if (ret != LZMA_OK)
278 return -1;
279 n = sizeof(lzfile->buf) - lzfile->strm.avail_out;
280 if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
281 return -1;
282 if (!lzfile->strm.avail_in)
283 return len;
284 }
285 }
286
myxzfopen(const char * fn,const char * mode)287 static inline FILE *myxzfopen(const char *fn, const char *mode)
288 {
289 LZFILE *lzf = lzopen(fn, mode, -1, 1);
290 return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
291 }
292
myxzfdopen(int fd,const char * mode)293 static inline FILE *myxzfdopen(int fd, const char *mode)
294 {
295 LZFILE *lzf = lzopen(0, mode, fd, 1);
296 return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
297 }
298
mylzfopen(const char * fn,const char * mode)299 static inline FILE *mylzfopen(const char *fn, const char *mode)
300 {
301 LZFILE *lzf = lzopen(fn, mode, -1, 0);
302 return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
303 }
304
mylzfdopen(int fd,const char * mode)305 static inline FILE *mylzfdopen(int fd, const char *mode)
306 {
307 LZFILE *lzf = lzopen(0, mode, fd, 0);
308 return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
309 }
310
311 #endif /* ENABLE_LZMA_COMPRESSION */
312
313
314 FILE *
solv_xfopen(const char * fn,const char * mode)315 solv_xfopen(const char *fn, const char *mode)
316 {
317 char *suf;
318
319 if (!fn)
320 return 0;
321 if (!mode)
322 mode = "r";
323 suf = strrchr(fn, '.');
324 if (suf && !strcmp(suf, ".gz"))
325 return mygzfopen(fn, mode);
326 #ifdef ENABLE_LZMA_COMPRESSION
327 if (suf && !strcmp(suf, ".xz"))
328 return myxzfopen(fn, mode);
329 if (suf && !strcmp(suf, ".lzma"))
330 return mylzfopen(fn, mode);
331 #else
332 if (suf && !strcmp(suf, ".xz"))
333 return 0;
334 if (suf && !strcmp(suf, ".lzma"))
335 return 0;
336 #endif
337 #ifdef ENABLE_BZIP2_COMPRESSION
338 if (suf && !strcmp(suf, ".bz2"))
339 return mybzfopen(fn, mode);
340 #else
341 if (suf && !strcmp(suf, ".bz2"))
342 return 0;
343 #endif
344 return fopen(fn, mode);
345 }
346
347 FILE *
solv_xfopen_fd(const char * fn,int fd,const char * mode)348 solv_xfopen_fd(const char *fn, int fd, const char *mode)
349 {
350 const char *simplemode = mode;
351 char *suf;
352
353 suf = fn ? strrchr(fn, '.') : 0;
354 if (!mode)
355 {
356 int fl = fcntl(fd, F_GETFL, 0);
357 if (fl == -1)
358 return 0;
359 fl &= O_RDONLY|O_WRONLY|O_RDWR;
360 if (fl == O_WRONLY)
361 mode = simplemode = "w";
362 else if (fl == O_RDWR)
363 {
364 mode = "r+";
365 simplemode = "r";
366 }
367 else
368 mode = simplemode = "r";
369 }
370 if (suf && !strcmp(suf, ".gz"))
371 return mygzfdopen(fd, simplemode);
372 #ifdef ENABLE_LZMA_COMPRESSION
373 if (suf && !strcmp(suf, ".xz"))
374 return myxzfdopen(fd, simplemode);
375 if (suf && !strcmp(suf, ".lzma"))
376 return mylzfdopen(fd, simplemode);
377 #else
378 if (suf && !strcmp(suf, ".xz"))
379 return 0;
380 if (suf && !strcmp(suf, ".lzma"))
381 return 0;
382 #endif
383 #ifdef ENABLE_BZIP2_COMPRESSION
384 if (suf && !strcmp(suf, ".bz2"))
385 return mybzfdopen(fd, simplemode);
386 #else
387 if (suf && !strcmp(suf, ".bz2"))
388 return 0;
389 #endif
390 return fdopen(fd, mode);
391 }
392
393 int
solv_xfopen_iscompressed(const char * fn)394 solv_xfopen_iscompressed(const char *fn)
395 {
396 const char *suf = fn ? strrchr(fn, '.') : 0;
397 if (!suf)
398 return 0;
399 if (!strcmp(suf, ".gz"))
400 return 1;
401 if (!strcmp(suf, ".xz") || !strcmp(suf, ".lzma"))
402 #ifdef ENABLE_LZMA_COMPRESSION
403 return 1;
404 #else
405 return -1;
406 #endif
407 if (!strcmp(suf, ".bz2"))
408 #ifdef ENABLE_BZIP2_COMPRESSION
409 return 1;
410 #else
411 return -1;
412 #endif
413 return 0;
414 }
415
416 struct bufcookie {
417 char **bufp;
418 size_t *buflp;
419 char *freemem;
420 size_t bufl_int;
421 };
422
cookie_bufread(void * cookie,char * buf,size_t nbytes)423 static ssize_t cookie_bufread(void *cookie, char *buf, size_t nbytes)
424 {
425 struct bufcookie *bc = cookie;
426 size_t n = *bc->buflp > nbytes ? nbytes : *bc->buflp;
427 if (n)
428 {
429 memcpy(buf, *bc->bufp, n);
430 *bc->bufp += n;
431 *bc->buflp -= n;
432 }
433 return n;
434 }
435
cookie_bufwrite(void * cookie,const char * buf,size_t nbytes)436 static ssize_t cookie_bufwrite(void *cookie, const char *buf, size_t nbytes)
437 {
438 struct bufcookie *bc = cookie;
439 int n = nbytes > 0x40000000 ? 0x40000000 : nbytes;
440 if (n)
441 {
442 *bc->bufp = solv_extend(*bc->bufp, *bc->buflp, n + 1, 1, 4095);
443 memcpy(*bc->bufp, buf, n);
444 (*bc->bufp)[n] = 0; /* zero-terminate */
445 *bc->buflp += n;
446 }
447 return n;
448 }
449
cookie_bufclose(void * cookie)450 static int cookie_bufclose(void *cookie)
451 {
452 struct bufcookie *bc = cookie;
453 if (bc->freemem)
454 solv_free(bc->freemem);
455 solv_free(bc);
456 return 0;
457 }
458
459 FILE *
solv_xfopen_buf(const char * fn,char ** bufp,size_t * buflp,const char * mode)460 solv_xfopen_buf(const char *fn, char **bufp, size_t *buflp, const char *mode)
461 {
462 struct bufcookie *bc;
463 FILE *fp;
464 if (*mode != 'r' && *mode != 'w')
465 return 0;
466 bc = solv_calloc(1, sizeof(*bc));
467 bc->freemem = 0;
468 bc->bufp = bufp;
469 if (!buflp)
470 {
471 bc->bufl_int = *mode == 'w' ? 0 : strlen(*bufp);
472 buflp = &bc->bufl_int;
473 }
474 bc->buflp = buflp;
475 if (*mode == 'w')
476 {
477 *bc->bufp = solv_extend(0, 0, 1, 1, 4095); /* always zero-terminate */
478 (*bc->bufp)[0] = 0;
479 *bc->buflp = 0;
480 }
481 fp = cookieopen(bc, mode, cookie_bufread, cookie_bufwrite, cookie_bufclose);
482 if (!strcmp(mode, "rf")) /* auto-free */
483 bc->freemem = *bufp;
484 if (!fp)
485 {
486 if (*mode == 'w')
487 *bc->bufp = solv_free(*bc->bufp);
488 cookie_bufclose(bc);
489 }
490 return fp;
491 }
492