conffile_list_pop: remove unused function
[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 static void *gzip_thread(void *ptr)
45 {
46         struct gzip_handle *zh = ptr;
47         char buf[4096];
48         int len = 0, 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         return NULL;
68 }
69
70 int gzip_exec(struct gzip_handle *zh, const char *filename)
71 {
72         int rpipe[2] = { -1, -1 }, wpipe[2] = {
73         -1, -1};
74         struct sigaction pipe_sa = {.sa_handler = SIG_IGN };
75
76         zh->rfd = -1;
77         zh->wfd = -1;
78
79         if (sigaction(SIGPIPE, &pipe_sa, &zh->pipe_sa) < 0)
80                 return -1;
81
82         if (pipe(rpipe) < 0)
83                 return -1;
84
85         if (!filename && pipe(wpipe) < 0) {
86                 close(rpipe[0]);
87                 close(rpipe[1]);
88                 return -1;
89         }
90
91         zh->pid = vfork();
92
93         switch (zh->pid) {
94         case -1:
95                 return -1;
96
97         case 0:
98                 to_devnull(STDERR_FILENO);
99
100                 if (filename) {
101                         to_devnull(STDIN_FILENO);
102                 } else {
103                         dup2(wpipe[0], STDIN_FILENO);
104                         close(wpipe[0]);
105                         close(wpipe[1]);
106                 }
107
108                 dup2(rpipe[1], STDOUT_FILENO);
109                 close(rpipe[0]);
110                 close(rpipe[1]);
111
112                 execlp("gzip", "gzip", "-d", "-c", filename, NULL);
113                 exit(-1);
114
115         default:
116                 zh->rfd = rpipe[0];
117                 zh->wfd = wpipe[1];
118
119                 fcntl(zh->rfd, F_SETFD, fcntl(zh->rfd, F_GETFD) | FD_CLOEXEC);
120                 close(rpipe[1]);
121
122                 if (zh->wfd >= 0) {
123                         fcntl(zh->wfd, F_SETFD,
124                               fcntl(zh->wfd, F_GETFD) | FD_CLOEXEC);
125                         close(wpipe[0]);
126                         pthread_create(&zh->thread, NULL, gzip_thread, zh);
127                 }
128         }
129
130         return 0;
131 }
132
133 ssize_t gzip_read(struct gzip_handle * zh, void *buf, ssize_t len)
134 {
135         ssize_t ret;
136
137         do {
138                 ret = read(zh->rfd, buf, len);
139         } while (ret == -1 && errno != EINTR);
140
141         return ret;
142 }
143
144 ssize_t gzip_copy(struct gzip_handle * zh, FILE * out, ssize_t len)
145 {
146         char buf[4096];
147         ssize_t rlen, total = 0;
148
149         while (len > 0) {
150                 rlen = gzip_read(zh, buf,
151                                  (len > sizeof(buf)) ? sizeof(buf) : len);
152
153                 if (rlen <= 0)
154                         break;
155
156                 if (out != NULL) {
157                         if (fwrite(buf, 1, rlen, out) != rlen)
158                                 break;
159                 }
160
161                 len -= rlen;
162                 total += rlen;
163         }
164
165         return total;
166 }
167
168 FILE *gzip_fdopen(struct gzip_handle * zh, const char *filename)
169 {
170         memset(zh, 0, sizeof(*zh));
171
172         if (!filename || gzip_exec(zh, filename) < 0)
173                 return NULL;
174
175         fcntl(zh->rfd, F_SETFL, fcntl(zh->rfd, F_GETFL) & ~O_NONBLOCK);
176
177         return fdopen(zh->rfd, "r");
178 }
179
180 int gzip_close(struct gzip_handle *zh)
181 {
182         int code = -1;
183
184         if (zh->rfd >= 0)
185                 close(zh->rfd);
186
187         if (zh->wfd >= 0)
188                 close(zh->wfd);
189
190         if (zh->pid > 0) {
191                 kill(zh->pid, SIGKILL);
192                 waitpid(zh->pid, &code, 0);
193         }
194
195         if (zh->file)
196                 fclose(zh->file);
197
198         if (zh->thread)
199                 pthread_join(zh->thread, NULL);
200
201         sigaction(SIGPIPE, &zh->pipe_sa, NULL);
202
203         return WIFEXITED(code) ? WEXITSTATUS(code) : -1;
204 }