diff --git a/sshfs.c b/sshfs.c index 4cdf1223..0682be9a 100644 --- a/sshfs.c +++ b/sshfs.c @@ -1322,7 +1322,7 @@ static int connect_to(struct conn *conn, char *host, char *port) return 0; } -static int connect_vsock(struct conn *conn, char *vsock) +static int connect_vsock(struct conn *conn, const char *vsock) { #ifndef __linux__ fprintf(stderr, "vsock is not available\n"); @@ -1333,14 +1333,13 @@ static int connect_vsock(struct conn *conn, char *vsock) struct sockaddr_vm addr; unsigned int cid; unsigned int port; - char *delim; + const char *delim; delim = strchr(vsock, ':'); if (delim == NULL) { fprintf(stderr, "invalid vsock, expecting CID:PORT\n"); return -1; } - *delim = '\0'; errno = 0; cid = strtoul(vsock, NULL, 10); if (errno) { diff --git a/test/meson.build b/test/meson.build index 18ac05ee..9e8934f5 100644 --- a/test/meson.build +++ b/test/meson.build @@ -19,6 +19,15 @@ test_cache = executable('test_cache', 'test_cache.c', install: false) test('cache', test_cache, suite: 'unit') +if host_machine.system() == 'linux' + test_vsock = executable('test_vsock', ['test_vsock.c', '../cache.c'], + include_directories: include_dirs, + dependencies: sshfs_deps, + c_args: ['-DFUSE_USE_VERSION=31'], + install: false) + test('vsock', test_vsock, suite: 'unit') +endif + # Provide something helpful when running 'ninja test' wrong_cmd = executable('wrong_command', 'wrong_command.c', install: false) diff --git a/test/test_vsock.c b/test/test_vsock.c new file mode 100644 index 00000000..7c72ba75 --- /dev/null +++ b/test/test_vsock.c @@ -0,0 +1,106 @@ +/* Exercise the real connection helper without a VSOCK device or FUSE mount. */ +#define _GNU_SOURCE +#include +#include + +static int test_socket(int domain, int type, int protocol); +static int test_connect(int fd, const struct sockaddr *addr, socklen_t len); +static int test_close(int fd); +int sshfs_main(int argc, char *argv[]); + +#define socket test_socket +#define connect test_connect +#define close test_close +#define main sshfs_main +#include "../sshfs.c" +#undef main +#undef close +#undef connect +#undef socket + +#define TEST_FD 42 + +enum failure { + NO_FAILURE, + SOCKET_FAILURE, + CONNECT_FAILURE, +}; + +static enum failure failure; +static int socket_calls; +static int connect_calls; +static int close_calls; + +static int test_socket(int domain, int type, int protocol) +{ + g_assert_cmpint(domain, ==, AF_VSOCK); + g_assert_cmpint(type, ==, SOCK_STREAM); + g_assert_cmpint(protocol, ==, 0); + socket_calls++; + if (failure == SOCKET_FAILURE && socket_calls == 1) { + errno = EAFNOSUPPORT; + return -1; + } + return TEST_FD; +} + +static int test_connect(int fd, const struct sockaddr *addr, socklen_t len) +{ + const struct sockaddr_vm *vm = (const struct sockaddr_vm *) addr; + + g_assert_cmpint(fd, ==, TEST_FD); + g_assert_cmpuint(len, ==, sizeof(*vm)); + g_assert_cmpint(vm->svm_family, ==, AF_VSOCK); + g_assert_cmpuint(vm->svm_cid, ==, 3); + g_assert_cmpuint(vm->svm_port, ==, 12345); + connect_calls++; + if (failure == CONNECT_FAILURE && connect_calls == 1) { + errno = ECONNREFUSED; + return -1; + } + return 0; +} + +static int test_close(int fd) +{ + g_assert_cmpint(fd, ==, TEST_FD); + close_calls++; + return 0; +} + +static void test_reuse_address(gconstpointer data) +{ + char address[] = "3:12345"; + struct conn first = { .rfd = -1, .wfd = -1 }; + struct conn second = { .rfd = -1, .wfd = -1 }; + int first_result; + int second_result; + + failure = GPOINTER_TO_INT(data); + socket_calls = connect_calls = close_calls = 0; + first_result = connect_vsock(&first, address); + second_result = connect_vsock(&second, address); + + g_assert_cmpint(first_result, ==, failure == NO_FAILURE ? 0 : -1); + g_assert_cmpint(second_result, ==, 0); + g_assert_cmpstr(address, ==, "3:12345"); + g_assert_cmpint(first.rfd, ==, failure == NO_FAILURE ? TEST_FD : -1); + g_assert_cmpint(first.wfd, ==, first.rfd); + g_assert_cmpint(second.rfd, ==, TEST_FD); + g_assert_cmpint(second.wfd, ==, TEST_FD); + g_assert_cmpint(socket_calls, ==, 2); + g_assert_cmpint(connect_calls, ==, failure == SOCKET_FAILURE ? 1 : 2); + g_assert_cmpint(close_calls, ==, failure == CONNECT_FAILURE ? 1 : 0); +} + +int main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_data_func("/vsock/reuse-after-success", + GINT_TO_POINTER(NO_FAILURE), test_reuse_address); + g_test_add_data_func("/vsock/retry-after-socket-failure", + GINT_TO_POINTER(SOCKET_FAILURE), test_reuse_address); + g_test_add_data_func("/vsock/retry-after-connect-failure", + GINT_TO_POINTER(CONNECT_FAILURE), test_reuse_address); + return g_test_run(); +}