From 7e0aac2408d16197ec4fb25f3922e354fa2a5ac2 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Fri, 10 May 2019 16:41:33 +0000 Subject: [PATCH] fusefs: return ENOTCONN instead of EIO if the daemon dies suddenly If the daemon dies, return ENOTCONN for all operations that have already been sent to the daemon, as well as any new ones. Sponsored by: The FreeBSD Foundation --- sys/fs/fuse/fuse_ipc.c | 10 ++++++- tests/sys/fs/fusefs/statfs.cc | 53 +++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/sys/fs/fuse/fuse_ipc.c b/sys/fs/fuse/fuse_ipc.c index 2736572b4ec5..eee5cece597e 100644 --- a/sys/fs/fuse/fuse_ipc.c +++ b/sys/fs/fuse/fuse_ipc.c @@ -1028,7 +1028,15 @@ fdisp_wait_answ(struct fuse_dispatcher *fdip) } } - if (fdip->tick->tk_aw_errno) { + if (fdip->tick->tk_aw_errno == ENOTCONN) { + /* The daemon died while we were waiting for a response */ + err = ENOTCONN; + goto out; + } else if (fdip->tick->tk_aw_errno) { + /* + * There was some sort of communication error with the daemon + * that the client wouldn't understand. + */ SDT_PROBE2(fusefs, , ipc, fdisp_wait_answ_error, "IPC: explicit EIO-ing", fdip->tick->tk_aw_errno); err = EIO; diff --git a/tests/sys/fs/fusefs/statfs.cc b/tests/sys/fs/fusefs/statfs.cc index d3ec22bc2ade..d09d0de427a7 100644 --- a/tests/sys/fs/fusefs/statfs.cc +++ b/tests/sys/fs/fusefs/statfs.cc @@ -31,6 +31,7 @@ extern "C" { #include #include +#include } #include "mockfs.hh" @@ -76,6 +77,58 @@ TEST_F(Statfs, enotconn) EXPECT_EQ(0, strcmp(mp, statbuf.f_mntonname)); } +static void* statfs_th(void* arg) { + ssize_t r; + struct statfs *sb = (struct statfs*)arg; + + r = statfs("mountpoint", sb); + if (r >= 0) + return 0; + else + return (void*)(intptr_t)errno; +} + +/* + * Like the enotconn test, but in this case the daemon dies after we send the + * FUSE_STATFS operation but before we get a response. + */ +TEST_F(Statfs, enotconn_while_blocked) +{ + struct statfs statbuf; + void *thr0_value; + pthread_t th0; + char mp[PATH_MAX]; + sem_t sem; + + ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno); + + EXPECT_CALL(*m_mock, process( + ResultOf([](auto in) { + return (in->header.opcode == FUSE_STATFS); + }, Eq(true)), + _) + ).WillOnce(Invoke([&](auto in __unused, auto &out __unused) { + sem_post(&sem); + /* Just block until the daemon dies */ + })); + + ASSERT_NE(NULL, getcwd(mp, PATH_MAX)) << strerror(errno); + strlcat(mp, "/mountpoint", PATH_MAX); + ASSERT_EQ(0, pthread_create(&th0, NULL, statfs_th, (void*)&statbuf)) + << strerror(errno); + + ASSERT_EQ(0, sem_wait(&sem)) << strerror(errno); + m_mock->kill_daemon(); + + pthread_join(th0, &thr0_value); + ASSERT_EQ(0, (intptr_t)thr0_value); + + EXPECT_EQ(getuid(), statbuf.f_owner); + EXPECT_EQ(0, strcmp("fusefs", statbuf.f_fstypename)); + EXPECT_EQ(0, strcmp("/dev/fuse", statbuf.f_mntfromname)); + EXPECT_EQ(0, strcmp(mp, statbuf.f_mntonname)); +} + TEST_F(Statfs, ok) { struct statfs statbuf;