From 05a20774d59160125ce87563b5b52adbbc6dabd3 Mon Sep 17 00:00:00 2001
From: Hao Wang <wanghao232@huawei.com>
Date: Mon, 7 Dec 2020 14:18:54 +0800
Subject: [PATCH patch management 2/2] libcare-ctl: introduce patch-id

Unique patch-id is necessary when multi-patches unpatching and querying.
Introduce patch-id and bring two interface changes:

1.making kpatch:
kpatch_make -b <buildid> -i <patch_id>

2.unapplying kpatch:
libcare-ctl unpatch -p <pid> -i <patch_id>

Change-Id: Iac0db514151dd68decd74f9ebbafd38f15ab095c
Signed-off-by: imxcc <xingchaochao@huawei.com>
---
 libcare.spec                 |  5 +++-
 src/include/kpatch_file.h    |  1 +
 src/include/kpatch_patch.h   |  1 +
 src/include/kpatch_process.h |  7 +++--
 src/kpatch_make.c            | 17 +++++++----
 src/kpatch_patch.c           | 40 ++++++++++++++++++--------
 src/kpatch_process.c         | 55 ++++++++++++++++++++++++++----------
 src/kpatch_user.c            | 36 ++++++++++++++---------
 src/libcare-patch-make       | 16 +++++++++--
 9 files changed, 126 insertions(+), 52 deletions(-)

diff --git a/libcare.spec b/libcare.spec
index e940cf1..24c4868 100644
--- a/libcare.spec
+++ b/libcare.spec
@@ -1,7 +1,7 @@
 Version: 0.1.4
 Name: libcare
 Summary: LibCare tools
-Release: 11%{?dist}
+Release: 12%{?dist}
 Group: Applications/System
 License: GPLv2
 Url: http://www.cloudlinux.com
@@ -158,6 +158,9 @@ exit 0
 %endif
 
 %changelog
+* Thu Sep 23 2021 Hao Wang <wanghao232@huawei.com> - 0.1.4-12
+- libcare-ctl: introduce patch-id
+
 * Thu Sep 23 2021 Hao Wang <wanghao232@huawei.com> - 0.1.4-11
 - libcare-ctl: implement applied patch list
 
