Line data Source code
1 : /*-
2 : * Copyright (c) 2011-2014 Baptiste Daroussin <bapt@FreeBSD.org>
3 : * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
4 : * Copyright (c) 2014-2015 Matthew Seaman <matthew@FreeBSD.org>
5 : * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
6 : * All rights reserved.
7 : *
8 : * Redistribution and use in source and binary forms, with or without
9 : * modification, are permitted provided that the following conditions
10 : * are met:
11 : * 1. Redistributions of source code must retain the above copyright
12 : * notice, this list of conditions and the following disclaimer
13 : * in this position and unchanged.
14 : * 2. Redistributions in binary form must reproduce the above copyright
15 : * notice, this list of conditions and the following disclaimer in the
16 : * documentation and/or other materials provided with the distribution.
17 : *
18 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
19 : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 : * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
22 : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 : */
29 :
30 : #include <sys/stat.h>
31 :
32 : #include <errno.h>
33 : #include <regex.h>
34 : #include <fcntl.h>
35 :
36 : #include <bsd_compat.h>
37 :
38 : #include "pkg.h"
39 : #include "private/event.h"
40 : #include "private/pkg.h"
41 :
42 : #define TICK 100
43 :
44 : static int pkg_create_from_dir(struct pkg *, const char *, struct packing *);
45 : static void counter_init(const char *what, int64_t max);
46 : static void counter_count();
47 : static void counter_end();
48 :
49 : static int
50 59 : pkg_create_from_dir(struct pkg *pkg, const char *root,
51 : struct packing *pkg_archive)
52 : {
53 : char fpath[MAXPATHLEN];
54 59 : struct pkg_file *file = NULL;
55 59 : struct pkg_dir *dir = NULL;
56 : int ret;
57 : struct stat st;
58 59 : int64_t flatsize = 0;
59 : int64_t nfiles;
60 : const char *relocation;
61 : hardlinks_t *hardlinks;
62 :
63 59 : if (pkg_is_valid(pkg) != EPKG_OK) {
64 0 : pkg_emit_error("the package is not valid");
65 0 : return (EPKG_FATAL);
66 : }
67 :
68 59 : relocation = pkg_kv_get(&pkg->annotations, "relocated");
69 59 : if (relocation == NULL)
70 59 : relocation = "";
71 59 : if (pkg_rootdir != NULL)
72 0 : relocation = pkg_rootdir;
73 :
74 : /*
75 : * Get / compute size / checksum if not provided in the manifest
76 : */
77 :
78 59 : nfiles = kh_count(pkg->files);
79 59 : counter_init("file sizes/checksums", nfiles);
80 :
81 59 : hardlinks = kh_init_hardlinks();
82 166 : while (pkg_files(pkg, &file) == EPKG_OK) {
83 :
84 48 : snprintf(fpath, sizeof(fpath), "%s%s%s", root ? root : "",
85 48 : relocation, file->path);
86 :
87 48 : if (lstat(fpath, &st) == -1) {
88 0 : pkg_emit_error("file '%s' is missing", fpath);
89 0 : return (EPKG_FATAL);
90 : }
91 :
92 48 : if (file->size == 0)
93 48 : file->size = (int64_t)st.st_size;
94 :
95 48 : if (st.st_nlink == 1 || !check_for_hardlink(hardlinks, &st)) {
96 48 : flatsize += file->size;
97 : }
98 :
99 48 : file->sum = pkg_checksum_generate_file(fpath,
100 : PKG_HASH_TYPE_SHA256_HEX);
101 48 : if (file->sum == NULL)
102 0 : return (EPKG_FATAL);
103 :
104 48 : counter_count();
105 : }
106 59 : kh_destroy_hardlinks(hardlinks);
107 :
108 59 : counter_end();
109 :
110 59 : pkg->flatsize = flatsize;
111 :
112 59 : if (pkg->type == PKG_OLD_FILE) {
113 0 : pkg_emit_error("Cannot create an old format package");
114 0 : return (EPKG_FATAL);
115 : } else {
116 : /*
117 : * Register shared libraries used by the package if
118 : * SHLIBS enabled in conf. Deletes shlib info if not.
119 : */
120 59 : struct sbuf *b = sbuf_new_auto();
121 :
122 59 : pkg_analyse_files(NULL, pkg, root);
123 :
124 59 : pkg_emit_manifest_sbuf(pkg, b, PKG_MANIFEST_EMIT_COMPACT, NULL);
125 59 : packing_append_buffer(pkg_archive, sbuf_data(b), "+COMPACT_MANIFEST", sbuf_len(b));
126 59 : sbuf_clear(b);
127 59 : pkg_emit_manifest_sbuf(pkg, b, 0, NULL);
128 59 : sbuf_finish(b);
129 59 : packing_append_buffer(pkg_archive, sbuf_data(b), "+MANIFEST", sbuf_len(b));
130 59 : sbuf_delete(b);
131 : }
132 :
133 59 : counter_init("packing files", nfiles);
134 :
135 166 : while (pkg_files(pkg, &file) == EPKG_OK) {
136 :
137 48 : snprintf(fpath, sizeof(fpath), "%s%s%s", root ? root : "",
138 48 : relocation, file->path);
139 :
140 144 : ret = packing_append_file_attr(pkg_archive, fpath, file->path,
141 144 : file->uname, file->gname, file->perm, file->fflags);
142 48 : if (developer_mode && ret != EPKG_OK)
143 0 : return (ret);
144 48 : counter_count();
145 : }
146 :
147 59 : counter_end();
148 :
149 59 : nfiles = kh_count(pkg->dirs);
150 59 : counter_init("packing directories", nfiles);
151 :
152 126 : while (pkg_dirs(pkg, &dir) == EPKG_OK) {
153 8 : snprintf(fpath, sizeof(fpath), "%s%s%s", root ? root : "",
154 8 : relocation, dir->path);
155 :
156 24 : ret = packing_append_file_attr(pkg_archive, fpath, dir->path,
157 24 : dir->uname, dir->gname, dir->perm, dir->fflags);
158 8 : if (developer_mode && ret != EPKG_OK)
159 0 : return (ret);
160 8 : counter_count();
161 : }
162 :
163 59 : counter_end();
164 :
165 59 : return (EPKG_OK);
166 : }
167 :
168 : static struct packing *
169 59 : pkg_create_archive(const char *outdir, struct pkg *pkg, pkg_formats format,
170 : unsigned required_flags)
171 : {
172 59 : char *pkg_path = NULL;
173 59 : struct packing *pkg_archive = NULL;
174 :
175 : /*
176 : * Ensure that we have all the information we need
177 : */
178 59 : if (pkg->type != PKG_OLD_FILE)
179 59 : assert((pkg->flags & required_flags) == required_flags);
180 :
181 59 : if (mkdirs(outdir) != EPKG_OK)
182 0 : return NULL;
183 :
184 59 : if (pkg_asprintf(&pkg_path, "%S/%n-%v", outdir, pkg, pkg) == -1) {
185 0 : pkg_emit_errno("pkg_asprintf", "");
186 0 : return (NULL);
187 : }
188 :
189 59 : if (packing_init(&pkg_archive, pkg_path, format, false) != EPKG_OK)
190 0 : pkg_archive = NULL;
191 :
192 59 : free(pkg_path);
193 :
194 59 : return pkg_archive;
195 : }
196 :
197 : static const char * const scripts[] = {
198 : "+INSTALL",
199 : "+PRE_INSTALL",
200 : "+POST_INSTALL",
201 : "+POST_INSTALL",
202 : "+DEINSTALL",
203 : "+PRE_DEINSTALL",
204 : "+POST_DEINSTALL",
205 : "+UPGRADE",
206 : "+PRE_UPGRADE",
207 : "+POST_UPGRADE",
208 : "pkg-install",
209 : "pkg-pre-install",
210 : "pkg-post-install",
211 : "pkg-deinstall",
212 : "pkg-pre-deinstall",
213 : "pkg-post-deinstall",
214 : "pkg-upgrade",
215 : "pkg-pre-upgrade",
216 : "pkg-post-upgrade",
217 : NULL
218 : };
219 :
220 :
221 : /* The "no concessions to old pkg_tools" variant: just get everything
222 : * from the manifest */
223 : int
224 41 : pkg_create_from_manifest(const char *outdir, pkg_formats format,
225 : const char *rootdir, const char *manifest, const char *plist)
226 : {
227 41 : struct pkg *pkg = NULL;
228 41 : struct packing *pkg_archive = NULL;
229 : char arch[BUFSIZ];
230 41 : int ret = ENOMEM;
231 41 : struct pkg_manifest_key *keys = NULL;
232 :
233 41 : pkg_debug(1, "Creating package from stage directory: '%s'", rootdir);
234 :
235 41 : if(pkg_new(&pkg, PKG_FILE) != EPKG_OK) {
236 0 : ret = EPKG_FATAL;
237 0 : goto cleanup;
238 : }
239 :
240 41 : pkg_manifest_keys_new(&keys);
241 41 : if ((ret = pkg_parse_manifest_file(pkg, manifest, keys)) != EPKG_OK) {
242 0 : ret = EPKG_FATAL;
243 0 : goto cleanup;
244 : }
245 :
246 : /* if no arch autodetermine it */
247 41 : if (pkg->abi == NULL) {
248 24 : pkg_get_myarch(arch, BUFSIZ);
249 24 : pkg->abi = strdup(arch);
250 : }
251 :
252 42 : if (plist != NULL &&
253 1 : ports_parse_plist(pkg, plist, rootdir) != EPKG_OK) {
254 0 : ret = EPKG_FATAL;
255 0 : goto cleanup;
256 : }
257 :
258 : /* Create the archive */
259 41 : pkg_archive = pkg_create_archive(outdir, pkg, format, 0);
260 41 : if (pkg_archive == NULL) {
261 0 : ret = EPKG_FATAL; /* XXX do better */
262 0 : goto cleanup;
263 : }
264 :
265 41 : pkg_create_from_dir(pkg, rootdir, pkg_archive);
266 41 : ret = EPKG_OK;
267 :
268 : cleanup:
269 41 : free(pkg);
270 41 : pkg_manifest_keys_free(keys);
271 41 : packing_finish(pkg_archive);
272 :
273 41 : return (ret);
274 : }
275 :
276 : static void
277 25 : pkg_load_from_file(int fd, struct pkg *pkg, pkg_attr attr, const char *path)
278 : {
279 :
280 25 : if (faccessat(fd, path, F_OK, 0) == 0) {
281 0 : pkg_debug(1, "Reading: '%s'", path);
282 0 : pkg_set_from_fileat(fd, pkg, attr, path, false);
283 : }
284 25 : }
285 :
286 : int
287 25 : pkg_create_staged(const char *outdir, pkg_formats format, const char *rootdir,
288 : const char *md_dir, char *plist)
289 : {
290 25 : struct pkg *pkg = NULL;
291 25 : struct pkg_file *file = NULL;
292 25 : struct pkg_dir *dir = NULL;
293 25 : struct packing *pkg_archive = NULL;
294 25 : char *manifest = NULL;
295 : char arch[BUFSIZ];
296 25 : int ret = ENOMEM;
297 : int i, mfd;
298 : regex_t preg;
299 : regmatch_t pmatch[2];
300 : size_t size;
301 25 : struct pkg_manifest_key *keys = NULL;
302 :
303 25 : pkg_debug(1, "Creating package from stage directory: '%s'", rootdir);
304 :
305 25 : if ((mfd = open(md_dir, O_DIRECTORY)) == -1) {
306 0 : pkg_emit_errno("open", md_dir);
307 0 : goto cleanup;
308 : }
309 :
310 25 : if(pkg_new(&pkg, PKG_FILE) != EPKG_OK) {
311 0 : ret = EPKG_FATAL;
312 0 : goto cleanup;
313 : }
314 :
315 25 : pkg_manifest_keys_new(&keys);
316 : /* Load the manifest from the metadata directory */
317 25 : if ((ret = pkg_parse_manifest_fileat(mfd, pkg, "+MANIFEST", keys))
318 : != EPKG_OK) {
319 0 : ret = EPKG_FATAL;
320 0 : goto cleanup;
321 : }
322 :
323 : /* if no descriptions provided then try to get it from a file */
324 25 : if (pkg->desc == NULL)
325 0 : pkg_load_from_file(mfd, pkg, PKG_DESC, "+DESC");
326 :
327 : /* if no message try to get it from a file */
328 25 : if (pkg->message == NULL)
329 25 : pkg_load_from_file(mfd, pkg, PKG_MESSAGE, "+DISPLAY");
330 :
331 : /* if no arch autodetermine it */
332 25 : if (pkg->abi == NULL) {
333 1 : pkg_get_myarch(arch, BUFSIZ);
334 1 : pkg->abi = strdup(arch);
335 : }
336 :
337 500 : for (i = 0; scripts[i] != NULL; i++) {
338 475 : if (faccessat(mfd, scripts[i], F_OK, 0) == 0)
339 0 : pkg_addscript_fileat(mfd, pkg, scripts[i]);
340 : }
341 :
342 49 : if (plist != NULL &&
343 24 : ports_parse_plist(pkg, plist, rootdir) != EPKG_OK) {
344 7 : ret = EPKG_FATAL;
345 7 : goto cleanup;
346 : }
347 :
348 18 : if (pkg->www == NULL) {
349 0 : if (pkg->desc == NULL) {
350 0 : pkg_emit_error("No www or desc defined in manifest");
351 0 : ret = EPKG_FATAL;
352 0 : goto cleanup;
353 : }
354 0 : regcomp(&preg, "^WWW:[[:space:]]*(.*)$",
355 : REG_EXTENDED|REG_ICASE|REG_NEWLINE);
356 0 : if (regexec(&preg, pkg->desc, 2, pmatch, 0) == 0) {
357 0 : size = pmatch[1].rm_eo - pmatch[1].rm_so;
358 0 : pkg->www = strndup(&pkg->desc[pmatch[1].rm_so], size);
359 : } else {
360 0 : pkg->www = strdup("UNKNOWN");
361 : }
362 0 : regfree(&preg);
363 : }
364 :
365 : /* Create the archive */
366 18 : pkg_archive = pkg_create_archive(outdir, pkg, format, 0);
367 18 : if (pkg_archive == NULL) {
368 0 : ret = EPKG_FATAL; /* XXX do better */
369 0 : goto cleanup;
370 : }
371 :
372 : /* XXX: autoplist support doesn't work right with meta-ports */
373 : if (0 && pkg_files(pkg, &file) != EPKG_OK &&
374 : pkg_dirs(pkg, &dir) != EPKG_OK) {
375 : /* Now traverse the file directories, adding to the archive */
376 : packing_append_tree(pkg_archive, md_dir, NULL);
377 : packing_append_tree(pkg_archive, rootdir, "/");
378 : ret = EPKG_OK;
379 : } else {
380 18 : ret = pkg_create_from_dir(pkg, rootdir, pkg_archive);
381 : }
382 :
383 :
384 : cleanup:
385 25 : if (mfd != -1)
386 25 : close(mfd);
387 25 : free(pkg);
388 25 : free(manifest);
389 25 : pkg_manifest_keys_free(keys);
390 25 : packing_finish(pkg_archive);
391 :
392 25 : return (ret);
393 : }
394 :
395 : int
396 0 : pkg_create_installed(const char *outdir, pkg_formats format, struct pkg *pkg)
397 : {
398 : struct packing *pkg_archive;
399 :
400 0 : unsigned required_flags = PKG_LOAD_DEPS | PKG_LOAD_FILES |
401 : PKG_LOAD_CATEGORIES | PKG_LOAD_DIRS | PKG_LOAD_SCRIPTS |
402 : PKG_LOAD_OPTIONS | PKG_LOAD_LICENSES ;
403 :
404 0 : assert(pkg->type == PKG_INSTALLED || pkg->type == PKG_OLD_FILE);
405 :
406 0 : pkg_archive = pkg_create_archive(outdir, pkg, format, required_flags);
407 0 : if (pkg_archive == NULL) {
408 0 : pkg_emit_error("unable to create archive");
409 0 : return (EPKG_FATAL);
410 : }
411 :
412 0 : pkg_create_from_dir(pkg, NULL, pkg_archive);
413 :
414 0 : packing_finish(pkg_archive);
415 :
416 0 : return (EPKG_OK);
417 : }
418 :
419 : static int64_t count;
420 : static int64_t maxcount;
421 : static const char *what;
422 :
423 177 : static int magnitude(int64_t num)
424 : {
425 : int oom;
426 :
427 177 : if (num == 0)
428 79 : return (1);
429 98 : if (num < 0)
430 0 : num = -num;
431 :
432 98 : for (oom = 1; num >= 10; oom++)
433 0 : num /= 10;
434 :
435 98 : return (oom);
436 : }
437 :
438 : static void
439 177 : counter_init(const char *count_what, int64_t max)
440 : {
441 177 : count = 0;
442 177 : what = count_what;
443 177 : maxcount = max;
444 354 : pkg_emit_progress_start("%-20s%*s[%ld]", what,
445 177 : 6 - magnitude(maxcount), " ", maxcount);
446 :
447 177 : return;
448 : }
449 :
450 : static void
451 104 : counter_count()
452 : {
453 104 : count++;
454 :
455 104 : if (count % TICK == 0)
456 0 : pkg_emit_progress_tick(count, maxcount);
457 :
458 104 : return;
459 : }
460 :
461 : static void
462 177 : counter_end()
463 : {
464 177 : pkg_emit_progress_tick(count, maxcount);
465 177 : return;
466 : }
|