From 850fe769aab961cf25e420edc39ec3935f136a67 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 8 Jul 2026 14:31:58 +0000 Subject: [PATCH] Apply patch to fix RHEL-193276 Resolves: RHEL-193276 Signed-off-by: Adrian Reber --- 2876.patch | 47 ++++ 2880.patch | 349 ++++++++++++++++++++++++ 2881.patch | 103 +++++++ 3043.patch | 778 +++++++++++++++++++++++++++++++++++++++++++++++++++++ criu.spec | 16 +- 5 files changed, 1292 insertions(+), 1 deletion(-) create mode 100644 2876.patch create mode 100644 2880.patch create mode 100644 2881.patch create mode 100644 3043.patch diff --git a/2876.patch b/2876.patch new file mode 100644 index 0000000..a50c83d --- /dev/null +++ b/2876.patch @@ -0,0 +1,47 @@ +From 89150bea7218b9c7131b7a7712a84b9c564d8feb Mon Sep 17 00:00:00 2001 +From: 3idey +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 +--- + 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); + } diff --git a/2880.patch b/2880.patch new file mode 100644 index 0000000..ef0b4aa --- /dev/null +++ b/2880.patch @@ -0,0 +1,349 @@ +From 9502782b84efbabfd603e66b665d95663a01086e Mon Sep 17 00:00:00 2001 +From: 3idey +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 +--- + 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; + } + diff --git a/2881.patch b/2881.patch new file mode 100644 index 0000000..f3da997 --- /dev/null +++ b/2881.patch @@ -0,0 +1,103 @@ +From 33ebc40be98ed7a7132c2daff155c582dc74b5ee Mon Sep 17 00:00:00 2001 +From: 3idey +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 +--- + 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 +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 +--- + 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 = { diff --git a/3043.patch b/3043.patch new file mode 100644 index 0000000..17906e7 --- /dev/null +++ b/3043.patch @@ -0,0 +1,778 @@ +From 143b1978b47c519c3754bd4139798bc177cddd97 Mon Sep 17 00:00:00 2001 +From: Adrian Reber +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 +--- + 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 + #include + #include ++#include + #include + #include + #include +@@ -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 +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 +--- + 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 ++#include ++#include ++#include ++#include ++ ++#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 \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 +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 +--- + 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 ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 diff --git a/criu.spec b/criu.spec index 3cdea73..f9dcc4e 100644 --- a/criu.spec +++ b/criu.spec @@ -13,7 +13,7 @@ Name: criu Version: 4.2 -Release: 6%{?dist} +Release: 7%{?dist} 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,6 +203,10 @@ rm -f $RPM_BUILD_ROOT%{_libdir}/libcriu.a %doc %{_mandir}/man1/criu-ns.1* %changelog +* Wed Jul 08 2026 Adrian Reber - 4.2-7 +- Apply patch to fix RHEL-193276 +- Resolves: RHEL-193276 + * Thu Jul 02 2026 Adrian Reber - 4.2-6 - Apply patch to fix RHEL-189245 RHEL-189246 RHEL-189248 - Resolves: RHEL-189245