From d61c009f7a7ba9c99797855d8cd4e0a503c2fda5 Mon Sep 17 00:00:00 2001
From: Fredrik Gustafsson <fredrigu@axis.com>
Date: Wed, 20 Nov 2019 11:48:48 +0100
Subject: [PATCH] apk: do not manage file ownership as non-root or when asked
 so

If apk is run as a non-root user, it's not possible to chown files.

Maintainers note: minor wording changes on commit log and man page.

Signed-off-by: Fredrik Gustafsson <fredrigu@axis.com>
---
 doc/apk-add.8.scd  |  6 ++++++
 src/apk_archive.h  |  5 ++++-
 src/apk_database.h |  1 +
 src/app_add.c      |  8 ++++++++
 src/database.c     |  6 +++++-
 src/io_archive.c   | 29 ++++++++++++++++-------------
 6 files changed, 40 insertions(+), 15 deletions(-)

diff --git a/doc/apk-add.8.scd b/doc/apk-add.8.scd
index f7b52bd3..7959a998 100644
--- a/doc/apk-add.8.scd
+++ b/doc/apk-add.8.scd
@@ -39,6 +39,12 @@ following options:
 	virtual package, and by deleting that package the *apk add* operation
 	may be easily reverted later.
 
+*--no-chown*
+	Do not change file owner or group. By default apk will manage the file
+	ownership when running as root. However, this option is turned on when
+	running as non-root user, as changing file ownership is not permitted
+	by the system then.
+
 # AUTHORS
 
 Natanael Copa <ncopa@alpinelinux.org>++
diff --git a/src/apk_archive.h b/src/apk_archive.h
index bb16c48c..bd24733d 100644
--- a/src/apk_archive.h
+++ b/src/apk_archive.h
@@ -16,6 +16,8 @@
 #include "apk_blob.h"
 #include "apk_io.h"
 
+#define APK_EXTRACTF_NO_CHOWN	0x0001
+
 typedef int (*apk_archive_entry_parser)(void *ctx,
 					const struct apk_file_info *ae,
 					struct apk_istream *istream);
@@ -30,6 +32,7 @@ int apk_tar_write_padding(struct apk_ostream *, const struct apk_file_info *ae);
 int apk_archive_entry_extract(int atfd, const struct apk_file_info *ae,
 			      const char *extract_name, const char *hardlink_name,
 			      struct apk_istream *is,
-			      apk_progress_cb cb, void *cb_ctx);
+			      apk_progress_cb cb, void *cb_ctx,
+			      unsigned int extract_flags);
 
 #endif
