xref: /illumos-gate/usr/src/test/os-tests/tests/vfs/syncfs.c (revision 2833423dc59f4c35fe4713dbb942950c82df0437)
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