Line data Source code
1 : /*-
2 : * Copyright (c) 2011-2014 Baptiste Daroussin <bapt@FreeBSD.org>
3 : * Copyright (c) 2011 Will Andrews <will@FreeBSD.org>
4 : * All rights reserved.
5 : *
6 : * Redistribution and use in source and binary forms, with or without
7 : * modification, are permitted provided that the following conditions
8 : * are met:
9 : * 1. Redistributions of source code must retain the above copyright
10 : * notice, this list of conditions and the following disclaimer
11 : * in this position and unchanged.
12 : * 2. Redistributions in binary form must reproduce the above copyright
13 : * notice, this list of conditions and the following disclaimer in the
14 : * documentation and/or other materials provided with the distribution.
15 : *
16 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
17 : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 : * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
20 : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 : */
27 :
28 :
29 : #include <archive.h>
30 : #include <archive_entry.h>
31 : #include <assert.h>
32 : #include <fcntl.h>
33 : #include <fts.h>
34 : #include <string.h>
35 : #include <sys/mman.h>
36 : #include <pwd.h>
37 : #include <grp.h>
38 :
39 : #include "pkg.h"
40 : #include "private/event.h"
41 : #include "private/pkg.h"
42 :
43 : static const char *packing_set_format(struct archive *a, pkg_formats format);
44 :
45 : struct packing {
46 : bool pass;
47 : struct archive *aread;
48 : struct archive *awrite;
49 : struct archive_entry_linkresolver *resolver;
50 : };
51 :
52 : int
53 93 : packing_init(struct packing **pack, const char *path, pkg_formats format, bool passmode)
54 : {
55 : char archive_path[MAXPATHLEN];
56 : const char *ext;
57 :
58 93 : assert(pack != NULL);
59 :
60 93 : if (passmode && !is_dir(path)) {
61 0 : pkg_emit_error("When using passmode, a directory should be provided");
62 0 : return (EPKG_FATAL);
63 : }
64 :
65 93 : if ((*pack = calloc(1, sizeof(struct packing))) == NULL) {
66 0 : pkg_emit_errno("calloc", "packing");
67 0 : return (EPKG_FATAL);
68 : }
69 :
70 93 : (*pack)->aread = archive_read_disk_new();
71 93 : archive_read_disk_set_standard_lookup((*pack)->aread);
72 93 : archive_read_disk_set_symlink_physical((*pack)->aread);
73 :
74 93 : if (!passmode) {
75 92 : (*pack)->pass = false;
76 92 : (*pack)->awrite = archive_write_new();
77 92 : archive_write_set_format_pax_restricted((*pack)->awrite);
78 92 : ext = packing_set_format((*pack)->awrite, format);
79 92 : if (ext == NULL) {
80 0 : archive_read_close((*pack)->aread);
81 0 : archive_read_free((*pack)->aread);
82 0 : archive_write_close((*pack)->awrite);
83 0 : archive_write_free((*pack)->awrite);
84 0 : *pack = NULL;
85 0 : return EPKG_FATAL; /* error set by _set_format() */
86 : }
87 92 : snprintf(archive_path, sizeof(archive_path), "%s.%s", path,
88 : ext);
89 :
90 92 : pkg_debug(1, "Packing to file '%s'", archive_path);
91 92 : if (archive_write_open_filename(
92 92 : (*pack)->awrite, archive_path) != ARCHIVE_OK) {
93 0 : pkg_emit_errno("archive_write_open_filename",
94 : archive_path);
95 0 : archive_read_close((*pack)->aread);
96 0 : archive_read_free((*pack)->aread);
97 0 : archive_write_close((*pack)->awrite);
98 0 : archive_write_free((*pack)->awrite);
99 0 : *pack = NULL;
100 0 : return EPKG_FATAL;
101 : }
102 : } else { /* pass mode directly write to the disk */
103 1 : pkg_debug(1, "Packing to directory '%s' (pass mode)", path);
104 1 : (*pack)->pass = true;
105 1 : (*pack)->awrite = archive_write_disk_new();
106 1 : archive_write_disk_set_options((*pack)->awrite,
107 : EXTRACT_ARCHIVE_FLAGS);
108 : }
109 :
110 93 : (*pack)->resolver = archive_entry_linkresolver_new();
111 93 : archive_entry_linkresolver_set_strategy((*pack)->resolver,
112 : ARCHIVE_FORMAT_TAR_PAX_RESTRICTED);
113 :
114 93 : return (EPKG_OK);
115 : }
116 :
117 : int
118 118 : packing_append_buffer(struct packing *pack, const char *buffer,
119 : const char *path, int size)
120 : {
121 : struct archive_entry *entry;
122 118 : int ret = EPKG_OK;
123 :
124 118 : entry = archive_entry_new();
125 118 : archive_entry_clear(entry);
126 118 : archive_entry_set_filetype(entry, AE_IFREG);
127 118 : archive_entry_set_perm(entry, 0644);
128 118 : archive_entry_set_gname(entry, "wheel");
129 118 : archive_entry_set_uname(entry, "root");
130 118 : archive_entry_set_pathname(entry, path);
131 118 : archive_entry_set_size(entry, size);
132 118 : if (archive_write_header(pack->awrite, entry) == -1) {
133 0 : pkg_emit_errno("archive_write_header", path);
134 0 : ret = EPKG_FATAL;
135 0 : goto cleanup;
136 : }
137 :
138 118 : if (archive_write_data(pack->awrite, buffer, size) == -1) {
139 0 : pkg_emit_errno("archive_write_data", path);
140 0 : ret = EPKG_FATAL;
141 : }
142 :
143 : cleanup:
144 118 : archive_entry_free(entry);
145 :
146 118 : return (ret);
147 : }
148 :
149 : int
150 90 : packing_append_file_attr(struct packing *pack, const char *filepath,
151 : const char *newpath, const char *uname, const char *gname, mode_t perm,
152 : u_long fflags)
153 : {
154 : int fd;
155 : char *map;
156 90 : int retcode = EPKG_OK;
157 : int ret;
158 : struct stat st;
159 : struct archive_entry *entry, *sparse_entry;
160 : bool unset_timestamp;
161 :
162 90 : entry = archive_entry_new();
163 90 : archive_entry_copy_sourcepath(entry, filepath);
164 :
165 90 : pkg_debug(2, "Packing file '%s'", filepath);
166 :
167 90 : if (lstat(filepath, &st) != 0) {
168 0 : pkg_emit_errno("lstat", filepath);
169 0 : retcode = EPKG_FATAL;
170 0 : goto cleanup;
171 : }
172 :
173 90 : ret = archive_read_disk_entry_from_file(pack->aread, entry, -1,
174 : &st);
175 90 : if (ret != ARCHIVE_OK) {
176 0 : pkg_emit_error("%s: %s", filepath,
177 : archive_error_string(pack->aread));
178 0 : retcode = EPKG_FATAL;
179 0 : goto cleanup;
180 : }
181 :
182 90 : if (newpath != NULL)
183 90 : archive_entry_set_pathname(entry, newpath);
184 :
185 90 : if (archive_entry_filetype(entry) != AE_IFREG) {
186 8 : archive_entry_set_size(entry, 0);
187 : }
188 :
189 90 : if (uname != NULL && uname[0] != '\0') {
190 53 : if (pack->pass) {
191 0 : struct passwd* pw = getpwnam(uname);
192 0 : if (pw == NULL) {
193 0 : pkg_emit_error("Unknown user: '%s'", uname);
194 0 : retcode = EPKG_FATAL;
195 0 : goto cleanup;
196 : }
197 0 : archive_entry_set_uid(entry, pw->pw_uid);
198 : }
199 53 : archive_entry_set_uname(entry, uname);
200 : }
201 :
202 90 : if (gname != NULL && gname[0] != '\0') {
203 53 : if (pack->pass) {
204 0 : struct group *gr = (getgrnam(gname));
205 0 : if (gr == NULL) {
206 0 : pkg_emit_error("Unknown group: '%s'", gname);
207 0 : retcode = EPKG_FATAL;
208 0 : goto cleanup;
209 : }
210 0 : archive_entry_set_gid(entry, gr->gr_gid);
211 : }
212 53 : archive_entry_set_gname(entry, gname);
213 : }
214 :
215 90 : if (fflags > 0)
216 1 : archive_entry_set_fflags(entry, fflags, 0);
217 :
218 90 : if (perm != 0)
219 34 : archive_entry_set_perm(entry, perm);
220 :
221 90 : unset_timestamp = pkg_object_bool(pkg_config_get("UNSET_TIMESTAMP"));
222 :
223 90 : if (unset_timestamp) {
224 0 : archive_entry_unset_atime(entry);
225 0 : archive_entry_unset_ctime(entry);
226 0 : archive_entry_unset_mtime(entry);
227 0 : archive_entry_unset_birthtime(entry);
228 : }
229 :
230 90 : archive_entry_linkify(pack->resolver, &entry, &sparse_entry);
231 :
232 90 : if (sparse_entry != NULL && entry == NULL)
233 0 : entry = sparse_entry;
234 :
235 90 : archive_write_header(pack->awrite, entry);
236 :
237 90 : if (archive_entry_size(entry) > 0) {
238 34 : if ((fd = open(filepath, O_RDONLY)) < 0) {
239 0 : pkg_emit_errno("open", filepath);
240 0 : retcode = EPKG_FATAL;
241 0 : goto cleanup;
242 : }
243 : if (st.st_size > SSIZE_MAX) {
244 : char buf[BUFSIZ];
245 : int len;
246 :
247 : while ((len = read(fd, buf, sizeof(buf))) > 0)
248 : if (archive_write_data(pack->awrite, buf, len) == -1) {
249 : pkg_emit_errno("archive_write_data", "archive write error");
250 : retcode = EPKG_FATAL;
251 : break;
252 : }
253 :
254 : if (len == -1) {
255 : pkg_emit_errno("read", "file read error");
256 : retcode = EPKG_FATAL;
257 : }
258 : close(fd);
259 : }
260 : else {
261 34 : if ((map = mmap(NULL, st.st_size, PROT_READ,
262 : MAP_SHARED, fd, 0)) != MAP_FAILED) {
263 34 : close(fd);
264 34 : if (archive_write_data(pack->awrite, map, st.st_size) == -1) {
265 0 : pkg_emit_errno("archive_write_data", "archive write error");
266 0 : retcode = EPKG_FATAL;
267 : }
268 34 : munmap(map, st.st_size);
269 : }
270 : else {
271 0 : close(fd);
272 0 : pkg_emit_errno("open", filepath);
273 0 : retcode = EPKG_FATAL;
274 0 : goto cleanup;
275 : }
276 : }
277 : }
278 :
279 : cleanup:
280 90 : archive_entry_free(entry);
281 90 : return (retcode);
282 : }
283 :
284 : int
285 0 : packing_append_tree(struct packing *pack, const char *treepath,
286 : const char *newroot)
287 : {
288 0 : FTS *fts = NULL;
289 0 : FTSENT *fts_e = NULL;
290 : size_t treelen;
291 : struct sbuf *sb;
292 0 : char *paths[2] = { __DECONST(char *, treepath), NULL };
293 :
294 0 : treelen = strlen(treepath);
295 0 : fts = fts_open(paths, FTS_PHYSICAL | FTS_XDEV, NULL);
296 0 : if (fts == NULL)
297 0 : goto cleanup;
298 :
299 0 : sb = sbuf_new_auto();
300 0 : while ((fts_e = fts_read(fts)) != NULL) {
301 0 : switch(fts_e->fts_info) {
302 : case FTS_D:
303 : case FTS_DEFAULT:
304 : case FTS_F:
305 : case FTS_SL:
306 : case FTS_SLNONE:
307 : /* Entries not within this tree are irrelevant. */
308 0 : if (fts_e->fts_pathlen <= treelen)
309 0 : break;
310 0 : sbuf_clear(sb);
311 : /* Strip the prefix to obtain the target path */
312 0 : if (newroot) /* Prepend a root if one is specified */
313 0 : sbuf_cat(sb, newroot);
314 : /* +1 = skip trailing slash */
315 0 : sbuf_cat(sb, fts_e->fts_path + treelen + 1);
316 0 : sbuf_finish(sb);
317 0 : packing_append_file_attr(pack, fts_e->fts_name,
318 0 : sbuf_data(sb), NULL, NULL, 0, 0);
319 0 : break;
320 : case FTS_DC:
321 : case FTS_DNR:
322 : case FTS_ERR:
323 : case FTS_NS:
324 : /* XXX error cases, check fts_e->fts_errno and
325 : * bubble up the call chain */
326 0 : break;
327 : default:
328 0 : break;
329 : }
330 : }
331 0 : sbuf_free(sb);
332 : cleanup:
333 0 : fts_close(fts);
334 0 : return EPKG_OK;
335 : }
336 :
337 : void
338 100 : packing_finish(struct packing *pack)
339 : {
340 100 : if (pack == NULL)
341 107 : return;
342 :
343 93 : archive_read_close(pack->aread);
344 93 : archive_read_free(pack->aread);
345 :
346 93 : archive_write_close(pack->awrite);
347 93 : archive_write_free(pack->awrite);
348 :
349 93 : free(pack);
350 : }
351 :
352 : static const char *
353 92 : packing_set_format(struct archive *a, pkg_formats format)
354 : {
355 92 : const char *notsupp_fmt = "%s is not supported, trying %s";
356 :
357 92 : switch (format) {
358 : case TXZ:
359 92 : if (archive_write_add_filter_xz(a) == ARCHIVE_OK)
360 92 : return ("txz");
361 : else
362 0 : pkg_emit_error(notsupp_fmt, "xz", "bzip2");
363 : case TBZ:
364 0 : if (archive_write_add_filter_bzip2(a) == ARCHIVE_OK)
365 0 : return ("tbz");
366 : else
367 0 : pkg_emit_error(notsupp_fmt, "bzip2", "gzip");
368 : case TGZ:
369 0 : if (archive_write_add_filter_gzip(a) == ARCHIVE_OK)
370 0 : return ("tgz");
371 : else
372 0 : pkg_emit_error(notsupp_fmt, "gzip", "plain tar");
373 : case TAR:
374 0 : archive_write_add_filter_none(a);
375 0 : return ("tar");
376 : }
377 0 : return (NULL);
378 : }
379 :
380 : pkg_formats
381 34 : packing_format_from_string(const char *str)
382 : {
383 34 : if (str == NULL)
384 0 : return TXZ;
385 34 : if (strcmp(str, "txz") == 0)
386 34 : return TXZ;
387 0 : if (strcmp(str, "tbz") == 0)
388 0 : return TBZ;
389 0 : if (strcmp(str, "tgz") == 0)
390 0 : return TGZ;
391 0 : if (strcmp(str, "tar") == 0)
392 0 : return TAR;
393 0 : pkg_emit_error("unknown format %s, using txz", str);
394 0 : return TXZ;
395 : }
396 :
397 : const char*
398 336 : packing_format_to_string(pkg_formats format)
399 : {
400 336 : const char *res = NULL;
401 :
402 336 : switch (format) {
403 : case TXZ:
404 336 : res = "txz";
405 336 : break;
406 : case TBZ:
407 0 : res = "tbz";
408 0 : break;
409 : case TGZ:
410 0 : res = "tgz";
411 0 : break;
412 : case TAR:
413 0 : res = "tar";
414 0 : break;
415 : }
416 :
417 336 : return (res);
418 : }
|