tests: fix build of opkg_extract_test
[oweals/opkg-lede.git] / libbb / gzip.c
1 /*
2  *  Copyright (C) 2016 Jo-Philipp Wich <jo@mein.io>
3  *  Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
4  *
5  *  Zlib decrompression utility routines.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU Library General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */
21
22 #include <string.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <poll.h>
27 #include <stdlib.h>
28 #include <sys/stat.h>
29 #include <sys/wait.h>
30
31 #include "gzip.h"
32
33 static void to_devnull(int fd)
34 {
35         int devnull = open("/dev/null", fd ? O_WRONLY : O_RDONLY);
36
37         if (devnull >= 0)
38                 dup2(devnull, fd);
39
40         if (devnull > STDERR_FILENO)
41                 close(devnull);
42 }
43
44 void *gzip_thread(void *ptr)
45 {
46         struct gzip_handle *zh = ptr;
47         char buf[4096];
48         int len, ret;
49
50         while (1) {
51                 if (zh->file)
52                         len = fread(buf, 1, sizeof(buf), zh->file);
53                 else if (zh->gzip)
54                         len = gzip_read(zh->gzip, buf, sizeof(buf));
55
56                 if (len <= 0)
57                         break;
58
59                 do {
60                         ret = write(zh->wfd, buf, len);
61                 } while (ret == -1 && errno == EINTR);
62         }
63
64         close(zh->wfd);
65         zh->wfd = -1;
66 }
67
68 int gzip_exec(struct gzip_handle *zh, const char *filename)
69 {
70         int rpipe[2] = { -1, -1 }, wpipe[2] = {
71         -1, -1};
72         struct sigaction pipe_sa = {.sa_handler = SIG_IGN };
73
74         zh->rfd = -1;
75         zh->wfd = -1;
76
77         if (sigaction(SIGPIPE, &pipe_sa, &zh->pipe_sa) < 0)
78                 return -1;
79
80         if (pipe(rpipe) < 0)
81                 return -1;
82
83         if (!filename && pipe(wpipe) < 0) {
84                 close(rpipe[0]);
85                 close(rpipe[1]);
86                 return -1;
87         }
88
89         zh->pid = vfork();
90
91         switch (zh->pid) {
92         case -1:
93                 return -1;
94
95         case 0:
96                 to_devnull(STDERR_FILENO);
97
98                 if (filename) {
99                         to_devnull(STDIN_FILENO);
100                 } else {
101                         dup2(wpipe[0], STDIN_FILENO);
102                         close(wpipe[0]);
103                         close(wpipe[1]);
104                 }
105
106                 dup2(rpipe[1], STDOUT_FILENO);
107                 close(rpipe[0]);
108                 close(rpipe[1]);
109
110                 execlp("gzip", "gzip", "-d", "-c", filename, NULL);
111                 exit(-1);
112
113         default:
114                 zh->rfd = rpipe[0];
115                 zh->wfd = wpipe[1];
116
117                 fcntl(zh->rfd, F_SETFD, fcntl(zh->rfd, F_GETFD) | FD_CLOEXEC);
118                 close(rpipe[1]);
119
120                 if (zh->wfd >= 0) {
121                         fcntl(zh->wfd, F_SETFD,
122                               fcntl(zh->wfd, F_GETFD) | FD_CLOEXEC);
123                         close(wpipe[0]);
124                         pthread_create(&zh->thread, NULL, gzip_thread, zh);
125                 }
126         }
127
128         return 0;
129 }
130
131 ssize_t gzip_read(struct gzip_handle * zh, char *buf, ssize_t len)
132 {
133         ssize_t ret;
134
135         do {
136                 ret = read(zh->rfd, buf, len);
137         } while (ret == -1 && errno != EINTR);
138
139         return ret;
140 }
141
142 ssize_t gzip_copy(struct gzip_handle * zh, FILE * out, ssize_t len)
143 {
144         char buf[4096];
145         ssize_t rlen, total = 0;
146
147         while (len > 0) {
148                 rlen = gzip_read(zh, buf,
149                                  (len > sizeof(buf)) ? sizeof(buf) : len);
150
151                 if (rlen <= 0)
152                         break;
153
154                 if (out != NULL) {
155                         if (fwrite(buf, 1, rlen, out) != rlen)
156                                 break;
157                 }
158
159                 len -= rlen;
160                 total += rlen;
161         }
162
163         return total;
164 }
165
166 FILE *gzip_fdopen(struct gzip_handle * zh, const char *filename)
167 {
168         memset(zh, 0, sizeof(*zh));
169
170         if (!filename || gzip_exec(zh, filename) < 0)
171                 return NULL;
172
173         fcntl(zh->rfd, F_SETFL, fcntl(zh->rfd, F_GETFL) & ~O_NONBLOCK);
174
175         return fdopen(zh->rfd, "r");
176 }
177
178 int gzip_close(struct gzip_handle *zh)
179 {
180         int code = -1;
181
182         if (zh->rfd >= 0)
183                 close(zh->rfd);
184
185         if (zh->wfd >= 0)
186                 close(zh->wfd);
187
188         if (zh->pid > 0) {
189                 kill(zh->pid, SIGKILL);
190                 waitpid(zh->pid, &code, 0);
191         }
192
193         if (zh->file)
194                 fclose(zh->file);
195
196         if (zh->thread)
197                 pthread_join(zh->thread, NULL);
198
199         sigaction(SIGPIPE, &zh->pipe_sa, NULL);
200
201         return WIFEXITED(code) ? WEXITSTATUS(code) : -1;
202 }