diff --git a/src/include/kpatch_file.h b/src/include/kpatch_file.h
index 0b314e3..615c3f0 100644
--- a/src/include/kpatch_file.h
+++ b/src/include/kpatch_file.h
@@ -113,6 +113,7 @@ struct kpatch_payload {
 
 struct kpatch_file {
 	char magic[8];			/* magic string */
+	char id[8];			/* unique patch id */
 	unsigned char flags;
 	unsigned char safety_method;
 	char pad[6];
diff --git a/src/include/kpatch_patch.h b/src/include/kpatch_patch.h
index fa96b08..56c38cd 100644
--- a/src/include/kpatch_patch.h
+++ b/src/include/kpatch_patch.h
@@ -20,6 +20,7 @@ struct patch_data {
 struct unpatch_data {
 	char **buildids;
 	int nbuildids;
+	const char *patch_id;
 };
 
 int process_patch(int pid, void *_data);
diff --git a/src/include/kpatch_process.h b/src/include/kpatch_process.h
index ae00678..f7cf823 100644
--- a/src/include/kpatch_process.h
+++ b/src/include/kpatch_process.h
@@ -174,11 +174,11 @@ kpatch_object_allocate_patch(struct object_file *obj,
 			     size_t sz);
 
 int
-kpatch_process_associate_patches(kpatch_process_t *proc);
+kpatch_process_associate_patches(kpatch_process_t *proc, const char *patch_id);
 int
 kpatch_process_parse_proc_maps(kpatch_process_t *proc);
 int
-kpatch_process_map_object_files(kpatch_process_t *proc);
+kpatch_process_map_object_files(kpatch_process_t *proc, const char *patch_id);
 int
 kpatch_process_attach(kpatch_process_t *proc);
 
@@ -227,7 +227,8 @@ unsigned long object_find_patch_region(struct object_file *obj,
 			 size_t memsize,
 			 struct vm_hole **hole);
 
+int kpatch_object_check_duplicate_id(struct object_file *o, const char *patch_id);
 int kpatch_object_add_applied_patch(struct object_file *o, struct object_file *new);
-struct object_file * kpatch_object_find_applied_patch(struct object_file *o);
+struct object_file * kpatch_object_find_applied_patch(struct object_file *o, const char *patch_id);
 
 #endif /* ifndef __KPATCH_PROCESS__ */
diff --git a/src/kpatch_make.c b/src/kpatch_make.c
index 6a173e6..cab3302 100644
--- a/src/kpatch_make.c
+++ b/src/kpatch_make.c
@@ -26,7 +26,7 @@ static void xerror(const char *fmt, ...)
 	exit(1);
 }
 
-int make_file(int fdo, void *buf1, off_t size, const char *buildid)
+int make_file(int fdo, void *buf1, off_t size, const char *buildid, const char *patch_id)
 {
 	int res;
 	struct kpatch_file khdr;
@@ -34,6 +34,7 @@ int make_file(int fdo, void *buf1, off_t size, const char *buildid)
 	memset(&khdr, 0, sizeof(khdr));
 
 	memcpy(khdr.magic, KPATCH_FILE_MAGIC1, sizeof(khdr.magic));
+	strncpy(khdr.id, patch_id, sizeof(khdr.id));
 	strncpy(khdr.uname, buildid, sizeof(khdr.uname));
 	khdr.build_time = (uint64_t)time(NULL);
 	khdr.csum = 0;		/* FIXME */
@@ -55,9 +56,10 @@ int make_file(int fdo, void *buf1, off_t size, const char *buildid)
 
 static void usage(void)
 {
-	printf("Usage: kpatch_make [-d] -n <modulename> [-v <version>] -e <entryaddr> [-o <output>] <input1> [input2]\n");
+	printf("Usage: kpatch_make [-d] -n <modulename> [-v <version>] -e <entryaddr> [-o <output>] -i <patch_id> <input1> [input2]\n");
 	printf("   -b buildid = target buildid for patch\n");
 	printf("   -d debug (verbose)\n");
+	printf("   -i unique patch id\n");
 	printf("\n");
 	printf("   result is printed to output and is the following:\n");
 	printf("      header          - struct kpatch_file\n");
@@ -71,9 +73,9 @@ int main(int argc, char **argv)
 	int fd1, fdo;
 	void *buf;
 	struct stat st;
-	char *buildid = NULL, *outputname = NULL;
+	char *buildid = NULL, *outputname = NULL, *patch_id = NULL;
 
-	while ((opt = getopt(argc, argv, "db:o:v:s:")) != -1) {
+	while ((opt = getopt(argc, argv, "db:o:i:v:s:")) != -1) {
 		switch (opt) {
 		case 'd':
 			verbose = 1;
@@ -84,12 +86,15 @@ int main(int argc, char **argv)
 		case 'o':
 			outputname = strdup(optarg);
 			break;
+		case 'i':
+			patch_id = strdup(optarg);
+			break;
 		default: /* '?' */
 			usage();
 		}
 	}
 
-	if (buildid == NULL)
+	if (buildid == NULL || patch_id == NULL)
 		usage();
 
 	fd1 = open(argv[optind], O_RDONLY);
@@ -109,5 +114,5 @@ int main(int argc, char **argv)
 			xerror("Can't open output file '%s'", outputname);
 	}
 
-	return make_file(fdo, buf, st.st_size, buildid);
+	return make_file(fdo, buf, st.st_size, buildid, patch_id);
 }
diff --git a/src/kpatch_patch.c b/src/kpatch_patch.c
index 5b6e3f2..ebfa678 100644
--- a/src/kpatch_patch.c
+++ b/src/kpatch_patch.c
@@ -306,6 +306,12 @@ object_apply_patch(struct object_file *o)
 		return -1;
 	}
 
+	if (kpatch_object_check_duplicate_id(o, (const char *)o->kpfile.patch->id)) {
+		kplogerror("A patch with patch-id(%s) is already applied by %s",
+			   o->kpfile.patch->id, o->name);
+		return -1;
+	}
+
 	ret = kpatch_elf_load_kpatch_info(o);
 	if (ret < 0)
 		return ret;
@@ -445,7 +451,7 @@ int process_patch(int pid, void *_data)
 	 * Because we know uniq BuildID of the object the section addresses
 	 * stored in the patch are valid for the original object.
 	 */
-	ret = kpatch_process_map_object_files(proc);
+	ret = kpatch_process_map_object_files(proc, NULL);
 	if (ret < 0)
 		goto out_free;
 
@@ -504,11 +510,23 @@ object_find_applied_patch_info(struct object_file *o)
 	size_t nalloc = 0;
 	struct process_mem_iter *iter;
 	int ret;
-	struct object_file *target = kpatch_object_find_applied_patch(o);
+	struct object_file *applied_patch;
+	const char *patch_id = NULL;
 
 	if (o->info != NULL)
 		return 0;
 
+	if (o->kpfile.patch)
+		patch_id = o->kpfile.patch->id;
+	else
+		return -1;
+
+	applied_patch = kpatch_object_find_applied_patch(o, patch_id);
+	if (!applied_patch) {
+		fprintf(stderr, "Failed to find target applied patch!\n");
+		return -1;
+	}
+
 	iter = kpatch_process_mem_iter_init(o->proc);
 	if (iter == NULL)
 		return -1;
@@ -533,8 +551,8 @@ object_find_applied_patch_info(struct object_file *o)
 		o->ninfo++;
 	} while (1);
 
-	target->info = o->info;
-	target->ninfo = o->ninfo;
+	applied_patch->info = o->info;
+	applied_patch->ninfo = o->ninfo;
 
 err:
 	kpatch_process_mem_iter_free(iter);
@@ -608,20 +626,19 @@ kpatch_should_unapply_patch(struct object_file *o,
 static int
 kpatch_unapply_patches(kpatch_process_t *proc,
 		       char *buildids[],
-		       int nbuildids)
+		       int nbuildids,
+		       const char *patch_id)
 {
 	struct object_file *o;
 	int ret;
 	size_t unapplied = 0;
 
-	/* TODO: disable before patch->id is introduced
-	ret = kpatch_process_associate_patches(proc);
+	ret = kpatch_process_associate_patches(proc, patch_id);
 	if (ret < 0)
 		return ret;
-	*/
 
 	list_for_each_entry(o, &proc->objs, list) {
-		if (o->num_applied_patch == 0)
+		if (o->is_patch || o->num_applied_patch == 0)
 			continue;
 
 		if (!kpatch_should_unapply_patch(o, buildids, nbuildids))
@@ -643,6 +660,7 @@ int process_unpatch(int pid, void *_data)
 	struct unpatch_data *data = _data;
 	char **buildids = data->buildids;
 	int nbuildids = data->nbuildids;
+	const char *patch_id = data->patch_id;
 
 	ret = kpatch_process_init(proc, pid, /* start */ 0, /* send_fd */ -1);
 	if (ret < 0)
@@ -654,7 +672,7 @@ int process_unpatch(int pid, void *_data)
 	if (ret < 0)
 		goto out;
 
-	ret = kpatch_process_map_object_files(proc);
+	ret = kpatch_process_map_object_files(proc, patch_id);
 	if (ret < 0)
 		goto out;
 
@@ -662,7 +680,7 @@ int process_unpatch(int pid, void *_data)
 	if (ret < 0)
 		goto out;
 
-	ret = kpatch_unapply_patches(proc, buildids, nbuildids);
+	ret = kpatch_unapply_patches(proc, buildids, nbuildids, patch_id);
 
 out:
 	kpatch_process_free(proc);
diff --git a/src/kpatch_process.c b/src/kpatch_process.c
index 3e5ff70..9aca949 100644
--- a/src/kpatch_process.c
+++ b/src/kpatch_process.c
@@ -384,10 +384,11 @@ process_add_vm_hole(kpatch_process_t *proc,
 }
 
 int
-kpatch_process_associate_patches(kpatch_process_t *proc)
+kpatch_process_associate_patches(kpatch_process_t *proc, const char *patch_id)
 {
 	struct object_file *o, *objpatch;
 	size_t found = 0;
+	size_t found_target = 0;
 
 	list_for_each_entry(objpatch, &proc->objs, list) {
 
@@ -405,17 +406,24 @@ kpatch_process_associate_patches(kpatch_process_t *proc)
 
 			if (kpatch_object_add_applied_patch(o, objpatch) < 0)
 				return -1;
-			patchvma = list_first_entry(&objpatch->vma,
-						    struct obj_vm_area,
-						    list);
-			o->kpta = patchvma->inmem.start;
-			o->kpfile = objpatch->kpfile;
+
+			if (patch_id && !strcmp(objpatch->kpfile.patch->id, patch_id)) {
+				patchvma = list_first_entry(&objpatch->vma,
+							    struct obj_vm_area,
+							    list);
+				o->kpta = patchvma->inmem.start;
+				o->kpfile = objpatch->kpfile;
+				found_target = 1;
+			}
 
 			found++;
 			break;
 		}
 	}
 
+	if (patch_id && !found_target)
+		fprintf(stderr, "Failed to find target patch with patch_id=%s!\n", patch_id);
+
 	return found;
 }
 
@@ -513,7 +521,7 @@ error:
 }
 
 int
-kpatch_process_map_object_files(kpatch_process_t *proc)
+kpatch_process_map_object_files(kpatch_process_t *proc, const char *patch_id)
 {
 	struct object_file *o;
 	int ret;
@@ -528,7 +536,7 @@ kpatch_process_map_object_files(kpatch_process_t *proc)
 			return -1;
 	}
 
-	ret = kpatch_process_associate_patches(proc);
+	ret = kpatch_process_associate_patches(proc, patch_id);
 	if (ret >= 0) {
 		kpinfo("Found %d applied patch(es).\n", ret);
 	}
@@ -1132,17 +1140,32 @@ kpatch_process_get_obj_by_regex(kpatch_process_t *proc, const char *regex)
 	return found;
 }
 
