Skip to content
Snippets Groups Projects
database.c 54.3 KiB
Newer Older
	*apk_string_array_add(&db->protected_paths) = apk_blob_cstr(blob);
	return 0;
}

static int apk_db_create(struct apk_database *db)
{
	int fd;

	mkdirat(db->root_fd, "tmp", 01777);
	mkdirat(db->root_fd, "dev", 0755);
	mknodat(db->root_fd, "dev/null", 0666, makedev(1, 3));
	mkdirat(db->root_fd, "var", 0755);
	mkdirat(db->root_fd, "var/lib", 0755);
	mkdirat(db->root_fd, "var/lib/apk", 0755);
	mkdirat(db->root_fd, "var/cache", 0755);
	mkdirat(db->root_fd, "var/cache/misc", 0755);
	fd = openat(db->root_fd, "var/lib/apk/world", O_CREAT|O_RDWR|O_TRUNC|O_CLOEXEC, 0644);
int apk_db_open(struct apk_database *db, struct apk_db_options *dbopts)
	const char *msg = NULL;
	struct apk_repository_list *repo = NULL;
Timo Teräs's avatar
Timo Teräs committed
	struct apk_bstream *bs;
	apk_blob_t blob;
	memset(db, 0, sizeof(*db));
	if (dbopts->open_flags == 0) {
		msg = "Invalid open flags (internal error)";
		r = -1;
		goto ret_r;
	}

	apk_hash_init(&db->available.names, &pkg_name_hash_ops, 1000);
	apk_hash_init(&db->available.packages, &pkg_info_hash_ops, 4000);
	apk_hash_init(&db->installed.dirs, &dir_hash_ops, 2000);
	apk_hash_init(&db->installed.files, &file_hash_ops, 10000);
	list_init(&db->installed.packages);
	list_init(&db->installed.triggers);
Timo Teräs's avatar
Timo Teräs committed
	apk_dependency_array_init(&db->world);
	apk_string_array_init(&db->protected_paths);
	db->cache_dir = apk_static_cache_dir;
	db->root = strdup(dbopts->root ?: "/");
	db->root_fd = openat(AT_FDCWD, db->root, O_RDONLY | O_CLOEXEC);
	if (db->root_fd < 0 && (dbopts->open_flags & APK_OPENF_CREATE)) {
		mkdirat(AT_FDCWD, db->root, 0755);
		db->root_fd = openat(AT_FDCWD, db->root, O_RDONLY | O_CLOEXEC);
	}
	if (db->root_fd < 0) {
		msg = "Unable to open root";
		goto ret_errno;
	}
	if (fstat64(db->root_fd, &st) != 0 || major(st.st_dev) == 0)
		db->permanent = 0;

	if (fstatat64(db->root_fd, apk_linked_cache_dir, &st, 0) == 0 &&
	    S_ISDIR(st.st_mode) && major(st.st_dev) != 0)
		db->cache_dir = apk_linked_cache_dir;

	if (dbopts->open_flags & APK_OPENF_WRITE) {
		db->lock_fd = openat(db->root_fd, "var/lib/apk/lock",
				     O_CREAT | O_RDWR | O_CLOEXEC, 0400);
		if (db->lock_fd < 0 && errno == ENOENT &&
		    (dbopts->open_flags & APK_OPENF_CREATE)) {
			r = apk_db_create(db);
			if (r != 0) {
				msg = "Unable to create database";
				goto ret_r;
			}
			db->lock_fd = openat(db->root_fd, "var/lib/apk/lock",
					     O_CREAT | O_RDWR | O_CLOEXEC, 0400);
		}
		if (db->lock_fd < 0 ||
		    flock(db->lock_fd, LOCK_EX | LOCK_NB) < 0) {
			msg = "Unable to lock database";
			if (dbopts->lock_wait) {
				struct sigaction sa, old_sa;

				apk_message("Waiting for repository lock");
				memset(&sa, 0, sizeof sa);
				sa.sa_handler = handle_alarm;
				sa.sa_flags   = SA_ONESHOT;
				sigaction(SIGALRM, &sa, &old_sa);

				alarm(dbopts->lock_wait);
				if (flock(db->lock_fd, LOCK_EX) < 0)
					goto ret_errno;

				alarm(0);
				sigaction(SIGALRM, &old_sa, NULL);
			} else
				goto ret_errno;
	blob = APK_BLOB_STR("etc:*etc/init.d");
	apk_blob_for_each_segment(blob, ":", add_protected_path, db);

	db->cache_fd = openat(db->root_fd, db->cache_dir, O_RDONLY | O_CLOEXEC);
	mkdirat(db->cache_fd, "tmp", 0644);
	db->cachetmp_fd = openat(db->cache_fd, "tmp", O_RDONLY | O_CLOEXEC);
	db->keys_fd = openat(db->root_fd,
			     dbopts->keys_dir ?: "etc/apk/keys",
			     O_RDONLY | O_CLOEXEC);
Timo Teräs's avatar
Timo Teräs committed
	if (apk_flags & APK_OVERLAY_FROM_STDIN) {
		apk_flags &= ~APK_OVERLAY_FROM_STDIN;
		apk_db_read_overlay(db, apk_bstream_from_istream(
				apk_istream_from_fd(STDIN_FILENO)));
	}

	r = apk_db_read_state(db, dbopts->open_flags);
	if (r == -ENOENT && (dbopts->open_flags & APK_OPENF_CREATE)) {
		r = apk_db_create(db);
Timo Teräs's avatar
Timo Teräs committed
		if (r != 0) {
			msg = "Unable to create database";
Timo Teräs's avatar
Timo Teräs committed
			goto ret_r;
		r = apk_db_read_state(db, dbopts->open_flags);
	}
	if (r != 0) {
		msg = "Unable to read database state";
		goto ret_r;
	if (!(dbopts->open_flags & APK_OPENF_NO_INSTALLED_REPO)) {
		if (apk_db_cache_active(db)) {
			bs = apk_bstream_from_file(db->cache_fd, "installed");
			if (bs != NULL) {
				apk_db_index_read(db, bs, -2);
				bs->close(bs, NULL);
			}
		}
	}
	if (!(dbopts->open_flags & APK_OPENF_NO_SYS_REPOS)) {
		list_for_each_entry(repo, &dbopts->repository_list, list) {
			r = apk_db_add_repository(db, APK_BLOB_STR(repo->url));
			rr = r ?: rr;
		}
		blob = apk_blob_from_file(
			db->root_fd,
			dbopts->repositories_file ?: "etc/apk/repositories");
		if (!APK_BLOB_IS_NULL(blob)) {
			r = apk_blob_for_each_segment(
				blob, "\n",
				apk_db_add_repository, db);
			rr = r ?: rr;
			free(blob.ptr);
		}
		if (apk_flags & APK_UPDATE_CACHE)
			apk_db_index_write_nr_cache(db);
	if (rr != 0) {
		r = rr;
		goto ret_r;
	}
	if (msg != NULL)
		apk_error("%s: %s", msg, strerror(-r));
int apk_db_write_config(struct apk_database *db)
Timo Teräs's avatar
Timo Teräs committed
	struct apk_ostream *os;
	if ((apk_flags & APK_SIMULATE) || db->root == NULL)
	if (db->lock_fd == 0) {
		apk_error("Refusing to write db without write lock!");
		return -1;
	}

	os = apk_ostream_to_file(db->root_fd,
				 "var/lib/apk/world",
				 "var/lib/apk/world.new",
				 0644);
Timo Teräs's avatar
Timo Teräs committed
	if (os == NULL)
	apk_deps_write(db->world, os);
	os->write(os, "\n", 1);
	r = os->close(os);
	if (r < 0)
		return r;
	os = apk_ostream_to_file(db->root_fd,
				 "var/lib/apk/installed",
				 "var/lib/apk/installed.new",
				 0644);
Timo Teräs's avatar
Timo Teräs committed
	if (os == NULL)
Timo Teräs's avatar
Timo Teräs committed
	apk_db_write_fdb(db, os);
	r = os->close(os);
	if (r < 0)
		return r;
	os = apk_ostream_to_file(db->root_fd,
				 "var/lib/apk/scripts.tar",
				 "var/lib/apk/scripts.tar.new",
				 0644);
Timo Teräs's avatar
Timo Teräs committed
	if (os == NULL)
Timo Teräs's avatar
Timo Teräs committed
	apk_db_scriptdb_write(db, os);
	r = os->close(os);
	if (r < 0)
		return r;
	unlinkat(db->root_fd, "var/lib/apk/scripts", 0);
	apk_db_index_write_nr_cache(db);
	os = apk_ostream_to_file(db->root_fd,
				 "var/lib/apk/triggers",
				 "var/lib/apk/triggers.new",
				 0644);
	if (os == NULL)
		return -1;
	apk_db_triggers_write(db, os);
	r = os->close(os);
	if (r < 0)
		return r;

void apk_db_close(struct apk_database *db)
	struct apk_installed_package *ipkg;
	struct apk_db_dir_instance *diri;
	struct hlist_node *dc, *dn;
	list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list) {
		hlist_for_each_entry_safe(diri, dc, dn, &ipkg->owned_dirs, pkg_dirs_list) {
			apk_db_diri_free(db, diri);
		}
	}

	for (i = 0; i < db->num_repos; i++) {
		free(db->repos[i].url);
Timo Teräs's avatar
Timo Teräs committed
	for (i = 0; i < db->protected_paths->num; i++)
		free(db->protected_paths->item[i]);
	apk_dependency_array_free(&db->world);
	apk_string_array_free(&db->protected_paths);
	apk_hash_free(&db->available.packages);
	apk_hash_free(&db->available.names);
	apk_hash_free(&db->installed.files);
	if (db->keys_fd)
		close(db->keys_fd);
	if (db->cachetmp_fd)
		close(db->cachetmp_fd);
	if (db->cache_fd)
		close(db->cache_fd);
		close(db->root_fd);
	if (db->lock_fd)
		close(db->lock_fd);
	if (db->root != NULL)
static int fire_triggers(apk_hash_item item, void *ctx)
{
	struct apk_database *db = (struct apk_database *) ctx;
	struct apk_db_dir *dbd = (struct apk_db_dir *) item;
	struct apk_installed_package *ipkg;
	int i;

	list_for_each_entry(ipkg, &db->installed.triggers, trigger_pkgs_list) {
		if (((ipkg->flags & APK_IPKGF_RUN_ALL_TRIGGERS) == 0) &&
		    ((dbd->flags & APK_DBDIRF_MODIFIED) == 0))
			continue;

		for (i = 0; i < ipkg->triggers->num; i++) {
			if (ipkg->triggers->item[i][0] != '/')
				continue;

			if (fnmatch(ipkg->triggers->item[i], dbd->rooted_name,
				    FNM_PATHNAME) != 0)
				continue;

Timo Teräs's avatar
Timo Teräs committed
			/* And place holder for script name */
			if (ipkg->pending_triggers->num == 0)
Timo Teräs's avatar
Timo Teräs committed
				*apk_string_array_add(&ipkg->pending_triggers) =
					NULL;
			*apk_string_array_add(&ipkg->pending_triggers) =
				dbd->rooted_name;
			break;
		}
	}

	return 0;
}

int apk_db_run_triggers(struct apk_database *db)
{
	struct apk_installed_package *ipkg;

	apk_hash_foreach(&db->installed.dirs, fire_triggers, db);

	list_for_each_entry(ipkg, &db->installed.triggers, trigger_pkgs_list) {
Timo Teräs's avatar
Timo Teräs committed
		if (ipkg->pending_triggers->num == 0)
			continue;

		*apk_string_array_add(&ipkg->pending_triggers) = NULL;
		apk_ipkg_run_script(ipkg, db->root_fd, APK_SCRIPT_TRIGGER,
				    ipkg->pending_triggers->item);
Timo Teräs's avatar
Timo Teräs committed
		apk_string_array_free(&ipkg->pending_triggers);
Timo Teräs's avatar
Timo Teräs committed
int apk_db_cache_active(struct apk_database *db)
{
	return db->cache_dir != apk_static_cache_dir;
}

int apk_db_permanent(struct apk_database *db)
{
	return db->permanent;
Timo Teräs's avatar
Timo Teräs committed
struct apk_package *apk_db_get_pkg(struct apk_database *db,
				   struct apk_checksum *csum)
Timo Teräs's avatar
Timo Teräs committed
	return apk_hash_get(&db->available.packages, APK_BLOB_CSUM(*csum));
struct apk_package *apk_db_get_file_owner(struct apk_database *db,
					  apk_blob_t filename)
{
	if (filename.len && filename.ptr[0] == '/')
		filename.len--, filename.ptr++;
	if (!apk_blob_rsplit(filename, '/', &key.dirname, &key.filename))
		return NULL;

	dbf = (struct apk_db_file *) apk_hash_get(&db->installed.files,
						  APK_BLOB_BUF(&key));
static int apk_repo_is_remote(struct apk_repository *repo)
{
	return repo->csum.type != APK_CHECKSUM_NONE;
}

static struct apk_bstream *apk_repo_file_open(struct apk_repository *repo,
					      const char *file)
{
	char tmp[256];
Timo Teräs's avatar
Timo Teräs committed
	const char *url = repo->url;
	if ((apk_flags & APK_NO_NETWORK) && apk_repo_is_remote(repo))
		return NULL;

	/* We should not get called for non-repository files */
	if (strcmp(repo->url, "cache") == 0)
		return NULL;

Timo Teräs's avatar
Timo Teräs committed
	snprintf(tmp, sizeof(tmp), "%s%s%s",
		 url, url[strlen(url)-1] == '/' ? "" : "/", file);

	return apk_bstream_from_url(tmp);
}

struct apk_repository *apk_db_select_repo(struct apk_database *db,
					  struct apk_package *pkg)
{
	static struct apk_repository cache_repo = {
		.url = "cache",
		.csum.data = {
			0xb0,0x35,0x92,0x80,0x6e,0xfa,0xbf,0xee,0xb7,0x09,
			0xf5,0xa7,0x0a,0x7c,0x17,0x26,0x69,0xb0,0x05,0x38 },
		.csum.type = APK_CHECKSUM_SHA1,
	};
	unsigned int repos = pkg->repos;
	int i;

	/* Always prefer local repositories */
	if ((repos & db->local_repos) != 0)
		repos &= db->local_repos;

	/* Pick first repository providing this package */
	for (i = 0; i < APK_MAX_REPOS; i++)
		if (repos & BIT(i))
			break;

	/* If this is a remote repository, and we have no network,
	 * check that we have it in cache */
	if ((i >= APK_MAX_REPOS) ||
	    ((db->local_repos & BIT(i)) == 0 && (apk_flags & APK_NO_NETWORK))) {
		char cacheitem[PATH_MAX];

		apk_pkg_format_cache(pkg, APK_BLOB_BUF(cacheitem));
		if (faccessat(db->cache_fd, cacheitem, R_OK, 0) != 0)
			return NULL;
	}

	if (i >= APK_MAX_REPOS)
		return &cache_repo;

Timo Teräs's avatar
Timo Teräs committed
int apk_repository_update(struct apk_database *db, struct apk_repository *repo)
{
	char cacheitem[PATH_MAX];
	if (!apk_repo_is_remote(repo))
Timo Teräs's avatar
Timo Teräs committed
		return 0;

	apk_cache_format_index(APK_BLOB_BUF(cacheitem), repo, 0);
	r = apk_cache_download(db, repo->url, apkindex_tar_gz, cacheitem,
			       (apk_flags & APK_ALLOW_UNTRUSTED) ?
			       APK_SIGN_NONE : APK_SIGN_VERIFY);
Timo Teräs's avatar
Timo Teräs committed
	if (r == 0 || r == -ENOKEY || r == -EKEYREJECTED) {
		if (r != 0)
			apk_error("%s: %s", repo->url, apk_error_str(r));
	apk_cache_format_index(APK_BLOB_BUF(cacheitem), repo, 1);
	r = apk_cache_download(db, repo->url, apk_index_gz, cacheitem,
			       APK_SIGN_NONE);
	if (r != 0)
Timo Teräs's avatar
Timo Teräs committed
		apk_error("Failed to update %s: download failed", repo->url);
}

struct apkindex_ctx {
	struct apk_database *db;
	struct apk_sign_ctx sctx;
};

static int load_apkindex(void *sctx, const struct apk_file_info *fi,
			 struct apk_istream *is)
{
	struct apkindex_ctx *ctx = (struct apkindex_ctx *) sctx;
	struct apk_bstream *bs;

	if (apk_sign_ctx_process_file(&ctx->sctx, fi, is) == 0)
		return 0;

	repo = &ctx->db->repos[ctx->repo];
	if (strcmp(fi->name, "DESCRIPTION") == 0) {
		repo->description = apk_blob_from_istream(is, fi->size);
	} else if (strcmp(fi->name, "APKINDEX") == 0) {
		ctx->found = 1;
		bs = apk_bstream_from_istream(is);
		apk_db_index_read(ctx->db, bs, ctx->repo);
		bs->close(bs, NULL);
	}
static int load_index(struct apk_database *db, struct apk_bstream *bs,
		      int targz, int repo)
{
	if (targz) {
		struct apk_istream *is;
		struct apkindex_ctx ctx;

		ctx.db = db;
		ctx.repo = repo;
		apk_sign_ctx_init(&ctx.sctx, APK_SIGN_VERIFY, NULL, db->keys_fd);
		is = apk_bstream_gunzip_mpart(bs, apk_sign_ctx_mpart_cb, &ctx.sctx);
		r = apk_tar_parse(is, load_apkindex, &ctx, FALSE);
		is->close(is);
		apk_sign_ctx_free(&ctx.sctx);
			r = -ENOMSG;
	} else {
		bs = apk_bstream_from_istream(apk_bstream_gunzip(bs));
		apk_db_index_read(db, bs, repo);
		bs->close(bs, NULL);
	}
}

int apk_db_index_read_file(struct apk_database *db, const char *file, int repo)
{
	int targz = 1;

	if (strstr(file, ".tar.gz") == NULL && strstr(file, ".gz") != NULL)
		targz = 0;

	return load_index(db, apk_bstream_from_file(AT_FDCWD, file), targz, repo);
int apk_db_add_repository(apk_database_t _db, apk_blob_t repository)
{
	struct apk_database *db = _db.db;
	struct apk_bstream *bs = NULL;
	struct apk_repository *repo;
	int r, targz = 1;
Timo Teräs's avatar
Timo Teräs committed
	if (repository.ptr == NULL || repository.len == 0 ||
	    *repository.ptr == '#')
	if (db->num_repos >= APK_MAX_REPOS)
		return -1;

	r = db->num_repos++;

	repo = &db->repos[r];
	*repo = (struct apk_repository) {
		.url = apk_blob_cstr(repository),
	if (apk_url_local_file(repo->url) == NULL) {
		char cacheitem[PATH_MAX];

		apk_blob_checksum(repository, apk_checksum_default(), &repo->csum);
		if (apk_flags & APK_UPDATE_CACHE)
			apk_repository_update(db, repo);
		apk_cache_format_index(APK_BLOB_BUF(cacheitem), repo, 0);
		bs = apk_bstream_from_file(db->cache_fd, cacheitem);
		if (bs == NULL) {
			apk_cache_format_index(APK_BLOB_BUF(cacheitem), repo, 1);
			bs = apk_bstream_from_file(db->cache_fd, cacheitem);
			targz = 0;
		bs = apk_repo_file_open(repo, apkindex_tar_gz);
		if (bs == NULL) {
			bs = apk_repo_file_open(repo, apk_index_gz);
		apk_warning("Failed to open index for %s", repo->url);
		return 0;
	r = load_index(db, bs, targz, r);
	if (r != 0)
		apk_error("%s: Bad repository signature", repo->url);
	return r;
static void extract_cb(void *_ctx, size_t progress)
{
	struct install_ctx *ctx = (struct install_ctx *) _ctx;

	if (ctx->cb) {
		size_t size = ctx->installed_size;

		size += muldiv(progress, ctx->current_file_size, APK_PROGRESS_SCALE);
		if (size > ctx->pkg->installed_size)
			size = ctx->pkg->installed_size;

		ctx->cb(ctx->cb_ctx, muldiv(APK_PROGRESS_SCALE, size, ctx->pkg->installed_size));
	}
}

static int apk_db_run_pending_script(struct install_ctx *ctx)
{
	int r;

	if (!ctx->script_pending)
		return 0;
	if (!ctx->sctx.control_verified)
		return 0;

	ctx->script_pending = FALSE;
	r = apk_ipkg_run_script(ctx->ipkg, ctx->db->root_fd, ctx->script,
				ctx->script_args);
	if (r != 0)
		apk_error("%s-%s: Failed to execute "
			  "pre-install/upgrade script",
			  ctx->pkg->name->name, ctx->pkg->version);
	return r;
}

static int parse_replaces(void *_ctx, apk_blob_t blob)
{
	struct install_ctx *ctx = (struct install_ctx *) _ctx;

	if (blob.len == 0)
		return 0;

	*apk_name_array_add(&ctx->replaces) = apk_db_get_name(ctx->db, blob);
	return 0;
}

static int read_info_line(void *_ctx, apk_blob_t line)
{
	struct install_ctx *ctx = (struct install_ctx *) _ctx;
	struct apk_installed_package *ipkg = ctx->ipkg;
	struct apk_database *db = ctx->db;
	apk_blob_t l, r;

	if (line.ptr == NULL || line.len < 1 || line.ptr[0] == '#')
		return 0;

	if (!apk_blob_split(line, APK_BLOB_STR(" = "), &l, &r))
		return 0;

	if (apk_blob_compare(APK_BLOB_STR("replaces"), l) == 0) {
		apk_blob_for_each_segment(r, " ", parse_replaces, ctx);
	} else if (apk_blob_compare(APK_BLOB_STR("triggers"), l) == 0) {
Timo Teräs's avatar
Timo Teräs committed
		apk_string_array_resize(&ipkg->triggers, 0);
		apk_blob_for_each_segment(r, " ", parse_triggers, ctx->ipkg);
Timo Teräs's avatar
Timo Teräs committed
		if (ctx->ipkg->triggers->num != 0 &&
		    !list_hashed(&ipkg->trigger_pkgs_list))
			list_add_tail(&ipkg->trigger_pkgs_list,
				      &db->installed.triggers);
	} else {
		apk_sign_ctx_parse_pkginfo_line(&ctx->sctx, line);
static int apk_db_install_archive_entry(void *_ctx,
					const struct apk_file_info *ae,
					struct apk_istream *is)
	struct install_ctx *ctx = (struct install_ctx *) _ctx;
	struct apk_package *pkg = ctx->pkg, *opkg;
	struct apk_installed_package *ipkg = pkg->ipkg;
	apk_blob_t name = APK_BLOB_STR(ae->name), bdir, bfile;
	struct apk_db_dir_instance *diri = ctx->diri;
	int r = 0, type = APK_SCRIPT_INVALID;
	if (apk_sign_ctx_process_file(&ctx->sctx, ae, is) == 0)
		return 0;

	/* Package metainfo and script processing */
	if (ae->name[0] == '.') {
		/* APK 2.0 format */
		if (strcmp(ae->name, ".PKGINFO") == 0) {
			apk_blob_t blob = apk_blob_from_istream(is, ae->size);
			apk_blob_for_each_segment(blob, "\n", read_info_line, ctx);
			free(blob.ptr);
			return 0;
		}
		type = apk_script_type(&ae->name[1]);
		if (type == APK_SCRIPT_INVALID)
			return 0;
	} else if (strncmp(ae->name, "var/db/apk/", 11) == 0) {
		/* APK 1.0 format */
		p = &ae->name[11];
		if (strncmp(p, pkg->name->name, strlen(pkg->name->name)) != 0)
			return 0;
		p += strlen(pkg->name->name) + 1;
		if (strncmp(p, pkg->version, strlen(pkg->version)) != 0)
			return 0;
		p += strlen(pkg->version) + 1;

		type = apk_script_type(p);
		if (type == APK_SCRIPT_INVALID)
	/* Handle script */
	if (type != APK_SCRIPT_INVALID) {
		apk_ipkg_add_script(ipkg, is, type, ae->size);
		if (type == ctx->script)
			ctx->script_pending = TRUE;
		return apk_db_run_pending_script(ctx);
	}
	r = apk_db_run_pending_script(ctx);
	if (r != 0)
	/* Show progress */
	if (ctx->cb) {
		size_t size = ctx->installed_size;
		if (size > pkg->installed_size)
			size = pkg->installed_size;
		ctx->cb(ctx->cb_ctx, muldiv(APK_PROGRESS_SCALE, size, pkg->installed_size));
	}

	/* Installable entry */
	ctx->current_file_size = apk_calc_installed_size(ae->size);
		if (!apk_blob_rsplit(name, '/', &bdir, &bfile))
			return 0;

		if (bfile.len > 6 && memcmp(bfile.ptr, ".keep_", 6) == 0)
			return 0;

		/* Make sure the file is part of the cached directory tree */
		diri = find_diri(ipkg, bdir, diri, &ctx->file_diri_node);
		if (diri == NULL) {
			apk_error("%s: File '%*s' entry without directory entry.\n",
				  pkg->name->name, name.len, name.ptr);
			return -1;
Timo Teräs's avatar
Timo Teräs committed
		file = apk_db_file_query(db, bdir, bfile);
		if (file != NULL) {
			opkg = file->diri->pkg;
Timo Teräs's avatar
Timo Teräs committed
				/* Overlay file? */
				if (opkg->name == NULL)
					break;
				/* Upgrading package? */
				if (opkg->name == pkg->name)
					break;
Timo Teräs's avatar
Timo Teräs committed
				/* Overwriting with permission? */
				for (i = 0; i < ctx->replaces->num; i++)
					if (opkg->name == ctx->replaces->item[i])
						break;
				if (i < ctx->replaces->num)
				if (!(apk_flags & APK_FORCE)) {
					apk_error("%s: Trying to overwrite %s "
						  pkg->name->name, ae->name,
						  opkg->name->name);
					return -1;
				}
				apk_warning("%s: Overwriting %s owned by %s.",
					    pkg->name->name, ae->name,
					    opkg->name->name);
		if (opkg != pkg) {
			/* Create the file entry without adding it to hash */
			file = apk_db_file_new(diri, bfile, &ctx->file_diri_node);
		}
		if (apk_verbosity >= 3)
			apk_message("%s", ae->name);
Timo Teräs's avatar
Timo Teräs committed
		/* Extract the file as name.apk-new */
		r = apk_archive_entry_extract(db->root_fd, ae, ".apk-new", is,
Timo Teräs's avatar
Timo Teräs committed
					      extract_cb, ctx);

		/* Hardlinks need special care for checksum */
		if (ae->csum.type == APK_CHECKSUM_NONE &&
		    ae->link_target != NULL) {
			do {
				struct apk_db_file *lfile;
				struct apk_db_dir_instance *ldiri;
				struct hlist_node *n;

				if (!apk_blob_rsplit(APK_BLOB_STR(ae->link_target),
						     '/', &bdir, &bfile))
					break;

				ldiri = find_diri(ipkg, bdir, diri, NULL);
				if (ldiri == NULL)
					break;

				hlist_for_each_entry(lfile, n, &ldiri->owned_files,
						     diri_files_list) {
					if (apk_blob_compare(APK_BLOB_PTR_LEN(lfile->name, lfile->namelen),
							     bfile) == 0) {
						memcpy(&file->csum, &lfile->csum,
						       sizeof(file->csum));
						break;
					}
				}
			} while (0);

		} else
			memcpy(&file->csum, &ae->csum, sizeof(file->csum));
		if (apk_verbosity >= 3)
			apk_message("%s", ae->name);
		if (name.ptr[name.len-1] == '/')
			name.len--;

		if (ctx->diri_node == NULL)
			ctx->diri_node = hlist_tail_ptr(&ipkg->owned_dirs);
		ctx->diri = diri = apk_db_diri_new(db, pkg, name,
						   &ctx->diri_node);
		ctx->file_diri_node = hlist_tail_ptr(&diri->owned_files);

		apk_db_diri_set(diri, ae->mode & 0777, ae->uid, ae->gid);
		apk_db_diri_mkdir(db, diri);
	ctx->installed_size += ctx->current_file_size;
static void apk_db_purge_pkg(struct apk_database *db,
			     struct apk_installed_package *ipkg,
Timo Teräs's avatar
Timo Teräs committed
			     const char *exten)
	struct apk_db_dir_instance *diri;
	struct apk_db_file_hash_key key;
	struct apk_file_info fi;
	struct hlist_node *dc, *dn, *fc, *fn;
Timo Teräs's avatar
Timo Teräs committed
	unsigned long hash;
	hlist_for_each_entry_safe(diri, dc, dn, &ipkg->owned_dirs, pkg_dirs_list) {
		if (exten == NULL)
			diri->dir->flags |= APK_DBDIRF_MODIFIED;

		hlist_for_each_entry_safe(file, fc, fn, &diri->owned_files, diri_files_list) {
Timo Teräs's avatar
Timo Teräs committed
			snprintf(name, sizeof(name), "%s/%s%s",
				 diri->dir->name, file->name, exten ?: "");

			key = (struct apk_db_file_hash_key) {
Timo Teräs's avatar
Timo Teräs committed
				.dirname = APK_BLOB_PTR_LEN(diri->dir->name, diri->dir->namelen),
				.filename = APK_BLOB_PTR_LEN(file->name, file->namelen),
Timo Teräs's avatar
Timo Teräs committed
			hash = apk_blob_hash_seed(key.filename, diri->dir->hash);
			if (!(diri->dir->flags & APK_DBDIRF_PROTECTED) ||
			    (apk_flags & APK_PURGE) ||
			    (file->csum.type != APK_CHECKSUM_NONE &&
			     apk_file_get_info(db->root_fd, name, APK_FI_NOFOLLOW | file->csum.type, &fi) == 0 &&
			     apk_checksum_compare(&file->csum, &fi.csum) == 0))
				unlinkat(db->root_fd, name, 0);
			if (apk_verbosity >= 3)
				apk_message("%s", name);
			__hlist_del(fc, &diri->owned_files.first);
Timo Teräs's avatar
Timo Teräs committed
			if (exten == NULL) {
				apk_hash_delete_hashed(&db->installed.files, APK_BLOB_BUF(&key), hash);
				db->installed.stats.files--;
			}
		apk_db_diri_rmdir(db, diri);
		__hlist_del(dc, &ipkg->owned_dirs.first);
		apk_db_diri_free(db, diri);
Timo Teräs's avatar
Timo Teräs committed

static void apk_db_migrate_files(struct apk_database *db,
				 struct apk_installed_package *ipkg)
Timo Teräs's avatar
Timo Teräs committed
{
	struct apk_db_dir_instance *diri;
	struct apk_db_dir *dir;
	struct apk_db_file *file, *ofile;
	struct apk_db_file_hash_key key;
	struct apk_file_info fi;
	struct hlist_node *dc, *dn, *fc, *fn;
	unsigned long hash;
	char name[PATH_MAX], tmpname[PATH_MAX];
	hlist_for_each_entry_safe(diri, dc, dn, &ipkg->owned_dirs, pkg_dirs_list) {
Timo Teräs's avatar
Timo Teräs committed
		dir = diri->dir;
		dir->flags |= APK_DBDIRF_MODIFIED;
Timo Teräs's avatar
Timo Teräs committed

		hlist_for_each_entry_safe(file, fc, fn, &diri->owned_files, diri_files_list) {
			snprintf(name, sizeof(name), "%s/%s",
				 diri->dir->name, file->name);
			snprintf(tmpname, sizeof(tmpname), "%s/%s.apk-new",
				 diri->dir->name, file->name);

			key = (struct apk_db_file_hash_key) {
				.dirname = APK_BLOB_PTR_LEN(dir->name, dir->namelen),
				.filename = APK_BLOB_PTR_LEN(file->name, file->namelen),
			};

			hash = apk_blob_hash_seed(key.filename, dir->hash);

			/* check for existing file */
			ofile = (struct apk_db_file *) apk_hash_get_hashed(
				&db->installed.files, APK_BLOB_BUF(&key), hash);

			/* We want to compare checksums only if one exists
			 * in db, and the file is in a protected path */
			cstype = APK_CHECKSUM_NONE;
			if (ofile != NULL &&
			    (diri->dir->flags & APK_DBDIRF_PROTECTED))
				cstype = ofile->csum.type;
			cstype |= APK_FI_NOFOLLOW;
			r = apk_file_get_info(db->root_fd, name, cstype, &fi);
			if (ofile && ofile->diri->pkg->name == NULL) {
				/* File was from overlay, delete the
				 * packages version */
				unlinkat(db->root_fd, tmpname, 0);
			} else if ((diri->dir->flags & APK_DBDIRF_PROTECTED) &&
				   (r == 0) &&
				   (ofile == NULL ||
				    ofile->csum.type == APK_CHECKSUM_NONE ||
				    apk_checksum_compare(&ofile->csum, &fi.csum) != 0)) {
				/* Protected directory, with file without
				 * db entry, or local modifications.
				 *
				 * Delete the apk-new if it's identical with the
				 * existing file */
				if (ofile == NULL ||
				    ofile->csum.type != file->csum.type)
					apk_file_get_info(db->root_fd, name,
						APK_FI_NOFOLLOW | file->csum.type, &fi);
				if ((apk_flags & APK_CLEAN_PROTECTED) ||
				    (file->csum.type != APK_CHECKSUM_NONE &&
				     apk_checksum_compare(&file->csum, &fi.csum) == 0))
					unlinkat(db->root_fd, tmpname, 0);
Timo Teräs's avatar
Timo Teräs committed
			} else {
				/* Overwrite the old file */
				renameat(db->root_fd, tmpname,
					 db->root_fd, name);
Timo Teräs's avatar
Timo Teräs committed
			}

			/* Claim ownership of the file in db */
			if (ofile != file) {
				if (ofile != NULL) {
					hlist_del(&ofile->diri_files_list,
						&ofile->diri->owned_files);
					apk_hash_delete_hashed(&db->installed.files,
							       APK_BLOB_BUF(&key), hash);
				} else
					db->installed.stats.files++;

				apk_hash_insert_hashed(&db->installed.files, file, hash);
			}
static int apk_db_unpack_pkg(struct apk_database *db,
			     struct apk_installed_package *ipkg,
			     int upgrade, int reinstall,
			     apk_progress_cb cb, void *cb_ctx,
			     char **script_args)
	struct apk_bstream *bs = NULL;
	struct apk_istream *tar;
	struct apk_package *pkg = ipkg->pkg;
	char file[PATH_MAX];
	int r, need_copy = FALSE;
	if (pkg->filename == NULL) {
		struct apk_repository *repo;

		repo = apk_db_select_repo(db, pkg);
		if (repo == NULL) {
			apk_error("%s-%s: package is not currently available",
				  pkg->name->name, pkg->version);
		if (apk_db_cache_active(db) && apk_repo_is_remote(repo)) {
			apk_pkg_format_cache(pkg, APK_BLOB_BUF(file));