From 2eebb9de411333628ce8fc5894f072b6ed6179e0 Mon Sep 17 00:00:00 2001
From: Jingxian He <hejingxian@huawei.com>
Date: Wed, 19 May 2021 21:55:34 +0800
Subject: [PATCH 25/72] char_dev: add support for char device dump and restore

Add support for char device dump and restore during module upgrade.

`/sys/kernel/repairing_device` provides the char device whiltelist
with `IOCTL_CMD_{NEEDREPAIR, REPAIR}` command besides the internal
device list.
The device modules could use `mures_{add, del}_devname()` to add, or
delete the char device whitelist dynamically.

Signed-off-by: Xiaoguang Li <lixiaoguang2@huawei.com>
Signed-off-by: Jingxian He <hejingxian@huawei.com>
Signed-off-by: fu.lin <fulin10@huawei.com>
---
 criu/Makefile.crtools        |   2 +
 criu/config.c                |   1 +
 criu/cr-dump.c               |   4 ++
 criu/cr-restore.c            |   4 +-
 criu/crtools.c               |   2 +
 criu/devname.c               | 130 +++++++++++++++++++++++++++++++++++
 criu/files-chr.c             | 104 ++++++++++++++++++++++++++++
 criu/files-reg.c             |   6 +-
 criu/files.c                 |  93 ++++++++++++++++++++++++-
 criu/include/cr_options.h    |   1 +
 criu/include/files-chr.h     |  25 +++++++
 criu/include/files.h         |   6 ++
 criu/include/image-desc.h    |   1 +
 criu/include/image.h         |   1 +
 criu/include/protobuf-desc.h |   1 +
 criu/mem.c                   |   7 +-
 criu/proc_parse.c            |  21 +++++-
 images/Makefile              |   1 +
 images/chr.proto             |  12 ++++
 images/fdinfo.proto          |   3 +
 20 files changed, 417 insertions(+), 8 deletions(-)
 create mode 100644 criu/devname.c
 create mode 100644 criu/files-chr.c
 create mode 100644 criu/include/files-chr.h
 create mode 100644 images/chr.proto

diff --git a/criu/Makefile.crtools b/criu/Makefile.crtools
index 98c4135..2e82912 100644
--- a/criu/Makefile.crtools
+++ b/criu/Makefile.crtools
@@ -91,6 +91,8 @@ obj-y			+= pie-util-vdso.o
 obj-y			+= vdso.o
 obj-y			+= timens.o
 obj-y			+= pin-mem.o
+obj-y			+= devname.o
+obj-y			+= files-chr.o
 obj-$(CONFIG_HAS_LIBBPF)	+= bpfmap.o
 obj-$(CONFIG_COMPAT)	+= pie-util-vdso-elf32.o
 CFLAGS_pie-util-vdso-elf32.o	+= -DCONFIG_VDSO_32
diff --git a/criu/config.c b/criu/config.c
index 5d1cff6..03cad66 100644
--- a/criu/config.c
+++ b/criu/config.c
@@ -701,6 +701,7 @@ int parse_options(int argc, char **argv, bool *usage_error, bool *has_exec_cmd,
 		{ "network-lock", required_argument, 0, 1100 },
 		BOOL_OPT("use-fork-pid", &opts.use_fork_pid),
 		BOOL_OPT("with-notifier", &opts.with_notifier_kup),
+		BOOL_OPT("dump-char-dev", &opts.dump_char_dev),
 		{},
 	};
 
diff --git a/criu/cr-dump.c b/criu/cr-dump.c
index 50a2f9b..fd17413 100644
--- a/criu/cr-dump.c
+++ b/criu/cr-dump.c
@@ -88,6 +88,7 @@
 #include "asm/dump.h"
 #include "pin-mem.h"
 #include "notifier.h"
+#include "files-chr.h"
 
 /*
  * Architectures can overwrite this function to restore register sets that
@@ -1880,6 +1881,9 @@ int cr_pre_dump_tasks(pid_t pid)
 	 */
 	rlimit_unlimit_nofile();
 
+	if (opts.dump_char_dev && parse_devname() < 0)
+		goto err;
+
 	root_item = alloc_pstree_item();
 	if (!root_item)
 		goto err;
