From 12175cb3fb1a1de1e7ea5f1531f7de5157ab4db6 Mon Sep 17 00:00:00 2001
From: Sang Yan <sangyan@huawei.com>
Date: Thu, 8 Jul 2021 14:12:42 +0800
Subject: [PATCH 27/54] file-lock: add repair mode to dump file locks

Add new options "--file-locks-repair" to enable repair mode
while dumping file locks.
Repair mode keeps locks locked while process were killed in
dumping operation. Then resume the locks from repair mode at
process resuming.

Signed-off-by: Sang Yan <sangyan@huawei.com>
---
 criu/config.c                   |  1 +
 criu/cr-dump.c                  |  8 ++++++
 criu/crtools.c                  |  1 +
 criu/file-lock.c                | 10 +++++++
 criu/include/cr_options.h       |  1 +
 criu/include/fcntl.h            |  7 +++++
 criu/include/parasite-syscall.h |  2 ++
 criu/include/parasite.h         | 10 +++++++
 criu/parasite-syscall.c         | 33 ++++++++++++++++++++++
 criu/pie/parasite.c             | 50 +++++++++++++++++++++++++++++++++
 10 files changed, 123 insertions(+)

diff --git a/criu/config.c b/criu/config.c
index 90d3951..9854d4c 100644
--- a/criu/config.c
+++ b/criu/config.c
@@ -550,6 +550,7 @@ int parse_options(int argc, char **argv, bool *usage_error,
 		BOOL_OPT("mask-exit-notify", &opts.mask_exit_notify),
 		BOOL_OPT("weak-file-check", &opts.weak_file_check),
 		BOOL_OPT("ignore-special-dump", &opts.ignore_special_dump),
+		BOOL_OPT("file-locks-repair", &opts.file_locks_repair),
 		{ },
 	};
 
diff --git a/criu/cr-dump.c b/criu/cr-dump.c
index 9627190..9084847 100644
--- a/criu/cr-dump.c
+++ b/criu/cr-dump.c
@@ -1398,6 +1398,14 @@ static int dump_one_task(struct pstree_item *item, InventoryEntry *parent_ie)
 		goto err_cure;
 	}
 
