/*************************************** File uncompression. Part of the Routino routing software. ******************/ /****************** This file Copyright 2012 Andrew M. Bishop This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. ***************************************/ #include <stdlib.h> #include <unistd.h> #include <signal.h> #if defined(USE_BZIP2) && USE_BZIP2 #define BZ_NO_STDIO #include <bzlib.h> #endif #if defined(USE_GZIP) && USE_GZIP #include <zlib.h> #endif #include "logging.h" #include "uncompress.h" /* Local functions */ static int pipe_and_fork(int filefd,int *pipefd); #if defined(USE_BZIP2) && USE_BZIP2 static void uncompress_bzip2_pipe(int filefd,int pipefd); #endif #if defined(USE_GZIP) && USE_GZIP static void uncompress_gzip_pipe(int filefd,int pipefd); #endif /*++++++++++++++++++++++++++++++++++++++ Create a child process to uncompress data on a file descriptor as if it were a pipe. int Uncompress_Bzip2 Returns the file descriptor of the uncompressed end of the pipe. int filefd The file descriptor of the compressed end of the pipe. ++++++++++++++++++++++++++++++++++++++*/ int Uncompress_Bzip2(int filefd) { #if defined(USE_BZIP2) && USE_BZIP2 int pipefd=-1; if(pipe_and_fork(filefd,&pipefd)) return(pipefd); uncompress_bzip2_pipe(filefd,pipefd); exit(EXIT_SUCCESS); #else /* USE_BZIP2 */ logassert(0,"No bzip2 compression support available (re-compile and try again)"); return(0); #endif /* USE_BZIP2 */ } /*++++++++++++++++++++++++++++++++++++++ Create a child process to uncompress data on a file descriptor as if it were a pipe. int Uncompress_Gzip Returns the file descriptor of the uncompressed end of the pipe. int filefd The file descriptor of the compressed end of the pipe. ++++++++++++++++++++++++++++++++++++++*/ int Uncompress_Gzip(int filefd) { #if defined(USE_GZIP) && USE_GZIP int pipefd=-1; if(pipe_and_fork(filefd,&pipefd)) return(pipefd); uncompress_gzip_pipe(filefd,pipefd); exit(EXIT_SUCCESS); #else /* USE_GZIP */ logassert(0,"No gzip compression support available (re-compile and try again)"); return(0); #endif /* USE_GZIP */ } /*++++++++++++++++++++++++++++++++++++++ Create a pipe and then fork returning in the parent and child with a different end of the pipe. int pipe_and_fork Returns 1 for the reading (parent) end of the pipe and 0 for the writing (child) end. int filefd The file descriptor of the file. int *pipefd Returns the file descriptor for the end of the pipe. ++++++++++++++++++++++++++++++++++++++*/ static int pipe_and_fork(int filefd,int *pipefd) { int pipe_fd[2]={-1,-1}; pid_t childpid; #define PIPE_READER 0 #define PIPE_WRITER 1 if(pipe(pipe_fd)) { logassert(0,"Cannot create pipe for uncompressor (try without using a compressed file)"); return(1); } if((childpid=fork()) == -1) { logassert(0,"Cannot create new process for uncompressor (try without using a compressed file)"); return(1); } if(childpid==0) /* The child */ { int i; *pipefd=pipe_fd[PIPE_WRITER]; /* Close all unneeded file descriptors */ for(i=0;i<255;i++) if(i!=filefd && i!=*pipefd) close(i); return(0); } else /* The parent */ { struct sigaction action; *pipefd=pipe_fd[PIPE_READER]; /* Close all unneeded file descriptors */ close(pipe_fd[PIPE_WRITER]); close(filefd); /* Ignore child exiting and pipe signals */ /* SIGCHLD */ action.sa_handler=SIG_IGN; sigemptyset(&action.sa_mask); action.sa_flags=0; sigaction(SIGCHLD,&action,NULL); /* SIGPIPE */ action.sa_handler=SIG_IGN; sigemptyset(&action.sa_mask); action.sa_flags=0; sigaction(SIGPIPE,&action,NULL); return(1); } } #if defined(USE_BZIP2) && USE_BZIP2 /*++++++++++++++++++++++++++++++++++++++ Uncompress a file using bzip2 as a pipeline. int filefd The incoming, compressed, data. int pipefd The outgoing, uncompressed, data. ++++++++++++++++++++++++++++++++++++++*/ static void uncompress_bzip2_pipe(int filefd,int pipefd) { bz_stream bz={0}; char inbuffer[16384],outbuffer[16384]; int infinished=0; int state; if(BZ2_bzDecompressInit(&bz,0,0)!=BZ_OK) exit(EXIT_FAILURE); do { if(bz.avail_in==0 && !infinished) { ssize_t n=read(filefd,inbuffer,sizeof(inbuffer)); if(n<=0) infinished=1; else { bz.next_in=inbuffer; bz.avail_in=n; } } bz.next_out=outbuffer; bz.avail_out=sizeof(outbuffer); state=BZ2_bzDecompress(&bz); if(state!=BZ_OK && state!=BZ_STREAM_END) exit(EXIT_FAILURE); if(bz.avail_out<sizeof(outbuffer)) { char *p; ssize_t m,n; p=outbuffer; n=sizeof(outbuffer)-bz.avail_out; while(n>0) { m=write(pipefd,p,n); if(m<=0) exit(EXIT_FAILURE); p+=m; n-=m; } } } while(state!=BZ_STREAM_END); if(BZ2_bzDecompressEnd(&bz)!=BZ_OK) exit(EXIT_FAILURE); exit(EXIT_SUCCESS); } #endif /* USE_BZIP2 */ #if defined(USE_GZIP) && USE_GZIP /*++++++++++++++++++++++++++++++++++++++ Uncompress a file using gzip as a pipeline. int filefd The incoming, compressed, data. int pipefd The outgoing, uncompressed, data. ++++++++++++++++++++++++++++++++++++++*/ static void uncompress_gzip_pipe(int filefd,int pipefd) { z_stream z={0}; unsigned char inbuffer[16384],outbuffer[16384]; int infinished=0; int state; if((state=inflateInit2(&z,15+32))!=Z_OK) exit(EXIT_FAILURE); do { if(z.avail_in==0 && !infinished) { ssize_t n=read(filefd,inbuffer,sizeof(inbuffer)); if(n<=0) infinished=1; else { z.next_in=inbuffer; z.avail_in=n; } } z.next_out=outbuffer; z.avail_out=sizeof(outbuffer); state=inflate(&z,Z_NO_FLUSH); if(state!=Z_OK && state!=Z_STREAM_END) { exit(EXIT_FAILURE); } if(z.avail_out<sizeof(outbuffer)) { unsigned char *p; ssize_t n,m; p=outbuffer; n=sizeof(outbuffer)-z.avail_out; while(n>0) { m=write(pipefd,p,n); if(m<=0) exit(EXIT_FAILURE); p+=m; n-=m; } } } while(state!=Z_STREAM_END); if(inflateEnd(&z)!=Z_OK) exit(EXIT_FAILURE); exit(EXIT_SUCCESS); } #endif /* USE_GZIP */