[PATCH v2 2/2] erofs-utils: mount: add fanotify pre-content OCI backend
Gao Xiang
hsiangkao at linux.alibaba.com
Wed Apr 1 01:45:15 AEDT 2026
Hi Yifan,
On 2026/3/31 21:14, Yifan Zhao wrote:
> Add a fanotify-backed mount mode for OCI sources that uses
> FAN_PRE_ACCESS permission events to populate a local sparse file
> on demand before the kernel consumes the requested data.
>
> The new erofs.fanotify subtype resolves a single OCI blob,
> creates a sparse cache file, and runs a fanotify event loop
> that fetches missing ranges before allowing access to proceed.
>
> A pid file recording the canonical mountpoint and sparse-file
> source is written for unmount to track the corresponding worker.
>
> [ Developed with assistance from GPT-5.4 ]
I will apply this version, but some comments:
It should be marked as:
Assisted-by: AGENT_NAME:GPT-5.4
for example.
> Signed-off-by: Yifan Zhao <zhaoyifan28 at huawei.com>
> ---
> configure.ac | 28 +++
> lib/Makefile.am | 7 +
> lib/backends/fanotify.c | 283 ++++++++++++++++++++++++
> lib/liberofs_fanotify.h | 59 +++++
> lib/liberofs_oci.h | 3 +
> lib/remotes/oci.c | 10 +-
> mount/main.c | 476 +++++++++++++++++++++++++++++++++++++++-
> 7 files changed, 860 insertions(+), 6 deletions(-)
> create mode 100644 lib/backends/fanotify.c
> create mode 100644 lib/liberofs_fanotify.h
>
...
> +
> +static bool erofs_fanotify_range_in_sparse(int fd, u64 offset, size_t length)
> +{
> + off_t data_start, hole_start;
> +
> + data_start = lseek(fd, offset, SEEK_DATA);
> + if (data_start < 0)
> + return false;
> + if ((u64)data_start != offset)
> + return false;
> +
> + hole_start = lseek(fd, offset, SEEK_HOLE);
> + if (hole_start < 0)
> + return false;
> + if ((u64)hole_start < offset + length)
> + return false;
Here I really hope we could switch to bitmaps
instead of relying on holes in the following commits.
> +
> + return true;
> +}
...
> +
> +static int erofsmount_write_fanotify_state(const char *state_path, pid_t pid,
> + const char *mountpoint,
> + const char *source)
> +{
> + struct erofsmount_fanotify_state state;
> + char *tmp_path = NULL;
> + FILE *f = NULL;
> + int fd = -1, err;
> +
> + if (mkdir(EROFSMOUNT_RUNTIME_DIR, 0700) < 0 && errno != EEXIST)
> + return -errno;
> + if (mkdir(EROFSMOUNT_FANOTIFY_STATE_DIR, 0700) < 0 &&
> + errno != EEXIST)
> + return -errno;
> +
> + state.pid = pid;
> + state.mountpoint = (char *)mountpoint;
> + state.source = (char *)source;
> +
> + if (asprintf(&tmp_path, "%s.tmpXXXXXX", state_path) < 0)
> + return -ENOMEM;
> +
> + fd = mkstemp(tmp_path);
> + if (fd < 0) {
> + err = -errno;
> + goto out;
> + }
> +
> + f = fdopen(fd, "w");
> + if (!f) {
> + err = -errno;
> + goto out;
> + }
> + fd = -1;
> +
> + if (fprintf(f, "%d\n%s\n%s\n", state.pid, state.mountpoint,
> + state.source) < 0 || fflush(f) == EOF) {
Here, I do think you could identify the mountpoint
using mnt_id (e.g. you could use `mnt_id` as
filename), see statx(2):
https://man7.org/linux/man-pages/man2/statx.2.html
STATX_MNT_ID.
unique mnt_id seems an overkill since we will delete
such files when umounting.
> + err = errno ? -errno : -EIO;
> + goto out;
...
> +
> +static int erofsmount_read_fanotify_state(const char *state_path,
> + struct erofsmount_fanotify_state *state)
> +{
> + FILE *f;
> + size_t n = 0;
> + int err = 0;
> +
> + memset(state, 0, sizeof(*state));
> +
> + f = fopen(state_path, "r");
> + if (!f)
> + return -errno;
> +
> + if (fscanf(f, "%d", &state->pid) != 1)
> + err = -EINVAL;
> + else if (fgetc(f) != '\n')
> + err = -EINVAL;
> + else if (getline(&state->mountpoint, &n, f) < 0)
> + err = feof(f) ? -EINVAL : -errno;
> + else if (getline(&state->source, &n, f) < 0)
> + err = feof(f) ? -EINVAL : -errno;
> + fclose(f);
> + if (err) {
> + erofsmount_free_fanotify_state(state);
> + return err;
> + }
> +
> + state->mountpoint[strcspn(state->mountpoint, "\n")] = '\0';
> + state->source[strcspn(state->source, "\n")] = '\0';
> + return err;
> +}
> +
> +static int erofsmount_cleanup_fanotify_worker(const char *mountpoint,
> + const char *source)
> +{
> + DIR *dir;
> + struct dirent *de;
> + int err = 0;
> +
> + dir = opendir(EROFSMOUNT_FANOTIFY_STATE_DIR);
> + if (!dir) {
> + if (errno == ENOENT)
> + return 0;
> + return -errno;
> + }
> +
> + while ((de = readdir(dir)) != NULL) {
> + struct erofsmount_fanotify_state state;
> + char *state_path;
> +
> + if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
> + continue;
> + if (!strstr(de->d_name, ".state"))
> + continue;
> + if (asprintf(&state_path, "%s/%s", EROFSMOUNT_FANOTIFY_STATE_DIR,
> + de->d_name) < 0) {
> + err = -ENOMEM;
> + goto out;
> + }
> +
> + err = erofsmount_read_fanotify_state(state_path, &state);
same here, so that you don't need readdir() anymore, just
use mnt_id for indexing.
Thanks,
Gao Xiang
>
More information about the Linux-erofs
mailing list