Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Do not rely on libarchive for pkg register,
Baptiste Daroussin committed 9 years ago
commit b737b18069427deba4670be2b7a71649d8be5177
parent 06044ad
10 files changed +362 -199
modified libpkg/libpkg.ver
@@ -24,7 +24,6 @@ global:
	pkg_config_files;
	pkg_config_get;
	pkg_conflicts;
-
	pkg_copy_tree;
	pkg_create_from_manifest;
	pkg_create_installed;
	pkg_create_repo;
modified libpkg/packing.c
@@ -50,18 +50,13 @@ struct packing {
};

int
-
packing_init(struct packing **pack, const char *path, pkg_formats format, bool passmode)
+
packing_init(struct packing **pack, const char *path, pkg_formats format)
{
	char archive_path[MAXPATHLEN];
	const char *ext;

	assert(pack != NULL);

-
	if (passmode && !is_dir(path)) {
-
		pkg_emit_error("When using passmode, a directory should be provided");
-
		return (EPKG_FATAL);
-
	}
-

	if ((*pack = calloc(1, sizeof(struct packing))) == NULL) {
		pkg_emit_errno("calloc", "packing");
		return (EPKG_FATAL);
@@ -71,45 +66,37 @@ packing_init(struct packing **pack, const char *path, pkg_formats format, bool p
	archive_read_disk_set_standard_lookup((*pack)->aread);
	archive_read_disk_set_symlink_physical((*pack)->aread);

-
	if (!passmode) {
-
		(*pack)->pass = false;
-
		(*pack)->awrite = archive_write_new();
-
		archive_write_set_format_pax_restricted((*pack)->awrite);
-
		ext = packing_set_format((*pack)->awrite, format);
-
		if (ext == NULL) {
-
			archive_read_close((*pack)->aread);
-
			archive_read_free((*pack)->aread);
-
			archive_write_close((*pack)->awrite);
-
			archive_write_free((*pack)->awrite);
-
			*pack = NULL;
-
			return EPKG_FATAL; /* error set by _set_format() */
-
		}
-
		snprintf(archive_path, sizeof(archive_path), "%s.%s", path,
-
		    ext);
-

-
		pkg_debug(1, "Packing to file '%s'", archive_path);
-
		if (archive_write_open_filename(
-
		    (*pack)->awrite, archive_path) != ARCHIVE_OK) {
-
			pkg_emit_errno("archive_write_open_filename",
-
			    archive_path);
-
			archive_read_close((*pack)->aread);
-
			archive_read_free((*pack)->aread);
-
			archive_write_close((*pack)->awrite);
-
			archive_write_free((*pack)->awrite);
-
			*pack = NULL;
-
			return EPKG_FATAL;
-
		}
-
	} else { /* pass mode directly write to the disk */
-
		pkg_debug(1, "Packing to directory '%s' (pass mode)", path);
-
		(*pack)->pass = true;
-
		(*pack)->awrite = archive_write_disk_new();
-
		archive_write_disk_set_options((*pack)->awrite,
-
		    EXTRACT_ARCHIVE_FLAGS| ARCHIVE_EXTRACT_UNLINK);
+
	(*pack)->pass = false;
+
	(*pack)->awrite = archive_write_new();
+
	archive_write_set_format_pax_restricted((*pack)->awrite);
+
	ext = packing_set_format((*pack)->awrite, format);
+
	if (ext == NULL) {
+
		archive_read_close((*pack)->aread);
+
		archive_read_free((*pack)->aread);
+
		archive_write_close((*pack)->awrite);
+
		archive_write_free((*pack)->awrite);
+
		*pack = NULL;
+
		return EPKG_FATAL; /* error set by _set_format() */
+
	}
+
	snprintf(archive_path, sizeof(archive_path), "%s.%s", path,
+
	    ext);
+

+
	pkg_debug(1, "Packing to file '%s'", archive_path);
+
	if (archive_write_open_filename(
+
	    (*pack)->awrite, archive_path) != ARCHIVE_OK) {
+
		pkg_emit_errno("archive_write_open_filename",
+
		    archive_path);
+
		archive_read_close((*pack)->aread);
+
		archive_read_free((*pack)->aread);
+
		archive_write_close((*pack)->awrite);
+
		archive_write_free((*pack)->awrite);
+
		*pack = NULL;
+
		return EPKG_FATAL;
	}

	(*pack)->resolver = archive_entry_linkresolver_new();
	archive_entry_linkresolver_set_strategy((*pack)->resolver,
-
	    ARCHIVE_FORMAT_TAR_PAX_RESTRICTED);
+
	    archive_format((*pack)->awrite));

	return (EPKG_OK);
}
modified libpkg/pkg.c
@@ -1545,39 +1545,6 @@ pkg_validate(struct pkg *pkg, struct pkgdb *db)
}

int
-
pkg_copy_tree(struct pkg *pkg, const char *src, const char *dest)
-
{
-
	struct packing *pack;
-
	struct pkg_file *file = NULL;
-
	struct pkg_dir *dir = NULL;
-
	char spath[MAXPATHLEN];
-
	char dpath[MAXPATHLEN];
-

-
	if (packing_init(&pack, dest, 0, true) != EPKG_OK) {
-
		/* TODO */
-
		return EPKG_FATAL;
-
	}
-

-
	while (pkg_dirs(pkg, &dir) == EPKG_OK) {
-
		snprintf(spath, sizeof(spath), "%s%s", src, dir->path);
-
		snprintf(dpath, sizeof(dpath), "%s%s", dest, dir->path);
-
		packing_append_file_attr(pack, spath, dpath,
-
		    dir->uname, dir->gname, dir->perm, dir->fflags);
-
	}
-

-
	while (pkg_files(pkg, &file) == EPKG_OK) {
-
		snprintf(spath, sizeof(spath), "%s%s", src, file->path);
-
		snprintf(dpath, sizeof(dpath), "%s%s", dest, file->path);
-
		packing_append_file_attr(pack, spath, dpath,
-
		    file->uname, file->gname, file->perm, file->fflags);
-
	}
-

-
	packing_finish(pack);
-

-
	return (EPKG_OK);
-
}
-

-
int
pkg_test_filesum(struct pkg *pkg)
{
	struct pkg_file *f = NULL;
modified libpkg/pkg.h.in
@@ -1216,11 +1216,6 @@ int pkg_repo_cached_name(struct pkg *pkg, char *dest, size_t destlen);
int ports_parse_plist(struct pkg *, const char *, const char *);

/**
-
 * @todo Document
-
 */
-
int pkg_copy_tree(struct pkg *, const char *src, const char *dest);
-

-
/**
 * Special structure to report about conflicts
 */
struct pkg_event_conflict {
modified libpkg/pkg_add.c
@@ -50,6 +50,8 @@
#include "private/pkg.h"
#include "private/pkgdb.h"

+
KHASH_MAP_INIT_INT(hls, char *);
+

#if defined(UF_NOUNLINK)
#define NOCHANGESFLAGS	(UF_IMMUTABLE | UF_APPEND | UF_NOUNLINK | SF_IMMUTABLE | SF_APPEND | SF_NOUNLINK)
#else
@@ -314,6 +316,38 @@ fill_timespec_buf(const struct stat *aest, struct timespec tspec[2])
#endif
}

+
static int
+
create_dir(struct pkg *pkg, struct pkg_dir *d)
+
{
+
	struct stat st;
+

+
	if (mkdirat(pkg->rootfd, RELATIVE_PATH(d->path), 0755) == -1)
+
		if (!mkdirat_p(pkg->rootfd, RELATIVE_PATH(d->path)))
+
			return (EPKG_FATAL);
+
	if (fstatat(pkg->rootfd, RELATIVE_PATH(d->path), &st, 0) == -1) {
+
		if (errno != ENOENT) {
+
			pkg_emit_error("Fail to stat directory %s: %s", d->path,
+
			    strerror(errno));
+
			return (EPKG_FATAL);
+
		}
+
		if (fstatat(pkg->rootfd, RELATIVE_PATH(d->path), &st, AT_SYMLINK_NOFOLLOW) == 0) {
+
			unlinkat(pkg->rootfd, RELATIVE_PATH(d->path), 0);
+
		}
+
		if (mkdirat(pkg->rootfd, RELATIVE_PATH(d->path), 0755) == -1) {
+
			pkg_emit_error("Fail to create directory %s: %s", d->path,
+
			    strerror(errno));
+
			return (EPKG_FATAL);
+
		}
+
	}
+

+
	if (st.st_uid == d->uid && st.st_gid == d->gid &&
+
	    (st.st_mode & ~S_IFMT) == (d->perm & ~S_IFMT)) {
+
		d->noattrs = true;
+
	}
+

+
	return (EPKG_OK);
+
}
+

/* In case of directories create the dir and extract the creds */
static int
do_extract_dir(struct pkg* pkg, struct archive *a __unused, struct archive_entry *ae,
@@ -321,7 +355,6 @@ do_extract_dir(struct pkg* pkg, struct archive *a __unused, struct archive_entry
{
	struct pkg_dir *d;
	const struct stat *aest;
-
	struct stat st;
	unsigned long clear;

	d = pkg_get_dir(pkg, path);
@@ -336,28 +369,8 @@ do_extract_dir(struct pkg* pkg, struct archive *a __unused, struct archive_entry
	fill_timespec_buf(aest, d->time);
	archive_entry_fflags(ae, &d->fflags, &clear);

-
	if (mkdirat(pkg->rootfd, RELATIVE_PATH(path), 0755) == -1)
-
		if (!mkdirat_p(pkg->rootfd, RELATIVE_PATH(path)))
-
			return (EPKG_FATAL);
-
	if (fstatat(pkg->rootfd, RELATIVE_PATH(path), &st, 0) == -1) {
-
		if (errno != ENOENT) {
-
			pkg_emit_error("Fail to stat directory %s: %s", path,
-
			    strerror(errno));
-
			return (EPKG_FATAL);
-
		}
-
		if (fstatat(pkg->rootfd, RELATIVE_PATH(path), &st, AT_SYMLINK_NOFOLLOW) == 0) {
-
			unlinkat(pkg->rootfd, RELATIVE_PATH(path), 0);
-
		}
-
		if (mkdirat(pkg->rootfd, RELATIVE_PATH(path), 0755) == -1) {
-
			pkg_emit_error("Fail to create directory %s: %s", path,
-
			    strerror(errno));
-
			return (EPKG_FATAL);
-
		}
-
	}
-

-
	if (st.st_uid == d->uid && st.st_gid == d->gid &&
-
	    (st.st_mode & ~S_IFMT) == (d->perm & ~S_IFMT)) {
-
		d->noattrs = true;
+
	if (create_dir(pkg, d) == EPKG_FATAL) {
+
		return (EPKG_FATAL);
	}

	metalog_add(PKG_METALOG_DIR, RELATIVE_PATH(path),
@@ -367,32 +380,16 @@ do_extract_dir(struct pkg* pkg, struct archive *a __unused, struct archive_entry
	return (EPKG_OK);
}

-
/* In case of a symlink create it directly with a random name */
static int
-
do_extract_symlink(struct pkg *pkg, struct archive *a __unused, struct archive_entry *ae,
-
    const char *path, struct pkg *local __unused)
+
create_symlinks(struct pkg *pkg, struct pkg_file *f, const char *target)
{
-
	struct pkg_file *f;
-
	const struct stat *aest;
-
	unsigned long clear;
-
	struct timespec tspec[2];
	bool tried_mkdir = false;

-
	f = pkg_get_file(pkg, path);
-
	if (f == NULL) {
-
		pkg_emit_error("Symlink %s not specified in the manifest");
-
		return (EPKG_FATAL);
-
	}
-

-
	aest = archive_entry_stat(ae);
-
	archive_entry_fflags(ae, &f->fflags, &clear);
-

-
	pkg_hidden_tempfile(f->temppath, sizeof(f->temppath), path);
+
	pkg_hidden_tempfile(f->temppath, sizeof(f->temppath), f->path);
retry:
-
	if (symlinkat(archive_entry_symlink(ae), pkg->rootfd,
-
	    RELATIVE_PATH(f->temppath)) == -1) {
+
	if (symlinkat(target, pkg->rootfd, RELATIVE_PATH(f->temppath)) == -1) {
		if (!tried_mkdir) {
-
			if (!mkdirat_p(pkg->rootfd, bsd_dirname(path)))
+
			if (!mkdirat_p(pkg->rootfd, RELATIVE_PATH(bsd_dirname(f->path))))
				return (EPKG_FATAL);
			tried_mkdir = true;
			goto retry;
@@ -403,14 +400,39 @@ retry:
		return (EPKG_FATAL);
	}

-
	fill_timespec_buf(aest, tspec);
+
	if (set_attrs(pkg->rootfd, f->temppath, f->perm, f->uid, f->gid,
+
	    &f->time[0], &f->time[1]) != EPKG_OK) {
+
		return (EPKG_FATAL);
+
	}
+
	return (EPKG_OK);
+
}

-
	if (set_attrs(pkg->rootfd, f->temppath, aest->st_mode,
-
	    get_uid_from_archive(ae), get_gid_from_archive(ae),
-
	    &tspec[0], &tspec[1]) != EPKG_OK) {
+
/* In case of a symlink create it directly with a random name */
+
static int
+
do_extract_symlink(struct pkg *pkg, struct archive *a __unused, struct archive_entry *ae,
+
    const char *path, struct pkg *local __unused)
+
{
+
	struct pkg_file *f;
+
	const struct stat *aest;
+
	unsigned long clear;
+

+
	f = pkg_get_file(pkg, path);
+
	if (f == NULL) {
+
		pkg_emit_error("Symlink %s not specified in the manifest");
		return (EPKG_FATAL);
	}

+
	aest = archive_entry_stat(ae);
+
	archive_entry_fflags(ae, &f->fflags, &clear);
+
	f->uid = get_uid_from_archive(ae);
+
	f->gid = get_gid_from_archive(ae);
+
	f->perm = aest->st_mode;
+
	fill_timespec_buf(aest, f->time);
+
	archive_entry_fflags(ae, &f->fflags, &clear);
+

+
	if (create_symlinks(pkg, f, archive_entry_symlink(ae)) == EPKG_FATAL)
+
		return (EPKG_FATAL);
+

	metalog_add(PKG_METALOG_LINK, RELATIVE_PATH(path),
	    archive_entry_uname(ae), archive_entry_gname(ae),
	    aest->st_mode & ~S_IFLNK, archive_entry_symlink(ae));
@@ -419,29 +441,19 @@ retry:
}

static int
-
do_extract_hardlink(struct pkg *pkg, struct archive *a __unused, struct archive_entry *ae,
-
    const char *path, struct pkg *local __unused)
+
create_hardlink(struct pkg *pkg, struct pkg_file *f, const char *path)
{
-
	struct pkg_file *f, *fh;
-
	const struct stat *aest;
-
	const char *lp;
	bool tried_mkdir = false;
+
	struct pkg_file *fh;

-
	f = pkg_get_file(pkg, path);
-
	if (f == NULL) {
-
		pkg_emit_error("Hardlink %s not specified in the manifest");
-
		return (EPKG_FATAL);
-
	}
-
	lp = archive_entry_hardlink(ae);
-
	fh = pkg_get_file(pkg, lp);
+
	pkg_hidden_tempfile(f->temppath, sizeof(f->temppath), f->path);
+
	fh = pkg_get_file(pkg, path);
	if (fh == NULL) {
		pkg_emit_error("Can't find the file %s is supposed to be"
-
		    " hardlinked to in the archive: %s", path, lp);
+
		    " hardlinked to %s", f->path, path);
		return (EPKG_FATAL);
	}

-
	pkg_hidden_tempfile(f->temppath, sizeof(f->temppath), path);
-
	aest = archive_entry_stat(ae);

retry:
	if (linkat(pkg->rootfd, RELATIVE_PATH(fh->temppath),
@@ -458,43 +470,54 @@ retry:
		return (EPKG_FATAL);
	}

-
	metalog_add(PKG_METALOG_FILE, RELATIVE_PATH(path),
-
	    archive_entry_uname(ae), archive_entry_gname(ae),
-
	    aest->st_mode & ~S_IFREG, NULL);
-

	return (EPKG_OK);
}

static int
-
do_extract_regfile(struct pkg *pkg, struct archive *a, struct archive_entry *ae,
-
    const char *path, struct pkg *local)
+
do_extract_hardlink(struct pkg *pkg, struct archive *a __unused, struct archive_entry *ae,
+
    const char *path, struct pkg *local __unused)
{
	struct pkg_file *f;
	const struct stat *aest;
-
	unsigned long clear;
-
	int fd = -1;
-
	size_t len;
-
	struct timespec tspec[2];
-
	bool tried_mkdir = false;
+
	const char *lp;

	f = pkg_get_file(pkg, path);
	if (f == NULL) {
-
		pkg_emit_error("File %s not specified in the manifest");
+
		pkg_emit_error("Hardlink %s not specified in the manifest");
		return (EPKG_FATAL);
	}
-

+
	lp = archive_entry_hardlink(ae);
	aest = archive_entry_stat(ae);
-
	archive_entry_fflags(ae, &f->fflags, &clear);

-
	pkg_hidden_tempfile(f->temppath, sizeof(f->temppath), path);
+
	if (create_hardlink(pkg, f, lp) == EPKG_FATAL)
+
		return (EPKG_FATAL);
+

+
	metalog_add(PKG_METALOG_FILE, RELATIVE_PATH(path),
+
	    archive_entry_uname(ae), archive_entry_gname(ae),
+
	    aest->st_mode & ~S_IFREG, NULL);
+

+
	return (EPKG_OK);
+
}
+

+
static int
+
create_regfile(struct pkg *pkg, struct pkg_file *f, struct archive *a,
+
    struct archive_entry *ae, int fromfd, struct pkg *local)
+
{
+
	int fd = -1;
+
	bool tried_mkdir = false;
+
	size_t len;
+
	char buf[BUFSIZ];
+

+
	pkg_hidden_tempfile(f->temppath, sizeof(f->temppath), f->path);

retry:
	/* Create the new temp file */
	fd = openat(pkg->rootfd, RELATIVE_PATH(f->temppath),
-
	    O_CREAT|O_WRONLY|O_EXCL, aest->st_mode & ~S_IFMT);
+
	    O_CREAT|O_WRONLY|O_EXCL, f->perm);
	if (fd == -1) {
		if (!tried_mkdir) {
-
			if (!mkdirat_p(pkg->rootfd, bsd_dirname(path))) {
+
			if (!mkdirat_p(pkg->rootfd,
+
			    RELATIVE_PATH(bsd_dirname(f->path)))) {
				return (EPKG_FATAL);
			}
			tried_mkdir = true;
@@ -505,43 +528,75 @@ retry:
		return (EPKG_FATAL);
	}

-
	/* check if this is a config file */
-
	kh_find(pkg_config_files, pkg->config_files, f->path,
-
	    f->config);
-
	if (f->config) {
-
		const char *cfdata;
-
		bool merge = pkg_object_bool(pkg_config_get("AUTOMERGE"));
-

-
		pkg_debug(1, "Populating config_file %s", f->path);
-
		len = archive_entry_size(ae);
-
		f->config->content = malloc(len + 1);
-
		archive_read_data(a, f->config->content, len);
-
		f->config->content[len] = '\0';
-
		cfdata = f->config->content;
-
		attempt_to_merge(pkg->rootfd, f->config, local, merge);
-
		if (f->config->status == MERGE_SUCCESS)
-
			cfdata = f->config->newcontent;
-
		dprintf(fd, "%s", cfdata);
-
		if (f->config->newcontent != NULL)
-
			free(f->config->newcontent);
-
	}
+
	if (fromfd == -1) {
+
		/* check if this is a config file */
+
		kh_find(pkg_config_files, pkg->config_files, f->path,
+
		    f->config);
+
		if (f->config) {
+
			const char *cfdata;
+
			bool merge = pkg_object_bool(pkg_config_get("AUTOMERGE"));
+

+
			pkg_debug(1, "Populating config_file %s", f->path);
+
			len = archive_entry_size(ae);
+
			f->config->content = malloc(len + 1);
+
			archive_read_data(a, f->config->content, len);
+
			f->config->content[len] = '\0';
+
			cfdata = f->config->content;
+
			attempt_to_merge(pkg->rootfd, f->config, local, merge);
+
			if (f->config->status == MERGE_SUCCESS)
+
				cfdata = f->config->newcontent;
+
			dprintf(fd, "%s", cfdata);
+
			if (f->config->newcontent != NULL)
+
				free(f->config->newcontent);
+
		}

-
	if (!f->config && archive_read_data_into_fd(a, fd) != ARCHIVE_OK) {
-
		pkg_emit_error("Fail to extract %s from package: %s",
-
		    path, archive_error_string(a));
-
		return (EPKG_FATAL);
+
		if (!f->config && archive_read_data_into_fd(a, fd) != ARCHIVE_OK) {
+
			pkg_emit_error("Fail to extract %s from package: %s",
+
			    f->path, archive_error_string(a));
+
			return (EPKG_FATAL);
+
		}
+
	} else {
+
		while ((len = read(fromfd, buf, sizeof(buf))) > 0)
+
			if (write(fd, buf, len) == -1) {
+
				pkg_emit_error("Fail to write file: %s", strerror(errno));
+
			}
	}
	if (fd != -1) {
		close(fd);
	}

-
	fill_timespec_buf(aest, tspec);
-

-
	if (set_attrs(pkg->rootfd, f->temppath, aest->st_mode,
-
		    get_uid_from_archive(ae), get_gid_from_archive(ae),
-
		    &tspec[0], &tspec[1]) != EPKG_OK)
+
	if (set_attrs(pkg->rootfd, f->temppath, f->perm, f->uid, f->gid,
+
	    &f->time[0], &f->time[1]) != EPKG_OK)
			return (EPKG_FATAL);

+
	return (EPKG_OK);
+
}
+

+
static int
+
do_extract_regfile(struct pkg *pkg, struct archive *a, struct archive_entry *ae,
+
    const char *path, struct pkg *local)
+
{
+
	struct pkg_file *f;
+
	const struct stat *aest;
+
	unsigned long clear;
+

+
	f = pkg_get_file(pkg, path);
+
	if (f == NULL) {
+
		pkg_emit_error("File %s not specified in the manifest");
+
		return (EPKG_FATAL);
+
	}
+

+
	aest = archive_entry_stat(ae);
+
	archive_entry_fflags(ae, &f->fflags, &clear);
+
	f->perm = aest->st_mode;
+
	f->uid = get_uid_from_archive(ae);
+
	f->gid = get_gid_from_archive(ae);
+
	fill_timespec_buf(aest, f->time);
+
	archive_entry_fflags(ae, &f->fflags, &clear);
+

+
	if (create_regfile(pkg, f, a, ae, -1, local) == EPKG_FATAL)
+
		return (EPKG_FATAL);
+

	metalog_add(PKG_METALOG_FILE, RELATIVE_PATH(path),
	    archive_entry_uname(ae), archive_entry_gname(ae),
	    aest->st_mode & ~S_IFREG, NULL);
@@ -626,7 +681,6 @@ do_extract(struct archive *a, struct archive_entry *ae,
			pkg_emit_progress_tick(cur_file++, nfiles);
		}
	} while ((ret = archive_read_next_header(a, &ae)) == ARCHIVE_OK);
-

	pkg_emit_progress_tick(cur_file++, nfiles);

	if (ret != ARCHIVE_EOF) {
@@ -910,7 +964,7 @@ pkg_add_cleanup_old(struct pkgdb *db, struct pkg *old, struct pkg *new, int flag
	return (ret);
}

-
static void
+
void
pkg_rollback_pkg(struct pkg *p)
{
	struct pkg_file *f = NULL;
@@ -922,7 +976,7 @@ pkg_rollback_pkg(struct pkg *p)
	}
}

-
static void
+
void
pkg_rollback_cb(void *data)
{
	pkg_rollback_pkg((struct pkg *)data);
@@ -1169,3 +1223,130 @@ pkg_add_upgrade(struct pkgdb *db, const char *path, unsigned flags,

	return pkg_add_common(db, path, flags, keys, location, rp, lp);
}
+

+
int
+
pkg_add_fromdir(struct pkg *pkg, const char *src)
+
{
+
	struct stat st;
+
	struct pkg_dir *d = NULL;
+
	struct pkg_file *f = NULL;
+
	char target[MAXPATHLEN];
+
	struct passwd *pw;
+
	struct group *gr;
+
	int fd, fromfd;
+
	int retcode;
+
	kh_hls_t *hardlinks = NULL;;
+
	const char *path;
+

+
	fromfd = open(src, O_DIRECTORY);
+
	if (fromfd == -1) {
+
		pkg_emit_error("Unable to open source directory '%s': %s'",
+
		    src, strerror(errno));
+
		return (EPKG_FATAL);
+
	}
+
	pkg_open_root_fd(pkg);
+

+
	while (pkg_dirs(pkg, &d) == EPKG_OK) {
+
		if (fstatat(fromfd, RELATIVE_PATH(d->path), &st, 0) == -1) {
+
			pkg_emit_error("%s%s: %s", src, d->path,
+
			    strerror(errno));
+
			return (EPKG_FATAL);
+
		}
+
		if (d->perm == 0)
+
			d->perm = st.st_mode & ~S_IFMT;
+
		if (d->uname[0] != '\0') {
+
			pw = getpwnam(d->uname);
+
			if (pw == NULL) {
+
				pkg_emit_error("Unknown user: '%s'", d->uname);
+
				return (EPKG_FATAL);
+
			}
+
			d->uid = pw->pw_uid;
+
		} else {
+
			d->uid = st.st_uid;
+
		}
+
		if (d->gname[0] != '\0') {
+
			gr = (getgrnam(d->gname));
+
			if (gr == NULL) {
+
				pkg_emit_error("Unknown group: '%s'", d->gname);
+
				return (EPKG_FATAL);
+
			}
+
			d->gid = gr->gr_gid;
+
		} else {
+
			d->gid = st.st_gid;
+
		}
+
		d->time[0] = st.st_atim;
+
		d->time[1] = st.st_mtim;
+

+
		if (create_dir(pkg, d) == EPKG_FATAL)
+
			return (EPKG_FATAL);
+
	}
+

+
	hardlinks = kh_init_hls();
+
	while (pkg_files(pkg, &f) == EPKG_OK) {
+
		if (fstatat(fromfd, RELATIVE_PATH(f->path), &st,
+
		    AT_SYMLINK_NOFOLLOW) == -1) {
+
			pkg_emit_error("%s%s: %s", src, f->path,
+
			    strerror(errno));
+
			return (EPKG_FATAL);
+
		}
+
		if (f->uname[0] != '\0') {
+
			pw = getpwnam(f->uname);
+
			if (pw == NULL) {
+
				pkg_emit_error("Unknown user: '%s'", f->uname);
+
				return (EPKG_FATAL);
+
			}
+
			f->uid = pw->pw_uid;
+
		} else {
+
			f->uid = st.st_uid;
+
		}
+

+
		if (f->gname[0] != '\0') {
+
			gr = (getgrnam(f->gname));
+
			if (gr == NULL) {
+
				pkg_emit_error("Unknown group: '%s'", f->gname);
+
				return (EPKG_FATAL);
+
			}
+
			f->gid = gr->gr_gid;
+
		} else {
+
			f->uid = st.st_uid;
+
		}
+

+
		if (f->perm == 0)
+
			f->perm = st.st_mode & ~S_IFMT;
+
		if (f->uid == 0)
+
			f->uid = st.st_uid;
+

+
		if (S_ISLNK(st.st_mode)) {
+
			readlinkat(pkg->rootfd, RELATIVE_PATH(f->path), target, sizeof(target));
+
			if (create_symlinks(pkg, f, target) == EPKG_FATAL) {
+
				return (EPKG_FATAL);
+
			}
+
		} else if (S_ISREG(st.st_mode)) {
+
			if ((fd = openat(fromfd, RELATIVE_PATH(f->path), O_RDONLY)) == -1) {
+
				pkg_emit_error("Impossible to open source file '%s': %s",
+
				    RELATIVE_PATH(f->path), strerror(errno));
+
				return (EPKG_FATAL);
+
			}
+
			kh_find(hls, hardlinks, st.st_ino, path);
+
			if (path != NULL) {
+
				if (create_hardlink(pkg, f, path) == EPKG_FATAL)
+
					return (EPKG_FATAL);
+
			} else {
+
				if (create_regfile(pkg, f, NULL, NULL, fd, NULL) == EPKG_FATAL) {
+
					close(fd);
+
					return (EPKG_FATAL);
+
				}
+
				kh_safe_add(hls, hardlinks, f->path, st.st_ino);
+
			}
+
		} else {
+
			pkg_emit_error("Invalid file type");
+
			return (EPKG_FATAL);
+
		}
+
	}
+
	kh_destroy_hls(hardlinks);
+

+
	retcode = pkg_extract_finalize(pkg);
+
	close(fromfd);
+
	return (retcode);
+
}
+

modified libpkg/pkg_create.c
@@ -186,7 +186,7 @@ pkg_create_archive(const char *outdir, struct pkg *pkg, pkg_formats format,
		return (NULL);
	}

-
	if (packing_init(&pkg_archive, pkg_path, format, false) != EPKG_OK)
+
	if (packing_init(&pkg_archive, pkg_path, format) != EPKG_OK)
		pkg_archive = NULL;

	free(pkg_path);
modified libpkg/pkg_ports.c
@@ -1308,9 +1308,15 @@ pkg_add_port(struct pkgdb *db, struct pkg *pkg, const char *input_path,
		/* Execute pre-install scripts */
		pkg_script_run(pkg, PKG_SCRIPT_PRE_INSTALL);

-
		if (input_path != NULL)
-
			pkg_copy_tree(pkg, input_path, \
-
			    location ? location : "/");
+
		if (input_path != NULL) {
+
			pkg_register_cleanup_callback(pkg_rollback_cb, pkg);
+
			rc = pkg_add_fromdir(pkg, input_path);
+
			pkg_unregister_cleanup_callback(pkg_rollback_cb, pkg);
+
			if (rc != EPKG_OK) {
+
				pkg_rollback_pkg(pkg);
+
				pkg_delete_dirs(db, pkg, NULL);
+
			}
+
		}

		/* Execute post-install scripts */
		pkg_script_run(pkg, PKG_SCRIPT_POST_INSTALL);
modified libpkg/pkg_repo_create.c
@@ -936,7 +936,7 @@ pkg_repo_pack_db(const char *name, const char *archive, char *path,
	sig = NULL;
	pub = NULL;

-
	if (packing_init(&pack, archive, meta->packing_format, false) != EPKG_OK)
+
	if (packing_init(&pack, archive, meta->packing_format) != EPKG_OK)
		return (EPKG_FATAL);

	if (rsa != NULL) {
modified libpkg/private/pkg.h
@@ -361,6 +361,7 @@ struct pkg_file {
	char		 temppath[MAXPATHLEN];
	u_long		 fflags;
	struct pkg_config_file *config;
+
	struct timespec	 time[2];
	struct pkg_file	*next;
};

@@ -678,8 +679,7 @@ void pkg_config_file_free(struct pkg_config_file *);

struct packing;

-
int packing_init(struct packing **pack, const char *path, pkg_formats format,
-
    bool passmode);
+
int packing_init(struct packing **pack, const char *path, pkg_formats format);
int packing_append_file_attr(struct packing *pack, const char *filepath,
     const char *newpath, const char *uname, const char *gname, mode_t perm,
     u_long fflags);
@@ -810,5 +810,8 @@ enum pkg_metalog_type {

int pkg_set_from_file(struct pkg *pkg, pkg_attr attr, const char *file, bool trimcr);
int pkg_set_from_fileat(int fd, struct pkg *pkg, pkg_attr attr, const char *file, bool trimcr);
+
void pkg_rollback_cb(void *);
+
void pkg_rollback_pkg(struct pkg *);
+
int pkg_add_fromdir(struct pkg *, const char *);

#endif
modified tests/frontend/register.sh
@@ -4,7 +4,8 @@

tests_init \
	register_conflicts \
-
	register_message
+
	register_message \
+
	prefix_is_a_symlink

register_conflicts_body() {
	mkdir -p teststage/${TMPDIR}
@@ -102,3 +103,27 @@ remove
	atf_check -o inline:"${OUTPUT}" pkg info -D test2

}
+

+
prefix_is_a_symlink_body()
+
{
+
	new_pkg "test" "test" "1"
+
	mkdir -p ${TMPDIR}/${TMPDIR}/plop/bla
+
	echo "something" > ${TMPDIR}/${TMPDIR}/plop/bla/a
+
	ln ${TMPDIR}/${TMPDIR}/plop/bla/a ${TMPDIR}/${TMPDIR}/plop/bla/b
+
	sed -e "s,^prefix.*,prefix = ${TMPDIR},g" test.ucl > test2.ucl
+
	echo "plop/bla/a" > plist
+
	echo "plop/bla/b" >> plist
+

+
	mkdir -p ${TMPDIR}/target/${TMPDIR}/
+
	mkdir -p ${TMPDIR}/target/hey
+
	rmdir ${TMPDIR}/target/${TMPDIR}/
+
	ln -sf ${TMPDIR}/target/hey ${TMPDIR}/target/${TMPDIR}
+
	atf_check \
+
		-o ignore \
+
		pkg -r ${TMPDIR}/target register -M ${TMPDIR}/test2.ucl -f ${TMPDIR}/plist -i ${TMPDIR}
+
	test -f ${TMPDIR}/target/${TMPDIR}/plop/bla/a || atf_fail "hardlinks failed"
+
	test -f ${TMPDIR}/target/${TMPDIR}/plop/bla/b || atf_fail "hardlinks failed2"
+
	inode1=$(ls -i ${TMPDIR}/target/${TMPDIR}/plop/bla/a | awk '{ print $1 }')
+
	inode2=$(ls -i ${TMPDIR}/target/${TMPDIR}/plop/bla/b | awk '{ print $1 }')
+
	atf_check_equal $inode1 $inode2
+
}