diff --git a/criu/cr-restore.c b/criu/cr-restore.c
index b805265..2904a75 100644
--- a/criu/cr-restore.c
+++ b/criu/cr-restore.c
@@ -332,11 +332,11 @@ static int root_prepare_shared(void)
 		if (pi->pid->state == TASK_HELPER)
 			continue;
 
-		ret = prepare_mm_pid(pi);
+		ret = prepare_fd_pid(pi);
 		if (ret < 0)
 			break;
 
-		ret = prepare_fd_pid(pi);
+		ret = prepare_mm_pid(pi);
 		if (ret < 0)
 			break;
 
diff --git a/criu/crtools.c b/criu/crtools.c
index 1d08620..dc6d603 100644
--- a/criu/crtools.c
+++ b/criu/crtools.c
@@ -451,6 +451,8 @@ usage:
 	       "  --use-fork-pid	Allow to restore task pid by setting fork pid of task struct.\n"
 	       "  --with-notifier	Allow to checkpoint/restore kup notifier chain.\n"
 	       "			This feature needs the kernel assistance.\n"
+	       "  --dump-char-dev	Dump char dev files as normal file with repair cmd\n"
+	       \
 	       "\n"
 	       "Check options:\n"
 	       "  Without options, \"criu check\" checks availability of absolutely required\n"