+int
+kpatch_object_check_duplicate_id(struct object_file *o, const char *patch_id)
+{
+	struct obj_applied_patch *applied_patch;
+
+	list_for_each_entry(applied_patch, &o->applied_patch, list) {
+		if (!strcmp(patch_id, applied_patch->patch_file->kpfile.patch->id))
+			return 1;
+	}
+
+	return 0;
+}
+
 int
 kpatch_object_add_applied_patch(struct object_file *o, struct object_file *new)
 {
 	struct obj_applied_patch *patch;
+	const char *patch_id;
 
 	patch = malloc(sizeof(*patch));
 	if (!patch)
 		return -1;
 
-	if (!o || !new)
-		return -1;
+	patch_id = new->kpfile.patch->id;
+	if (kpatch_object_check_duplicate_id(o, patch_id))
+		return 0;
 
 	patch->patch_file = new;
 	list_add(&patch->list, &o->applied_patch);
@@ -1152,14 +1175,16 @@ kpatch_object_add_applied_patch(struct object_file *o, struct object_file *new)
 }
 
 struct object_file *
-kpatch_object_find_applied_patch(struct object_file *o)
+kpatch_object_find_applied_patch(struct object_file *o, const char *patch_id)
 {
-	struct obj_applied_patch *patch;
+	struct obj_applied_patch *applied_patch;
 	struct object_file *target = NULL;
 
-	list_for_each_entry(patch, &o->applied_patch, list) {
-		target = patch->patch_file;
+	list_for_each_entry(applied_patch, &o->applied_patch, list) {
+		target = applied_patch->patch_file;
+		if (!strcmp(target->kpfile.patch->id, patch_id))
+			return target;
 	}
 
-	return target;
+	return NULL;
 }
diff --git a/src/kpatch_user.c b/src/kpatch_user.c
index 67e9bf6..aa9ff6f 100644
--- a/src/kpatch_user.c
+++ b/src/kpatch_user.c
@@ -130,11 +130,12 @@ out_err:
 }
 
 static int