+	if (opts.file_locks_repair) {
+		ret = parasite_dump_file_locks(parasite_ctl, pid);
+		if (ret) {
+			pr_err("Can't parasite dump file locks (pid: %d)\n", pid);
+			goto err_cure;
+		}
+	}
+
 	ret = dump_task_core_all(parasite_ctl, item, &pps_buf, cr_imgset, &misc);
 	if (ret) {
 		pr_err("Dump core (pid: %d) failed with %d\n", pid, ret);
diff --git a/criu/crtools.c b/criu/crtools.c
index 0a957b0..bab5b1b 100644
--- a/criu/crtools.c
+++ b/criu/crtools.c
@@ -454,6 +454,7 @@ usage:
 "  --mask-exit-notify     Mask task exit notify during dump and restore\n"
 "  --weak-file-check	Allow file size and mod larger than dumping value\n"
 "  --ignore-special-dump Ignore special task tid page dump\n"
+"  --file-locks-repair    Use repair mode to dump and restore file locks\n"
 "\n"
 "Check options:\n"
 "  Without options, \"criu check\" checks availability of absolutely required\n"
diff --git a/criu/file-lock.c b/criu/file-lock.c
index 8be7589..44ecc92 100644
--- a/criu/file-lock.c
+++ b/criu/file-lock.c
@@ -428,6 +428,8 @@ void discard_dup_locks_tail(pid_t pid, int fd)
 	list_for_each_entry_safe_reverse(fl, p, &file_lock_list, list) {
 		if (fl->owners_fd != fd || pid != fl->fl_holder)
 			break;
+		if (fl->fl_kind == FL_POSIX)
+			continue;
 
 		list_del(&fl->list);
 		xfree(fl);
@@ -618,8 +620,12 @@ static int restore_file_lock(FileLockEntry *fle)
 			cmd = fle->type;
 		} else if (fle->type == F_RDLCK) {
 			cmd = LOCK_SH;
+			if (opts.file_locks_repair)
+				cmd = LOCK_REPAIR;
 		} else if (fle->type == F_WRLCK) {
 			cmd = LOCK_EX;
+			if (opts.file_locks_repair)
+				cmd = LOCK_REPAIR;
 		} else if (fle->type == F_UNLCK) {
 			cmd = LOCK_UN;
 		} else {
@@ -645,6 +651,10 @@ static int restore_file_lock(FileLockEntry *fle)
 		flk.l_pid    = fle->pid;
 		flk.l_type   = fle->type;
 
+		if (opts.file_locks_repair)
+			if (fle->type == F_RDLCK || fle->type == F_WRLCK)
+				flk.l_type = F_REPAIR;
+
 		pr_info("(posix)flag: %d, type: %d, pid: %d, fd: %d, "
 			"start: %8"PRIx64", len: %8"PRIx64"\n",
 			fle->flag, fle->type, fle->pid, fle->fd,
diff --git a/criu/include/cr_options.h b/criu/include/cr_options.h
index 681b519..e227bcd 100644
--- a/criu/include/cr_options.h
+++ b/criu/include/cr_options.h
@@ -184,6 +184,7 @@ struct cr_options {
 	int			mask_exit_notify;
 	int			weak_file_check;
 	int			ignore_special_dump;
+	int			file_locks_repair;
 };
 
 extern struct cr_options opts;
diff --git a/criu/include/fcntl.h b/criu/include/fcntl.h
index 0936337..65f8b36 100644
--- a/criu/include/fcntl.h
+++ b/criu/include/fcntl.h
@@ -23,6 +23,13 @@ struct f_owner_ex {
 #define F_SETCRED 18
 #endif
 
+#ifndef F_NEED_REPAIR
+#define F_NEED_REPAIR		16
+#define F_REPAIR		32
+#define LOCK_NEED_REPAIR	256     /* REPAIRING lock */
+#define LOCK_REPAIR		512     /* REPAIR lock */
+#endif
+
 /*
  * These things are required to compile on CentOS-6
  */
diff --git a/criu/include/parasite-syscall.h b/criu/include/parasite-syscall.h
index c86a724..14e1f31 100644
--- a/criu/include/parasite-syscall.h
+++ b/criu/include/parasite-syscall.h
@@ -54,4 +54,6 @@ extern int parasite_dump_cgroup(struct parasite_ctl *ctl, struct parasite_dump_c
 
 extern struct parasite_tty_args *parasite_dump_tty(struct parasite_ctl *ctl, int fd, int type);
 
+extern int parasite_dump_file_locks(struct parasite_ctl *ctl, int pid);
+
 #endif /* __CR_PARASITE_SYSCALL_H__ */
diff --git a/criu/include/parasite.h b/criu/include/parasite.h
index d957094..1c702f0 100644
--- a/criu/include/parasite.h
+++ b/criu/include/parasite.h
@@ -35,6 +35,7 @@ enum {
 	PARASITE_CMD_CHECK_VDSO_MARK,
 	PARASITE_CMD_CHECK_AIOS,
 	PARASITE_CMD_DUMP_CGROUP,
+	PARASITE_CMD_DUMP_FILELOCKS,
 
 	PARASITE_CMD_MAX,
 };
@@ -236,6 +237,15 @@ struct parasite_dump_cgroup_args {
 	char contents[1 << 12];
 };
 
+struct parasite_dump_filelocks_args {
+	short	kind;
+	short	type;
+	long	start;
+	long	len;
+	int	pid;
+	int	fd;
+};
+
 #endif /* !__ASSEMBLY__ */
 
 #endif /* __CR_PARASITE_H__ */
diff --git a/criu/parasite-syscall.c b/criu/parasite-syscall.c
index 8d9e01b..8fdb475 100644
--- a/criu/parasite-syscall.c
+++ b/criu/parasite-syscall.c
@@ -32,6 +32,7 @@
 #include <compel/plugins/std/syscall-codes.h>
 #include "signal.h"
 #include "sigframe.h"
+#include "file-lock.h"
 
 #include <string.h>
 #include <stdlib.h>
@@ -591,3 +592,35 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, struct pstree_item *item,
 
 	return ctl;
 }
+
+int parasite_dump_file_locks(struct parasite_ctl *ctl, int pid)
+{
+	struct parasite_dump_filelocks_args *args;
+	struct file_lock *fl;
+	int ret;
+
+	args = compel_parasite_args(ctl, struct parasite_dump_filelocks_args);
+
+	list_for_each_entry(fl, &file_lock_list, list) {
+		if (fl->real_owner != pid)
+			continue;
+
+		args->pid = fl->real_owner;
+		args->fd = fl->owners_fd;
+		args->kind = fl->fl_kind;
+		args->type = fl->fl_ltype;
+		args->start = fl->start;
+		if (!strncmp(fl->end, "EOF", 3))
+			args->len = 0;
+		else
+			args->len = (atoll(fl->end) + 1) - fl->start;
+
+		ret = compel_rpc_call_sync(PARASITE_CMD_DUMP_FILELOCKS, ctl);
+		if (ret < 0) {
+			pr_err("Parasite dump file lock failed! (pid: %d)\n", pid);
+			return ret;
+		}
+	}
+
+	return 0;
+}
diff --git a/criu/pie/parasite.c b/criu/pie/parasite.c
index d839783..635c3f8 100644
--- a/criu/pie/parasite.c
+++ b/criu/pie/parasite.c
@@ -7,6 +7,8 @@
 #include <stdarg.h>
 #include <sys/ioctl.h>
 #include <sys/uio.h>
+#include <unistd.h>
+#include <fcntl.h>
 
 #include "common/config.h"
 #include "int.h"
@@ -20,6 +22,7 @@
 #include "criu-log.h"
 #include "tty.h"
 #include "aio.h"
+#include "file-lock.h"
 
 #include "asm/parasite.h"
 #include "restorer.h"
@@ -677,6 +680,50 @@ static int parasite_dump_cgroup(struct parasite_dump_cgroup_args *args)
 	return 0;
 }
 
+static int set_filelocks_needrepair(struct parasite_dump_filelocks_args *args)
+{
+	int ret;
+
+	if (args->kind == FL_FLOCK) {
+		if (args->type == F_RDLCK || args->type == F_WRLCK) {
+			int cmd = LOCK_NEED_REPAIR;
+
+			pr_info("Need Repair flock kind: %d, type: %d, cmd: %d, pid: %d, fd: %d\n",
+				args->kind, args->type, cmd, args->pid, args->fd);
+
+			ret = sys_flock(args->fd, cmd);
+			if (ret < 0) {
+				pr_err("Can not set NEED_REPAIR flock!\n");
+				return ret;
+			}
+		}
+	} else if (args->kind == FL_POSIX) {
+		if (args->type == F_RDLCK || args->type == F_WRLCK) {
+			struct flock flk;
+			memset(&flk, 0, sizeof(flk));
+
+			flk.l_whence = SEEK_SET;
+			flk.l_start  = args->start;
+			flk.l_len    = args->len;
+			flk.l_pid    = args->pid;
+			flk.l_type   = F_NEED_REPAIR;
+
+			pr_info("Need Repair posix lock kind: %d, type: %d, cmd: %d, pid: %d, fd: %d, "
+				"start: %8"PRIx64", len: %8"PRIx64"\n",
+				args->kind, args->type, flk.l_type, args->pid, args->fd,
+				args->start, args->len);
+
+			ret = sys_fcntl(args->fd, F_SETLKW, (long)&flk);
+			if (ret < 0) {
+				pr_err("Can not set NEED_REPAIR posix lock!\n");
+				return ret;
+			}
+		}
+	}
+
+	return 0;
+}
+
 void parasite_cleanup(void)
 {
 	if (mprotect_args) {
@@ -729,6 +776,9 @@ int parasite_daemon_cmd(int cmd, void *args)
 	case PARASITE_CMD_DUMP_CGROUP:
 		ret = parasite_dump_cgroup(args);
 		break;
+	case PARASITE_CMD_DUMP_FILELOCKS:
+		ret = set_filelocks_needrepair(args);
+		break;
 	default:
 		pr_err("Unknown command in parasite daemon thread leader: %d\n", cmd);
 		ret = -1;
-- 
2.33.1

