xref: /illumos-gate/usr/src/cmd/bhyve/test/tests/mevent/vnode_file.c (revision 957246c9e6c47389c40079995d73eebcc659fb29)
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 2018 Joyent, Inc.
14  * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
15  */
16 
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <pthread.h>
20 #include <signal.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <strings.h>
24 #include <unistd.h>
25 
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 
29 #include "testlib.h"
30 #include "mevent.h"
31 
32 static char *cookie = "Chocolate chip with fudge stripes";
33 
34 static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
35 static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
36 
37 static void
38 callback(int fd, enum ev_type ev, void *arg)
39 {
40 	static off_t size = 0;
41 	struct stat st;
42 
43 	ASSERT_INT_EQ(("bad event"), ev, EVF_VNODE);
44 	ASSERT_PTR_EQ(("bad cookie"), arg, cookie);
45 
46 	if (fstat(fd, &st) != 0)
47 		FAIL_ERRNO("fstat failed");
48 
49 	ASSERT_INT64_NEQ(("File size has not changed"), size, st.st_size);
50 	size = st.st_size;
51 
52 	pthread_mutex_lock(&mtx);
53 	pthread_cond_signal(&cv);
54 	VERBOSE(("wakeup"));
55 	pthread_mutex_unlock(&mtx);
56 }
57 
58 static void
59 test_fd(int fd, char *tag)
60 {
61 	struct mevent *evp;
62 	int err;
63 
64 	evp = mevent_add_flags(fd, EVF_VNODE, EVFF_ATTRIB, callback, cookie);
65 	ASSERT_PTR_NEQ(("%s: mevent_add", tag), evp, NULL);
66 
67 	for (uint_t i = 0; cookie[i] != '\0'; i++) {
68 		ssize_t written;
69 
70 		pthread_mutex_lock(&mtx);
71 
72 		if (i > 0) {
73 			/*
74 			 * Check that no events are emitted for writes which do
75 			 * not alter the size.
76 			 */
77 			if (lseek(fd, -1, SEEK_CUR) == -1)
78 				FAIL_ERRNO("lseek");
79 			if (write(fd, "X", 1) == -1)
80 				FAIL_ERRNO("write");
81 		}
82 
83 		written = write(fd, cookie + i, 1);
84 		if (written < 0)
85 			FAIL_ERRNO("bad write");
86 		ASSERT_INT64_EQ(("write byte %d of cookie", i), written, 1);
87 
88 		/* Wait for the size change to be processed */
89 		pthread_cond_wait(&cv, &mtx);
90 		pthread_mutex_unlock(&mtx);
91 		/*
92 		 * This is a bit unsatisfactory but we need to allow time
93 		 * for mevent to re-associate the port or the next write could
94 		 * be missed.
95 		 */
96 		usleep(500);
97 	}
98 
99 	err = mevent_disable(evp);
100 	ASSERT_INT_EQ(("%s: mevent_disable: %s", tag, strerror(err)), err, 0);
101 
102 	(void) printf("PASS %s - %s\n", testlib_prog, tag);
103 }
104 
105 int
106 main(int argc, const char **argv)
107 {
108 	start_test(argv[0], 5);
109 	start_event_thread();
110 	int fd;
111 
112 	/* Test with a temporary file in /tmp */
113 	char *template = strdup("/tmp/mevent.vnode.XXXXXX");
114 	ASSERT_PTR_NEQ(("strdup"), template, NULL);
115 	fd = mkstemp(template);
116 	if (fd == -1)
117 		FAIL_ERRNO("Couldn't create temporary file with mkstemp");
118 
119 	VERBOSE(("Opened temporary file at '%s'", template));
120 
121 	test_fd(fd, "temporary file");
122 
123 	/* Test with a file which is unlinked from the filesystem */
124 	FILE *fp = tmpfile();
125 	ASSERT_PTR_NEQ(("tmpfile"), fp, NULL);
126 
127 	fd = fileno(fp);
128 	if (fd == -1)
129 		FAIL_ERRNO("Couldn't get file descriptor for temporary file");
130 
131 	test_fd(fd, "anon file");
132 
133 	/*
134 	 * Defer to here to avoid generating a new event before the disable has
135 	 * been processed and the port deassociated.
136 	 */
137 	unlink(template);
138 	free(template);
139 
140 	PASS();
141 }
142