From 1429f414b0714d632ac3c7f56352eceeebcf0a5f Mon Sep 17 00:00:00 2001
From: Hao Wang <wanghao232@huawei.com>
Date: Sat, 5 Dec 2020 17:54:02 +0800
Subject: [PATCH patch management 1/2] libcare-ctl: implement applied patch
 list

Implement applied patch list to support multi-patches unpatching
and querying.

Change-Id: I20d6b9b901dbd378b674ebaf00c6d7725ed09864
Signed-off-by: imxcc <xingchaochao@huawei.com>
---
 libcare.spec                 |  5 +++-
 src/include/kpatch_process.h | 24 +++++++++++-----
 src/kpatch_patch.c           | 56 ++++--------------------------------
 src/kpatch_process.c         | 51 ++++++++++++++++++++++++++++----
 src/kpatch_user.c            | 17 +++++------
 5 files changed, 81 insertions(+), 72 deletions(-)

diff --git a/libcare.spec b/libcare.spec
index b1ce414..e940cf1 100644
--- a/libcare.spec
+++ b/libcare.spec
@@ -1,7 +1,7 @@
 Version: 0.1.4
 Name: libcare
 Summary: LibCare tools
-Release: 10%{?dist}
+Release: 11%{?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-11
+- libcare-ctl: implement applied patch list
+
 * Thu Sep 23 2021 Jiajie Li <lijiajie11@huawei.com> - 0.1.4-10
 - arch/aarch64/arch_parse: modify is_variable_start function for gensrc in arm
 
diff --git a/src/include/kpatch_process.h b/src/include/kpatch_process.h
index b96a6da..ae00678 100644
--- a/src/include/kpatch_process.h
+++ b/src/include/kpatch_process.h
@@ -32,6 +32,11 @@ struct obj_vm_area {
 	struct list_head list;
 };
 
+struct obj_applied_patch {
+	struct list_head list;
+	struct object_file *patch_file;
+};
+
 struct object_file {
 	struct list_head list;
 	kpatch_process_t *proc;
@@ -95,8 +100,10 @@ struct object_file {
 	/* Pointer to the previous hole in the patient's mapping */
 	struct vm_hole *previous_hole;
 
-	/* Pointer to the applied patch, if any */
-	struct object_file *applied_patch;
+	/* Pointer to the applied patch list, if any */
+	struct list_head applied_patch;
+	/* The number of applied patch */
+	size_t num_applied_patch;
 
 	/* Do we have patch for the object? */
 	unsigned int has_patch:1;
@@ -204,11 +211,11 @@ kpatch_process_get_obj_by_regex(kpatch_process_t *proc, const char *regex);
 static inline int
 is_kernel_object_name(char *name)
 {
-       if ((name[0] == '[') && (name[strlen(name) - 1] == ']'))
-               return 1;
-       if (strncmp(name, "anon_inode", 10) == 0)
-               return 1;
-       return 0;
+	if ((name[0] == '[') && (name[strlen(name) - 1] == ']'))
+		return 1;
+	if (strncmp(name, "anon_inode", 10) == 0)
+		return 1;
+	return 0;
 }
 
 struct vm_hole *next_hole(struct vm_hole *hole, struct list_head *head);
@@ -220,4 +227,7 @@ unsigned long object_find_patch_region(struct object_file *obj,
 			 size_t memsize,
 			 struct vm_hole **hole);
 
+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);
+
 #endif /* ifndef __KPATCH_PROCESS__ */
diff --git a/src/kpatch_patch.c b/src/kpatch_patch.c
index 4a1d149..5b6e3f2 100644
--- a/src/kpatch_patch.c
+++ b/src/kpatch_patch.c
@@ -300,12 +300,6 @@ object_apply_patch(struct object_file *o)
 	if (o->skpfile == NULL || o->is_patch)
 		return 0;
 
-	if (o->applied_patch) {
-		kpinfo("Object '%s' already have a patch, not patching\n",
-		       o->name);
-		return 0;
-	}
-
 	ret = duplicate_kp_file(o);
 	if (ret < 0) {
 		kplogerror("can't duplicate kp_file\n");
@@ -379,43 +373,6 @@ object_apply_patch(struct object_file *o)
 static int
 object_unapply_patch(struct object_file *o, int check_flag);
 
-static int
-object_unapply_old_patch(struct object_file *o)
-{
-	struct kpatch_file *kpatch_applied, *kpatch_storage;
-	int ret;
-
-	if (o->skpfile == NULL || o->is_patch || o->applied_patch == NULL)
-		return 0;
-
-	kpatch_applied = o->applied_patch->kpfile.patch;
-	kpatch_storage = o->skpfile->patch;
-
-	if (kpatch_applied->user_level >= kpatch_storage->user_level) {
-		kpinfo("'%s' applied patch level is %d (storage has %d\n)\n",
-		       o->name,
-		       kpatch_applied->user_level,
-		       kpatch_storage->user_level);
-		return 1;
-	}
-
-	printf("%s: replacing patch level %d with level %d\n",
-	       o->name,
-	       kpatch_applied->user_level,
-	       kpatch_storage->user_level);
-	ret = object_unapply_patch(o, /* check_flag */ 0);
-	if (ret < 0)
-		kperr("can't unapply patch for %s\n", o->name);
-	else {
-		/* TODO(pboldin): handle joining the holes here */
-		o->applied_patch = NULL;
-		o->info = NULL;
-		o->ninfo = 0;
-	}
-
-	return ret;
-}
-
 static int
 kpatch_apply_patches(kpatch_process_t *proc)
 {
@@ -424,10 +381,6 @@ kpatch_apply_patches(kpatch_process_t *proc)
 
 	list_for_each_entry(o, &proc->objs, list) {
 
-		ret = object_unapply_old_patch(o);
-		if (ret < 0)
-			break;
-
 		ret = object_apply_patch(o);
 		if (ret < 0)
 			goto unpatch;
@@ -551,6 +504,7 @@ 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);
 
 	if (o->info != NULL)
 		return 0;
@@ -579,8 +533,8 @@ object_find_applied_patch_info(struct object_file *o)
 		o->ninfo++;
 	} while (1);
 
-	o->applied_patch->info = o->info;
-	o->applied_patch->ninfo = o->ninfo;
+	target->info = o->info;
+	target->ninfo = o->ninfo;
 
 err:
 	kpatch_process_mem_iter_free(iter);
@@ -660,12 +614,14 @@ kpatch_unapply_patches(kpatch_process_t *proc,
 	int ret;
 	size_t unapplied = 0;
 
+	/* TODO: disable before patch->id is introduced
 	ret = kpatch_process_associate_patches(proc);
 	if (ret < 0)
 		return ret;
+	*/
 
 	list_for_each_entry(o, &proc->objs, list) {
-		if (o->applied_patch == NULL)
+		if (o->num_applied_patch == 0)
 			continue;
 
 		if (!kpatch_should_unapply_patch(o, buildids, nbuildids))
diff --git a/src/kpatch_process.c b/src/kpatch_process.c
index f987b7e..3e5ff70 100644
--- a/src/kpatch_process.c
+++ b/src/kpatch_process.c
@@ -107,6 +107,8 @@ process_new_object(kpatch_process_t *proc,
 	}
 	list_init(&o->list);
 	list_init(&o->vma);
+	list_init(&o->applied_patch);
+	o->num_applied_patch = 0;
 	o->proc = proc;
 	o->skpfile = NULL;
 	o->dev = dev;
@@ -126,7 +128,6 @@ process_new_object(kpatch_process_t *proc,
 	o->kpta = 0UL;
 	o->info = NULL;
 	o->ninfo = 0;
-	o->applied_patch = NULL;
 	o->vma_start = ~(unsigned long)0;
 	o->load_offset = ~(unsigned long)0;
 	memset(&o->ehdr, 0, sizeof(o->ehdr));
@@ -289,12 +290,17 @@ static void
 object_destroy(struct object_file *o)
 {
 	struct obj_vm_area *ovma, *tmp;
+	struct obj_applied_patch *patch, *temp;
 
 	list_del(&o->list);
 	list_for_each_entry_safe(ovma, tmp, &o->vma, list) {
 		list_del(&ovma->list);
 		free(ovma);
 	}
+	list_for_each_entry_safe(patch, temp, &o->applied_patch, list) {
+		list_del(&patch->list);
+		free(patch);
+	}
 	o->proc->num_objs--;
 	if (o->jmp_table)
 		free(o->jmp_table);
@@ -309,7 +315,7 @@ object_destroy(struct object_file *o)
 	if (o->is_patch) {
 		free(o->info);
 	}
-	if (o->applied_patch == NULL)
+	if (o->num_applied_patch == 0)
 		free(o->kpfile.patch);
 	free(o);
 }
@@ -329,8 +335,8 @@ kpatch_object_dump(struct object_file *o)
 	if (log_level < LOG_INFO)
 		return;
 
-	if (o->applied_patch)
-		patchinfo = o->applied_patch->name;
+	if (o->num_applied_patch > 0)
+		patchinfo = "yes";
 	else
 		patchinfo = o->skpfile != NULL ? "yes" : "no";
 
@@ -393,11 +399,12 @@ kpatch_process_associate_patches(kpatch_process_t *proc)
 			struct obj_vm_area *patchvma;
 
 			bid = kpatch_get_buildid(o);
-			if (o->applied_patch != NULL || bid == NULL ||
+			if (bid == NULL ||
 			    strcmp(bid, objpatch->kpfile.patch->uname))
 				continue;
 
-			o->applied_patch = objpatch;
+			if (kpatch_object_add_applied_patch(o, objpatch) < 0)
+				return -1;
 			patchvma = list_first_entry(&objpatch->vma,
 						    struct obj_vm_area,
 						    list);
@@ -1124,3 +1131,35 @@ kpatch_process_get_obj_by_regex(kpatch_process_t *proc, const char *regex)
 	regfree(&r);
 	return found;
 }
+
+int
+kpatch_object_add_applied_patch(struct object_file *o, struct object_file *new)
+{
+	struct obj_applied_patch *patch;
+
+	patch = malloc(sizeof(*patch));
+	if (!patch)
+		return -1;
+
+	if (!o || !new)
+		return -1;
+
+	patch->patch_file = new;
+	list_add(&patch->list, &o->applied_patch);
+	o->num_applied_patch++;
+
+	return 0;
+}
+
+struct object_file *
+kpatch_object_find_applied_patch(struct object_file *o)
+{
+	struct obj_applied_patch *patch;
+	struct object_file *target = NULL;
+
+	list_for_each_entry(patch, &o->applied_patch, list) {
+		target = patch->patch_file;
+	}
+
+	return target;
+}
diff --git a/src/kpatch_user.c b/src/kpatch_user.c
index 11e3408..67e9bf6 100644
--- a/src/kpatch_user.c
+++ b/src/kpatch_user.c
@@ -232,6 +232,7 @@ object_info(struct info_data *data, struct object_file *o,
 	int pid = proc->pid;
 	struct kpatch_storage_patch *patch = NULL;
 	int patch_found = PATCH_NOT_FOUND;
+	struct obj_applied_patch *applied_patch;
 
 	if (!o->is_elf || is_kernel_object_name(o->name))
 		return 0;
@@ -262,7 +263,7 @@ object_info(struct info_data *data, struct object_file *o,
 						 buildid,
 						 &patch);
 
-	if (o->applied_patch == NULL && !patch_found)
+	if (o->num_applied_patch == 0 && !patch_found)
 		return 0;
 
 	if (!*pid_printed) {
@@ -270,17 +271,17 @@ object_info(struct info_data *data, struct object_file *o,
 		*pid_printed = 1;
 	}
 
-	printf("%s buildid=%s", o->name, buildid);
-	if (o->applied_patch != NULL) {
-		int patchlvl = o->kpfile.patch->user_level;
-		printf(" patchlvl=%d", patchlvl);
+	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);
 	}
+
 	if (storage_patch_found(patch) && patch->patchlevel) {
 		printf(" latest=%d", patch->patchlevel);
 	}
 
 	/* empty patch patchlevel=0 with description of bugs in the version */
-	if (patch && patch->patchlevel == 0 && o->applied_patch == NULL) {
+	if (patch && patch->patchlevel == 0 && o->num_applied_patch == 0) {
 		printf(" %sVULNERABLE%s\n", RED, RESET);
 
 		if (data->print_description) {
@@ -299,7 +300,7 @@ object_info(struct info_data *data, struct object_file *o,
 
 	/* Old or no patch applied but we have one in storage */
 	if (patch && patch->patchlevel != 0 &&
-	    (o->applied_patch == NULL || patch->patchlevel > o->kpfile.patch->user_level)) {
+	    (o->num_applied_patch == 0 || patch->patchlevel > o->kpfile.patch->user_level)) {
 		if (data->print_description) {
 			char *desc;
 
@@ -314,7 +315,7 @@ object_info(struct info_data *data, struct object_file *o,
 	}
 
 	/* patch applied and is latest version. show descripition for it */
-	if (patch && o->applied_patch != NULL && data->print_description) {
+	if (patch && (o->num_applied_patch > 0) && data->print_description) {
 		char *desc;
 
 		printf("\n%slatest patch applied\n", GREEN);
-- 
2.19.1