diff --git a/criu/devname.c b/criu/devname.c
new file mode 100644
index 0000000..5f6fbed
--- /dev/null
+++ b/criu/devname.c
@@ -0,0 +1,130 @@
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "log.h"
+#include "common/xmalloc.h"
+
+#define REPAIRING_DEVICE_FILE	"/sys/kernel/repairing_device"
+#define ASCII_SIZE	128
+
+static void *root_bucket[ASCII_SIZE];
+
+static int insert_devname_internal(void *bucket[], const char *name)
+{
+	void *new = NULL;
+	int idx = *name;
+
+	if (bucket[idx] != NULL)
+		return insert_devname_internal(bucket[idx], name+1);
+	else if (idx == '\0') {
+		new = xmalloc(sizeof(void *));
+		if (!new) {
+			pr_perror("alloc devname failed\n");
+			return -1;
+		}
+		bucket[idx] = new;
+		return 0;
+	} else {
+		new = xmalloc(sizeof(void *) * ASCII_SIZE);
+		if (!new) {
+			pr_perror("alloc devname failed\n");
+			return -1;
+		}
+		memset(new, 0, sizeof(void *) * ASCII_SIZE);
+		bucket[idx] = new;
+		return insert_devname_internal(bucket[idx], name+1);
+	}
+}
+
+int insert_devname(const char *devname)
+{
+	if (devname == NULL || *devname == '\0') // ignore
+		return 0;
+
+	pr_debug("insert device '%s'\n", devname);
+	return insert_devname_internal(root_bucket, devname);
+}
+
+int parse_devname(void)
+{
+	int retval = -1;
+	char *line = NULL;
+	size_t len = 0;
+	ssize_t nread = 0;
+	FILE *fp = NULL;
+
+	fp = fopen(REPAIRING_DEVICE_FILE, "r");
+	if (fp == NULL) {
+		pr_info("Unable to open %s, downgrade to use internal whitelist\n",
+				REPAIRING_DEVICE_FILE);
+		return 0;
+	}
+
+	while ((nread = getline(&line, &len, fp)) != -1) {
+		if (nread <= 1) // ignore empty string
+			continue;
+
+		line[nread-1] = '\0'; // drop '\n'
+		retval = insert_devname(line);
+		if (retval != 0)
+			goto out;
+	}
+	retval = 0;
+
+out:
+	free(line);
+	fclose(fp);
+	return retval;
+}
+
+static const char *steal_devname(const char *name, ssize_t len)
+{
+	ssize_t off = len;
+
+	for (off -= 1; off > 0; off--) {
+		if (name[off] == '/')
+			break;
+	}
+
+	return name + off + 1;
+}
+
+static bool find_devname_internal(void *bucket[], const char *name)
+{
+	int idx = *name;
+
+	if (*name == '\0' && bucket[idx] != NULL)
+		return true;
+	else if (bucket[idx] == NULL)
+		return false;
+	else {
+		return find_devname_internal(bucket[idx], name+1);
+	}
+}
+
+bool find_devname(const char *name)
+{
+	const char *devname;
+	size_t len = 0;
+	bool found = false;
+
+	if (name == NULL)
+		return false;
+	else if ((len = strlen(name)) == 0)
+		return false;
+
+	devname = steal_devname(name, len);
+	found = find_devname_internal(root_bucket, devname);
+
+	pr_debug("device '%s' (original name '%s') %s found in %s\n",
+			devname, name, found ? "is" : "isn't", REPAIRING_DEVICE_FILE);
+
+	/* Compatible with the old version, there are still `strstr` branch in the following */
+	found |= (strstr(name, "uverbs") != NULL
+			|| strstr(name, "rdma_cm") != NULL
+			|| strstr(name, "umad") != NULL);
+
+	return found;
+}
diff --git a/criu/files-chr.c b/criu/files-chr.c
new file mode 100644
index 0000000..2eb023e
--- /dev/null
+++ b/criu/files-chr.c
@@ -0,0 +1,104 @@
+#include <sys/ioctl.h>
+
+#include "imgset.h"
+#include "pstree.h"
+#include "files-chr.h"
+#include "log.h"
+
+#include "protobuf.h"
+
+/* Checks if file descriptor @lfd is infinibandevent */
+int is_infiniband_link(char *link)
+{
+	return is_anon_link_type(link, "[infinibandevent]");
+}
+
+static int chrfile_open(struct file_desc *d, int *new_fd)
+{
+	int fd, mntns_root;
+	int ret = 0;
+	struct chrfile_info *ci;
+
+	ci = container_of(d, struct chrfile_info, d);
+
+	if (ci->cfe->repair)
+		ci->cfe->flags |= O_REPAIR;
+
+	mntns_root = open_pid_proc(getpid());
+	fd = openat(mntns_root, ci->path, ci->cfe->flags);
+	if (fd < 0){
+		pr_err("open chr file failed\n");
+		return -1;
+	}
+
+	if (ci->cfe->repair) {
+		ret = ioctl(fd, IOCTL_CMD_REPAIR , ci->cfe->index);
+		pr_info("repair ioctl return: %d, index: %d\n", ret, ci->cfe->index);
+		if (ret)
+			goto err;
+	}
+
+	*new_fd = fd;
+	return ret;
+err:
+	close(fd);
+	return ret;
+}
+
+static struct file_desc_ops chrfile_desc_ops = {
+	.type = FD_TYPES__CHR,
+	.open = chrfile_open,
+};
+
+static int collect_one_chrfile(void *o, ProtobufCMessage *base, struct cr_img *i)
+{
+	struct chrfile_info *ci = o;
+	static char dot[] = ".";
+
+	ci->cfe = pb_msg(base, ChrfileEntry);
+	if (ci->cfe->name[1] == '\0')
+		ci->path = dot;
+	else
+		ci->path = ci->cfe->name;
+
+	pr_info("Collected chr file: %#x, name: %s\n", ci->cfe->id, ci->path);
+	file_desc_add(&ci->d, ci->cfe->id, &chrfile_desc_ops);
+
+	return 0;
+}
+
+struct collect_image_info chrfile_cinfo = {
+	.fd_type = CR_FD_CHRFILE,
+	.pb_type = PB_CHRFILE,
+	.priv_size = sizeof(struct chrfile_info),
+	.collect = collect_one_chrfile,
+};
+
+int collect_chr_map(struct pstree_item *me, struct vma_area *vma)
+{
+	struct list_head *list = &rsti(me)->fds;
+	struct fdinfo_list_entry *fle, *tmp;
+	struct chrfile_info *ci;
+	bool exist_fd;
+
+
+	list_for_each_entry_safe(fle, tmp, list, ps_list) {
+		struct file_desc *d = fle->desc;
+
+		if (d->ops->type != FD_TYPES__CHR)
+			continue;
+
+		ci = container_of(d, struct chrfile_info, d);
+		if (!strcmp(vma->e->name, ci->path)) {
+			vma->vmfd = d;
+			vma->e->fd = fle->fe->fd;
+			exist_fd = true;
+			break;
+		}
+	}
+
+	if (!exist_fd)
+		return -EEXIST;
+
+	return 0;
+}
diff --git a/criu/files-reg.c b/criu/files-reg.c
index fbdf811..b9576a4 100644
--- a/criu/files-reg.c
+++ b/criu/files-reg.c
@@ -45,6 +45,7 @@
 #include "fault-injection.h"
 #include "external.h"
 #include "memfd.h"
