File Coverage

arch_io.c
Criterion Covered Total %
statement 101 173 58.3
branch 59 142 41.5
condition n/a
subroutine n/a
pod n/a
total 160 315 50.7


line stmt bran cond sub pod time code
1             /*
2             * arch_io.c - registry + byte source/sink implementations.
3             *
4             * Mirrors the same shape File::Raw uses for file_plugin.h: a small
5             * static array of registered plugins, lookup by name, probe-based
6             * auto-detection. Sister dists call archive_register_plugin from BOOT.
7             *
8             * Byte sources/sinks: fd, membuf, gzip streaming inflater/deflater.
9             * The gzip wrappers talk to libz directly; when File::Raw::Gzip 0.02
10             * exposes its streaming state machine in gz.h these wrappers can be
11             * simplified to a pull-through.
12             */
13              
14             #define PERL_NO_GET_CONTEXT
15             #include "EXTERN.h"
16             #include "perl.h"
17             #include "XSUB.h"
18              
19             #include "archive_plugin.h"
20             #include "arch_io.h"
21              
22             #include
23             #include
24             #include
25             #include
26             #include
27             #include
28              
29             #ifdef __linux__
30             # include
31             # define ARCHIVE_HAVE_XATTR 1
32             #endif
33             #ifdef __APPLE__
34             # include
35             # define ARCHIVE_HAVE_XATTR 1
36             # define ARCHIVE_DARWIN_XATTR 1
37             #endif
38             #if defined(__FreeBSD__) || defined(__DragonFly__)
39             # include
40             # include
41             # define ARCHIVE_HAVE_EXTATTR 1
42             #endif
43              
44             /* ============================================================
45             * Registry
46             * ============================================================ */
47              
48             #define MAX_ARCHIVE_PLUGINS 16
49             static const ArchivePlugin *g_plugins[MAX_ARCHIVE_PLUGINS];
50             static int g_plugin_count = 0;
51              
52             int
53 29           archive_register_plugin(pTHX_ const ArchivePlugin *plugin)
54             {
55 29 50         if (!plugin || !plugin->name || !*plugin->name) return -1;
    50          
    50          
56 29 50         if (g_plugin_count >= MAX_ARCHIVE_PLUGINS) return -1;
57             int i;
58 29 50         for (i = 0; i < g_plugin_count; i++) {
59 0 0         if (strcmp(g_plugins[i]->name, plugin->name) == 0) return 0;
60             }
61 29           g_plugins[g_plugin_count++] = plugin;
62 29           return 1;
63             }
64              
65             int
66 0           archive_unregister_plugin(pTHX_ const char *name)
67             {
68             int i, j;
69 0 0         if (!name) return 0;
70 0 0         for (i = 0; i < g_plugin_count; i++) {
71 0 0         if (strcmp(g_plugins[i]->name, name) == 0) {
72 0 0         for (j = i + 1; j < g_plugin_count; j++) {
73 0           g_plugins[j - 1] = g_plugins[j];
74             }
75 0           g_plugin_count--;
76 0           return 1;
77             }
78             }
79 0           return 0;
80             }
81              
82             const ArchivePlugin *
83 109           archive_lookup_plugin(pTHX_ const char *name)
84             {
85             int i;
86 109 50         if (!name) return NULL;
87 110 100         for (i = 0; i < g_plugin_count; i++) {
88 109 100         if (strcmp(g_plugins[i]->name, name) == 0) return g_plugins[i];
89             }
90 1           return NULL;
91             }
92              
93             const ArchivePlugin *
94 0           archive_probe_for(pTHX_ const char *bytes, size_t len)
95             {
96 0           int i, best_score = 0;
97 0           const ArchivePlugin *best = NULL;
98 0 0         for (i = 0; i < g_plugin_count; i++) {
99 0 0         if (!g_plugins[i]->probe) continue;
100 0           int score = g_plugins[i]->probe(bytes, len);
101 0 0         if (score > best_score) {
102 0           best_score = score;
103 0           best = g_plugins[i];
104             }
105             }
106 0           return best;
107             }
108              
109             /* ============================================================
110             * fd source / sink
111             * ============================================================ */
112              
113             int
114 2763           archive_pull_fd(void *state, char *buf, size_t len)
115             {
116 2763           archive_fd_state_t *s = (archive_fd_state_t *)state;
117 2763           ssize_t total = 0;
118 5525 100         while ((size_t)total < len) {
119 2764           ssize_t n = read(s->fd, buf + total, len - total);
120 2764 50         if (n < 0) {
121 0 0         if (errno == EINTR) continue;
122 0           return -1;
123             }
124 2764 100         if (n == 0) break; /* EOF */
125 2762           total += n;
126             }
127 2763           return (int)total;
128             }
129              
130             int
131 2147           archive_push_fd(void *state, const char *buf, size_t len)
132             {
133 2147           archive_fd_state_t *s = (archive_fd_state_t *)state;
134 2147           size_t total = 0;
135 4294 100         while (total < len) {
136 2147           ssize_t n = write(s->fd, buf + total, len - total);
137 2147 50         if (n < 0) {
138 0 0         if (errno == EINTR) continue;
139 0           return -1;
140             }
141 2147           total += n;
142             }
143 2147           return (int)total;
144             }
145              
146             /* ============================================================
147             * membuf source / sink
148             * ============================================================ */
149              
150             int
151 0           archive_pull_membuf(void *state, char *buf, size_t len)
152             {
153 0           archive_membuf_src_t *s = (archive_membuf_src_t *)state;
154 0 0         size_t avail = (s->pos < s->len) ? (s->len - s->pos) : 0;
155 0           size_t take = (avail < len) ? avail : len;
156 0 0         if (take) memcpy(buf, s->buf + s->pos, take);
157 0           s->pos += take;
158 0           return (int)take;
159             }
160              
161             void
162 0           archive_membuf_sink_init(archive_membuf_sink_t *s)
163             {
164 0           s->buf = NULL;
165 0           s->cap = 0;
166 0           s->len = 0;
167 0           }
168              
169             void
170 0           archive_membuf_sink_free(archive_membuf_sink_t *s)
171             {
172 0 0         if (s->buf) free(s->buf);
173 0           s->buf = NULL;
174 0           s->cap = 0;
175 0           s->len = 0;
176 0           }
177              
178             int
179 0           archive_push_membuf(void *state, const char *buf, size_t len)
180             {
181 0           archive_membuf_sink_t *s = (archive_membuf_sink_t *)state;
182 0 0         if (s->len + len > s->cap) {
183 0 0         size_t want = s->cap ? s->cap * 2 : 4096;
184 0 0         while (want < s->len + len) want *= 2;
185 0           char *p = (char *)realloc(s->buf, want);
186 0 0         if (!p) return -1;
187 0           s->buf = p;
188 0           s->cap = want;
189             }
190 0           memcpy(s->buf + s->len, buf, len);
191 0           s->len += len;
192 0           return (int)len;
193             }
194              
195             /* ============================================================
196             * gzip streaming source
197             * ============================================================ */
198              
199             struct archive_gz_src {
200             int fd;
201             z_stream zs;
202             int zs_inited;
203             int stream_end;
204             char *raw;
205             size_t raw_cap;
206             int eof;
207             };
208              
209             archive_gz_src_t *
210 8           archive_gz_src_new(int fd, size_t chunk_size)
211             {
212 8           archive_gz_src_t *s = (archive_gz_src_t *)calloc(1, sizeof *s);
213 8 50         if (!s) return NULL;
214 8           s->fd = fd;
215 8 50         s->raw_cap = chunk_size ? chunk_size : ARCHIVE_DEFAULT_CHUNK;
216 8           s->raw = (char *)malloc(s->raw_cap);
217 8 50         if (!s->raw) { free(s); return NULL; }
218 8 50         if (inflateInit2(&s->zs, MAX_WBITS | 32) != Z_OK) {
219 0           free(s->raw);
220 0           free(s);
221 0           return NULL;
222             }
223 8           s->zs_inited = 1;
224 8           return s;
225             }
226              
227             void
228 8           archive_gz_src_free(archive_gz_src_t *s)
229             {
230 8 50         if (!s) return;
231 8 50         if (s->zs_inited) inflateEnd(&s->zs);
232 8           free(s->raw);
233 8           free(s);
234             }
235              
236             int
237 127           archive_pull_gz(void *state, char *buf, size_t len)
238             {
239 127           archive_gz_src_t *s = (archive_gz_src_t *)state;
240 127 50         if (s->stream_end) return 0;
241              
242 127           s->zs.next_out = (Bytef *)buf;
243 127           s->zs.avail_out = (uInt)len;
244              
245 328 100         while (s->zs.avail_out > 0) {
246 207 100         if (s->zs.avail_in == 0 && !s->eof) {
    50          
247 88           ssize_t n = read(s->fd, s->raw, s->raw_cap);
248 88 50         if (n < 0) {
249 0 0         if (errno == EINTR) continue;
250 0           return -1;
251             }
252 88 50         if (n == 0) {
253 0           s->eof = 1;
254             } else {
255 88           s->zs.next_in = (Bytef *)s->raw;
256 88           s->zs.avail_in = (uInt)n;
257             }
258             }
259              
260 207 50         int rc = inflate(&s->zs, s->eof ? Z_FINISH : Z_NO_FLUSH);
261 207 100         if (rc == Z_STREAM_END) {
262 5           s->stream_end = 1;
263 5           break;
264             }
265 202 50         if (rc == Z_BUF_ERROR && s->eof && s->zs.avail_in == 0) {
    0          
    0          
266 0           return -1;
267             }
268 202 100         if (rc != Z_OK && rc != Z_BUF_ERROR) return -1;
    50          
269             }
270              
271 126           return (int)(len - s->zs.avail_out);
272             }
273              
274             /* ============================================================
275             * gzip streaming sink
276             * ============================================================ */
277              
278             struct archive_gz_sink {
279             int fd;
280             z_stream zs;
281             int zs_inited;
282             char *out;
283             size_t out_cap;
284             };
285              
286             archive_gz_sink_t *
287 4           archive_gz_sink_new(int fd, size_t chunk_size, int level)
288             {
289 4           archive_gz_sink_t *s = (archive_gz_sink_t *)calloc(1, sizeof *s);
290 4 50         if (!s) return NULL;
291 4           s->fd = fd;
292 4 50         s->out_cap = chunk_size ? chunk_size : ARCHIVE_DEFAULT_CHUNK;
293 4           s->out = (char *)malloc(s->out_cap);
294 4 50         if (!s->out) { free(s); return NULL; }
295 4 50         if (level < 0 || level > 9) level = 6;
    50          
296 4 50         if (deflateInit2(&s->zs, level, Z_DEFLATED, MAX_WBITS | 16, 8,
297             Z_DEFAULT_STRATEGY) != Z_OK) {
298 0           free(s->out);
299 0           free(s);
300 0           return NULL;
301             }
302 4           s->zs_inited = 1;
303 4           return s;
304             }
305              
306             static int
307 26           gz_sink_drain(archive_gz_sink_t *s, int finish)
308             {
309 26 100         int flush = finish ? Z_FINISH : Z_NO_FLUSH;
310 79           for (;;) {
311 105           s->zs.next_out = (Bytef *)s->out;
312 105           s->zs.avail_out = (uInt)s->out_cap;
313 105           int rc = deflate(&s->zs, flush);
314 105           size_t produced = s->out_cap - s->zs.avail_out;
315 105 100         if (produced) {
316 88           archive_fd_state_t fdst = { s->fd };
317 88 50         if (archive_push_fd(&fdst, s->out, produced) < 0) return -1;
318             }
319 105 100         if (rc == Z_STREAM_END) return 0;
320 101 50         if (rc != Z_OK) return -1;
321 101 50         if (!finish && s->zs.avail_in == 0) return 0;
    100          
322             }
323             }
324              
325             int
326 22           archive_push_gz(void *state, const char *buf, size_t len)
327             {
328 22           archive_gz_sink_t *s = (archive_gz_sink_t *)state;
329 22           s->zs.next_in = (Bytef *)buf;
330 22           s->zs.avail_in = (uInt)len;
331 22 50         if (gz_sink_drain(s, 0) < 0) return -1;
332 22           return (int)len;
333             }
334              
335             int
336 4           archive_gz_sink_finish(archive_gz_sink_t *s)
337             {
338 4           s->zs.next_in = NULL;
339 4           s->zs.avail_in = 0;
340 4           return gz_sink_drain(s, 1);
341             }
342              
343             void
344 4           archive_gz_sink_free(archive_gz_sink_t *s)
345             {
346 4 50         if (!s) return;
347 4 50         if (s->zs_inited) deflateEnd(&s->zs);
348 4           free(s->out);
349 4           free(s);
350             }
351              
352             /* ============================================================
353             * Cross-platform xattr application
354             * ============================================================ */
355              
356             int
357 0           archive_apply_xattrs(int fd, const ArchiveXattr *xattrs, size_t n)
358             {
359 0 0         if (!xattrs || !n) return 0;
    0          
360             #if defined(ARCHIVE_HAVE_XATTR)
361             size_t i;
362 0 0         for (i = 0; i < n; i++) {
363             char keybuf[256];
364 0 0         if (xattrs[i].key_len >= sizeof keybuf) { errno = ENAMETOOLONG; return -1; }
365 0           memcpy(keybuf, xattrs[i].key, xattrs[i].key_len);
366 0           keybuf[xattrs[i].key_len] = '\0';
367             # if defined(ARCHIVE_DARWIN_XATTR)
368             if (fsetxattr(fd, keybuf, xattrs[i].value, xattrs[i].value_len, 0, 0) < 0) return -1;
369             # else
370 0 0         if (fsetxattr(fd, keybuf, xattrs[i].value, xattrs[i].value_len, 0) < 0) return -1;
371             # endif
372             }
373 0           return 0;
374             #elif defined(ARCHIVE_HAVE_EXTATTR)
375             size_t i;
376             for (i = 0; i < n; i++) {
377             char keybuf[256];
378             if (xattrs[i].key_len >= sizeof keybuf) { errno = ENAMETOOLONG; return -1; }
379             memcpy(keybuf, xattrs[i].key, xattrs[i].key_len);
380             keybuf[xattrs[i].key_len] = '\0';
381             const char *name = keybuf;
382             int ns = EXTATTR_NAMESPACE_USER;
383             if (strncmp(keybuf, "user.", 5) == 0) name = keybuf + 5;
384             else if (strncmp(keybuf, "system.", 7) == 0) {
385             name = keybuf + 7;
386             ns = EXTATTR_NAMESPACE_SYSTEM;
387             }
388             if (extattr_set_fd(fd, ns, name, xattrs[i].value, xattrs[i].value_len) < 0) return -1;
389             }
390             return 0;
391             #else
392             (void)fd;
393             return 0;
394             #endif
395             }