File Coverage

extract.c
Criterion Covered Total %
statement 73 143 51.0
branch 58 152 38.1
condition n/a
subroutine n/a
pod n/a
total 131 295 44.4


line stmt bran cond sub pod time code
1             /*
2             * extract.c - reusable per-entry write helpers (Phase A of XS refactor).
3             *
4             * Pure C except where the plugin's read_data callback drags pTHX_ in.
5             * No callers wired up yet; Phase B onward replaces Perl loops with
6             * single XSUB calls into these helpers.
7             */
8              
9             #define PERL_NO_GET_CONTEXT
10             #include "EXTERN.h"
11             #include "perl.h"
12              
13             #include "archive_plugin.h"
14             #include "extract.h"
15              
16             #include
17             #include
18             #include
19             #include
20             #include
21             #include
22             #include
23             #include
24             #include
25              
26             #define ARCHIVE_PATH_MAX 4096
27             #define ARCHIVE_DATA_CHUNK (64 * 1024)
28              
29             /* ============================================================
30             * Path safety
31             * ============================================================ */
32              
33             int
34 147           archive_path_is_safe(const char *name, size_t name_len)
35             {
36 147           size_t i = 0;
37 147 50         if (name_len == 0) return 0;
38 147 50         if (name[0] == '/') return 0; /* absolute */
39 576 100         while (i < name_len) {
40 284           size_t start = i;
41 2516 100         while (i < name_len && name[i] != '/') i++;
    100          
42 284           size_t comp = i - start;
43 284 100         if (comp == 2 && name[start] == '.' && name[start + 1] == '.') return 0;
    50          
    50          
44 282 100         if (i < name_len) i++; /* skip the '/' */
45             }
46 145           return 1;
47             }
48              
49             /* ============================================================
50             * Recursive mkpath
51             * ============================================================ */
52              
53             int
54 157           archive_mkpath(const char *path, uint32_t mode)
55             {
56             char buf[ARCHIVE_PATH_MAX];
57 157           size_t n = strlen(path);
58             size_t i;
59 157 50         if (n == 0) return 0;
60 157 50         if (n >= sizeof buf) { errno = ENAMETOOLONG; return -1; }
61 157           memcpy(buf, path, n + 1);
62              
63 4161 100         for (i = 0; i <= n; i++) {
64 4004 100         if (buf[i] == '/' || buf[i] == '\0') {
    100          
65 766 100         if (i == 0) continue; /* leading "/" */
66 609           char saved = buf[i];
67 609           buf[i] = '\0';
68             struct stat st;
69 609 100         if (stat(buf, &st) < 0) {
70 37 50         if (errno != ENOENT) { buf[i] = saved; return -1; }
71 37 50         if (mkdir(buf, mode ? mode : 0755) < 0 && errno != EEXIST) {
    50          
    0          
72 0           buf[i] = saved;
73 0           return -1;
74             }
75 572 50         } else if (!S_ISDIR(st.st_mode)) {
76 0           buf[i] = saved;
77 0           errno = ENOTDIR;
78 0           return -1;
79             }
80 609           buf[i] = saved;
81             }
82             }
83 157           return 0;
84             }
85              
86             int
87 144           archive_make_parent_dir(const char *path)
88             {
89 144           const char *slash = strrchr(path, '/');
90 144 50         if (!slash || slash == path) return 0; /* no parent or "/x" */
    50          
91 144           size_t parent_len = (size_t)(slash - path);
92             char buf[ARCHIVE_PATH_MAX];
93 144 50         if (parent_len >= sizeof buf) { errno = ENAMETOOLONG; return -1; }
94 144           memcpy(buf, path, parent_len);
95 144           buf[parent_len] = '\0';
96 144           return archive_mkpath(buf, 0755);
97             }
98              
99             /* ============================================================
100             * Directory / symlink entries
101             * ============================================================ */
102              
103             int
104 2           archive_extract_dir(const char *out_path, uint32_t mode)
105             {
106 2 50         if (archive_mkpath(out_path, mode ? (mode & 07777) : 0755) < 0) return -1;
    50          
107 2 50         if (mode) {
108             /* mkpath used `mode` for newly-created components; existing
109             * components keep their existing mode. Force the leaf to
110             * match what the archive wanted. */
111 2 50         if (chmod(out_path, mode & 07777) < 0) return -1;
112             }
113 2           return 0;
114             }
115              
116             int
117 2           archive_extract_symlink(const char *out_path, const char *target)
118             {
119             struct stat st;
120 2 50         if (lstat(out_path, &st) == 0 && S_ISLNK(st.st_mode)) {
    0          
121 0           unlink(out_path);
122             }
123 2 50         if (symlink(target, out_path) < 0) return -1;
124 2           return 0;
125             }
126              
127             /* ============================================================
128             * Metadata application
129             * ============================================================ */
130              
131             int
132 84           archive_apply_metadata_fd(int fd, const ArchiveEntry *e, int apply_xattrs)
133             {
134 84 50         if (e->mode) {
135 84 50         if (fchmod(fd, e->mode & 07777) < 0) return -1;
136             }
137 84 50         if (apply_xattrs && e->xattrs && e->xattr_count > 0) {
    50          
    0          
138 0 0         if (archive_apply_xattrs(fd, e->xattrs, e->xattr_count) < 0) return -1;
139             }
140 84           return 0;
141             }
142              
143             int
144 0           archive_apply_metadata_path(const char *path, const ArchiveEntry *e)
145             {
146 0 0         if (e->mode) {
147 0 0         if (chmod(path, e->mode & 07777) < 0) return -1;
148             }
149 0 0         if (e->mtime) {
150             struct utimbuf t;
151 0           t.actime = (time_t)e->mtime;
152 0           t.modtime = (time_t)e->mtime;
153 0 0         if (utime(path, &t) < 0) return -1;
154             }
155 0           return 0;
156             }
157              
158             /* ============================================================
159             * Payload pump
160             * ============================================================ */
161              
162             int
163 84           archive_write_entry_data(pTHX_ const ArchivePlugin *plugin,
164             void *cursor, int fd)
165             {
166             char buf[ARCHIVE_DATA_CHUNK];
167 163           for (;;) {
168 247           int n = plugin->read_data(aTHX_ plugin, cursor, buf, sizeof buf);
169 247 50         if (n < 0) return -1;
170 247 100         if (n == 0) break; /* end of entry */
171 163           size_t put = 0;
172 326 100         while (put < (size_t)n) {
173 163           ssize_t w = write(fd, buf + put, (size_t)n - put);
174 163 50         if (w < 0) {
175 0 0         if (errno == EINTR) continue;
176 0           return -1;
177             }
178 163           put += (size_t)w;
179             }
180             }
181 84           return 0;
182             }
183              
184             /* ============================================================
185             * Composite extract
186             * ============================================================ */
187              
188             int
189 84           archive_extract_entry(pTHX_ const ArchivePlugin *plugin,
190             void *cursor,
191             const ArchiveEntry *e,
192             const char *out_path,
193             int apply_xattrs,
194             const char **errstage)
195             {
196             int fd;
197              
198 84 50         if (errstage) *errstage = NULL;
199              
200 84           fd = open(out_path, O_WRONLY | O_CREAT | O_TRUNC,
201 84 50         e->mode ? (mode_t)(e->mode & 07777) : (mode_t)0644);
202 84 50         if (fd < 0) {
203 0 0         if (errstage) *errstage = "open";
204 0           return -1;
205             }
206              
207 84 50         if (archive_write_entry_data(aTHX_ plugin, cursor, fd) < 0) {
208 0 0         if (errstage) *errstage = "write";
209 0           int saved = errno;
210 0           close(fd);
211 0           unlink(out_path);
212 0           errno = saved;
213 0           return -1;
214             }
215              
216 84 50         if (archive_apply_metadata_fd(fd, e, apply_xattrs) < 0) {
217 0 0         if (errstage) *errstage = "chmod_or_xattr";
218 0           int saved = errno;
219 0           close(fd);
220 0           errno = saved;
221 0           return -1;
222             }
223              
224 84 50         if (close(fd) < 0) {
225 0 0         if (errstage) *errstage = "close";
226 0           return -1;
227             }
228              
229 84 100         if (e->mtime) {
230             struct utimbuf t;
231 4           t.actime = (time_t)e->mtime;
232 4           t.modtime = (time_t)e->mtime;
233 4 50         if (utime(out_path, &t) < 0) {
234 0 0         if (errstage) *errstage = "utime";
235 0           return -1;
236             }
237             }
238              
239 84           return 0;
240             }
241              
242             int
243 0           archive_extract_bytes(const ArchiveEntry *e,
244             const char *out_path,
245             const char *content, size_t content_len,
246             int apply_xattrs,
247             const char **errstage)
248             {
249             int fd;
250              
251 0 0         if (errstage) *errstage = NULL;
252              
253 0           fd = open(out_path, O_WRONLY | O_CREAT | O_TRUNC,
254 0 0         e->mode ? (mode_t)(e->mode & 07777) : (mode_t)0644);
255 0 0         if (fd < 0) {
256 0 0         if (errstage) *errstage = "open";
257 0           return -1;
258             }
259              
260 0 0         if (content_len > 0 && content) {
    0          
261 0           size_t put = 0;
262 0 0         while (put < content_len) {
263 0           ssize_t w = write(fd, content + put, content_len - put);
264 0 0         if (w < 0) {
265 0 0         if (errno == EINTR) continue;
266 0 0         if (errstage) *errstage = "write";
267 0           int saved = errno;
268 0           close(fd);
269 0           unlink(out_path);
270 0           errno = saved;
271 0           return -1;
272             }
273 0           put += (size_t)w;
274             }
275             }
276              
277 0 0         if (archive_apply_metadata_fd(fd, e, apply_xattrs) < 0) {
278 0 0         if (errstage) *errstage = "chmod_or_xattr";
279 0           int saved = errno;
280 0           close(fd);
281 0           errno = saved;
282 0           return -1;
283             }
284              
285 0 0         if (close(fd) < 0) {
286 0 0         if (errstage) *errstage = "close";
287 0           return -1;
288             }
289              
290 0 0         if (e->mtime) {
291             struct utimbuf t;
292 0           t.actime = (time_t)e->mtime;
293 0           t.modtime = (time_t)e->mtime;
294 0 0         if (utime(out_path, &t) < 0) {
295 0 0         if (errstage) *errstage = "utime";
296 0           return -1;
297             }
298             }
299              
300 0           return 0;
301             }