+#include "files-chr.h"
 
 #include "protobuf.h"
 #include "util.h"
@@ -1640,7 +1641,8 @@ int dump_one_reg_file(int lfd, u32 id, const struct fd_parms *p)
 		rfe.has_mnt_id = true;
 	}
 
-	pr_info("Dumping path for %d fd via self %d [%s]\n", p->fd, lfd, &link->name[1]);
+	pr_info("Dumping path for %d fd via self %d [%s], id: %d\n",
+		p->fd, lfd, &link->name[1], id);
 
 	/*
 	 * The regular path we can handle should start with slash.
@@ -2373,7 +2375,7 @@ static int collect_one_regfile(void *o, ProtobufCMessage *base, struct cr_img *i
 	rfi->remap = NULL;
 	rfi->size_mode_checked = false;
 
-	pr_info("Collected [%s] ID %#x\n", rfi->path, rfi->rfe->id);
+	pr_info("Collected regfile [%s] ID %#x\n", rfi->path, rfi->rfe->id);
 	return file_desc_add(&rfi->d, rfi->rfe->id, &reg_desc_ops);
 }
 
diff --git a/criu/files.c b/criu/files.c
index f262d80..e1681a1 100644
--- a/criu/files.c
+++ b/criu/files.c
@@ -49,6 +49,7 @@
 #include "kerndat.h"
 #include "fdstore.h"
 #include "bpfmap.h"
+#include "files-chr.h"
 
 #include "protobuf.h"
 #include "util.h"
@@ -325,10 +326,32 @@ int do_dump_gen_file(struct fd_parms *p, int lfd, const struct fdtype_ops *ops,
 	e->fd = p->fd;
 	e->flags = p->fd_flags;
 
+	pr_info("fdinfoEntry fd: %d\n", e->fd);
 	ret = fd_id_generate(p->pid, e, p);
 	if (ret == 1) /* new ID generated */
 		ret = ops->dump(lfd, e->id, p);
-	else
+	else if (ops->type == FD_TYPES__CHR) {
+		/*
+		 * Sometimes the app_data subprocess may inherit the fd from
+		 * app_data. Those fds may result the unconditional oops during
+		 * the restoration of app_data. Therefore, prevent the dump in
+		 * those condition.
+		 */
+		struct fd_link _link, *link;
+
+		if (!p->link) {
+			if (fill_fdlink(lfd, p, &_link))
+				return -1;
+			link = &_link;
+		} else
+			link = p->link;
+
+		if (find_devname(link->name)) {
+			pr_err("char dev '%s' fd %d is owned by multi-processes\n",
+			       link->name, e->fd);
+			ret = -1;
+		}
+	} else
 		/* Remove locks generated by the fd before going to the next */
 		discard_dup_locks_tail(p->pid, e->fd);
 
@@ -466,6 +489,58 @@ static int dump_blkdev(struct fd_parms *p, int lfd, FdinfoEntry *e)
 	return err;
 }
 
