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 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 54 static ssize_t cookie_gzread(void *cookie, char *buf, size_t nbytes) 55 { 56 return gzread((gzFile)cookie, buf, nbytes); 57 } 58 59 static ssize_t cookie_gzwrite(void *cookie, const char *buf, size_t nbytes) 60 { 61 return gzwrite((gzFile)cookie, buf, nbytes); 62 } 63 64 static int cookie_gzclose(void *cookie) 65 { 66 return gzclose((gzFile)cookie); 67 } 68 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 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 87 static ssize_t cookie_bzread(void *cookie, char *buf, size_t nbytes) 88 { 89 return BZ2_bzread((BZFILE *)cookie, buf, nbytes); 90 } 91 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 97 static int cookie_bzclose(void *cookie) 98 { 99 BZ2_bzclose((BZFILE *)cookie); 100 return 0; 101 } 102 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 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 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 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 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 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 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 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 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 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 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 * 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 * 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 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 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 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 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 * 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