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 >