+static int dump_chr_file(int lfd, u32 id, const struct fd_parms *p)
+{
+	int ret;
+	struct fd_link _link, *link;
+	struct cr_img *img;
+	FileEntry fe = FILE_ENTRY__INIT;
+	ChrfileEntry cfe = CHRFILE_ENTRY__INIT;
+
+	if (!p->link) {
+		if (fill_fdlink(lfd, p, &_link))
+			return -1;
+		link = &_link;
+	} else
+		link = p->link;
+
+	pr_info("Dumping chr-file fd %d with lfd %d with id %d, name: %s\n", p->fd, lfd, id, link->name);
+
+	if (strstr(link->name, "(deleted)") != NULL) {
+		pr_err("char device '%s' is deleted\n", link->name);
+		return -ENXIO;
+	}
+
+	cfe.repair = false;
+	if (find_devname(link->name)) {
+		ret = ioctl(lfd, IOCTL_CMD_NEEDREPAIR, 0);
+		if (ret <= 0) {
+			pr_err("ioctl cmd needrepair failed, errno: %d, %s\n", ret, strerror(errno));
+			return -1;
+		} else {
+			pr_info("char device needrepair cmd return: %d\n", ret);
+			cfe.index = ret;
+			cfe.repair = true;
+		}
+	}
+
+	cfe.id = id;
+	cfe.name = &link->name[1];
+	cfe.flags = p->flags;
+	fe.type = FD_TYPES__CHR;
+	fe.id = cfe.id;
+	fe.chr = &cfe;
+
+	img = img_from_set(glob_imgset, CR_FD_FILES);
+	ret = pb_write_one(img, &fe, PB_FILE);
+	return ret;
+}
+
+const struct fdtype_ops chr_dump_ops = {
+	.type = FD_TYPES__CHR,
+	.dump = dump_chr_file,
+};
+
 static int dump_chrdev(struct fd_parms *p, int lfd, FdinfoEntry *e)
 {
 	struct fd_link *link_old = p->link;
@@ -493,6 +568,10 @@ static int dump_chrdev(struct fd_parms *p, int lfd, FdinfoEntry *e)
 			ops = &tty_dump_ops;
 			break;
 		}
+		if (opts.dump_char_dev) {
+			ops = &chr_dump_ops;
+			break;
+		}
 
 		sprintf(more, "%d:%d", maj, minor(p->stat.st_rdev));
 		err = dump_unsupp_fd(p, lfd, "chr", more, e);
