Re-creating an open file descriptor during restoration is far more complex than simply calling open(path, flags). This article explores the numerous edge cases CRIU must handle to faithfully reconstruct the file state.
At its simplest, a file is defined by its path and access mode:
int fd = open(f->path, f->mode);
However, this is only the beginning of the process.
A standard open() call on a FIFO (named pipe) can hang indefinitely if there is no corresponding reader or writer on the other end. CRIU avoids this by first opening the FIFO with O_RDWR (to ensure at least one of each is present) and then using dup2 to establish the final descriptor with the correct original flags.
Linux allows files to be deleted while they are still open. These “invisible” files no longer have a path in the filesystem.
Directories cannot be hard-linked. If a directory was unlinked, CRIU must recreate it, open it, and then remove it. For files with multiple hard links that were all deleted, CRIU must ensure they all point back to the same physical inode upon restoration, requiring careful tracking of “temporary” paths and user-space reference counts.
The same path (e.g., /etc/passwd) might refer to entirely different files depending on the mount namespace or chroot environment of the process.
openat() targets the correct physical file, regardless of the restorer’s current root.Files can have an associated “owner” (a PID or PGID) that receives signals (like SIGIO or SIGPOLL) when I/O events occur.
fcntl call to satisfy kernel permission checks if the file owner differs from the restorer.pos) is restored using lseek.O_CREAT, O_EXCL, O_TRUNC) only make sense during the initial creation of a file. CRIU strips these before the restore-time open() to avoid accidentally creating or truncating existing files.O_PATH are handled as pure path references; they do not have positions, ownership, or data access.Once a file is successfully opened (at a temporary descriptor number assigned by the kernel), it must be moved to the exact numeric descriptor the application expects (e.g., FD 42). This is achieved via dup2(), but requires coordination when descriptors are shared across a process tree.