turns printfs back on
[freebsd-src/fkvm-freebsd.git] / tools / regression / posixshm / posixshm.c
blob6f4c3069397fce3ced14db68b9a902edf80bd4d8
1 /*-
2 * Copyright (c) 2006 Robert N. M. Watson
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
30 #include <sys/param.h>
31 #include <sys/mman.h>
32 #include <sys/resource.h>
33 #include <sys/stat.h>
34 #include <sys/syscall.h>
35 #include <sys/wait.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
44 #include "test.h"
46 #define TEST_PATH "/tmp/posixshm_regression_test"
49 * Attempt a shm_open() that should fail with an expected error of 'error'.
51 static void
52 shm_open_should_fail(const char *path, int flags, mode_t mode, int error)
54 int fd;
56 fd = shm_open(path, flags, mode);
57 if (fd >= 0) {
58 fail_err("shm_open() didn't fail");
59 close(fd);
60 return;
62 if (errno != error) {
63 fail_errno("shm_open");
64 return;
66 pass();
70 * Attempt a shm_unlink() that should fail with an expected error of 'error'.
72 static void
73 shm_unlink_should_fail(const char *path, int error)
76 if (shm_unlink(path) >= 0) {
77 fail_err("shm_unlink() didn't fail");
78 return;
80 if (errno != error) {
81 fail_errno("shm_unlink");
82 return;
84 pass();
88 * Open the test object and write '1' to the first byte. Returns valid fd
89 * on success and -1 on failure.
91 static int
92 scribble_object(void)
94 char *page;
95 int fd;
97 fd = shm_open(TEST_PATH, O_CREAT | O_EXCL | O_RDWR, 0777);
98 if (fd < 0 && errno == EEXIST) {
99 if (shm_unlink(TEST_PATH) < 0) {
100 fail_errno("shm_unlink");
101 return (-1);
103 fd = shm_open(TEST_PATH, O_CREAT | O_EXCL | O_RDWR, 0777);
105 if (fd < 0) {
106 fail_errno("shm_open");
107 return (-1);
109 if (ftruncate(fd, getpagesize()) < 0) {
110 fail_errno("ftruncate");
111 close(fd);
112 shm_unlink(TEST_PATH);
113 return (-1);
116 page = mmap(0, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd,
118 if (page == MAP_FAILED) {
119 fail_errno("mmap");
120 close(fd);
121 shm_unlink(TEST_PATH);
122 return (-1);
125 page[0] = '1';
127 if (munmap(page, getpagesize()) < 0) {
128 fail_errno("munmap");
129 close(fd);
130 shm_unlink(TEST_PATH);
131 return (-1);
134 return (fd);
137 static void
138 remap_object(void)
140 char *page;
141 int fd;
143 fd = scribble_object();
144 if (fd < 0)
145 return;
147 page = mmap(0, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd,
149 if (page == MAP_FAILED) {
150 fail_errno("mmap(2)");
151 close(fd);
152 shm_unlink(TEST_PATH);
153 return;
156 if (page[0] != '1') {
157 fail_err("missing data");
158 close(fd);
159 shm_unlink(TEST_PATH);
160 return;
163 close(fd);
164 if (munmap(page, getpagesize()) < 0) {
165 fail_errno("munmap");
166 shm_unlink(TEST_PATH);
167 return;
170 if (shm_unlink(TEST_PATH) < 0) {
171 fail_errno("shm_unlink");
172 return;
175 pass();
177 TEST(remap_object, "remap object");
179 static void
180 reopen_object(void)
182 char *page;
183 int fd;
185 fd = scribble_object();
186 if (fd < 0)
187 return;
188 close(fd);
190 fd = shm_open(TEST_PATH, O_RDONLY, 0777);
191 if (fd < 0) {
192 fail_errno("shm_open(2)");
193 shm_unlink(TEST_PATH);
194 return;
196 page = mmap(0, getpagesize(), PROT_READ, MAP_SHARED, fd, 0);
197 if (page == MAP_FAILED) {
198 fail_errno("mmap(2)");
199 close(fd);
200 shm_unlink(TEST_PATH);
201 return;
204 if (page[0] != '1') {
205 fail_err("missing data");
206 munmap(page, getpagesize());
207 close(fd);
208 shm_unlink(TEST_PATH);
209 return;
212 munmap(page, getpagesize());
213 close(fd);
214 shm_unlink(TEST_PATH);
215 pass();
217 TEST(reopen_object, "reopen object");
219 static void
220 readonly_mmap_write(void)
222 char *page;
223 int fd;
225 fd = shm_open(TEST_PATH, O_RDONLY | O_CREAT, 0777);
226 if (fd < 0) {
227 fail_errno("shm_open");
228 return;
231 /* PROT_WRITE should fail with EACCES. */
232 page = mmap(0, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd,
234 if (page != MAP_FAILED) {
235 fail_err("mmap(PROT_WRITE) succeeded");
236 munmap(page, getpagesize());
237 close(fd);
238 shm_unlink(TEST_PATH);
239 return;
241 if (errno != EACCES) {
242 fail_errno("mmap");
243 close(fd);
244 shm_unlink(TEST_PATH);
245 return;
248 close(fd);
249 shm_unlink(TEST_PATH);
250 pass();
252 TEST(readonly_mmap_write, "RDONLY object");
254 static void
255 open_after_unlink(void)
257 int fd;
259 fd = shm_open(TEST_PATH, O_RDONLY | O_CREAT, 0777);
260 if (fd < 0) {
261 fail_errno("shm_open(1)");
262 return;
264 close(fd);
266 if (shm_unlink(TEST_PATH) < 0) {
267 fail_errno("shm_unlink");
268 return;
271 shm_open_should_fail(TEST_PATH, O_RDONLY, 0777, ENOENT);
273 TEST(open_after_unlink, "open after unlink");
275 static void
276 open_invalid_path(void)
279 shm_open_should_fail("blah", O_RDONLY, 0777, EINVAL);
281 TEST(open_invalid_path, "open invalid path");
283 static void
284 open_write_only(void)
287 shm_open_should_fail(TEST_PATH, O_WRONLY, 0777, EINVAL);
289 TEST(open_write_only, "open with O_WRONLY");
291 static void
292 open_extra_flags(void)
295 shm_open_should_fail(TEST_PATH, O_RDONLY | O_DIRECT, 0777, EINVAL);
297 TEST(open_extra_flags, "open with extra flags");
299 static void
300 open_anon(void)
302 int fd;
304 fd = shm_open(SHM_ANON, O_RDWR, 0777);
305 if (fd < 0) {
306 fail_errno("shm_open");
307 return;
309 close(fd);
310 pass();
312 TEST(open_anon, "open anonymous object");
314 static void
315 open_anon_readonly(void)
318 shm_open_should_fail(SHM_ANON, O_RDONLY, 0777, EINVAL);
320 TEST(open_anon_readonly, "open SHM_ANON with O_RDONLY");
322 static void
323 open_bad_path_pointer(void)
326 shm_open_should_fail((char *)1024, O_RDONLY, 0777, EFAULT);
328 TEST(open_bad_path_pointer, "open bad path pointer");
330 static void
331 open_path_too_long(void)
333 char *page;
335 page = malloc(MAXPATHLEN + 1);
336 memset(page, 'a', MAXPATHLEN);
337 page[MAXPATHLEN] = '\0';
338 shm_open_should_fail(page, O_RDONLY, 0777, ENAMETOOLONG);
339 free(page);
341 TEST(open_path_too_long, "open pathname too long");
343 static void
344 open_nonexisting_object(void)
347 shm_open_should_fail("/notreallythere", O_RDONLY, 0777, ENOENT);
349 TEST(open_nonexisting_object, "open nonexistent object");
351 static void
352 exclusive_create_existing_object(void)
354 int fd;
356 fd = shm_open("/tmp/notreallythere", O_RDONLY | O_CREAT, 0777);
357 if (fd < 0) {
358 fail_errno("shm_open(O_CREAT)");
359 return;
361 close(fd);
363 shm_open_should_fail("/tmp/notreallythere", O_RDONLY | O_CREAT | O_EXCL,
364 0777, EEXIST);
366 shm_unlink("/tmp/notreallythere");
368 TEST(exclusive_create_existing_object, "O_EXCL of existing object");
370 static void
371 trunc_resets_object(void)
373 struct stat sb;
374 int fd;
376 /* Create object and set size to 1024. */
377 fd = shm_open(TEST_PATH, O_RDWR | O_CREAT, 0777);
378 if (fd < 0) {
379 fail_errno("shm_open(1)");
380 return;
382 if (ftruncate(fd, 1024) < 0) {
383 fail_errno("ftruncate");
384 close(fd);
385 return;
387 if (fstat(fd, &sb) < 0) {
388 fail_errno("fstat(1)");
389 close(fd);
390 return;
392 if (sb.st_size != 1024) {
393 fail_err("size %d != 1024", (int)sb.st_size);
394 close(fd);
395 return;
397 close(fd);
399 /* Open with O_TRUNC which should reset size to 0. */
400 fd = shm_open(TEST_PATH, O_RDWR | O_TRUNC, 0777);
401 if (fd < 0) {
402 fail_errno("shm_open(2)");
403 return;
405 if (fstat(fd, &sb) < 0) {
406 fail_errno("fstat(2)");
407 close(fd);
408 return;
410 if (sb.st_size != 0) {
411 fail_err("size after O_TRUNC %d != 0", (int)sb.st_size);
412 close(fd);
413 return;
415 close(fd);
416 if (shm_unlink(TEST_PATH) < 0) {
417 fail_errno("shm_unlink");
418 return;
420 pass();
422 TEST(trunc_resets_object, "O_TRUNC resets size");
424 static void
425 unlink_bad_path_pointer(void)
428 shm_unlink_should_fail((char *)1024, EFAULT);
430 TEST(unlink_bad_path_pointer, "unlink bad path pointer");
432 static void
433 unlink_path_too_long(void)
435 char *page;
437 page = malloc(MAXPATHLEN + 1);
438 memset(page, 'a', MAXPATHLEN);
439 page[MAXPATHLEN] = '\0';
440 shm_unlink_should_fail(page, ENAMETOOLONG);
441 free(page);
443 TEST(unlink_path_too_long, "unlink pathname too long");
445 static void
446 test_object_resize(void)
448 pid_t pid;
449 struct stat sb;
450 char *page;
451 int fd, status;
453 /* Start off with a size of a single page. */
454 fd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0777);
455 if (fd < 0) {
456 fail_errno("shm_open");
457 return;
459 if (ftruncate(fd, getpagesize()) < 0) {
460 fail_errno("ftruncate(1)");
461 close(fd);
462 return;
464 if (fstat(fd, &sb) < 0) {
465 fail_errno("fstat(1)");
466 close(fd);
467 return;
469 if (sb.st_size != getpagesize()) {
470 fail_err("first resize failed");
471 close(fd);
472 return;
475 /* Write a '1' to the first byte. */
476 page = mmap(0, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd,
478 if (page == MAP_FAILED) {
479 fail_errno("mmap(1)");
480 close(fd);
481 return;
484 page[0] = '1';
486 if (munmap(page, getpagesize()) < 0) {
487 fail_errno("munmap(1)");
488 close(fd);
489 return;
492 /* Grow the object to 2 pages. */
493 if (ftruncate(fd, getpagesize() * 2) < 0) {
494 fail_errno("ftruncate(2)");
495 close(fd);
496 return;
498 if (fstat(fd, &sb) < 0) {
499 fail_errno("fstat(2)");
500 close(fd);
501 return;
503 if (sb.st_size != getpagesize() * 2) {
504 fail_err("second resize failed");
505 close(fd);
506 return;
509 /* Check for '1' at the first byte. */
510 page = mmap(0, getpagesize() * 2, PROT_READ | PROT_WRITE, MAP_SHARED,
511 fd, 0);
512 if (page == MAP_FAILED) {
513 fail_errno("mmap(2)");
514 close(fd);
515 return;
518 if (page[0] != '1') {
519 fail_err("missing data at 0");
520 close(fd);
521 return;
524 /* Write a '2' at the start of the second page. */
525 page[getpagesize()] = '2';
527 /* Shrink the object back to 1 page. */
528 if (ftruncate(fd, getpagesize()) < 0) {
529 fail_errno("ftruncate(3)");
530 close(fd);
531 return;
533 if (fstat(fd, &sb) < 0) {
534 fail_errno("fstat(3)");
535 close(fd);
536 return;
538 if (sb.st_size != getpagesize()) {
539 fail_err("third resize failed");
540 close(fd);
541 return;
545 * Fork a child process to make sure the second page is no
546 * longer valid.
548 pid = fork();
549 if (pid < 0) {
550 fail_errno("fork");
551 close(fd);
552 return;
555 if (pid == 0) {
556 struct rlimit lim;
557 char c;
559 /* Don't generate a core dump. */
560 getrlimit(RLIMIT_CORE, &lim);
561 lim.rlim_cur = 0;
562 setrlimit(RLIMIT_CORE, &lim);
565 * The previous ftruncate(2) shrunk the backing object
566 * so that this address is no longer valid, so reading
567 * from it should trigger a SIGSEGV.
569 c = page[getpagesize()];
570 fprintf(stderr, "child: page 1: '%c'\n", c);
571 exit(0);
573 if (wait(&status) < 0) {
574 fail_errno("wait");
575 close(fd);
576 return;
578 if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGSEGV) {
579 fail_err("child terminated with status %x", status);
580 close(fd);
581 return;
584 /* Grow the object back to 2 pages. */
585 if (ftruncate(fd, getpagesize() * 2) < 0) {
586 fail_errno("ftruncate(4)");
587 close(fd);
588 return;
590 if (fstat(fd, &sb) < 0) {
591 fail_errno("fstat(4)");
592 close(fd);
593 return;
595 if (sb.st_size != getpagesize() * 2) {
596 fail_err("second resize failed");
597 close(fd);
598 return;
602 * Note that the mapping at 'page' for the second page is
603 * still valid, and now that the shm object has been grown
604 * back up to 2 pages, there is now memory backing this page
605 * so the read will work. However, the data should be zero
606 * rather than '2' as the old data was thrown away when the
607 * object was shrunk and the new pages when an object are
608 * grown are zero-filled.
610 if (page[getpagesize()] != 0) {
611 fail_err("invalid data at %d", getpagesize());
612 close(fd);
613 return;
616 close(fd);
617 pass();
619 TEST(test_object_resize, "object resize");
622 main(int argc, char *argv[])
625 run_tests();
626 return (0);