@@ -559,6 +638,8 @@ static int dump_one_file(struct pid *pid, int fd, int lfd, struct fd_opts *opts,
 			ops = &signalfd_dump_ops;
 		else if (is_timerfd_link(link))
 			ops = &timerfd_dump_ops;
+		else if (is_infiniband_link(link))
+			return 1;
 #ifdef CONFIG_HAS_LIBBPF
 		else if (is_bpfmap_link(link))
 			ops = &bpfmap_dump_ops;
@@ -663,6 +744,11 @@ int dump_task_files_seized(struct parasite_ctl *ctl, struct pstree_item *item, s
 			ret = dump_one_file(item->pid, dfds->fds[i + off], lfds[i], opts + i, ctl, &e, dfds);
 			if (ret)
 				break;
+			/* infiniband link file */
+			if (ret > 0) {
+				ret = 0;
+				continue;
+			}
 
 			ret = pb_write_one(img, &e, PB_FDINFO);
 			if (ret)
@@ -917,6 +1003,7 @@ int prepare_fd_pid(struct pstree_item *item)
 	if (!img)
 		return -1;
 
+	pr_info("prepare_fd_pid\n");
 	while (1) {
 		FdinfoEntry *e;
 
@@ -1125,6 +1212,7 @@ int setup_and_serve_out(struct fdinfo_list_entry *fle, int new_fd)
 	if (reopen_fd_as(fle->fe->fd, new_fd))
 		return -1;
 
+	pr_info("*******flags: %d",fle->fe->flags);
 	if (fcntl(fle->fe->fd, F_SETFD, fle->fe->flags) == -1) {
 		pr_perror("Unable to set file descriptor flags");
 		return -1;
@@ -1761,6 +1849,9 @@ static int collect_one_file(void *o, ProtobufCMessage *base, struct cr_img *i)
 		ret = collect_one_file_entry(fe, fe->bpf->id, &fe->bpf->base, &bpfmap_cinfo);
 		break;
 #endif
+	case FD_TYPES__CHR:
+		ret = collect_one_file_entry(fe, fe->chr->id, &fe->chr->base, &chrfile_cinfo);
+		break;
 	}
 
 	return ret;
diff --git a/criu/include/cr_options.h b/criu/include/cr_options.h
index 039edba..226acb2 100644
--- a/criu/include/cr_options.h
+++ b/criu/include/cr_options.h
@@ -193,6 +193,7 @@ struct cr_options {
 	int pin_memory;
 	int use_fork_pid;
 	int with_notifier_kup;
+	int dump_char_dev;
 };
 
 extern struct cr_options opts;
diff --git a/criu/include/files-chr.h b/criu/include/files-chr.h
new file mode 100644
index 0000000..5be11f5
--- /dev/null
+++ b/criu/include/files-chr.h
@@ -0,0 +1,25 @@
+#ifndef __CRIU_FILES_CHR_H__
+#define __CRIU_FILES_CHR_H__
+
+#include "files.h"
+
+#include "images/chr.pb-c.h"
+
+struct chrfile_info {
+	struct file_desc d;
+	ChrfileEntry *cfe;
+	char *path;
+};
+
+extern struct collect_image_info chrfile_cinfo;
+
+extern const struct fdtype_ops chr_dump_ops;
+extern int collect_chr_map(struct pstree_item *me, struct vma_area *);
+
+int parse_devname(void);
+bool find_devname(const char *name);
+
+int collect_chr_map(struct pstree_item *me, struct vma_area *vma);
+int is_infiniband_link(char *link);
+
+#endif /* __CRIU_FILES_CHR_H__ */
diff --git a/criu/include/files.h b/criu/include/files.h
index 96face7..1d979a9 100644
--- a/criu/include/files.h
+++ b/criu/include/files.h
@@ -15,6 +15,12 @@
 #include "images/fown.pb-c.h"
 #include "images/vma.pb-c.h"
 
+#ifndef IOCTL_CMD_NEEDREPAIR
+#define IOCTL_CMD_NEEDREPAIR 0x00100000UL
+#define IOCTL_CMD_REPAIR 0x00200000UL
+#define O_REPAIR        040000000
+#endif
+
 struct parasite_drain_fd;
 struct pstree_item;
 struct file_desc;
diff --git a/criu/include/image-desc.h b/criu/include/image-desc.h
index 5045bae..e35f8b2 100644
--- a/criu/include/image-desc.h
+++ b/criu/include/image-desc.h
@@ -115,6 +115,7 @@ enum {
 	CR_FD_MEMFD_FILE,
 
 	CR_FD_AUTOFS,
+	CR_FD_CHRFILE,
 
 	CR_FD_MAX
 };
diff --git a/criu/include/image.h b/criu/include/image.h
index f598de7..66492c0 100644
--- a/criu/include/image.h
+++ b/criu/include/image.h
@@ -85,6 +85,7 @@
 #define VMA_AREA_AIORING (1 << 13)
 #define VMA_AREA_MEMFD	 (1 << 14)
 #define VMA_AREA_ANON_INODE (1 << 15)
+#define VMA_AREA_CHR     (1 << 16)
 
 #define VMA_CLOSE	  (1 << 28)
 #define VMA_NO_PROT_WRITE (1 << 29)
diff --git a/criu/include/protobuf-desc.h b/criu/include/protobuf-desc.h
index 3824de1..2468e8f 100644
--- a/criu/include/protobuf-desc.h
+++ b/criu/include/protobuf-desc.h
@@ -70,6 +70,7 @@ enum {
 	PB_BPFMAP_FILE,
 	PB_BPFMAP_DATA,
 	PB_APPARMOR,
+	PB_CHRFILE,
 
 	/* PB_AUTOGEN_STOP */
 
diff --git a/criu/mem.c b/criu/mem.c
index 00965f0..b955d66 100644
--- a/criu/mem.c
+++ b/criu/mem.c
@@ -32,6 +32,7 @@
 #include "compel/infect-util.h"
 #include "pidfd-store.h"
 #include "pin-mem.h"
+#include "files-chr.h"
 
 #include "protobuf.h"
 #include "images/pagemap.pb-c.h"
@@ -717,7 +718,9 @@ int prepare_mm_pid(struct pstree_item *i)
 
 		pr_info("vma 0x%" PRIx64 " 0x%" PRIx64 "\n", vma->e->start, vma->e->end);
 
-		if (vma_area_is(vma, VMA_ANON_SHARED))
+		if (vma_area_is(vma, VMA_AREA_CHR))
+			ret = collect_chr_map(i, vma);
+		else if (vma_area_is(vma, VMA_ANON_SHARED))
 			ret = collect_shmem(pid, vma);
 		else if (vma_area_is(vma, VMA_FILE_PRIVATE) || vma_area_is(vma, VMA_FILE_SHARED))
 			ret = collect_filemap(vma);
@@ -1358,7 +1361,7 @@ int open_vmas(struct pstree_item *t)
 	filemap_ctx_init(false);
 
 	list_for_each_entry(vma, &vmas->h, list) {
-		if (vma_area_is(vma, VMA_AREA_ANON_INODE))
+		if (vma_area_is(vma, VMA_AREA_ANON_INODE) || vma_area_is(vma, VMA_AREA_CHR))
 			continue;
 
 		if (!vma_area_is(vma, VMA_AREA_REGULAR) || !vma->vm_open)
diff --git a/criu/proc_parse.c b/criu/proc_parse.c
index e41d43a..8913d93 100644
--- a/criu/proc_parse.c
+++ b/criu/proc_parse.c
@@ -603,11 +603,30 @@ static int handle_vma(pid_t pid, struct vma_area *vma_area, const char *file_pat
 	} else if (*vm_file_fd >= 0) {
 		struct stat *st_buf = vma_area->vmst;
 
+		pr_info("file mode is: %x, st_ino: %ld\n",
+				st_buf->st_mode, st_buf->st_ino);
 		if (S_ISREG(st_buf->st_mode))
 			/* regular file mapping -- supported */;
 		else if (S_ISCHR(st_buf->st_mode) && (st_buf->st_rdev == DEVZERO))
 			/* devzero mapping -- also makes sense */;
-		else {
+		else if (S_ISCHR(st_buf->st_mode) && opts.dump_char_dev) {
+			/* NOTICE: if `--dump-char-dev` option is set, permmit
+			 * all char device memory area dumping.
+			 */
+			 if (strstr(file_path, "uverbs") != NULL) {
+				int len = strlen(file_path) + 1;
+
+				vma_area->e->status |= VMA_AREA_CHR;
+				vma_area->e->name = xmalloc(len);
+				if (!vma_area->e->name) {
+					pr_err("alloc vma area name failed\n");
+					goto err;
+					strncpy(vma_area->e->name, file_path, len);
+					pr_info("vma name content is: %s\n",
+							vma_area->e->name);
+				}
+			 }
+		} else {
 			pr_err("Can't handle non-regular mapping on %d's map %" PRIx64 "\n", pid, vma_area->e->start);
 			goto err;
 		}
diff --git a/images/Makefile b/images/Makefile
index 004e22e..37dff9a 100644
--- a/images/Makefile
+++ b/images/Makefile
@@ -72,6 +72,7 @@ proto-obj-y	+= bpfmap-file.o
 proto-obj-y	+= bpfmap-data.o
 proto-obj-y	+= apparmor.o
 proto-obj-y	+= rseq.o
+proto-obj-y += chr.o
 
 CFLAGS		+= -iquote $(obj)/
 
diff --git a/images/chr.proto b/images/chr.proto
new file mode 100644
index 0000000..67929db
--- /dev/null
+++ b/images/chr.proto
@@ -0,0 +1,12 @@
+syntax = "proto2";
+
+import "opts.proto";
+
+message chrfile_entry {
+	required uint32		id	= 1;
+	required uint32		flags	= 2 [(criu).flags = "rfile.flags"];
+	required uint32		index	= 3;
+	required string		name	= 4;
+	required bool		repair	= 5;
+};
+
diff --git a/images/fdinfo.proto b/images/fdinfo.proto
index 88f1c11..6549472 100644
--- a/images/fdinfo.proto
+++ b/images/fdinfo.proto
@@ -20,6 +20,7 @@ import "pipe.proto";
 import "tty.proto";
 import "memfd.proto";
 import "bpfmap-file.proto";
+import "chr.proto";
 
 enum fd_types {
 	UND		= 0;
@@ -42,6 +43,7 @@ enum fd_types {
 	TIMERFD		= 17;
 	MEMFD		= 18;
 	BPFMAP		= 19;
+	CHR		= 21;
 
 	/* Any number above the real used. Not stored to image */
 	CTL_TTY		= 65534;
@@ -78,4 +80,5 @@ message file_entry {
 	optional tty_file_entry		tty	= 19;
 	optional memfd_file_entry	memfd	= 20;
 	optional bpfmap_file_entry	bpf	= 21;
+	optional chrfile_entry          chr     = 23;
 }
-- 
2.34.1

