1/* $NetBSD: build.c,v 1.15 2010/04/20 00:39:13 joerg Exp $ */ 2 3#if HAVE_CONFIG_H 4#include "config.h" 5#endif 6#include <nbcompat.h> 7#if HAVE_SYS_CDEFS_H 8#include <sys/cdefs.h> 9#endif 10__RCSID("$NetBSD: build.c,v 1.15 2010/04/20 00:39:13 joerg Exp $"); 11 12/*- 13 * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>. 14 * All rights reserved. 15 * 16 * This code was developed as part of Google's Summer of Code 2007 program. 17 * 18 * Redistribution and use in source and binary forms, with or without 19 * modification, are permitted provided that the following conditions 20 * are met: 21 * 22 * 1. Redistributions of source code must retain the above copyright 23 * notice, this list of conditions and the following disclaimer. 24 * 2. Redistributions in binary form must reproduce the above copyright 25 * notice, this list of conditions and the following disclaimer in 26 * the documentation and/or other materials provided with the 27 * distribution. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 30 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 31 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 32 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 33 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 34 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 35 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 36 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 37 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 38 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 39 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40 * SUCH DAMAGE. 41 */ 42 43/* 44 * FreeBSD install - a package for the installation and maintainance 45 * of non-core utilities. 46 * 47 * Redistribution and use in source and binary forms, with or without 48 * modification, are permitted provided that the following conditions 49 * are met: 50 * 1. Redistributions of source code must retain the above copyright 51 * notice, this list of conditions and the following disclaimer. 52 * 2. Redistributions in binary form must reproduce the above copyright 53 * notice, this list of conditions and the following disclaimer in the 54 * documentation and/or other materials provided with the distribution. 55 * 56 * Jordan K. Hubbard 57 * 18 July 1993 58 * 59 * This is the main body of the create module. 60 * 61 */ 62 63#include "lib.h" 64#include "create.h" 65 66#if HAVE_ERR_H 67#include <err.h> 68#endif 69#if HAVE_GRP_H 70#include <grp.h> 71#endif 72#if HAVE_PWD_H 73#include <pwd.h> 74#endif 75#if HAVE_UNISTD_H 76#include <unistd.h> 77#endif 78#if HAVE_FCNTL_H 79#include <fcntl.h> 80#endif 81 82#include <archive.h> 83#include <archive_entry.h> 84 85static struct memory_file *contents_file; 86static struct memory_file *comment_file; 87static struct memory_file *desc_file; 88static struct memory_file *install_file; 89static struct memory_file *deinstall_file; 90static struct memory_file *display_file; 91static struct memory_file *build_version_file; 92static struct memory_file *build_info_file; 93static struct memory_file *size_pkg_file; 94static struct memory_file *size_all_file; 95static struct memory_file *preserve_file; 96static struct memory_file *views_file; 97 98static void 99write_meta_file(struct memory_file *file, struct archive *archive) 100{ 101 struct archive_entry *entry; 102 103 entry = archive_entry_new(); 104 archive_entry_set_pathname(entry, file->name); 105 archive_entry_copy_stat(entry, &file->st); 106 107 archive_entry_set_uname(entry, file->owner); 108 archive_entry_set_gname(entry, file->group); 109 110 if (archive_write_header(archive, entry)) 111 errx(2, "cannot write to archive: %s", archive_error_string(archive)); 112 113 archive_write_data(archive, file->data, file->len); 114 115 archive_entry_free(entry); 116} 117 118static void 119write_entry(struct archive *archive, struct archive_entry *entry) 120{ 121 char buf[16384]; 122 const char *name; 123 int fd; 124 off_t len; 125 ssize_t buf_len; 126 127 if (archive_entry_pathname(entry) == NULL) { 128 warnx("entry with NULL path"); 129 return; 130 } 131 132 if (archive_write_header(archive, entry)) { 133 errx(2, "cannot write %s to archive: %s", 134 archive_entry_pathname(entry), 135 archive_error_string(archive)); 136 } 137 138 /* Only regular files can have data. */ 139 if (archive_entry_filetype(entry) != AE_IFREG || 140 archive_entry_size(entry) == 0) { 141 archive_entry_free(entry); 142 return; 143 } 144 145 name = archive_entry_pathname(entry); 146 147 if ((fd = open(name, O_RDONLY)) == -1) 148 err(2, "cannot open data file %s", name); 149 150 len = archive_entry_size(entry); 151 152 while (len > 0) { 153 buf_len = (len > (off_t)sizeof(buf)) ? (ssize_t)sizeof(buf) : (ssize_t)len; 154 155 if ((buf_len = read(fd, buf, buf_len)) == 0) 156 break; 157 else if (buf_len < 0) 158 err(2, "cannot read from %s", name); 159 160 archive_write_data(archive, buf, (size_t)buf_len); 161 len -= buf_len; 162 } 163 164 close(fd); 165 166 archive_entry_free(entry); 167} 168 169static void 170write_normal_file(const char *name, struct archive *archive, 171 struct archive_entry_linkresolver *resolver, 172 const char *owner, const char *group) 173{ 174 char buf[16384]; 175 ssize_t buf_len; 176 struct archive_entry *entry, *sparse_entry; 177 struct stat st; 178 179 if (lstat(name, &st) == -1) 180 err(2, "lstat failed for file %s", name); 181 182 entry = archive_entry_new(); 183 archive_entry_set_pathname(entry, name); 184 archive_entry_copy_stat(entry, &st); 185 186 if (owner != NULL) { 187 uid_t uid; 188 189 archive_entry_set_uname(entry, owner); 190 if (uid_from_user(owner, &uid) == -1) 191 errx(2, "user %s unknown", owner); 192 archive_entry_set_uid(entry, uid); 193 } else { 194 archive_entry_set_uname(entry, user_from_uid(st.st_uid, 1)); 195 } 196 197 if (group != NULL) { 198 gid_t gid; 199 200 archive_entry_set_gname(entry, group); 201 if (gid_from_group(group, &gid) == -1) 202 errx(2, "group %s unknown", group); 203 archive_entry_set_gid(entry, gid); 204 } else { 205 archive_entry_set_gname(entry, group_from_gid(st.st_gid, 1)); 206 } 207 208 if ((st.st_mode & S_IFMT) == S_IFLNK) { 209 buf_len = readlink(name, buf, sizeof buf); 210 if (buf_len < 0) 211 err(2, "cannot read symlink %s", name); 212 buf[buf_len] = '\0'; 213 archive_entry_set_symlink(entry, buf); 214 } 215 216 archive_entry_linkify(resolver, &entry, &sparse_entry); 217 218 if (entry != NULL) 219 write_entry(archive, entry); 220 if (sparse_entry != NULL) 221 write_entry(archive, sparse_entry); 222} 223 224static void 225make_dist(const char *pkg, const char *suffix, const package_t *plist) 226{ 227 char *archive_name; 228 const char *owner, *group; 229 const plist_t *p; 230 struct archive *archive; 231 struct archive_entry *entry, *sparse_entry; 232 struct archive_entry_linkresolver *resolver; 233 char *initial_cwd; 234 235 archive = archive_write_new(); 236 archive_write_set_format_pax_restricted(archive); 237 if ((resolver = archive_entry_linkresolver_new()) == NULL) 238 errx(2, "cannot create link resolver"); 239 archive_entry_linkresolver_set_strategy(resolver, 240 archive_format(archive)); 241 242 if (CompressionType == NULL) { 243 if (strcmp(suffix, "tbz") == 0 || 244 strcmp(suffix, "tar.bz2") == 0) 245 CompressionType = "bzip2"; 246 else if (strcmp(suffix, "tgz") == 0 || 247 strcmp(suffix, "tar.gz") == 0) 248 CompressionType = "gzip"; 249 else 250 CompressionType = "none"; 251 } 252 253 if (strcmp(CompressionType, "bzip2") == 0) 254 archive_write_set_compression_bzip2(archive); 255 else if (strcmp(CompressionType, "gzip") == 0) 256 archive_write_set_compression_gzip(archive); 257 else if (strcmp(CompressionType, "xz") == 0) 258 archive_write_set_compression_xz(archive); 259 else if (strcmp(CompressionType, "none") == 0) 260 archive_write_set_compression_none(archive); 261 else 262 errx(1, "Unspported compression type for -F: %s", 263 CompressionType); 264 265 archive_name = xasprintf("%s.%s", pkg, suffix); 266 267 if (archive_write_open_file(archive, archive_name)) 268 errx(2, "cannot create archive: %s", archive_error_string(archive)); 269 270 free(archive_name); 271 272 owner = DefaultOwner; 273 group = DefaultGroup; 274 275 write_meta_file(contents_file, archive); 276 write_meta_file(comment_file, archive); 277 write_meta_file(desc_file, archive); 278 279 if (Install) 280 write_meta_file(install_file, archive); 281 if (DeInstall) 282 write_meta_file(deinstall_file, archive); 283 if (Display) 284 write_meta_file(display_file, archive); 285 if (BuildVersion) 286 write_meta_file(build_version_file, archive); 287 if (BuildInfo) 288 write_meta_file(build_info_file, archive); 289 if (SizePkg) 290 write_meta_file(size_pkg_file, archive); 291 if (SizeAll) 292 write_meta_file(size_all_file, archive); 293 if (Preserve) 294 write_meta_file(preserve_file, archive); 295 if (create_views) 296 write_meta_file(views_file, archive); 297 298 initial_cwd = getcwd(NULL, 0); 299 300 for (p = plist->head; p; p = p->next) { 301 if (p->type == PLIST_FILE) { 302 write_normal_file(p->name, archive, resolver, owner, group); 303 } else if (p->type == PLIST_CWD) { 304 chdir(p->name); 305 } else if (p->type == PLIST_IGNORE) { 306 p = p->next; 307 } else if (p->type == PLIST_CHOWN) { 308 if (p->name != NULL) 309 owner = p->name; 310 else 311 owner = DefaultOwner; 312 } else if (p->type == PLIST_CHGRP) { 313 if (p->name != NULL) 314 group = p->name; 315 else 316 group = DefaultGroup; 317 } 318 } 319 320 entry = NULL; 321 archive_entry_linkify(resolver, &entry, &sparse_entry); 322 while (entry != NULL) { 323 write_entry(archive, entry); 324 entry = NULL; 325 archive_entry_linkify(resolver, &entry, &sparse_entry); 326 } 327 328 archive_entry_linkresolver_free(resolver); 329 330 if (archive_write_close(archive)) 331 errx(2, "cannot finish archive: %s", archive_error_string(archive)); 332 archive_write_finish(archive); 333 334 free(initial_cwd); 335} 336 337static struct memory_file * 338load_and_add(package_t *plist, const char *input_name, 339 const char *target_name, mode_t perm) 340{ 341 struct memory_file *file; 342 343 file = load_memory_file(input_name, target_name, DefaultOwner, 344 DefaultGroup, perm); 345 add_plist(plist, PLIST_IGNORE, NULL); 346 add_plist(plist, PLIST_FILE, target_name); 347 348 return file; 349} 350 351static struct memory_file * 352make_and_add(package_t *plist, const char *target_name, 353 char *content, mode_t perm) 354{ 355 struct memory_file *file; 356 357 file = make_memory_file(target_name, content, strlen(content), 358 DefaultOwner, DefaultGroup, perm); 359 add_plist(plist, PLIST_IGNORE, NULL); 360 add_plist(plist, PLIST_FILE, target_name); 361 362 return file; 363} 364 365int 366pkg_build(const char *pkg, const char *full_pkg, const char *suffix, 367 package_t *plist) 368{ 369 char *plist_buf; 370 size_t plist_len; 371 372 /* Now put the release specific items in */ 373 add_plist(plist, PLIST_CWD, "."); 374 comment_file = make_and_add(plist, COMMENT_FNAME, Comment, 0444); 375 desc_file = make_and_add(plist, DESC_FNAME, Desc, 0444); 376 377 if (Install) { 378 install_file = load_and_add(plist, Install, INSTALL_FNAME, 379 0555); 380 } 381 if (DeInstall) { 382 deinstall_file = load_and_add(plist, DeInstall, 383 DEINSTALL_FNAME, 0555); 384 } 385 if (Display) { 386 display_file = load_and_add(plist, Display, 387 DISPLAY_FNAME, 0444); 388 add_plist(plist, PLIST_DISPLAY, DISPLAY_FNAME); 389 } 390 if (BuildVersion) { 391 build_version_file = load_and_add(plist, BuildVersion, 392 BUILD_VERSION_FNAME, 0444); 393 } 394 if (BuildInfo) { 395 build_info_file = load_and_add(plist, BuildInfo, 396 BUILD_INFO_FNAME, 0444); 397 } 398 if (SizePkg) { 399 size_pkg_file = load_and_add(plist, SizePkg, 400 SIZE_PKG_FNAME, 0444); 401 } 402 if (SizeAll) { 403 size_all_file = load_and_add(plist, SizeAll, 404 SIZE_ALL_FNAME, 0444); 405 } 406 if (Preserve) { 407 preserve_file = load_and_add(plist, Preserve, 408 PRESERVE_FNAME, 0444); 409 } 410 if (create_views) 411 views_file = make_and_add(plist, VIEWS_FNAME, xstrdup(""), 0444); 412 413 /* Finally, write out the packing list */ 414 stringify_plist(plist, &plist_buf, &plist_len, realprefix); 415 contents_file = make_memory_file(CONTENTS_FNAME, plist_buf, plist_len, 416 DefaultOwner, DefaultGroup, 0644); 417 418 /* And stick it into a tar ball */ 419 make_dist(pkg, suffix, plist); 420 421 return TRUE; /* Success */ 422} 423