[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