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 2020 Robert Mustacchi
14  */
15 
16 /*
17  * Test different O_DIRECTORY open cases.
18  */
19 
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <err.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <limits.h>
30 #include <door.h>
31 #include <stropts.h>
32 #include <sys/socket.h>
33 
34 static uint_t odir_failures;
35 static char odir_fpath[PATH_MAX];
36 static char odir_dpath[PATH_MAX];
37 static char odir_doorpath[PATH_MAX];
38 static char odir_enoent[PATH_MAX];
39 static char odir_udspath[PATH_MAX];
40 static int odir_did = -1;
41 static int odir_uds = -1;
42 
43 static void
odir_test_one(const char * test,const char * path,int flags,int err)44 odir_test_one(const char *test, const char *path, int flags, int err)
45 {
46 	int fd = open(path, flags | O_DIRECTORY | O_RDONLY, 0644);
47 	if (fd >= 0) {
48 		(void) close(fd);
49 		if (err != 0) {
50 			odir_failures++;
51 			warnx("TEST FAILED: %s: opened %s, but expected error: "
52 			    "%d", test, path, err);
53 		}
54 	} else {
55 		if (err == 0) {
56 			odir_failures++;
57 			warnx("TEST FAILED: %s: failed to open %s, error: %d",
58 			    test, path, err);
59 		} else if (err != errno) {
60 			odir_failures++;
61 			warnx("TEST FAILED: %s: wrong error for path %s, "
62 			    "found %d, expected %d", test, path, errno, err);
63 		}
64 	}
65 }
66 
67 static void
odir_door_server(void * cookie,char * argp,size_t arg_size,door_desc_t * dp,uint_t ndesc)68 odir_door_server(void *cookie, char *argp, size_t arg_size, door_desc_t *dp,
69     uint_t ndesc)
70 {
71 	(void) door_return(NULL, 0, NULL, 0);
72 }
73 
74 static boolean_t
odir_setup(void)75 odir_setup(void)
76 {
77 	int fd;
78 	struct stat st;
79 	struct sockaddr_un un;
80 	pid_t pid = getpid();
81 
82 	(void) snprintf(odir_fpath, sizeof (odir_fpath),
83 	    "/tmp/odir.%d.file", pid);
84 	if ((fd = creat(odir_fpath, 0644)) < 0) {
85 		warn("failed to create temp file %s", odir_fpath);
86 		odir_fpath[0] = '\0';
87 		return (B_FALSE);
88 	}
89 	(void) close(fd);
90 
91 	(void) snprintf(odir_dpath, sizeof (odir_dpath),
92 	    "/tmp/odir.%d.dir", pid);
93 	if (mkdir(odir_dpath, 0755) != 0) {
94 		warn("failed to create temp directory %s", odir_dpath);
95 		odir_dpath[0] = '\0';
96 		return (B_FALSE);
97 	}
98 
99 	odir_did = door_create(odir_door_server, NULL, 0);
100 	if (odir_did == -1) {
101 		warnx("failed to create door");
102 		return (B_FALSE);
103 	}
104 	(void) snprintf(odir_doorpath, sizeof (odir_doorpath),
105 	    "/tmp/odir.%d.door", pid);
106 	if ((fd = creat(odir_doorpath, 0644)) < 0) {
107 		warn("failed to create %s", odir_doorpath);
108 		odir_doorpath[0] = '\0';
109 		return (B_FALSE);
110 	}
111 	(void) close(fd);
112 	if (fattach(odir_did, odir_doorpath) != 0) {
113 		warn("failed to attach door to %s", odir_doorpath);
114 		(void) unlink(odir_doorpath);
115 		odir_doorpath[0] = '\0';
116 		return (B_FALSE);
117 	}
118 
119 	(void) snprintf(odir_enoent, sizeof (odir_enoent),
120 	    "/tmp/odir.%d.enoent", pid);
121 	if (stat(odir_enoent, &st) == 0) {
122 		warnx("somehow random file %s exists!", odir_enoent);
123 	}
124 
125 	odir_uds = socket(PF_UNIX, SOCK_STREAM, 0);
126 	if (odir_uds == -1) {
127 		warn("failed to create UDS");
128 		return (B_FALSE);
129 	}
130 	(void) snprintf(odir_udspath, sizeof (odir_udspath),
131 	    "/tmp/odir.%d.uds", pid);
132 	(void) memset(&un, '\0', sizeof (un));
133 	un.sun_family = AF_UNIX;
134 	if (strlcpy(un.sun_path, odir_udspath, sizeof (un.sun_path)) >=
135 	    sizeof (un.sun_path)) {
136 		warnx("%s overflows AF_UNIX path", odir_udspath);
137 		odir_udspath[0] = '\0';
138 		return (B_FALSE);
139 	}
140 
141 	if (bind(odir_uds, (struct sockaddr *)&un, SUN_LEN(&un)) != 0) {
142 		warn("failed to bind %s", odir_udspath);
143 		odir_udspath[0] = '\0';
144 		return (B_FALSE);
145 	}
146 
147 	if (listen(odir_uds, 1) != 0) {
148 		warn("failed to listen on %s", odir_udspath);
149 		return (B_FALSE);
150 	}
151 
152 	return (B_TRUE);
153 }
154 
155 static void
odir_verify_enoent(void)156 odir_verify_enoent(void)
157 {
158 	struct stat st;
159 
160 	if (stat(odir_enoent, &st) == 0) {
161 		warnx("TEST FAILED: %s was created", odir_enoent);
162 		odir_failures++;
163 	} else if (errno != ENOENT) {
164 		warn("TEST FAILED: stat on %s failed", odir_enoent);
165 		odir_failures++;
166 	}
167 }
168 
169 static void
odir_cleanup(void)170 odir_cleanup(void)
171 {
172 	if (odir_udspath[0] != '\0') {
173 		if (unlink(odir_udspath) != 0) {
174 			warn("failed to unlink %s", odir_udspath);
175 		}
176 	}
177 
178 	if (odir_uds != -1) {
179 		if (close(odir_uds) != 0) {
180 			warn("failed to close UDS");
181 		}
182 	}
183 
184 	if (odir_doorpath[0] != '\0') {
185 		if (fdetach(odir_doorpath) != 0) {
186 			warn("failed to detach door %s", odir_doorpath);
187 		}
188 
189 		if (unlink(odir_doorpath) != 0) {
190 			warn("failed to unlink %s", odir_doorpath);
191 		}
192 	}
193 
194 	if (odir_did != -1) {
195 		if (door_revoke(odir_did) != 0) {
196 			warn("failed to revoke door");
197 		}
198 	}
199 
200 	if (odir_dpath[0] != '\0') {
201 		if (rmdir(odir_dpath) != 0) {
202 			warn("failed to clean up %s", odir_dpath);
203 		}
204 	}
205 
206 	if (odir_fpath[0] != '\0') {
207 		if (unlink(odir_fpath) != 0) {
208 			warn("failed to clean up %s", odir_fpath);
209 		}
210 	}
211 }
212 
213 int
main(void)214 main(void)
215 {
216 	if (!odir_setup()) {
217 		odir_cleanup();
218 		return (EXIT_FAILURE);
219 	}
220 
221 	odir_test_one("regular file", odir_fpath, 0, ENOTDIR);
222 	odir_test_one("directory", odir_dpath, 0, 0);
223 	odir_test_one("character device", "/dev/null", 0, ENOTDIR);
224 	odir_test_one("door server", odir_doorpath, 0, ENOTDIR);
225 	odir_test_one("missing file", odir_enoent, 0, ENOENT);
226 	odir_test_one("UDS", odir_udspath, 0, ENOTDIR);
227 
228 	odir_test_one("O_CREAT | O_DIRECTORY on a regular file", odir_fpath,
229 	    O_CREAT, ENOTDIR);
230 	odir_test_one("O_CREAT | O_DIRECTORY | O_EXCL on a regular file",
231 	    odir_fpath, O_CREAT | O_EXCL, EINVAL);
232 
233 	odir_test_one("O_CREAT | O_DIRECTORY on a directory", odir_dpath,
234 	    O_CREAT, 0);
235 	odir_test_one("O_CREAT | O_DIRECTORY | O_EXCL on a directory",
236 	    odir_dpath, O_CREAT | O_EXCL, EINVAL);
237 
238 	odir_test_one("O_CREAT | O_DIRECTORY on a missing file", odir_enoent,
239 	    O_CREAT, ENOENT);
240 	odir_verify_enoent();
241 	odir_test_one("O_CREAT | O_DIRECTORY | O_EXCL on a missing file",
242 	    odir_enoent, O_CREAT | O_EXCL, EINVAL);
243 	odir_verify_enoent();
244 
245 	odir_cleanup();
246 	if (odir_failures > 0) {
247 		warnx("%u tests failed", odir_failures);
248 	}
249 
250 	return (odir_failures > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
251 }
252