Re: [PATCH v2 2/2] erofs-utils: mount: add fanotify pre-content OCI backend

Gao Xiang <[email protected]>
Newsgroups org.ozlabs.lists.linux-erofs
Message-ID <[email protected]>
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 <[email protected]>
> ---
>   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
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.