-processes_unpatch(int pid, char *buildids[], int nbuildids)
+processes_unpatch(int pid, char *buildids[], int nbuildids, const char *patch_id)
 {
 	struct unpatch_data data = {
 		.buildids = buildids,
-		.nbuildids = nbuildids
+		.nbuildids = nbuildids,
+		.patch_id = patch_id
 	};
 
 	return processes_do(pid, process_unpatch, &data);
@@ -144,22 +145,24 @@ static int usage_unpatch(const char *err)
 {
 	if (err)
 		fprintf(stderr, "err: %s\n", err);
-	fprintf(stderr, "usage: libcare-ctl unpatch [options] <-p PID> "
+	fprintf(stderr, "usage: libcare-ctl unpatch [options] <-p PID> <-i patch_id>"
 		"[Build-ID or name ...]\n");
 	fprintf(stderr, "\nOptions:\n");
-	fprintf(stderr, "  -h          - this message\n");
-	fprintf(stderr, "  -p <PID>    - target process\n");
+	fprintf(stderr, "  -h               - this message\n");
+	fprintf(stderr, "  -p <PID>         - target process\n");
+	fprintf(stderr, "  -i <patch_id>    - target patch id\n");
 	return err ? 0 : -1;
 }
 
 int cmd_unpatch_user(int argc, char *argv[])
 {
 	int opt, pid = -1, is_pid_set = 0;
+	const char *patch_id = NULL;
 
-	if (argc < 3)
+	if (argc < 4)
 		return usage_unpatch(NULL);
 
-	while ((opt = getopt(argc, argv, "hp:")) != EOF) {
+	while ((opt = getopt(argc, argv, "hp:i:")) != EOF) {
 		switch (opt) {
 		case 'h':
 			return usage_unpatch(NULL);
@@ -168,6 +171,9 @@ int cmd_unpatch_user(int argc, char *argv[])
 				pid = atoi(optarg);
 			is_pid_set = 1;
 			break;
+		case 'i':
+			patch_id = optarg;
+			break;
 		default:
 			return usage_unpatch("unknown option");
 		}
@@ -176,13 +182,13 @@ int cmd_unpatch_user(int argc, char *argv[])
 	argc -= optind;
 	argv += optind;
 
-	if (!is_pid_set)
-		return usage_patch("PID argument is mandatory");
+	if (!is_pid_set || !patch_id)
+		return usage_unpatch("PID or patch_id argument is mandatory");
 
 	if (!kpatch_check_system())
 		return -1;
 
-	return processes_unpatch(pid, argv, argc);
+	return processes_unpatch(pid, argv, argc, patch_id);
 }
 
 static
@@ -233,6 +239,7 @@ object_info(struct info_data *data, struct object_file *o,
 	struct kpatch_storage_patch *patch = NULL;
 	int patch_found = PATCH_NOT_FOUND;
 	struct obj_applied_patch *applied_patch;
+	int i = 1;
 
 	if (!o->is_elf || is_kernel_object_name(o->name))
 		return 0;
@@ -271,9 +278,10 @@ object_info(struct info_data *data, struct object_file *o,
 		*pid_printed = 1;
 	}
 
-	printf("process=%s; buildid=%s; applied patch number=%ld\n", o->name, buildid, o->num_applied_patch);
+	printf("process = %s; buildid = %s; applied patch number = %ld:\n", o->name, buildid, o->num_applied_patch);
 	list_for_each_entry(applied_patch, &o->applied_patch, list) {
-		printf("--->patch->magic=%s\n", applied_patch->patch_file->kpfile.patch->magic);
+		printf("%d. patch->id = %s\n", i, applied_patch->patch_file->kpfile.patch->id);
+		i++;
 	}
 
 	if (storage_patch_found(patch) && patch->patchlevel) {
@@ -344,7 +352,7 @@ process_info(int pid, void *_data)
 	if (ret < 0)
 		goto out;
 
-	ret = kpatch_process_map_object_files(proc);
+	ret = kpatch_process_map_object_files(proc, NULL);
 	if (ret < 0)
 		goto out;
 
@@ -615,7 +623,7 @@ server_stress_test(int fd, int argc, char *argv[])
 		if (server_wait(pid, delay) < 0)
 			return 0;
 
-		while (processes_unpatch(pid, 0, 0) < 0)
+		while (processes_unpatch(pid, 0, 0, NULL) < 0)
 			if (server_wait(pid, 1) < 0)
 				return 0;
 		test_info.stat_cycle_num++;
diff --git a/src/libcare-patch-make b/src/libcare-patch-make
index 7138f89..8ecec1d 100755
--- a/src/libcare-patch-make
+++ b/src/libcare-patch-make
@@ -7,6 +7,7 @@ Makes `kpatch'es for the makesystem in the current directory.
 Usage:	libcare-patch-make [-h|--help] [-u|--update || -c|--clean]
 	[-s|--srcdir=SRCDIR] \
 	[-d|--destdir=DESTDIRVAR] \
+	[-i|--id=PATCH-ID] \
 	PATCH1 PATCH2 ...
 
 Run from inside the directory with `make'ble software. Makesystem must support
@@ -17,6 +18,7 @@ install with specified DESTDIR.
 		working on patch utils.
   -d --destdir	specify variable makefile system uses to specify destination
 		directory for the installation
+  -i --id	give this patch an unique id (less than 7 char)
 EOF
 		exit ${1-0}
 }
@@ -139,7 +141,7 @@ build_kpatches() {
 			"${patchedexec}.stripped" || continue
 		/usr/bin/strip --strip-unneeded "${patchedexec}.stripped"
 		$KPATCH_PATH/kpatch_strip --undo-link "$origexec" "${patchedexec}.stripped"
-		$KPATCH_PATH/kpatch_make -b "$buildid" \
+		$KPATCH_PATH/kpatch_make -b "$buildid" -i "$patch_id" \
 			"${patchedexec}.stripped" -o "${patchedexec}.kpatch"
 		cp "${patchedexec}.kpatch" "${LPMAKE_PATCHROOT}"/${buildid}.kpatch
 		echo "patch for ${origexec} is in ${LPMAKE_PATCHROOT}/${buildid}.kpatch"
@@ -149,7 +151,7 @@ build_kpatches() {
 main() {
 	PROG_NAME=$(basename $0)
 
-	TEMP=$(getopt -o s:ucd --long srcdir:,update,clean,destdir: -n ${PROG_NAME} -- "$@" || usage 1)
+	TEMP=$(getopt -o s:ucdi: --long srcdir:,update,clean,destdir:,id: -n ${PROG_NAME} -- "$@" || usage 1)
 	eval set -- "$TEMP"
 
 	destdir="DESTDIR"
@@ -173,12 +175,22 @@ main() {
 			destdir=$1
 			shift
 			;;
+		-i|--id)
+			shift
+			patch_id="$1"
+			shift
+			;;
 		--)
 			shift; break;
 			;;
 		esac
 	done
 
+	if [ -z "$patch_id" ] || [ ${#patch_id} -gt 7 ];then
+		echo "Invalid patch-id!" >&2
+		usage 1
+	fi
+
 	prepare_env
 
 	if test -z "$only_update"; then
-- 
2.19.1