diff --git a/src/apk_database.h b/src/apk_database.h
index 30684c33..bcd5719c 100644
--- a/src/apk_database.h
+++ b/src/apk_database.h
@@ -155,6 +155,7 @@ struct apk_database {
 	unsigned int local_repos, available_repos, cache_max_age;
 	unsigned int repo_update_errors, repo_update_counter;
 	unsigned int pending_triggers;
+	unsigned int extract_flags;
 	int performing_self_upgrade : 1;
 	int permanent : 1;
 	int autoupdate : 1;
diff --git a/src/app_add.c b/src/app_add.c
index 8b0ed1ad..f5edae0e 100644
--- a/src/app_add.c
+++ b/src/app_add.c
@@ -20,6 +20,7 @@
 struct add_ctx {
 	const char *virtpkg;
 	unsigned short solver_flags;
+	unsigned short extract_flags;
 };
 
 static int option_parse_applet(void *ctx, struct apk_db_options *dbopts, int optch, const char *optarg)
@@ -30,6 +31,9 @@ static int option_parse_applet(void *ctx, struct apk_db_options *dbopts, int opt
 	case 0x10000:
 		dbopts->open_flags |= APK_OPENF_CREATE;
 		break;
+	case 0x10001:
+		actx->extract_flags |= APK_EXTRACTF_NO_CHOWN;
+		break;
 	case 'u':
 		actx->solver_flags |= APK_SOLVERF_UPGRADE;
 		break;
@@ -47,6 +51,7 @@ static int option_parse_applet(void *ctx, struct apk_db_options *dbopts, int opt
 
 static const struct apk_option options_applet[] = {
 	{ 0x10000,	"initdb" },
+	{ 0x10001,	"no-chown" },
 	{ 'u',		"upgrade" },
 	{ 'l',		"latest" },
 	{ 't',		"virtual", required_argument, "NAME" },
@@ -118,6 +123,9 @@ static int add_main(void *ctx, struct apk_database *db, struct apk_string_array
 
 	apk_dependency_array_copy(&world, db->world);
 
+	if (getuid() != 0 || (actx->extract_flags & APK_EXTRACTF_NO_CHOWN))
+		db->extract_flags |= APK_EXTRACTF_NO_CHOWN;
+
 	if (actx->virtpkg) {
 		apk_blob_t b = APK_BLOB_STR(actx->virtpkg);
 		apk_blob_pull_dep(&b, db, &virtdep);
diff --git a/src/database.c b/src/database.c
index cbc66675..dd27e2a7 100644
--- a/src/database.c
+++ b/src/database.c
@@ -1347,6 +1347,10 @@ static int apk_db_create(struct apk_database *db)
 	if (fd < 0)
 		return -errno;
 	close(fd);
+	fd = openat(db->root_fd, apk_installed_file, O_CREAT|O_RDWR|O_TRUNC|O_CLOEXEC, 0644);
+	if (fd < 0)
+		return -errno;
+	close(fd);
 
 	return 0;
 }
@@ -2554,7 +2558,7 @@ static int apk_db_install_archive_entry(void *_ctx,
 				db->root_fd, ae,
 				format_tmpname(pkg, file, tmpname_file),
 				format_tmpname(pkg, link_target_file, tmpname_link_target),
-				is, extract_cb, ctx);
+				is, extract_cb, ctx, db->extract_flags);
 
 		switch (r) {
 		case 0:
diff --git a/src/io_archive.c b/src/io_archive.c
index 22145aba..1d349271 100644
--- a/src/io_archive.c
+++ b/src/io_archive.c
@@ -337,7 +337,8 @@ int apk_tar_write_padding(struct apk_ostream *os, const struct apk_file_info *ae
 int apk_archive_entry_extract(int atfd, const struct apk_file_info *ae,
 			      const char *extract_name, const char *link_target,
 			      struct apk_istream *is,
-			      apk_progress_cb cb, void *cb_ctx)
+			      apk_progress_cb cb, void *cb_ctx,
+			      unsigned int apk_extract_flags)
 {
 	struct apk_xattr *xattr;
 	const char *fn = extract_name ?: ae->name;
@@ -385,22 +386,24 @@ int apk_archive_entry_extract(int atfd, const struct apk_file_info *ae,
 		return ret;
 	}
 
-	r = fchownat(atfd, fn, ae->uid, ae->gid, atflags);
-	if (r < 0) {
-		apk_error("Failed to set ownership on %s: %s",
-			  fn, strerror(errno));
-		if (!ret) ret = -errno;
-	}
-
-	/* chown resets suid bit so we need set it again */
-	if (ae->mode & 07000) {
-		r = fchmodat(atfd, fn, ae->mode & 07777, atflags);
+	if (!(apk_extract_flags & APK_EXTRACTF_NO_CHOWN)) {
+		r = fchownat(atfd, fn, ae->uid, ae->gid, atflags);
 		if (r < 0) {
-			apk_error("Failed to set file permissions "
-				  "on %s: %s",
+			apk_error("Failed to set ownership on %s: %s",
 				  fn, strerror(errno));
 			if (!ret) ret = -errno;
 		}
+
+		/* chown resets suid bit so we need set it again */
+		if (ae->mode & 07000) {
+			r = fchmodat(atfd, fn, ae->mode & 07777, atflags);
+			if (r < 0) {
+				apk_error("Failed to set file permissions "
+					  "on %s: %s",
+					  fn, strerror(errno));
+				if (!ret) ret = -errno;
+			}
+		}
 	}
 
 	/* extract xattrs */
-- 
GitLab