summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Somers <asomers@gmail.com>2019-05-15 14:35:57 -0600
committerNikolaus Rath <Nikolaus@rath.org>2019-05-15 21:35:57 +0100
commit1f842c996e46788115d6b5ca142fad949712c8e9 (patch)
tree2a070544cd76fad641913c2b9a489712fc99e012
parent7a5e1a9a9a61416c759ce02a48e600814fd13711 (diff)
downloadfuse-1f842c996e46788115d6b5ca142fad949712c8e9.tar.gz
passthrough: fix unix-domain sockets on FreeBSD (#413)
FreeBSD doesn't allow creating sockets using mknod(2). Instead, one has to use socket(2) and bind(2). Add appropriate logic to the examples and add a test case.
-rw-r--r--example/passthrough.c17
-rw-r--r--example/passthrough_helpers.h76
-rw-r--r--example/passthrough_ll.c13
-rwxr-xr-xtest/test_examples.py31
-rw-r--r--test/test_syscalls.c53
5 files changed, 163 insertions, 27 deletions
diff --git a/example/passthrough.c b/example/passthrough.c
index da91930..6de9fc1 100644
--- a/example/passthrough.c
+++ b/example/passthrough.c
@@ -44,11 +44,17 @@
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
+#ifdef __FreeBSD__
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif
#include <sys/time.h>
#ifdef HAVE_SETXATTR
#include <sys/xattr.h>
#endif
+#include "passthrough_helpers.h"
+
static void *xmp_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{
@@ -138,16 +144,7 @@ static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
{
int res;
- /* On Linux this could just be 'mknod(path, mode, rdev)' but this
- is more portable */
- if (S_ISREG(mode)) {
- res = open(path, O_CREAT | O_EXCL | O_WRONLY, mode);
- if (res >= 0)
- res = close(res);
- } else if (S_ISFIFO(mode))
- res = mkfifo(path, mode);
- else
- res = mknod(path, mode, rdev);
+ res = mknod_wrapper(AT_FDCWD, path, NULL, mode, rdev);
if (res == -1)
return -errno;
diff --git a/example/passthrough_helpers.h b/example/passthrough_helpers.h
new file mode 100644
index 0000000..6b77c33
--- /dev/null
+++ b/example/passthrough_helpers.h
@@ -0,0 +1,76 @@
+/*
+ * FUSE: Filesystem in Userspace
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE
+ */
+
+/*
+ * Creates files on the underlying file system in response to a FUSE_MKNOD
+ * operation
+ */
+static int mknod_wrapper(int dirfd, const char *path, const char *link,
+ int mode, dev_t rdev)
+{
+ int res;
+
+ if (S_ISREG(mode)) {
+ res = openat(dirfd, path, O_CREAT | O_EXCL | O_WRONLY, mode);
+ if (res >= 0)
+ res = close(res);
+ } else if (S_ISDIR(mode)) {
+ res = mkdirat(dirfd, path, mode);
+ } else if (S_ISLNK(mode) && link != NULL) {
+ res = symlinkat(link, dirfd, path);
+ } else if (S_ISFIFO(mode)) {
+ res = mkfifoat(dirfd, path, mode);
+#ifdef __FreeBSD__
+ } else if (S_ISSOCK(mode)) {
+ struct sockaddr_un su;
+ int fd;
+
+ if (strlen(path) >= sizeof(su.sun_path)) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd >= 0) {
+ /*
+ * We must bind the socket to the underlying file
+ * system to create the socket file, even though
+ * we'll never listen on this socket.
+ */
+ su.sun_family = AF_UNIX;
+ strncpy(su.sun_path, path, sizeof(su.sun_path));
+ res = bindat(dirfd, fd, (struct sockaddr*)&su,
+ sizeof(su));
+ if (res == 0)
+ close(fd);
+ } else {
+ res = -1;
+ }
+#endif
+ } else {
+ res = mknodat(dirfd, path, mode, rdev);
+ }
+
+ return res;
+}
diff --git a/example/passthrough_ll.c b/example/passthrough_ll.c
index b6ecaff..f0bc727 100644
--- a/example/passthrough_ll.c
+++ b/example/passthrough_ll.c
@@ -56,6 +56,8 @@
#include <sys/file.h>
#include <sys/xattr.h>
+#include "passthrough_helpers.h"
+
/* We are re-using pointers to our `struct lo_inode` and `struct
lo_dirp` elements as inodes. This means that we must be able to
store uintptr_t values in a fuse_ino_t variable. The following
@@ -381,7 +383,6 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
const char *name, mode_t mode, dev_t rdev,
const char *link)
{
- int newfd = -1;
int res;
int saverr;
struct lo_inode *dir = lo_inode(req, parent);
@@ -389,12 +390,8 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
saverr = ENOMEM;
- if (S_ISDIR(mode))
- res = mkdirat(dir->fd, name, mode);
- else if (S_ISLNK(mode))
- res = symlinkat(link, dir->fd, name);
- else
- res = mknodat(dir->fd, name, mode, rdev);
+ res = mknod_wrapper(dir->fd, name, link, mode, rdev);
+
saverr = errno;
if (res == -1)
goto out;
@@ -411,8 +408,6 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
return;
out:
- if (newfd != -1)
- close(newfd);
fuse_reply_err(req, saverr);
}
diff --git a/test/test_examples.py b/test/test_examples.py
index 3aabd19..d0da69d 100755
--- a/test/test_examples.py
+++ b/test/test_examples.py
@@ -8,10 +8,12 @@ if __name__ == '__main__':
import subprocess
import os
import sys
+import py
import pytest
import stat
import shutil
import filecmp
+import tempfile
import time
import errno
import sys
@@ -56,6 +58,20 @@ def invoke_mount_fuse_drop_privileges(mnt_dir, name, options):
return invoke_mount_fuse(mnt_dir, name, options + ('drop_privileges',))
+class raii_tmpdir:
+ def __init__(self):
+ self.d = tempfile.mkdtemp()
+
+ def __str__(self):
+ return str(self.d)
+
+ def mkdir(self, path):
+ return py.path.local(str(self.d)).mkdir(path)
+
+@pytest.fixture
+def short_tmpdir():
+ return raii_tmpdir()
+
@pytest.mark.parametrize("cmdline_builder", (invoke_directly, invoke_mount_fuse,
invoke_mount_fuse_drop_privileges))
@pytest.mark.parametrize("options", powerset(options))
@@ -84,8 +100,7 @@ def test_hello(tmpdir, name, options, cmdline_builder):
@pytest.mark.parametrize("writeback", (False, True))
@pytest.mark.parametrize("name", ('passthrough', 'passthrough_fh', 'passthrough_ll'))
@pytest.mark.parametrize("debug", (False, True))
-def test_passthrough(tmpdir, name, debug, capfd, writeback):
-
+def test_passthrough(short_tmpdir, name, debug, capfd, writeback):
# Avoid false positives from libfuse debug messages
if debug:
capfd.register_output(r'^ unique: [0-9]+, error: -[0-9]+ .+$',
@@ -95,8 +110,8 @@ def test_passthrough(tmpdir, name, debug, capfd, writeback):
capfd.register_output(r"^ \d\d \[[^\]]+ message: 'No error: 0'\]",
count=0)
- mnt_dir = str(tmpdir.mkdir('mnt'))
- src_dir = str(tmpdir.mkdir('src'))
+ mnt_dir = str(short_tmpdir.mkdir('mnt'))
+ src_dir = str(short_tmpdir.mkdir('src'))
cmdline = base_cmdline + \
[ pjoin(basename, 'example', name),
@@ -145,7 +160,7 @@ def test_passthrough(tmpdir, name, debug, capfd, writeback):
# When writeback caching is enabled, kernel has to open files for
# reading even when userspace opens with O_WDONLY. This fails if the
# filesystem process doesn't have special permission.
- syscall_test_cmd.append('-52')
+ syscall_test_cmd.append('-53')
subprocess.check_call(syscall_test_cmd)
except:
cleanup(mount_process, mnt_dir)
@@ -154,9 +169,9 @@ def test_passthrough(tmpdir, name, debug, capfd, writeback):
umount(mount_process, mnt_dir)
@pytest.mark.parametrize("cache", (False, True))
-def test_passthrough_hp(tmpdir, cache):
- mnt_dir = str(tmpdir.mkdir('mnt'))
- src_dir = str(tmpdir.mkdir('src'))
+def test_passthrough_hp(short_tmpdir, cache):
+ mnt_dir = str(short_tmpdir.mkdir('mnt'))
+ src_dir = str(short_tmpdir.mkdir('src'))
cmdline = base_cmdline + \
[ pjoin(basename, 'example', 'passthrough_hp'),
diff --git a/test/test_syscalls.c b/test/test_syscalls.c
index a7e2bc7..1d776fd 100644
--- a/test/test_syscalls.c
+++ b/test/test_syscalls.c
@@ -11,8 +11,10 @@
#include <utime.h>
#include <errno.h>
#include <assert.h>
+#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/un.h>
#ifndef ALLPERMS
# define ALLPERMS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)/* 07777 */
@@ -23,6 +25,7 @@ static char testfile[1024];
static char testfile2[1024];
static char testdir[1024];
static char testdir2[1024];
+static char testsock[1024];
static char subfile[1280];
static char testfile_r[1024];
@@ -1734,6 +1737,53 @@ static int test_mkdir(void)
return 0;
}
+static int test_socket(void)
+{
+ struct sockaddr_un su;
+ int fd;
+ int res;
+ int err = 0;
+
+ start_test("socket");
+ if (strlen(testsock) + 1 > sizeof(su.sun_path)) {
+ fprintf(stderr, "Need to shorten mount point by %lu chars\n",
+ strlen(testsock) + 1 - sizeof(su.sun_path));
+ return -1;
+ }
+ unlink(testsock);
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ PERROR("socket");
+ return -1;
+ }
+ su.sun_family = AF_UNIX;
+ strncpy(su.sun_path, testsock, sizeof(su.sun_path));
+ res = bind(fd, (struct sockaddr*)&su, sizeof(su));
+ if (res == -1) {
+ PERROR("bind");
+ return -1;
+ }
+
+ res = check_type(testsock, S_IFSOCK);
+ if (res == -1)
+ return -1;
+ err += check_nlink(testsock, 1);
+ close(fd);
+ res = unlink(testsock);
+ if (res == -1) {
+ PERROR("unlink");
+ return -1;
+ }
+ res = check_nonexist(testsock);
+ if (res == -1)
+ return -1;
+ if (err)
+ return -1;
+
+ success();
+ return 0;
+}
+
#define test_create_ro_dir(flags) \
do_test_create_ro_dir(flags, #flags)
@@ -1822,6 +1872,7 @@ int main(int argc, char *argv[])
sprintf(testdir, "%s/testdir", basepath);
sprintf(testdir2, "%s/testdir2", basepath);
sprintf(subfile, "%s/subfile", testdir2);
+ sprintf(testsock, "%s/testsock", basepath);
sprintf(testfile_r, "%s/testfile", realpath);
sprintf(testfile2_r, "%s/testfile2", realpath);
@@ -1845,6 +1896,7 @@ int main(int argc, char *argv[])
err += test_rename_dir();
err += test_rename_dir_loop();
err += test_seekdir();
+ err += test_socket();
err += test_utime();
err += test_truncate(0);
err += test_truncate(testdatalen / 2);
@@ -1903,6 +1955,7 @@ int main(int argc, char *argv[])
unlink(testfile);
unlink(testfile2);
+ unlink(testsock);
rmdir(testdir);
rmdir(testdir2);