Add riscv64 to ExclusiveArch
This commit is contained in:
commit
9027bcaa08
47
2876.patch
Normal file
47
2876.patch
Normal file
@ -0,0 +1,47 @@
|
||||
From 89150bea7218b9c7131b7a7712a84b9c564d8feb Mon Sep 17 00:00:00 2001
|
||||
From: 3idey <elaidya225@gmail.com>
|
||||
Date: Thu, 5 Feb 2026 20:58:01 +0200
|
||||
Subject: [PATCH] fsnotify: Fix file descriptor leak in restore_one_inotify()
|
||||
|
||||
When restoring inotify watches, the restore_one_inotify() function opens
|
||||
a file descriptor via get_mark_path() and stores it in the 'target'
|
||||
variable. However, in two error paths, the function returns directly
|
||||
without properly closing this file descriptor:
|
||||
|
||||
1. When INOTIFY_IOC_SETNEXTWD ioctl fails (line 556)
|
||||
2. When has_inotify_setnextwd is true but watch descriptor mismatch
|
||||
occurs (line 577)
|
||||
|
||||
Both cases bypass the cleanup code at the 'err' label which calls
|
||||
close_safe(&target), resulting in a file descriptor leak.
|
||||
|
||||
Fix this by using 'goto err' instead of 'return -1' in both error paths,
|
||||
ensuring proper cleanup of the target file descriptor.
|
||||
|
||||
Signed-off-by: 3idey <elaidya225@gmail.com>
|
||||
---
|
||||
criu/fsnotify.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/criu/fsnotify.c b/criu/fsnotify.c
|
||||
index 8572dc2f3c..7ede629077 100644
|
||||
--- a/criu/fsnotify.c
|
||||
+++ b/criu/fsnotify.c
|
||||
@@ -553,7 +553,7 @@ static int restore_one_inotify(int inotify_fd, struct fsnotify_mark_info *info)
|
||||
if (kdat.has_inotify_setnextwd) {
|
||||
if (ioctl(inotify_fd, INOTIFY_IOC_SETNEXTWD, iwe->wd)) {
|
||||
pr_perror("Can't set next inotify wd");
|
||||
- return -1;
|
||||
+ goto err;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -573,7 +573,7 @@ static int restore_one_inotify(int inotify_fd, struct fsnotify_mark_info *info)
|
||||
}
|
||||
|
||||
if (kdat.has_inotify_setnextwd)
|
||||
- return -1;
|
||||
+ goto err;
|
||||
|
||||
inotify_rm_watch(inotify_fd, wd);
|
||||
}
|
||||
349
2880.patch
Normal file
349
2880.patch
Normal file
@ -0,0 +1,349 @@
|
||||
From 9502782b84efbabfd603e66b665d95663a01086e Mon Sep 17 00:00:00 2001
|
||||
From: 3idey <elaidya225@gmail.com>
|
||||
Date: Sat, 7 Feb 2026 13:00:02 +0200
|
||||
Subject: [PATCH] fsnotify: Improve error messages with more context
|
||||
|
||||
Error messages now include additional context to aid debugging:
|
||||
- Device and inode numbers for file handles
|
||||
- Watch descriptor (wd) for inotify operations
|
||||
- Mask values for fanotify marks
|
||||
- Mount ID for mount-related operations
|
||||
|
||||
This makes it much easier to diagnose checkpoint/restore failures
|
||||
by providing complete information about which watch or mark failed
|
||||
and why, without needing to add debug logging.
|
||||
|
||||
Signed-off-by: 3idey <elaidya225@gmail.com>
|
||||
---
|
||||
criu/fsnotify.c | 119 ++++++++++++++++++++++++++++++++++++------------
|
||||
1 file changed, 89 insertions(+), 30 deletions(-)
|
||||
|
||||
diff --git a/criu/fsnotify.c b/criu/fsnotify.c
|
||||
index 7ede629077..dd468ecaaf 100644
|
||||
--- a/criu/fsnotify.c
|
||||
+++ b/criu/fsnotify.c
|
||||
@@ -102,7 +102,11 @@ static int open_by_handle(void *arg, int fd, int pid)
|
||||
return syscall(__NR_open_by_handle_at, fd, arg, O_PATH);
|
||||
}
|
||||
|
||||
-enum { ERR_NO_MOUNT = -1, ERR_NO_PATH_IN_MOUNT = -2, ERR_GENERIC = -3 };
|
||||
+enum {
|
||||
+ ERR_NO_MOUNT = -1,
|
||||
+ ERR_NO_PATH_IN_MOUNT = -2,
|
||||
+ ERR_GENERIC = -3
|
||||
+};
|
||||
|
||||
static char *alloc_openable(unsigned int s_dev, unsigned long i_ino, FhEntry *f_handle)
|
||||
{
|
||||
@@ -306,8 +310,11 @@ static int check_one_wd(InotifyWdEntry *we)
|
||||
pr_warn_once("\t\tDetected FS_EVENT_ON_CHILD bit "
|
||||
"in mask (will be ignored on restore)\n");
|
||||
|
||||
- if (check_open_handle(we->s_dev, we->i_ino, we->f_handle))
|
||||
+ if (check_open_handle(we->s_dev, we->i_ino, we->f_handle)) {
|
||||
+ pr_err("Failed to check handle for inotify wd %#x (dev %#x ino %#" PRIx64 " mask %#x)\n",
|
||||
+ we->wd, we->s_dev, we->i_ino, we->mask);
|
||||
return -1;
|
||||
+ }
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -328,8 +335,10 @@ static int dump_one_inotify(int lfd, u32 id, const struct fd_parms *p)
|
||||
ie.flags = p->flags;
|
||||
ie.fown = (FownEntry *)&p->fown;
|
||||
|
||||
- if (parse_fdinfo(lfd, FD_TYPES__INOTIFY, &ie))
|
||||
+ if (parse_fdinfo(lfd, FD_TYPES__INOTIFY, &ie)) {
|
||||
+ pr_err("Failed to parse fdinfo for inotify id %#x\n", id);
|
||||
goto free;
|
||||
+ }
|
||||
|
||||
for (i = 0; i < ie.n_wd; i++)
|
||||
if (check_one_wd(ie.wd[i]))
|
||||
@@ -357,14 +366,19 @@ static int pre_dump_one_inotify(int pid, int lfd)
|
||||
InotifyFileEntry ie = INOTIFY_FILE_ENTRY__INIT;
|
||||
int i;
|
||||
|
||||
- if (parse_fdinfo_pid(pid, lfd, FD_TYPES__INOTIFY, &ie))
|
||||
+ if (parse_fdinfo_pid(pid, lfd, FD_TYPES__INOTIFY, &ie)) {
|
||||
+ pr_err("Failed to parse fdinfo for inotify (pid %d lfd %d)\n", pid, lfd);
|
||||
return -1;
|
||||
+ }
|
||||
|
||||
for (i = 0; i < ie.n_wd; i++) {
|
||||
InotifyWdEntry *we = ie.wd[i];
|
||||
|
||||
- if (irmap_queue_cache(we->s_dev, we->i_ino, we->f_handle))
|
||||
+ if (irmap_queue_cache(we->s_dev, we->i_ino, we->f_handle)) {
|
||||
+ pr_err("Failed to queue irmap cache for inotify wd %#x (dev %#x ino %#" PRIx64 ")\n",
|
||||
+ we->wd, we->s_dev, we->i_ino);
|
||||
return -1;
|
||||
+ }
|
||||
|
||||
xfree(we);
|
||||
}
|
||||
@@ -400,7 +414,8 @@ static int check_one_mark(FanotifyMarkEntry *fme)
|
||||
|
||||
m = lookup_mnt_id(fme->me->mnt_id);
|
||||
if (!m) {
|
||||
- pr_err("Can't find mnt_id 0x%x\n", fme->me->mnt_id);
|
||||
+ pr_err("Can't find mnt_id %#x for fanotify mark (mask %#x)\n",
|
||||
+ fme->me->mnt_id, fme->mask);
|
||||
return -1;
|
||||
}
|
||||
if (!(root_ns_mask & CLONE_NEWNS))
|
||||
@@ -430,8 +445,10 @@ static int dump_one_fanotify(int lfd, u32 id, const struct fd_parms *p)
|
||||
fe.flags = p->flags;
|
||||
fe.fown = (FownEntry *)&p->fown;
|
||||
|
||||
- if (parse_fdinfo(lfd, FD_TYPES__FANOTIFY, &fe) < 0)
|
||||
+ if (parse_fdinfo(lfd, FD_TYPES__FANOTIFY, &fe) < 0) {
|
||||
+ pr_err("Failed to parse fdinfo for fanotify id %#x\n", id);
|
||||
goto free;
|
||||
+ }
|
||||
|
||||
for (i = 0; i < fe.n_mark; i++)
|
||||
if (check_one_mark(fe.mark[i]))
|
||||
@@ -456,18 +473,23 @@ static int pre_dump_one_fanotify(int pid, int lfd)
|
||||
FanotifyFileEntry fe = FANOTIFY_FILE_ENTRY__INIT;
|
||||
int i;
|
||||
|
||||
- if (parse_fdinfo_pid(pid, lfd, FD_TYPES__FANOTIFY, &fe))
|
||||
+ if (parse_fdinfo_pid(pid, lfd, FD_TYPES__FANOTIFY, &fe)) {
|
||||
+ pr_err("Failed to parse fdinfo for fanotify (pid %d lfd %d)\n", pid, lfd);
|
||||
return -1;
|
||||
+ }
|
||||
|
||||
for (i = 0; i < fe.n_mark; i++) {
|
||||
FanotifyMarkEntry *me = fe.mark[i];
|
||||
|
||||
- if (me->type == MARK_TYPE__INODE && irmap_queue_cache(me->s_dev, me->ie->i_ino, me->ie->f_handle))
|
||||
+ if (me->type == MARK_TYPE__INODE && irmap_queue_cache(me->s_dev, me->ie->i_ino, me->ie->f_handle)) {
|
||||
+ pr_err("Failed to queue irmap cache for fanotify mark (dev %#x ino %#" PRIx64 " mask %#x)\n",
|
||||
+ me->s_dev, me->ie->i_ino, me->mask);
|
||||
return -1;
|
||||
+ }
|
||||
|
||||
xfree(me);
|
||||
}
|
||||
- xfree(fe.mark);
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -507,7 +529,15 @@ static char *get_mark_path(const char *who, struct file_remap *remap, FhEntry *f
|
||||
*target = open_handle(s_dev, i_ino, f_handle);
|
||||
|
||||
if (*target < 0) {
|
||||
- pr_perror("Unable to open %s", f_handle->path);
|
||||
+ if (remap)
|
||||
+ pr_perror("Unable to open %s via remap %s (dev %#x ino %#lx)",
|
||||
+ who, remap->rpath, s_dev, i_ino);
|
||||
+ else if (f_handle->path)
|
||||
+ pr_perror("Unable to open %s via path %s (dev %#x ino %#lx)",
|
||||
+ who, f_handle->path, s_dev, i_ino);
|
||||
+ else
|
||||
+ pr_perror("Unable to open %s by handle (dev %#x ino %#lx)",
|
||||
+ who, s_dev, i_ino);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -542,8 +572,11 @@ static int restore_one_inotify(int inotify_fd, struct fsnotify_mark_info *info)
|
||||
uint32_t mask;
|
||||
|
||||
path = get_mark_path("inotify", info->remap, iwe->f_handle, iwe->i_ino, iwe->s_dev, buf, &target);
|
||||
- if (!path)
|
||||
+ if (!path) {
|
||||
+ pr_err("Failed to get path for inotify wd %#x (dev %#x ino %#" PRIx64 " mask %#x)\n",
|
||||
+ iwe->wd, iwe->s_dev, iwe->i_ino, iwe->mask);
|
||||
goto err;
|
||||
+ }
|
||||
|
||||
mask = iwe->mask & IN_ALL_EVENTS;
|
||||
if (iwe->mask & ~IN_ALL_EVENTS) {
|
||||
@@ -552,7 +585,8 @@ static int restore_one_inotify(int inotify_fd, struct fsnotify_mark_info *info)
|
||||
|
||||
if (kdat.has_inotify_setnextwd) {
|
||||
if (ioctl(inotify_fd, INOTIFY_IOC_SETNEXTWD, iwe->wd)) {
|
||||
- pr_perror("Can't set next inotify wd");
|
||||
+ pr_perror("Can't set next inotify wd to %#x (dev %#x ino %#" PRIx64 ")",
|
||||
+ iwe->wd, iwe->s_dev, iwe->i_ino);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
@@ -562,13 +596,15 @@ static int restore_one_inotify(int inotify_fd, struct fsnotify_mark_info *info)
|
||||
|
||||
wd = inotify_add_watch(inotify_fd, path, mask);
|
||||
if (wd < 0) {
|
||||
- pr_perror("Can't add watch for 0x%x with 0x%x", inotify_fd, iwe->wd);
|
||||
+ pr_perror("Can't add inotify watch for fd %d wd %#x (dev %#x ino %#" PRIx64 " mask %#x)",
|
||||
+ inotify_fd, iwe->wd, iwe->s_dev, iwe->i_ino, mask);
|
||||
break;
|
||||
} else if (wd == iwe->wd) {
|
||||
ret = 0;
|
||||
break;
|
||||
} else if (wd > iwe->wd) {
|
||||
- pr_err("Unsorted watch 0x%x found for 0x%x with 0x%x\n", wd, inotify_fd, iwe->wd);
|
||||
+ pr_err("Unsorted inotify watch %#x found (expected %#x, dev %#x ino %#" PRIx64 ")\n",
|
||||
+ wd, iwe->wd, iwe->s_dev, iwe->i_ino);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -599,7 +635,8 @@ static int restore_one_fanotify(int fd, struct fsnotify_mark_info *mark)
|
||||
if (root_ns_mask & CLONE_NEWNS) {
|
||||
m = lookup_mnt_id(fme->me->mnt_id);
|
||||
if (!m) {
|
||||
- pr_err("Can't find mount mnt_id 0x%x\n", fme->me->mnt_id);
|
||||
+ pr_err("Can't find mount mnt_id %#x for fanotify mark (mask %#x)\n",
|
||||
+ fme->me->mnt_id, fme->mask);
|
||||
return -1;
|
||||
}
|
||||
nsid = m->nsid;
|
||||
@@ -610,7 +647,8 @@ static int restore_one_fanotify(int fd, struct fsnotify_mark_info *mark)
|
||||
|
||||
target = openat(mntns_root, p, O_PATH);
|
||||
if (target == -1) {
|
||||
- pr_perror("Unable to open %s", p);
|
||||
+ pr_perror("Unable to open mount point %s (mnt_id %#x)",
|
||||
+ p, fme->me->mnt_id);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -620,10 +658,13 @@ static int restore_one_fanotify(int fd, struct fsnotify_mark_info *mark)
|
||||
} else if (fme->type == MARK_TYPE__INODE) {
|
||||
path = get_mark_path("fanotify", mark->remap, fme->ie->f_handle, fme->ie->i_ino, fme->s_dev, buf,
|
||||
&target);
|
||||
- if (!path)
|
||||
+ if (!path) {
|
||||
+ pr_err("Failed to get path for fanotify mark (dev %#x ino %#" PRIx64 " mask %#x)\n",
|
||||
+ fme->s_dev, fme->ie->i_ino, fme->mask);
|
||||
goto err;
|
||||
+ }
|
||||
} else {
|
||||
- pr_err("Bad fsnotify mark type 0x%x\n", fme->type);
|
||||
+ pr_err("Bad fsnotify mark type %#x (mask %#x)\n", fme->type, fme->mask);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -632,7 +673,8 @@ static int restore_one_fanotify(int fd, struct fsnotify_mark_info *mark)
|
||||
if (mark->fme->mask) {
|
||||
ret = fanotify_mark(fd, flags, fme->mask, AT_FDCWD, path);
|
||||
if (ret) {
|
||||
- pr_err("Adding fanotify mask 0x%x on 0x%x/%s failed (%d)\n", fme->mask, fme->id, path, ret);
|
||||
+ pr_perror("Failed to add fanotify mask %#x on id %#x (dev %#x path %s)",
|
||||
+ fme->mask, fme->id, fme->s_dev, path);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
@@ -640,8 +682,8 @@ static int restore_one_fanotify(int fd, struct fsnotify_mark_info *mark)
|
||||
if (fme->ignored_mask) {
|
||||
ret = fanotify_mark(fd, flags | FAN_MARK_IGNORED_MASK, fme->ignored_mask, AT_FDCWD, path);
|
||||
if (ret) {
|
||||
- pr_err("Adding fanotify ignored-mask 0x%x on 0x%x/%s failed (%d)\n", fme->ignored_mask, fme->id,
|
||||
- path, ret);
|
||||
+ pr_perror("Failed to add fanotify ignored-mask %#x on id %#x (dev %#x path %s)",
|
||||
+ fme->ignored_mask, fme->id, fme->s_dev, path);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
@@ -661,13 +703,16 @@ static int open_inotify_fd(struct file_desc *d, int *new_fd)
|
||||
|
||||
tmp = inotify_init1(info->ife->flags);
|
||||
if (tmp < 0) {
|
||||
- pr_perror("Can't create inotify for %#08x", info->ife->id);
|
||||
+ pr_perror("Can't create inotify fd for id %#08x (flags %#x)",
|
||||
+ info->ife->id, info->ife->flags);
|
||||
return -1;
|
||||
}
|
||||
|
||||
list_for_each_entry(wd_info, &info->marks, list) {
|
||||
pr_info("\tRestore 0x%x wd for %#08x\n", wd_info->iwe->wd, wd_info->iwe->id);
|
||||
if (restore_one_inotify(tmp, wd_info)) {
|
||||
+ pr_err("Failed to restore inotify wd %#x for id %#08x\n",
|
||||
+ wd_info->iwe->wd, wd_info->iwe->id);
|
||||
close_safe(&tmp);
|
||||
return -1;
|
||||
}
|
||||
@@ -698,13 +743,15 @@ static int open_fanotify_fd(struct file_desc *d, int *new_fd)
|
||||
|
||||
ret = fanotify_init(flags, info->ffe->evflags);
|
||||
if (ret < 0) {
|
||||
- pr_perror("Can't init fanotify mark (%d)", ret);
|
||||
+ pr_perror("Can't init fanotify for id %#08x (flags %#x evflags %#x)",
|
||||
+ info->ffe->id, flags, info->ffe->evflags);
|
||||
return -1;
|
||||
}
|
||||
|
||||
list_for_each_entry(mark, &info->marks, list) {
|
||||
pr_info("\tRestore fanotify for %#08x\n", mark->fme->id);
|
||||
if (restore_one_fanotify(ret, mark)) {
|
||||
+ pr_err("Failed to restore fanotify mark for id %#08x\n", mark->fme->id);
|
||||
close_safe(&ret);
|
||||
return -1;
|
||||
}
|
||||
@@ -786,15 +833,21 @@ static int collect_one_inotify(void *o, ProtobufCMessage *msg, struct cr_img *im
|
||||
struct fsnotify_mark_info *mark;
|
||||
|
||||
mark = xmalloc(sizeof(*mark));
|
||||
- if (!mark)
|
||||
+ if (!mark) {
|
||||
+ pr_err("Failed to allocate inotify mark for id %#08x wd %#x\n",
|
||||
+ info->ife->id, info->ife->wd[i]->wd);
|
||||
return -1;
|
||||
+ }
|
||||
|
||||
mark->iwe = info->ife->wd[i];
|
||||
INIT_LIST_HEAD(&mark->list);
|
||||
mark->remap = NULL;
|
||||
|
||||
- if (__collect_inotify_mark(info, mark))
|
||||
+ if (__collect_inotify_mark(info, mark)) {
|
||||
+ pr_err("Failed to collect inotify mark for id %#08x wd %#x\n",
|
||||
+ info->ife->id, info->ife->wd[i]->wd);
|
||||
return -1;
|
||||
+ }
|
||||
}
|
||||
|
||||
return file_desc_add(&info->d, info->ife->id, &inotify_desc_ops);
|
||||
@@ -820,15 +873,19 @@ static int collect_one_fanotify(void *o, ProtobufCMessage *msg, struct cr_img *i
|
||||
struct fsnotify_mark_info *mark;
|
||||
|
||||
mark = xmalloc(sizeof(*mark));
|
||||
- if (!mark)
|
||||
+ if (!mark) {
|
||||
+ pr_err("Failed to allocate fanotify mark for id %#08x\n", info->ffe->id);
|
||||
return -1;
|
||||
+ }
|
||||
|
||||
mark->fme = info->ffe->mark[i];
|
||||
INIT_LIST_HEAD(&mark->list);
|
||||
mark->remap = NULL;
|
||||
|
||||
- if (__collect_fanotify_mark(info, mark))
|
||||
+ if (__collect_fanotify_mark(info, mark)) {
|
||||
+ pr_err("Failed to collect fanotify mark for id %#08x\n", info->ffe->id);
|
||||
return -1;
|
||||
+ }
|
||||
}
|
||||
|
||||
return file_desc_add(&info->d, info->ffe->id, &fanotify_desc_ops);
|
||||
@@ -865,7 +922,8 @@ static int collect_one_inotify_mark(void *o, ProtobufCMessage *msg, struct cr_im
|
||||
|
||||
d = find_file_desc_raw(FD_TYPES__INOTIFY, mark->iwe->id);
|
||||
if (!d) {
|
||||
- pr_err("Can't find inotify with id %#08x\n", mark->iwe->id);
|
||||
+ pr_err("Can't find inotify with id %#08x for wd %#x\n",
|
||||
+ mark->iwe->id, mark->iwe->wd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -893,7 +951,8 @@ static int collect_one_fanotify_mark(void *o, ProtobufCMessage *msg, struct cr_i
|
||||
|
||||
d = find_file_desc_raw(FD_TYPES__FANOTIFY, mark->fme->id);
|
||||
if (!d) {
|
||||
- pr_err("Can't find fanotify with id %#08x\n", mark->fme->id);
|
||||
+ pr_err("Can't find fanotify with id %#08x for mark (mask %#x)\n",
|
||||
+ mark->fme->id, mark->fme->mask);
|
||||
return -1;
|
||||
}
|
||||
|
||||
103
2881.patch
Normal file
103
2881.patch
Normal file
@ -0,0 +1,103 @@
|
||||
From 33ebc40be98ed7a7132c2daff155c582dc74b5ee Mon Sep 17 00:00:00 2001
|
||||
From: 3idey <elaidya225@gmail.com>
|
||||
Date: Sun, 8 Feb 2026 12:36:49 +0200
|
||||
Subject: [PATCH 1/2] fsnotify: Fix memory leak in pre_dump_one_inotify error
|
||||
path
|
||||
|
||||
When irmap_queue_cache fails, the remaining inotify watch descriptors
|
||||
and the wd array are not freed, causing a memory leak.
|
||||
|
||||
Add cleanup label to ensure proper deallocation of remaining entries
|
||||
and the wd array on error paths.
|
||||
|
||||
Signed-off-by: 3idey <elaidya225@gmail.com>
|
||||
---
|
||||
criu/fsnotify.c | 11 ++++++++---
|
||||
1 file changed, 8 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/criu/fsnotify.c b/criu/fsnotify.c
|
||||
index dd468ecaaf..55bae63bd8 100644
|
||||
--- a/criu/fsnotify.c
|
||||
+++ b/criu/fsnotify.c
|
||||
@@ -364,7 +364,7 @@ static int dump_one_inotify(int lfd, u32 id, const struct fd_parms *p)
|
||||
static int pre_dump_one_inotify(int pid, int lfd)
|
||||
{
|
||||
InotifyFileEntry ie = INOTIFY_FILE_ENTRY__INIT;
|
||||
- int i;
|
||||
+ int i, ret = -1;
|
||||
|
||||
if (parse_fdinfo_pid(pid, lfd, FD_TYPES__INOTIFY, &ie)) {
|
||||
pr_err("Failed to parse fdinfo for inotify (pid %d lfd %d)\n", pid, lfd);
|
||||
@@ -377,13 +377,18 @@ static int pre_dump_one_inotify(int pid, int lfd)
|
||||
if (irmap_queue_cache(we->s_dev, we->i_ino, we->f_handle)) {
|
||||
pr_err("Failed to queue irmap cache for inotify wd %#x (dev %#x ino %#" PRIx64 ")\n",
|
||||
we->wd, we->s_dev, we->i_ino);
|
||||
- return -1;
|
||||
+ goto err;
|
||||
}
|
||||
|
||||
xfree(we);
|
||||
}
|
||||
|
||||
- return 0;
|
||||
+ ret = 0;
|
||||
+err:
|
||||
+ for (; i < ie.n_wd; i++)
|
||||
+ xfree(ie.wd[i]);
|
||||
+ xfree(ie.wd);
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
const struct fdtype_ops inotify_dump_ops = {
|
||||
|
||||
From 19ac1d63287abaa5a2b49c507c45b0d0a2cb4bb8 Mon Sep 17 00:00:00 2001
|
||||
From: 3idey <elaidya225@gmail.com>
|
||||
Date: Sun, 8 Feb 2026 12:37:11 +0200
|
||||
Subject: [PATCH 2/2] fsnotify: Fix memory leak in pre_dump_one_fanotify error
|
||||
path
|
||||
|
||||
When irmap_queue_cache fails, the remaining fanotify mark entries
|
||||
and the mark array are not freed, causing a memory leak.
|
||||
|
||||
Add cleanup label to ensure proper deallocation of remaining entries
|
||||
and the mark array on error paths.
|
||||
|
||||
Signed-off-by: 3idey <elaidya225@gmail.com>
|
||||
---
|
||||
criu/fsnotify.c | 11 ++++++++---
|
||||
1 file changed, 8 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/criu/fsnotify.c b/criu/fsnotify.c
|
||||
index 55bae63bd8..5c05fa28fe 100644
|
||||
--- a/criu/fsnotify.c
|
||||
+++ b/criu/fsnotify.c
|
||||
@@ -476,7 +476,7 @@ static int dump_one_fanotify(int lfd, u32 id, const struct fd_parms *p)
|
||||
static int pre_dump_one_fanotify(int pid, int lfd)
|
||||
{
|
||||
FanotifyFileEntry fe = FANOTIFY_FILE_ENTRY__INIT;
|
||||
- int i;
|
||||
+ int i, ret = -1;
|
||||
|
||||
if (parse_fdinfo_pid(pid, lfd, FD_TYPES__FANOTIFY, &fe)) {
|
||||
pr_err("Failed to parse fdinfo for fanotify (pid %d lfd %d)\n", pid, lfd);
|
||||
@@ -489,13 +489,18 @@ static int pre_dump_one_fanotify(int pid, int lfd)
|
||||
if (me->type == MARK_TYPE__INODE && irmap_queue_cache(me->s_dev, me->ie->i_ino, me->ie->f_handle)) {
|
||||
pr_err("Failed to queue irmap cache for fanotify mark (dev %#x ino %#" PRIx64 " mask %#x)\n",
|
||||
me->s_dev, me->ie->i_ino, me->mask);
|
||||
- return -1;
|
||||
+ goto err;
|
||||
}
|
||||
|
||||
xfree(me);
|
||||
}
|
||||
|
||||
- return 0;
|
||||
+ ret = 0;
|
||||
+err:
|
||||
+ for (; i < fe.n_mark; i++)
|
||||
+ xfree(fe.mark[i]);
|
||||
+ xfree(fe.mark);
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
const struct fdtype_ops fanotify_dump_ops = {
|
||||
778
3043.patch
Normal file
778
3043.patch
Normal file
@ -0,0 +1,778 @@
|
||||
From 143b1978b47c519c3754bd4139798bc177cddd97 Mon Sep 17 00:00:00 2001
|
||||
From: Adrian Reber <areber@redhat.com>
|
||||
Date: Thu, 11 Jun 2026 15:24:46 +0000
|
||||
Subject: [PATCH 1/3] fsnotify: add overlay directory walk fallback
|
||||
|
||||
When a process has inotify (or fanotify) watches on files inside an
|
||||
overlayfs mount, criu dump fails because open_by_handle_at() does not
|
||||
work for overlay file handles (type OVL_FILEID_V1 = 0xf8). The dump
|
||||
log shows:
|
||||
|
||||
fsnotify: wd 0x000001 s_dev 0x00002d i_ino 0x101ad2 mask 0x00033a
|
||||
fsnotify: [fhandle] bytes 0x000020 type 0x0000f8 ...
|
||||
fsnotify: Handle 0x2d:0x101ad2 cannot be opened
|
||||
Error (criu/fsnotify.c:284): fsnotify: Can't dump that handle
|
||||
|
||||
To reproduce manually:
|
||||
|
||||
mkdir -p /tmp/{lower,upper,work,merged}
|
||||
touch /tmp/lower/testfile
|
||||
mount -t overlay overlay \
|
||||
-o lowerdir=/tmp/lower,upperdir=/tmp/upper,workdir=/tmp/work \
|
||||
/tmp/merged
|
||||
inotifywait -m /tmp/merged/testfile &
|
||||
PID=$!
|
||||
criu dump -t $PID -D /tmp/imgs --shell-job --ext-unix-sk
|
||||
|
||||
alloc_openable() iterates mounts matching s_dev and tries
|
||||
open_by_handle_at() for each one. Overlay file handles use the
|
||||
private OVL_FILEID_V1 type which open_by_handle_at() does not
|
||||
support, so the call always fails and the dump aborts.
|
||||
|
||||
Add a fallback for overlay mounts: when open_by_handle_at() fails
|
||||
and the mount is identified as FSTYPE__OVERLAYFS, walk the overlay
|
||||
mount's directory tree with opendir/readdir/fstatat to find the
|
||||
file matching the (s_dev, i_ino) pair from the handle. Store the
|
||||
discovered absolute path in f_handle->path so that get_mark_path()
|
||||
uses the path-based openat() strategy on restore, which works on
|
||||
overlay mounts.
|
||||
|
||||
The fallback only triggers when open_by_handle_at() fails AND the
|
||||
mount type is FSTYPE__OVERLAYFS, so there is no impact on other
|
||||
filesystems.
|
||||
|
||||
Assisted-by: Claude Code (claude-opus-4-6):default
|
||||
Signed-off-by: Adrian Reber <areber@redhat.com>
|
||||
---
|
||||
criu/fsnotify.c | 183 +++++++++++++++++++++++++++++++++++++++++++++++-
|
||||
1 file changed, 182 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/criu/fsnotify.c b/criu/fsnotify.c
|
||||
index eb80dd9615..498e48635d 100644
|
||||
--- a/criu/fsnotify.c
|
||||
+++ b/criu/fsnotify.c
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <string.h>
|
||||
#include <utime.h>
|
||||
#include <limits.h>
|
||||
+#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/inotify.h>
|
||||
@@ -108,6 +109,163 @@ enum {
|
||||
ERR_GENERIC = -3
|
||||
};
|
||||
|
||||
+#define OVL_WALK_MAX_DEPTH 64
|
||||
+#define OVL_WALK_WARN_THRESHOLD 10000
|
||||
+
|
||||
+/*
|
||||
+ * Recursively walk a directory tree looking for an inode matching
|
||||
+ * (s_dev, i_ino). Returns an allocated path string on success,
|
||||
+ * NULL when not found. The caller is responsible for freeing the
|
||||
+ * returned string via xfree().
|
||||
+ *
|
||||
+ * Note: this function takes ownership of @dirfd and closes it
|
||||
+ * (via fdopendir/closedir) whether it succeeds or fails.
|
||||
+ *
|
||||
+ * @visited is incremented for each inode examined and is used to
|
||||
+ * emit a one-time warning when the walk becomes expensive.
|
||||
+ */
|
||||
+static char *__walk_overlay_dir(int dirfd, const char *base,
|
||||
+ unsigned int s_dev,
|
||||
+ unsigned long i_ino, int depth,
|
||||
+ unsigned long *visited)
|
||||
+{
|
||||
+ DIR *dfd;
|
||||
+ struct dirent *de;
|
||||
+ char *found = NULL;
|
||||
+
|
||||
+ if (depth <= 0) {
|
||||
+ close(dirfd);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ dfd = fdopendir(dirfd);
|
||||
+ if (!dfd) {
|
||||
+ pr_perror("Can't fdopendir for overlay walk");
|
||||
+ close(dirfd);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ while (1) {
|
||||
+ struct stat st;
|
||||
+
|
||||
+ errno = 0;
|
||||
+ de = readdir(dfd);
|
||||
+ if (!de)
|
||||
+ break;
|
||||
+
|
||||
+ if (dir_dots(de))
|
||||
+ continue;
|
||||
+
|
||||
+ if (fstatat(dirfd, de->d_name, &st,
|
||||
+ AT_SYMLINK_NOFOLLOW) < 0) {
|
||||
+ pr_debug("overlay walk: fstatat(%s/%s) failed: %s\n",
|
||||
+ base, de->d_name, strerror(errno));
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ (*visited)++;
|
||||
+ if (*visited == OVL_WALK_WARN_THRESHOLD)
|
||||
+ pr_warn("overlay walk: examined %lu entries so far "
|
||||
+ "looking for ino %lx, mount may be large\n",
|
||||
+ *visited, i_ino);
|
||||
+
|
||||
+ if (MKKDEV(major(st.st_dev), minor(st.st_dev)) == s_dev &&
|
||||
+ st.st_ino == i_ino) {
|
||||
+ found = xsprintf("%s/%s", base, de->d_name);
|
||||
+ if (!found)
|
||||
+ pr_err("OOM building overlay path for ino %lx\n",
|
||||
+ i_ino);
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ if (S_ISDIR(st.st_mode)) {
|
||||
+ int subfd;
|
||||
+ char *subbase;
|
||||
+
|
||||
+ subfd = openat(dirfd, de->d_name,
|
||||
+ O_RDONLY | O_DIRECTORY | O_CLOEXEC);
|
||||
+ if (subfd < 0)
|
||||
+ continue;
|
||||
+
|
||||
+ subbase = xsprintf("%s/%s", base, de->d_name);
|
||||
+ if (!subbase) {
|
||||
+ close(subfd);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ found = __walk_overlay_dir(subfd, subbase,
|
||||
+ s_dev, i_ino,
|
||||
+ depth - 1,
|
||||
+ visited);
|
||||
+ xfree(subbase);
|
||||
+ if (found)
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (!de && errno)
|
||||
+ pr_perror("overlay walk: readdir failed on %s", base);
|
||||
+
|
||||
+ closedir(dfd);
|
||||
+ return found;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Try to locate a file on an overlay mount by walking the directory
|
||||
+ * tree and matching (s_dev, i_ino). Returns an allocated absolute
|
||||
+ * path (e.g. "/tmp/merged/subdir/file") on success, NULL on failure.
|
||||
+ */
|
||||
+static char *find_path_on_overlay(struct mount_info *m,
|
||||
+ unsigned int s_dev,
|
||||
+ unsigned long i_ino)
|
||||
+{
|
||||
+ int root_fd, mount_fd;
|
||||
+ unsigned long visited = 0;
|
||||
+ char *base, *found;
|
||||
+ struct stat st;
|
||||
+
|
||||
+ root_fd = mntns_get_root_fd(m->nsid);
|
||||
+ if (root_fd < 0)
|
||||
+ return NULL;
|
||||
+
|
||||
+ /*
|
||||
+ * ns_mountpoint has a leading dot, e.g. "./tmp/merged",
|
||||
+ * which makes it relative to mntns root for openat.
|
||||
+ */
|
||||
+ mount_fd = openat(root_fd, m->ns_mountpoint, O_RDONLY | O_DIRECTORY);
|
||||
+ if (mount_fd < 0) {
|
||||
+ pr_perror("Can't open overlay mountpoint %s",
|
||||
+ m->ns_mountpoint);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
+ * The path stored in f_handle->path must be absolute so
|
||||
+ * that get_mark_path() can strip the leading "/" and use
|
||||
+ * openat(mntns_root, path + 1, O_PATH) on restore.
|
||||
+ * ns_mountpoint + 1 gives us e.g. "/tmp/merged".
|
||||
+ */
|
||||
+ base = m->ns_mountpoint + 1;
|
||||
+
|
||||
+ /* Check if the mountpoint directory itself is the target */
|
||||
+ if (fstat(mount_fd, &st) == 0 &&
|
||||
+ MKKDEV(major(st.st_dev), minor(st.st_dev)) == s_dev &&
|
||||
+ st.st_ino == i_ino) {
|
||||
+ close(mount_fd);
|
||||
+ return xstrdup(base);
|
||||
+ }
|
||||
+
|
||||
+ found = __walk_overlay_dir(mount_fd, base, s_dev, i_ino,
|
||||
+ OVL_WALK_MAX_DEPTH, &visited);
|
||||
+ /* mount_fd is consumed by fdopendir inside __walk_overlay_dir */
|
||||
+
|
||||
+ if (found)
|
||||
+ pr_debug("overlay walk: found ino %lx after %lu entries\n",
|
||||
+ i_ino, visited);
|
||||
+
|
||||
+ return found;
|
||||
+}
|
||||
+
|
||||
static char *alloc_openable(unsigned int s_dev, unsigned long i_ino, FhEntry *f_handle)
|
||||
{
|
||||
struct mount_info *m;
|
||||
@@ -144,8 +302,31 @@ static char *alloc_openable(unsigned int s_dev, unsigned long i_ino, FhEntry *f_
|
||||
|
||||
fd = userns_call(open_by_handle, UNS_FDOUT, &handle, sizeof(handle), mntfd);
|
||||
close(mntfd);
|
||||
- if (fd < 0)
|
||||
+ if (fd < 0) {
|
||||
+ if (m->fstype->code == FSTYPE__OVERLAYFS) {
|
||||
+ char *ovl_path;
|
||||
+
|
||||
+ pr_debug("\t\tHandle open failed on overlay,"
|
||||
+ " trying dir walk for %lx\n",
|
||||
+ i_ino);
|
||||
+ ovl_path = find_path_on_overlay(m, s_dev,
|
||||
+ i_ino);
|
||||
+ if (ovl_path) {
|
||||
+ if (root_ns_mask & CLONE_NEWNS) {
|
||||
+ f_handle->has_mnt_id = true;
|
||||
+ f_handle->mnt_id = m->mnt_id;
|
||||
+ }
|
||||
+ return ovl_path;
|
||||
+ }
|
||||
+ /*
|
||||
+ * Mark as found so the caller gets
|
||||
+ * ERR_NO_PATH_IN_MOUNT and falls
|
||||
+ * through to irmap lookup.
|
||||
+ */
|
||||
+ suitable_mount_found = 1;
|
||||
+ }
|
||||
continue;
|
||||
+ }
|
||||
suitable_mount_found = 1;
|
||||
|
||||
if (read_fd_link(fd, buf, sizeof(buf)) < 0) {
|
||||
|
||||
From 53b8358a1f39f9f94844f2c147be472fa0b38da1 Mon Sep 17 00:00:00 2001
|
||||
From: Adrian Reber <areber@redhat.com>
|
||||
Date: Thu, 11 Jun 2026 15:41:19 +0000
|
||||
Subject: [PATCH 2/3] test/others/overlayfs: add inotify C/R test
|
||||
|
||||
Add a test that exercises checkpoint/restore of a process with an
|
||||
inotify watch on a file inside an overlayfs mount.
|
||||
|
||||
The test compiles inotify_test.c (a simple program that sets up an
|
||||
inotify watch and blocks reading events), starts it watching a file
|
||||
on an overlay mount, dumps and restores via criu, then verifies the
|
||||
restored process is still alive.
|
||||
|
||||
Run with: make -C test/others/overlayfs run-inotify
|
||||
|
||||
Assisted-by: Claude Code (claude-opus-4-6):default
|
||||
Signed-off-by: Adrian Reber <areber@redhat.com>
|
||||
---
|
||||
test/others/overlayfs/.gitignore | 1 +
|
||||
test/others/overlayfs/Makefile | 17 ++++--
|
||||
test/others/overlayfs/inotify_test.c | 69 ++++++++++++++++++++++++
|
||||
test/others/overlayfs/run-inotify.sh | 78 ++++++++++++++++++++++++++++
|
||||
4 files changed, 162 insertions(+), 3 deletions(-)
|
||||
create mode 100644 test/others/overlayfs/.gitignore
|
||||
create mode 100644 test/others/overlayfs/inotify_test.c
|
||||
create mode 100755 test/others/overlayfs/run-inotify.sh
|
||||
|
||||
diff --git a/test/others/overlayfs/.gitignore b/test/others/overlayfs/.gitignore
|
||||
new file mode 100644
|
||||
index 0000000000..ec554b8965
|
||||
--- /dev/null
|
||||
+++ b/test/others/overlayfs/.gitignore
|
||||
@@ -0,0 +1 @@
|
||||
+inotify_test
|
||||
diff --git a/test/others/overlayfs/Makefile b/test/others/overlayfs/Makefile
|
||||
index 78c246bd88..1ebf139f60 100644
|
||||
--- a/test/others/overlayfs/Makefile
|
||||
+++ b/test/others/overlayfs/Makefile
|
||||
@@ -1,6 +1,17 @@
|
||||
-run:
|
||||
+CC ?= cc
|
||||
+
|
||||
+inotify_test: inotify_test.c
|
||||
+ $(CC) -Wall -o $@ $<
|
||||
+
|
||||
+run: run-inotify
|
||||
./run.sh
|
||||
|
||||
+run-inotify: inotify_test
|
||||
+ ./run-inotify.sh
|
||||
+
|
||||
clean:
|
||||
- umount -f overlay_test/z
|
||||
- rm -rf overlay_test output checkpoint
|
||||
+ umount -f overlay_test/z 2>/dev/null || true
|
||||
+ umount -f ovl_inotify/merged 2>/dev/null || true
|
||||
+ rm -rf overlay_test ovl_inotify output checkpoint inotify_test
|
||||
+
|
||||
+.PHONY: run run-inotify clean
|
||||
diff --git a/test/others/overlayfs/inotify_test.c b/test/others/overlayfs/inotify_test.c
|
||||
new file mode 100644
|
||||
index 0000000000..00f1b7f99b
|
||||
--- /dev/null
|
||||
+++ b/test/others/overlayfs/inotify_test.c
|
||||
@@ -0,0 +1,69 @@
|
||||
+#include <stdio.h>
|
||||
+#include <stdlib.h>
|
||||
+#include <errno.h>
|
||||
+#include <unistd.h>
|
||||
+#include <sys/inotify.h>
|
||||
+
|
||||
+#define EVENT_SIZE (sizeof(struct inotify_event))
|
||||
+#define BUF_LEN (1024 * (EVENT_SIZE + 16))
|
||||
+
|
||||
+int main(int argc, char **argv)
|
||||
+{
|
||||
+ char buf[BUF_LEN];
|
||||
+ int fd, wd, len, i;
|
||||
+
|
||||
+ if (argc < 2) {
|
||||
+ fprintf(stderr, "Usage: %s <path>\n", argv[0]);
|
||||
+ return 1;
|
||||
+ }
|
||||
+
|
||||
+ fd = inotify_init();
|
||||
+ if (fd < 0) {
|
||||
+ perror("inotify_init");
|
||||
+ return 1;
|
||||
+ }
|
||||
+
|
||||
+ wd = inotify_add_watch(fd, argv[1],
|
||||
+ IN_MODIFY | IN_CREATE | IN_DELETE |
|
||||
+ IN_OPEN | IN_CLOSE);
|
||||
+ if (wd < 0) {
|
||||
+ perror("inotify_add_watch");
|
||||
+ close(fd);
|
||||
+ return 1;
|
||||
+ }
|
||||
+
|
||||
+ printf("Watching %s for events...\n", argv[1]);
|
||||
+
|
||||
+ while (1) {
|
||||
+ len = read(fd, buf, BUF_LEN);
|
||||
+ if (len < 0) {
|
||||
+ perror("read");
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ i = 0;
|
||||
+ while (i < len) {
|
||||
+ struct inotify_event *event;
|
||||
+
|
||||
+ event = (struct inotify_event *)&buf[i];
|
||||
+
|
||||
+ if (event->mask & IN_CREATE)
|
||||
+ printf("CREATE: %s\n", event->name);
|
||||
+ if (event->mask & IN_DELETE)
|
||||
+ printf("DELETE: %s\n", event->name);
|
||||
+ if (event->mask & IN_MODIFY)
|
||||
+ printf("MODIFY: %s\n", event->name);
|
||||
+ if (event->mask & IN_OPEN)
|
||||
+ printf("OPEN: %s\n", event->name);
|
||||
+ if (event->mask & IN_CLOSE)
|
||||
+ printf("CLOSE: %s\n", event->name);
|
||||
+
|
||||
+ i += EVENT_SIZE + event->len;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ inotify_rm_watch(fd, wd);
|
||||
+ close(fd);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
diff --git a/test/others/overlayfs/run-inotify.sh b/test/others/overlayfs/run-inotify.sh
|
||||
new file mode 100755
|
||||
index 0000000000..8711797221
|
||||
--- /dev/null
|
||||
+++ b/test/others/overlayfs/run-inotify.sh
|
||||
@@ -0,0 +1,78 @@
|
||||
+#!/bin/bash
|
||||
+
|
||||
+set -eu
|
||||
+
|
||||
+SCRIPT_DIR=$(dirname "$(readlink -f "$0")")
|
||||
+CRIU="${SCRIPT_DIR}/../../../criu/criu"
|
||||
+ORIG_WD=$(pwd)
|
||||
+PROC_PID=""
|
||||
+
|
||||
+cleanup() {
|
||||
+ if [ -n "${PROC_PID}" ]; then
|
||||
+ kill "${PROC_PID}" > /dev/null 2>&1 || true
|
||||
+ wait "${PROC_PID}" 2>/dev/null || true
|
||||
+ fi
|
||||
+ cd "${ORIG_WD}"
|
||||
+ umount ovl_inotify/merged 2>/dev/null || true
|
||||
+ rm -rf ovl_inotify
|
||||
+}
|
||||
+
|
||||
+trap cleanup EXIT
|
||||
+
|
||||
+setup() {
|
||||
+ mkdir -p ovl_inotify
|
||||
+ cd ovl_inotify
|
||||
+ mkdir -p lower upper work merged checkpoint
|
||||
+
|
||||
+ cp "${SCRIPT_DIR}/inotify_test" .
|
||||
+ touch lower/testfile
|
||||
+ mount -t overlay overlay \
|
||||
+ -o lowerdir=lower,upperdir=upper,workdir=work merged
|
||||
+
|
||||
+ setsid bash -c 'echo $$ > inotify.pid; exec ./inotify_test "$@"' \
|
||||
+ -- merged/testfile < /dev/null &> output &
|
||||
+ sleep 1
|
||||
+ PROC_PID=$(cat inotify.pid)
|
||||
+ echo "PROC_PID=$PROC_PID"
|
||||
+}
|
||||
+
|
||||
+check_criu() {
|
||||
+ echo "Dumping $PROC_PID..."
|
||||
+ if ! $CRIU dump -D checkpoint -t "${PROC_PID}" --shell-job -v4; then
|
||||
+ echo "ERROR! dump failed"
|
||||
+ return 1
|
||||
+ fi
|
||||
+
|
||||
+ echo "Restoring..."
|
||||
+ if ! $CRIU restore -d -D checkpoint --shell-job -v4; then
|
||||
+ echo "ERROR! restore failed"
|
||||
+ return 1
|
||||
+ fi
|
||||
+
|
||||
+ sleep 1
|
||||
+
|
||||
+ # Trigger an inotify event on the restored process
|
||||
+ touch merged/testfile
|
||||
+ sleep 1
|
||||
+
|
||||
+ # The restored process should still be alive
|
||||
+ if ! kill -0 "${PROC_PID}" 2>/dev/null; then
|
||||
+ echo "ERROR! restored process is not running"
|
||||
+ return 1
|
||||
+ fi
|
||||
+
|
||||
+ return 0
|
||||
+}
|
||||
+
|
||||
+main() {
|
||||
+ setup
|
||||
+
|
||||
+ if ! check_criu; then
|
||||
+ exit 1
|
||||
+ fi
|
||||
+
|
||||
+ echo "OverlayFS inotify C/R successful."
|
||||
+ exit 0
|
||||
+}
|
||||
+
|
||||
+main
|
||||
|
||||
From 082ca25d2280a632e0c19540a52d54460a23cff4 Mon Sep 17 00:00:00 2001
|
||||
From: Adrian Reber <areber@redhat.com>
|
||||
Date: Thu, 11 Jun 2026 15:50:54 +0000
|
||||
Subject: [PATCH 3/3] zdtm: add inotify_overlayfs test
|
||||
|
||||
Add a ZDTM test that verifies inotify watches on files inside an
|
||||
overlayfs mount survive checkpoint/restore.
|
||||
|
||||
The test uses the external mount pattern (like mnt_ext_auto): in
|
||||
the ZDTM_NEWNS=1 phase it creates an overlay mount before
|
||||
unshare(CLONE_NEWNS), so CRIU treats it as an external mount and
|
||||
does not attempt to reconstruct the overlay on restore. After
|
||||
restore the test opens the watched file and reads back the inotify
|
||||
event to confirm the watch is intact.
|
||||
|
||||
This is needed because the test/others/overlayfs test only checks
|
||||
that the process survives C/R. This ZDTM test validates that the
|
||||
inotify watch itself is properly dumped and restored by exercising
|
||||
the overlay directory walk fallback added in fsnotify.c.
|
||||
|
||||
The test is restricted to the 'ns' flavor because the overlay
|
||||
setup requires the ZDTM_NEWNS=1 lifecycle and root privileges
|
||||
for mounting overlayfs.
|
||||
|
||||
Assisted-by: Claude Code (claude-opus-4-6):default
|
||||
Signed-off-by: Adrian Reber <areber@redhat.com>
|
||||
---
|
||||
test/zdtm/static/Makefile | 1 +
|
||||
test/zdtm/static/inotify_overlayfs.c | 213 ++++++++++++++++++++++++
|
||||
test/zdtm/static/inotify_overlayfs.desc | 2 +
|
||||
test/zdtm/static/inotify_overlayfs.hook | 19 +++
|
||||
4 files changed, 235 insertions(+)
|
||||
create mode 100644 test/zdtm/static/inotify_overlayfs.c
|
||||
create mode 100644 test/zdtm/static/inotify_overlayfs.desc
|
||||
create mode 100755 test/zdtm/static/inotify_overlayfs.hook
|
||||
|
||||
diff --git a/test/zdtm/static/Makefile b/test/zdtm/static/Makefile
|
||||
index 0e2580ed6e..d53eec2e2d 100644
|
||||
--- a/test/zdtm/static/Makefile
|
||||
+++ b/test/zdtm/static/Makefile
|
||||
@@ -481,6 +481,7 @@ TST_DIR = \
|
||||
symlink \
|
||||
symlink01 \
|
||||
unbindable \
|
||||
+ inotify_overlayfs \
|
||||
|
||||
TST_DIR_FILE = \
|
||||
chroot \
|
||||
diff --git a/test/zdtm/static/inotify_overlayfs.c b/test/zdtm/static/inotify_overlayfs.c
|
||||
new file mode 100644
|
||||
index 0000000000..5044037a9e
|
||||
--- /dev/null
|
||||
+++ b/test/zdtm/static/inotify_overlayfs.c
|
||||
@@ -0,0 +1,213 @@
|
||||
+#include <sched.h>
|
||||
+#include <unistd.h>
|
||||
+#include <limits.h>
|
||||
+#include <ftw.h>
|
||||
+
|
||||
+#include <sys/types.h>
|
||||
+#include <sys/stat.h>
|
||||
+#include <sys/mount.h>
|
||||
+#include <fcntl.h>
|
||||
+#include <string.h>
|
||||
+#include <stdio.h>
|
||||
+#include <errno.h>
|
||||
+#include <stdlib.h>
|
||||
+#include <dirent.h>
|
||||
+#include <sys/inotify.h>
|
||||
+
|
||||
+#include "zdtmtst.h"
|
||||
+
|
||||
+const char *test_doc = "Check inotify C/R on overlayfs";
|
||||
+const char *test_author = "CRIU contributors";
|
||||
+
|
||||
+char *dirname = "inotify_overlayfs.test";
|
||||
+TEST_OPTION(dirname, string, "directory name", 1);
|
||||
+
|
||||
+#define BUFF_SIZE ((sizeof(struct inotify_event) + PATH_MAX))
|
||||
+#define TEST_FNAME "testfile"
|
||||
+
|
||||
+#define OVL_DIR_SUFFIX "zdtm_inotify_ovl.XXXXXX"
|
||||
+
|
||||
+static int rm_entry(const char *path, const struct stat *st,
|
||||
+ int flag, struct FTW *ftw)
|
||||
+{
|
||||
+ return remove(path);
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Clean up overlay backing dirs matching the zdtm_inotify_ovl prefix
|
||||
+ * in the given directory. Called at setup to remove leftovers from
|
||||
+ * previous runs. Post-test cleanup is handled by
|
||||
+ * inotify_overlayfs.hook (--clean).
|
||||
+ */
|
||||
+static void cleanup_stale_ovl_dirs(const char *parent)
|
||||
+{
|
||||
+ DIR *d;
|
||||
+ struct dirent *de;
|
||||
+ char path[PATH_MAX];
|
||||
+
|
||||
+ d = opendir(parent);
|
||||
+ if (!d)
|
||||
+ return;
|
||||
+
|
||||
+ while ((de = readdir(d)) != NULL) {
|
||||
+ if (strncmp(de->d_name, "zdtm_inotify_ovl.",
|
||||
+ strlen("zdtm_inotify_ovl.")) != 0)
|
||||
+ continue;
|
||||
+ snprintf(path, sizeof(path), "%s/%s", parent, de->d_name);
|
||||
+ nftw(path, rm_entry, 16, FTW_DEPTH | FTW_PHYS);
|
||||
+ }
|
||||
+ closedir(d);
|
||||
+}
|
||||
+
|
||||
+int main(int argc, char *argv[])
|
||||
+{
|
||||
+ char merged[PATH_MAX], watch_path[PATH_MAX];
|
||||
+ char buf[BUFF_SIZE];
|
||||
+ int inotify_fd, wd, fd, dir_fd;
|
||||
+ char *zdtm_newns = getenv("ZDTM_NEWNS");
|
||||
+ cleanup_free char *cwd = NULL;
|
||||
+
|
||||
+ cwd = get_current_dir_name();
|
||||
+ snprintf(merged, sizeof(merged), "%s/%s", cwd, dirname);
|
||||
+
|
||||
+ if (zdtm_newns && !strcmp(zdtm_newns, "1")) {
|
||||
+ char ovl_dir[PATH_MAX - 2];
|
||||
+ char ovl_lower[PATH_MAX], ovl_upper[PATH_MAX];
|
||||
+ char ovl_work[PATH_MAX];
|
||||
+ char ovl_mnt[] = "/tmp/zdtm_ino_mnt.XXXXXX";
|
||||
+ char opts[3 * PATH_MAX + 32];
|
||||
+
|
||||
+ /*
|
||||
+ * Phase 1: running before namespace creation.
|
||||
+ *
|
||||
+ * Overlay backing dirs (lower/upper/work) are placed
|
||||
+ * in cwd so they live on a real filesystem — tmpfs
|
||||
+ * does not support overlayfs upperdir on all kernels.
|
||||
+ *
|
||||
+ * The overlay is mounted at a temporary directory
|
||||
+ * under /tmp, which is outside ZDTM_ROOT and will
|
||||
+ * not be visible after pivot_root. After unshare
|
||||
+ * we bind-mount it into the test directory; the bind
|
||||
+ * creates a shared/master relationship that
|
||||
+ * --external mnt[]:s can detect.
|
||||
+ */
|
||||
+ cleanup_stale_ovl_dirs(cwd);
|
||||
+
|
||||
+ snprintf(ovl_dir, sizeof(ovl_dir), "%s/" OVL_DIR_SUFFIX, cwd);
|
||||
+ if (!mkdtemp(ovl_dir)) {
|
||||
+ pr_perror("Can't create overlay tmpdir");
|
||||
+ return 1;
|
||||
+ }
|
||||
+
|
||||
+ snprintf(ovl_lower, sizeof(ovl_lower), "%s/l", ovl_dir);
|
||||
+ snprintf(ovl_upper, sizeof(ovl_upper), "%s/u", ovl_dir);
|
||||
+ snprintf(ovl_work, sizeof(ovl_work), "%s/w", ovl_dir);
|
||||
+
|
||||
+ if (mkdir(ovl_lower, 0700) < 0) {
|
||||
+ pr_perror("Can't mkdir %s", ovl_lower);
|
||||
+ return 1;
|
||||
+ }
|
||||
+ if (mkdir(ovl_upper, 0700) < 0) {
|
||||
+ pr_perror("Can't mkdir %s", ovl_upper);
|
||||
+ return 1;
|
||||
+ }
|
||||
+ if (mkdir(ovl_work, 0700) < 0) {
|
||||
+ pr_perror("Can't mkdir %s", ovl_work);
|
||||
+ return 1;
|
||||
+ }
|
||||
+
|
||||
+ /* Create a test file in the lower layer */
|
||||
+ dir_fd = openat(AT_FDCWD, ovl_lower, O_RDONLY | O_DIRECTORY);
|
||||
+ if (dir_fd < 0) {
|
||||
+ pr_perror("Can't open lower dir %s", ovl_lower);
|
||||
+ return 1;
|
||||
+ }
|
||||
+ fd = openat(dir_fd, TEST_FNAME, O_CREAT | O_WRONLY, 0644);
|
||||
+ close(dir_fd);
|
||||
+ if (fd < 0) {
|
||||
+ pr_perror("Can't create test file in lower");
|
||||
+ return 1;
|
||||
+ }
|
||||
+ close(fd);
|
||||
+
|
||||
+ /*
|
||||
+ * Mount overlay at a path outside ZDTM_ROOT so it
|
||||
+ * does not appear in mountinfo after pivot_root.
|
||||
+ */
|
||||
+ if (!mkdtemp(ovl_mnt)) {
|
||||
+ pr_perror("Can't create overlay mount dir");
|
||||
+ return 1;
|
||||
+ }
|
||||
+ snprintf(opts, sizeof(opts),
|
||||
+ "lowerdir=%s,upperdir=%s,workdir=%s",
|
||||
+ ovl_lower, ovl_upper, ovl_work);
|
||||
+
|
||||
+ if (mount("overlay", ovl_mnt, "overlay", 0, opts) < 0) {
|
||||
+ pr_perror("Can't mount overlayfs at %s", ovl_mnt);
|
||||
+ return 1;
|
||||
+ }
|
||||
+
|
||||
+ if (unshare(CLONE_NEWNS)) {
|
||||
+ pr_perror("unshare");
|
||||
+ return 1;
|
||||
+ }
|
||||
+
|
||||
+ /* Bind-mount into test directory inside new mntns */
|
||||
+ if (mkdir(merged, 0700) < 0) {
|
||||
+ pr_perror("Can't mkdir %s", merged);
|
||||
+ return 1;
|
||||
+ }
|
||||
+ if (mount(ovl_mnt, merged, NULL, MS_BIND, NULL) < 0) {
|
||||
+ pr_perror("Can't bind-mount overlay to %s", merged);
|
||||
+ return 1;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
+ * test_init handles ZDTM_NEWNS internally:
|
||||
+ * "1" -> ns_create() re-execs with "2" (never returns)
|
||||
+ * "2" -> ns_init() forks, child continues
|
||||
+ * unset -> normal execution
|
||||
+ */
|
||||
+ test_init(argc, argv);
|
||||
+
|
||||
+ snprintf(watch_path, sizeof(watch_path),
|
||||
+ "%s/" TEST_FNAME, dirname);
|
||||
+
|
||||
+ inotify_fd = inotify_init1(IN_NONBLOCK);
|
||||
+ if (inotify_fd < 0) {
|
||||
+ pr_perror("inotify_init1 failed");
|
||||
+ return 1;
|
||||
+ }
|
||||
+
|
||||
+ wd = inotify_add_watch(inotify_fd, watch_path, IN_OPEN);
|
||||
+ if (wd < 0) {
|
||||
+ pr_perror("inotify_add_watch failed for %s", watch_path);
|
||||
+ close(inotify_fd);
|
||||
+ return 1;
|
||||
+ }
|
||||
+
|
||||
+ test_daemon();
|
||||
+ test_waitsig();
|
||||
+
|
||||
+ /* After restore, trigger an inotify event */
|
||||
+ fd = open(watch_path, O_RDONLY);
|
||||
+ if (fd < 0) {
|
||||
+ fail("Can't open %s after restore", watch_path);
|
||||
+ close(inotify_fd);
|
||||
+ return 1;
|
||||
+ }
|
||||
+ close(fd);
|
||||
+
|
||||
+ /* Check that we got the event */
|
||||
+ memset(buf, 0, sizeof(buf));
|
||||
+ if (read(inotify_fd, buf, sizeof(buf)) <= 0) {
|
||||
+ fail("No inotify events after restore");
|
||||
+ close(inotify_fd);
|
||||
+ return 1;
|
||||
+ }
|
||||
+
|
||||
+ close(inotify_fd);
|
||||
+ pass();
|
||||
+ return 0;
|
||||
+}
|
||||
diff --git a/test/zdtm/static/inotify_overlayfs.desc b/test/zdtm/static/inotify_overlayfs.desc
|
||||
new file mode 100644
|
||||
index 0000000000..f1c3a17143
|
||||
--- /dev/null
|
||||
+++ b/test/zdtm/static/inotify_overlayfs.desc
|
||||
@@ -0,0 +1,2 @@
|
||||
+{'flags': 'suid', 'flavor': 'ns', 'feature': 'mnt_id',
|
||||
+ 'opts': '--external mnt[]:s'}
|
||||
diff --git a/test/zdtm/static/inotify_overlayfs.hook b/test/zdtm/static/inotify_overlayfs.hook
|
||||
new file mode 100755
|
||||
index 0000000000..06d128755e
|
||||
--- /dev/null
|
||||
+++ b/test/zdtm/static/inotify_overlayfs.hook
|
||||
@@ -0,0 +1,19 @@
|
||||
+#!/bin/bash
|
||||
+
|
||||
+[ "$1" == "--clean" ] || exit 0
|
||||
+
|
||||
+# Remove overlay backing dirs left by the ZDTM_NEWNS=1 phase.
|
||||
+# The test creates them in its working directory, which is the
|
||||
+# same directory as this hook script.
|
||||
+test_dir=$(dirname "$(readlink -f "$0")")
|
||||
+for dir in "$test_dir"/zdtm_inotify_ovl.* ; do
|
||||
+ [ -d "$dir" ] && rm -rf "$dir"
|
||||
+done
|
||||
+
|
||||
+# Remove overlay mount point dirs from /tmp.
|
||||
+for dir in /tmp/zdtm_ino_mnt.* ; do
|
||||
+ umount "$dir" 2>/dev/null || true
|
||||
+ [ -d "$dir" ] && rmdir "$dir"
|
||||
+done
|
||||
+
|
||||
+exit 0
|
||||
18
criu.spec
18
criu.spec
@ -13,7 +13,7 @@
|
||||
|
||||
Name: criu
|
||||
Version: 4.2
|
||||
Release: 6%{?dist}.alma.1
|
||||
Release: 7%{?dist}.alma.1
|
||||
Summary: Tool for Checkpoint/Restore in User-space
|
||||
License: GPL-2.0-only AND LGPL-2.1-only AND MIT
|
||||
URL: http://criu.org/
|
||||
@ -34,6 +34,12 @@ Patch2: https://patch-diff.githubusercontent.com/raw/checkpoint-restore/criu/pul
|
||||
# RHEL-189245 RHEL-189246 RHEL-189248
|
||||
Patch3: https://patch-diff.githubusercontent.com/raw/checkpoint-restore/criu/pull/3025.patch
|
||||
|
||||
# RHEL-193276
|
||||
Patch4: https://patch-diff.githubusercontent.com/raw/checkpoint-restore/criu/pull/2876.patch
|
||||
Patch5: https://patch-diff.githubusercontent.com/raw/checkpoint-restore/criu/pull/2880.patch
|
||||
Patch6: https://patch-diff.githubusercontent.com/raw/checkpoint-restore/criu/pull/2881.patch
|
||||
Patch7: https://patch-diff.githubusercontent.com/raw/checkpoint-restore/criu/pull/3043.patch
|
||||
|
||||
# Add protobuf-c as a dependency.
|
||||
# We use this patch because the protobuf-c package name
|
||||
# in RPM and DEB is different.
|
||||
@ -118,6 +124,10 @@ This script can help to workaround the so called "PID mismatch" problem.
|
||||
%patch -P 1 -p1
|
||||
%patch -P 2 -p1
|
||||
%patch -P 3 -p1
|
||||
%patch -P 4 -p1
|
||||
%patch -P 5 -p1
|
||||
%patch -P 6 -p1
|
||||
%patch -P 7 -p1
|
||||
%patch -P 99 -p1
|
||||
|
||||
%build
|
||||
@ -193,9 +203,13 @@ rm -f $RPM_BUILD_ROOT%{_libdir}/libcriu.a
|
||||
%doc %{_mandir}/man1/criu-ns.1*
|
||||
|
||||
%changelog
|
||||
* Fri Jul 03 2026 Andrew Lukoshko <alukoshko@almalinux.org> - 4.2-6.alma.1
|
||||
* Fri Jul 17 2026 Andrew Lukoshko <alukoshko@almalinux.org> - 4.2-7.alma.1
|
||||
- Add riscv64 to ExclusiveArch
|
||||
|
||||
* Wed Jul 08 2026 Adrian Reber <areber@redhat.com> - 4.2-7
|
||||
- Apply patch to fix RHEL-193276
|
||||
- Resolves: RHEL-193276
|
||||
|
||||
* Thu Jul 02 2026 Adrian Reber <areber@redhat.com> - 4.2-6
|
||||
- Apply patch to fix RHEL-189245 RHEL-189246 RHEL-189248
|
||||
- Resolves: RHEL-189245
|
||||
|
||||
Loading…
Reference in New Issue
Block a user