1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2024 Oxide Computer Company 14 */ 15 16 /* 17 * This provides a basic wrapper for tests around the syncfs(3C) operation. As 18 * it's difficult to inject I/O failures, we specifically test the following: 19 * 20 * - Verify that an invalid fd will result in EBADF 21 * - Use file systems that we know will never support syncfs() and get ENOSYS. 22 * For this we use bootfs, objfs, sockets, and related. 23 * - Attempt to find something that we know will support syncfs() and try to 24 * use it. This last one is the trickiest, we rely on the fact that we know 25 * /var/run will be a tmpfs, but allow additional paths to be specified on 26 * the command line to try. 27 */ 28 29 #include <stdlib.h> 30 #include <err.h> 31 #include <unistd.h> 32 #include <stdbool.h> 33 #include <errno.h> 34 #include <string.h> 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 #include <fcntl.h> 38 #include <sys/socket.h> 39 #include <sys/sysmacros.h> 40 #include <sys/debug.h> 41 #include <limits.h> 42 #include <port.h> 43 44 typedef struct syncfs_enosys { 45 int (*se_open)(const struct syncfs_enosys *); 46 const char *se_path; 47 } syncfs_enosys_t; 48 49 static int 50 syncfs_open_file(const syncfs_enosys_t *test) 51 { 52 int fd = open(test->se_path, O_RDONLY); 53 if (fd < 0) { 54 err(EXIT_FAILURE, "TEST FAILED: failed to open file %s", 55 test->se_path); 56 } 57 58 return (fd); 59 } 60 61 static int 62 syncfs_open_socket(const syncfs_enosys_t *test) 63 { 64 struct sockaddr_in in; 65 int fd = socket(PF_INET, SOCK_STREAM, 0); 66 if (fd < 0) { 67 err(EXIT_FAILURE, "TEST FAILED: failed to create basic " 68 "socket"); 69 } 70 71 (void) memset(&in, 0, sizeof (in)); 72 if (bind(fd, (struct sockaddr *)&in, sizeof (in)) != 0) { 73 err(EXIT_FAILURE, "TEST FAILED: failed to bind socket"); 74 } 75 76 return (fd); 77 } 78 79 static int 80 syncfs_open_uds(const syncfs_enosys_t *test) 81 { 82 int fd = socket(PF_UNIX, SOCK_STREAM, 0); 83 if (fd < 0) { 84 err(EXIT_FAILURE, "TEST FAILED: failed to create UDS"); 85 } 86 87 return (fd); 88 } 89 90 static int 91 syncfs_open_pipe(const syncfs_enosys_t *test) 92 { 93 int fds[2]; 94 95 if (pipe(fds) != 0) { 96 err(EXIT_FAILURE, "TEST FAILED: failed to create pipe"); 97 } 98 99 VERIFY0(close(fds[1])); 100 return (fds[0]); 101 } 102 103 static int 104 syncfs_open_port(const syncfs_enosys_t *test) 105 { 106 int fd = port_create(); 107 if (fd < 0) { 108 err(EXIT_FAILURE, "TEST FAILED: failed to create event port"); 109 } 110 111 return (fd); 112 } 113 114 static const syncfs_enosys_t syncfs_enosys[] = { 115 { syncfs_open_file, "/system/boot" }, 116 { syncfs_open_file, "/system/object" }, 117 { syncfs_open_file, "/proc/self/psinfo" }, 118 { syncfs_open_file, "/dev/tcp" }, 119 { syncfs_open_file, "/dev/null" }, 120 { syncfs_open_file, "/dev/net" }, 121 { syncfs_open_file, "/etc/dfs/sharetab" }, 122 { syncfs_open_socket, "localhost socket" }, 123 { syncfs_open_uds, "UDS socket" }, 124 { syncfs_open_pipe, "pipe" }, 125 { syncfs_open_file, "/var/run/name_service_door" }, 126 { syncfs_open_port, "event port" }, 127 }; 128 129 static const int syncfs_badfs[] = { -1, STDERR_FILENO + 1, INT_MAX - 1, 130 0x7777, -0x7777 }; 131 132 static bool 133 syncfs_fail(const char *desc, int fd, int exp_err) 134 { 135 int ret = syncfs(fd); 136 if (ret != -1) { 137 warnx("TEST FAILED: %s: syncfs succeeded, but expected " 138 "failure", desc); 139 return (false); 140 } 141 142 if (errno != exp_err) { 143 warnx("TEST FAILED: %s: syncfs returned %s, expected %s", 144 desc, strerrorname_np(ret), strerrorname_np(exp_err)); 145 return (false); 146 } 147 148 (void) printf("TEST PASSED: %s\n", desc); 149 return (true); 150 } 151 152 static bool 153 syncfs_pass(const char *path) 154 { 155 bool ret = true; 156 int fd = open(path, O_RDONLY); 157 if (fd < 0) { 158 err(EXIT_FAILURE, "failed to open %s", path); 159 } 160 161 if (syncfs(fd) != 0) { 162 warnx("TEST FAILED: syncfs failed with %s on %s", 163 strerrorname_np(errno), path); 164 ret = false; 165 } else { 166 (void) printf("TEST PASSED: syncfs returned 0 on %s\n", path); 167 } 168 169 VERIFY0(close(fd)); 170 return (ret); 171 } 172 173 int 174 main(int argc, char *argv[]) 175 { 176 int ret = EXIT_SUCCESS; 177 178 closefrom(STDERR_FILENO + 1); 179 180 for (size_t i = 0; i < ARRAY_SIZE(syncfs_badfs); i++) { 181 char msg[PATH_MAX]; 182 (void) snprintf(msg, sizeof (msg), "Invalid file descriptor " 183 "returns EBADF (%zu)", i); 184 if (!syncfs_fail(msg, syncfs_badfs[i], EBADF)) { 185 ret = EXIT_FAILURE; 186 } 187 } 188 189 for (size_t i = 0; i < ARRAY_SIZE(syncfs_enosys); i++) { 190 char msg[PATH_MAX]; 191 int fd = syncfs_enosys[i].se_open(&syncfs_enosys[i]); 192 (void) snprintf(msg, sizeof (msg), "Unsupported fs returns " 193 "ENOSYS: %s", syncfs_enosys[i].se_path); 194 if (!syncfs_fail(msg, fd, ENOSYS)) { 195 ret = EXIT_FAILURE; 196 } 197 198 VERIFY0(close(fd)); 199 } 200 201 if (!syncfs_pass("/var/run")) { 202 ret = EXIT_FAILURE; 203 } 204 205 for (int i = 1; i < argc; i++) { 206 if (!syncfs_pass(argv[i])) { 207 ret = EXIT_FAILURE; 208 } 209 } 210 211 if (ret == EXIT_SUCCESS) { 212 (void) printf("All tests completed successfully\n"); 213 } 214 215 return (ret); 216 } 217