[PATCH 1/2] erofs-utils: mount: gracefully handle long-lived backend workers
Yifan Zhao
zhaoyifan28 at huawei.com
Fri Apr 3 17:42:29 AEDT 2026
Currently long-lived backend workers were created with a bare fork() and
kept running with the caller's process context. They still inherited
CLI-oriented session and stdio, making the background worker fragile.
Refine the worker setup used by the NBD and fanotify mount paths so
forked children behave like well-formed long-lived background processes.
Assisted-by: Codex:GPT-5.4
Signed-off-by: Yifan Zhao <zhaoyifan28 at huawei.com>
---
mount/main.c | 208 ++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 156 insertions(+), 52 deletions(-)
diff --git a/mount/main.c b/mount/main.c
index e09e585..45ac32e 100644
--- a/mount/main.c
+++ b/mount/main.c
@@ -98,6 +98,75 @@ static struct erofsmount_source {
};
} mountsrc;
+static int erofsmount_spawn_worker(pid_t *pid)
+{
+ *pid = fork();
+ if (*pid < 0)
+ return -errno;
+ return *pid;
+}
+
+static int erofsmount_worker_set_signal(int signum, sighandler_t handler)
+{
+ struct sigaction act = {
+ .sa_handler = handler,
+ };
+
+ if (sigemptyset(&act.sa_mask) < 0)
+ return -errno;
+ if (sigaction(signum, &act, NULL) < 0)
+ return -errno;
+ return 0;
+}
+
+static int erofsmount_worker_detach(void)
+{
+ sigset_t mask;
+ int fd, err;
+
+ if (setsid() < 0)
+ return -errno;
+
+ if (sigemptyset(&mask) < 0)
+ return -errno;
+ if (sigprocmask(SIG_SETMASK, &mask, NULL) < 0)
+ return -errno;
+
+ err = erofsmount_worker_set_signal(SIGHUP, SIG_DFL);
+ if (err)
+ return err;
+ err = erofsmount_worker_set_signal(SIGINT, SIG_DFL);
+ if (err)
+ return err;
+ err = erofsmount_worker_set_signal(SIGQUIT, SIG_DFL);
+ if (err)
+ return err;
+ err = erofsmount_worker_set_signal(SIGTERM, SIG_DFL);
+ if (err)
+ return err;
+ err = erofsmount_worker_set_signal(SIGPIPE, SIG_IGN);
+ if (err)
+ return err;
+
+ fd = open("/dev/null", O_RDWR);
+ if (fd < 0)
+ return -errno;
+ if (dup2(fd, STDIN_FILENO) < 0 || dup2(fd, STDOUT_FILENO) < 0 ||
+ dup2(fd, STDERR_FILENO) < 0) {
+ err = -errno;
+ close(fd);
+ return err;
+ }
+ if (fd > STDERR_FILENO)
+ close(fd);
+ return 0;
+}
+
+static void erofsmount_worker_exit(int err)
+{
+ _exit(err ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
static void usage(int argc, char **argv)
{
printf("Usage: %s [OPTIONS] SOURCE [MOUNTPOINT]\n"
@@ -720,6 +789,13 @@ static int erofsmount_startnbd(int nbdfd, struct erofsmount_source *source)
}
ctx.sk.fd = err;
+ err = erofsmount_worker_detach();
+ if (err) {
+ erofs_io_close(&ctx.vd);
+ erofs_io_close(&ctx.sk);
+ goto out_closefd;
+ }
+
err = -pthread_create(&th, NULL, erofsmount_nbd_loopfn, &ctx);
if (err) {
erofs_io_close(&ctx.vd);
@@ -1050,67 +1126,79 @@ static int erofsmount_nbd_fix_backend_linkage(int num, char **recp)
static int erofsmount_startnbd_nl(pid_t *pid, struct erofsmount_source *source)
{
int pipefd[2], err, num;
+ pid_t ret;
err = pipe(pipefd);
if (err < 0)
return -errno;
- if ((*pid = fork()) == 0) {
+ ret = erofsmount_spawn_worker(pid);
+ if (ret < 0) {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ return ret;
+ }
+ if (ret == 0) {
struct erofsmount_nbd_ctx ctx = {};
char *recp;
- /* Otherwise, NBD disconnect sends SIGPIPE, skipping cleanup */
- if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
- exit(EXIT_FAILURE);
-
if (source->type == EROFSMOUNT_SOURCE_OCI) {
if (source->ocicfg.tarindex_path || source->ocicfg.zinfo_path) {
err = erofsmount_tarindex_open(&ctx.vd, &source->ocicfg,
source->ocicfg.tarindex_path,
source->ocicfg.zinfo_path);
if (err)
- exit(EXIT_FAILURE);
+ erofsmount_worker_exit(err);
} else {
err = ocierofs_io_open(&ctx.vd, &source->ocicfg);
if (err)
- exit(EXIT_FAILURE);
+ erofsmount_worker_exit(err);
}
} else {
err = open(source->device_path, O_RDONLY);
if (err < 0)
- exit(EXIT_FAILURE);
+ erofsmount_worker_exit(-errno);
ctx.vd.fd = err;
}
recp = erofsmount_write_recovery_info(source);
if (IS_ERR(recp)) {
erofs_io_close(&ctx.vd);
- exit(EXIT_FAILURE);
+ erofsmount_worker_exit(PTR_ERR(recp));
}
num = -1;
err = erofs_nbd_nl_connect(&num, 9, EROFSMOUNT_NBD_DISK_SIZE, recp);
- if (err >= 0) {
- ctx.sk.fd = err;
- err = erofsmount_nbd_fix_backend_linkage(num, &recp);
- if (err) {
- erofs_io_close(&ctx.sk);
- } else {
- err = write(pipefd[1], &num, sizeof(int));
- if (err < 0)
- err = -errno;
- close(pipefd[1]);
- close(pipefd[0]);
- if (err >= sizeof(int)) {
- err = (int)(uintptr_t)erofsmount_nbd_loopfn(&ctx);
- goto out_fork;
- }
- }
+ if (err < 0)
+ goto err_vd;
+
+ ctx.sk.fd = err;
+ err = erofsmount_nbd_fix_backend_linkage(num, &recp);
+ if (err)
+ goto err_sk;
+
+ err = erofsmount_worker_detach();
+ if (err)
+ goto err_sk;
+
+ err = write(pipefd[1], &num, sizeof(int));
+ if (err < 0)
+ err = -errno;
+ close(pipefd[1]);
+ close(pipefd[0]);
+ if (err >= (int)sizeof(int)) {
+ err = (int)(uintptr_t)erofsmount_nbd_loopfn(&ctx);
+ goto out_fork;
}
+ goto err_sk;
+
+err_sk:
+ erofs_io_close(&ctx.sk);
+err_vd:
erofs_io_close(&ctx.vd);
out_fork:
(void)unlink(recp);
free(recp);
- exit(err ? EXIT_FAILURE : EXIT_SUCCESS);
+ erofsmount_worker_exit(err);
}
close(pipefd[1]);
err = read(pipefd[0], &num, sizeof(int));
@@ -1122,11 +1210,12 @@ out_fork:
static int erofsmount_reattach(const char *target)
{
- char *identifier, *line, *source, *recp = NULL;
+ char *identifier, *line = NULL, *source, *recp = NULL;
struct erofsmount_nbd_ctx ctx = {};
+ pid_t pid;
int nbdnum, err;
struct stat st;
- size_t n;
+ size_t n = 0;
FILE *f;
err = lstat(target, &st);
@@ -1154,18 +1243,16 @@ static int erofsmount_reattach(const char *target)
}
f = fopen(identifier ?: recp, "r");
+ free(recp);
+
if (!f) {
err = -errno;
- free(recp);
goto err_identifier;
}
- free(recp);
-
- line = NULL;
if ((err = getline(&line, &n, f)) <= 0) {
err = -errno;
fclose(f);
- goto err_identifier;
+ goto err_line;
}
fclose(f);
if (err && line[err - 1] == '\n')
@@ -1202,18 +1289,27 @@ static int erofsmount_reattach(const char *target)
}
err = erofs_nbd_nl_reconnect(nbdnum, identifier);
- if (err >= 0) {
- ctx.sk.fd = err;
- if (fork() == 0) {
- free(line);
- free(identifier);
- if ((uintptr_t)erofsmount_nbd_loopfn(&ctx))
- return EXIT_FAILURE;
- return EXIT_SUCCESS;
- }
+ if (err < 0)
+ goto err_vd;
+ ctx.sk.fd = err;
+
+ err = erofsmount_spawn_worker(&pid);
+ if (err < 0) {
erofs_io_close(&ctx.sk);
- err = 0;
+ goto err_vd;
+ }
+ if (err == 0) {
+ free(line);
+ free(identifier);
+ err = erofsmount_worker_detach();
+ if (!err)
+ err = (int)(uintptr_t)erofsmount_nbd_loopfn(&ctx);
+ erofsmount_worker_exit(err);
}
+ erofs_io_close(&ctx.sk);
+
+ err = 0;
+err_vd:
erofs_io_close(&ctx.vd);
err_line:
free(line);
@@ -1251,9 +1347,15 @@ static int erofsmount_nbd(struct erofsmount_source *source,
if (nbdfd < 0)
return -errno;
- if ((pid = fork()) == 0)
- return erofsmount_startnbd(nbdfd, source) ?
- EXIT_FAILURE : EXIT_SUCCESS;
+ err = erofsmount_spawn_worker(&pid);
+ if (err < 0) {
+ close(nbdfd);
+ return err;
+ }
+ if (err == 0) {
+ err = erofsmount_startnbd(nbdfd, source);
+ erofsmount_worker_exit(err);
+ }
} else {
num = err;
(void)snprintf(nbdpath, sizeof(nbdpath), "/dev/nbd%d", num);
@@ -1661,6 +1763,10 @@ static int erofsmount_fanotify_child(struct erofs_fanotify_ctx *ctx,
if (err)
goto notify;
+ err = erofsmount_worker_detach();
+ if (err)
+ goto notify;
+
err = 0;
notify:
write(pipefd, &err, sizeof(err));
@@ -1730,19 +1836,17 @@ static int erofsmount_fanotify(struct erofsmount_source *source,
goto out;
}
- pid = fork();
- if (pid < 0) {
- err = -errno;
+ err = erofsmount_spawn_worker(&pid);
+ if (err < 0) {
close(pipefd[0]);
close(pipefd[1]);
goto out;
}
-
- if (pid == 0) {
+ if (err == 0) {
close(pipefd[0]);
err = erofsmount_fanotify_child(&ctx, pipefd[1]);
erofsmount_fanotify_ctx_cleanup(&ctx);
- exit(err ? EXIT_FAILURE : EXIT_SUCCESS);
+ erofsmount_worker_exit(err);
}
/* Wait for child to report fanotify initialization result */
--
2.47.3
More information about the Linux-erofs
mailing list