xref: /minix3/minix/tests/test85.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
16c46a77dSDavid van Moolenbroek /* Test for end-of-file during block device I/O - by D.C. van Moolenbroek */
26c46a77dSDavid van Moolenbroek /* This test needs to be run as root; it sets up and uses a VND instance. */
36c46a77dSDavid van Moolenbroek /*
46c46a77dSDavid van Moolenbroek  * The test should work with all root file system block sizes, but only tests
56c46a77dSDavid van Moolenbroek  * certain corner cases if the root FS block size is twice the page size.
66c46a77dSDavid van Moolenbroek  */
76c46a77dSDavid van Moolenbroek #include <stdlib.h>
86c46a77dSDavid van Moolenbroek #include <string.h>
96c46a77dSDavid van Moolenbroek #include <signal.h>
106c46a77dSDavid van Moolenbroek #include <sys/param.h>
116c46a77dSDavid van Moolenbroek #include <sys/wait.h>
126c46a77dSDavid van Moolenbroek #include <sys/mman.h>
136c46a77dSDavid van Moolenbroek #include <sys/ioctl.h>
146c46a77dSDavid van Moolenbroek #include <minix/partition.h>
156c46a77dSDavid van Moolenbroek #include <fcntl.h>
166c46a77dSDavid van Moolenbroek #include <unistd.h>
176c46a77dSDavid van Moolenbroek #include <assert.h>
186c46a77dSDavid van Moolenbroek 
196c46a77dSDavid van Moolenbroek #define VNCONFIG "/usr/sbin/vnconfig"
206c46a77dSDavid van Moolenbroek 
216c46a77dSDavid van Moolenbroek #define SECTOR_SIZE 512		/* this should be the sector size of VND */
226c46a77dSDavid van Moolenbroek 
236c46a77dSDavid van Moolenbroek #define ITERATIONS 3
246c46a77dSDavid van Moolenbroek 
256c46a77dSDavid van Moolenbroek enum {
266c46a77dSDavid van Moolenbroek 	BEFORE_EOF,
276c46a77dSDavid van Moolenbroek 	UPTO_EOF,
286c46a77dSDavid van Moolenbroek 	ACROSS_EOF,
296c46a77dSDavid van Moolenbroek 	ONEPAST_EOF,
306c46a77dSDavid van Moolenbroek 	FROM_EOF,
316c46a77dSDavid van Moolenbroek 	BEYOND_EOF
326c46a77dSDavid van Moolenbroek };
336c46a77dSDavid van Moolenbroek 
346c46a77dSDavid van Moolenbroek #include "common.h"
356c46a77dSDavid van Moolenbroek 
366c46a77dSDavid van Moolenbroek static int need_cleanup = 0;
376c46a77dSDavid van Moolenbroek 
386c46a77dSDavid van Moolenbroek static int dev_fd;
396c46a77dSDavid van Moolenbroek static size_t dev_size;
406c46a77dSDavid van Moolenbroek static char *dev_buf;
416c46a77dSDavid van Moolenbroek static char *dev_ref;
426c46a77dSDavid van Moolenbroek 
436c46a77dSDavid van Moolenbroek static size_t block_size;
446c46a77dSDavid van Moolenbroek static size_t page_size;
456c46a77dSDavid van Moolenbroek static int test_peek;
466c46a77dSDavid van Moolenbroek 
476c46a77dSDavid van Moolenbroek static char *mmap_ptr = NULL;
486c46a77dSDavid van Moolenbroek static size_t mmap_size;
496c46a77dSDavid van Moolenbroek 
506c46a77dSDavid van Moolenbroek static int pipe_fd[2];
516c46a77dSDavid van Moolenbroek 
526c46a77dSDavid van Moolenbroek /*
536c46a77dSDavid van Moolenbroek  * Fill the given buffer with random contents.
546c46a77dSDavid van Moolenbroek  */
556c46a77dSDavid van Moolenbroek static void
fill_buf(char * buf,size_t size)566c46a77dSDavid van Moolenbroek fill_buf(char * buf, size_t size)
576c46a77dSDavid van Moolenbroek {
586c46a77dSDavid van Moolenbroek 
596c46a77dSDavid van Moolenbroek 	while (size--)
606c46a77dSDavid van Moolenbroek 		*buf++ = lrand48() & 0xff;
616c46a77dSDavid van Moolenbroek }
626c46a77dSDavid van Moolenbroek 
636c46a77dSDavid van Moolenbroek /*
646c46a77dSDavid van Moolenbroek  * Place the elements of the source array in the destination array in random
656c46a77dSDavid van Moolenbroek  * order.  There are probably better ways to do this, but it is morning, and I
666c46a77dSDavid van Moolenbroek  * haven't had coffee yet, so go away.
676c46a77dSDavid van Moolenbroek  */
686c46a77dSDavid van Moolenbroek static void
scramble(int * dst,const int * src,int count)696c46a77dSDavid van Moolenbroek scramble(int * dst, const int * src, int count)
706c46a77dSDavid van Moolenbroek {
716c46a77dSDavid van Moolenbroek 	int i, j, k;
726c46a77dSDavid van Moolenbroek 
736c46a77dSDavid van Moolenbroek 	for (i = 0; i < count; i++)
746c46a77dSDavid van Moolenbroek 		dst[i] = i;
756c46a77dSDavid van Moolenbroek 
766c46a77dSDavid van Moolenbroek 	for (i = count - 1; i >= 0; i--) {
776c46a77dSDavid van Moolenbroek 		j = lrand48() % (i + 1);
786c46a77dSDavid van Moolenbroek 
796c46a77dSDavid van Moolenbroek 		k = dst[j];
806c46a77dSDavid van Moolenbroek 		dst[j] = dst[i];
816c46a77dSDavid van Moolenbroek 		dst[i] = src[k];
826c46a77dSDavid van Moolenbroek 	}
836c46a77dSDavid van Moolenbroek }
846c46a77dSDavid van Moolenbroek 
856c46a77dSDavid van Moolenbroek /*
866c46a77dSDavid van Moolenbroek  * Perform I/O using read(2) and check the returned results against the
876c46a77dSDavid van Moolenbroek  * expected result and the image reference data.
886c46a77dSDavid van Moolenbroek  */
896c46a77dSDavid van Moolenbroek static void
io_read(size_t pos,size_t len,size_t expected)906c46a77dSDavid van Moolenbroek io_read(size_t pos, size_t len, size_t expected)
916c46a77dSDavid van Moolenbroek {
926c46a77dSDavid van Moolenbroek 	ssize_t bytes;
936c46a77dSDavid van Moolenbroek 
946c46a77dSDavid van Moolenbroek 	assert(len > 0 && len <= dev_size);
956c46a77dSDavid van Moolenbroek 	assert(expected <= len);
966c46a77dSDavid van Moolenbroek 
976c46a77dSDavid van Moolenbroek 	if (lseek(dev_fd, (off_t)pos, SEEK_SET) != pos) e(0);
986c46a77dSDavid van Moolenbroek 
996c46a77dSDavid van Moolenbroek 	memset(dev_buf, 0, len);
1006c46a77dSDavid van Moolenbroek 
1016c46a77dSDavid van Moolenbroek 	if ((bytes = read(dev_fd, dev_buf, len)) < 0) e(0);
1026c46a77dSDavid van Moolenbroek 
1036c46a77dSDavid van Moolenbroek 	if (bytes != expected) e(0);
1046c46a77dSDavid van Moolenbroek 
1056c46a77dSDavid van Moolenbroek 	if (memcmp(&dev_ref[pos], dev_buf, bytes)) e(0);
1066c46a77dSDavid van Moolenbroek }
1076c46a77dSDavid van Moolenbroek 
1086c46a77dSDavid van Moolenbroek /*
1096c46a77dSDavid van Moolenbroek  * Perform I/O using write(2) and check the returned result against the
1106c46a77dSDavid van Moolenbroek  * expected result.  Update the image reference data as appropriate.
1116c46a77dSDavid van Moolenbroek  */
1126c46a77dSDavid van Moolenbroek static void
io_write(size_t pos,size_t len,size_t expected)1136c46a77dSDavid van Moolenbroek io_write(size_t pos, size_t len, size_t expected)
1146c46a77dSDavid van Moolenbroek {
1156c46a77dSDavid van Moolenbroek 	ssize_t bytes;
1166c46a77dSDavid van Moolenbroek 
1176c46a77dSDavid van Moolenbroek 	assert(len > 0 && len <= dev_size);
1186c46a77dSDavid van Moolenbroek 	assert(expected <= len);
1196c46a77dSDavid van Moolenbroek 
1206c46a77dSDavid van Moolenbroek 	if (lseek(dev_fd, (off_t)pos, SEEK_SET) != pos) e(0);
1216c46a77dSDavid van Moolenbroek 
1226c46a77dSDavid van Moolenbroek 	fill_buf(dev_buf, len);
1236c46a77dSDavid van Moolenbroek 
1246c46a77dSDavid van Moolenbroek 	if ((bytes = write(dev_fd, dev_buf, len)) < 0) e(0);
1256c46a77dSDavid van Moolenbroek 
1266c46a77dSDavid van Moolenbroek 	if (bytes != expected) e(0);
1276c46a77dSDavid van Moolenbroek 
1286c46a77dSDavid van Moolenbroek 	if (bytes > 0) {
1296c46a77dSDavid van Moolenbroek 		assert(pos + bytes <= dev_size);
1306c46a77dSDavid van Moolenbroek 
1316c46a77dSDavid van Moolenbroek 		memcpy(&dev_ref[pos], dev_buf, bytes);
1326c46a77dSDavid van Moolenbroek 	}
1336c46a77dSDavid van Moolenbroek }
1346c46a77dSDavid van Moolenbroek 
1356c46a77dSDavid van Moolenbroek /*
1366c46a77dSDavid van Moolenbroek  * Test if reading from the given pointer succeeds or not, and return the
1376c46a77dSDavid van Moolenbroek  * result.
1386c46a77dSDavid van Moolenbroek  */
1396c46a77dSDavid van Moolenbroek static int
is_readable(char * ptr)1406c46a77dSDavid van Moolenbroek is_readable(char * ptr)
1416c46a77dSDavid van Moolenbroek {
1426c46a77dSDavid van Moolenbroek 	ssize_t r;
1436c46a77dSDavid van Moolenbroek 	char byte;
1446c46a77dSDavid van Moolenbroek 
1456c46a77dSDavid van Moolenbroek 	/*
1466c46a77dSDavid van Moolenbroek 	 * If we access the pointer directly, we will get a fatal signal.
1476c46a77dSDavid van Moolenbroek 	 * Thus, for that to work we would need a child process, making the
1486c46a77dSDavid van Moolenbroek 	 * whole test slow and noisy.  Let a service try the operation instead.
1496c46a77dSDavid van Moolenbroek 	 */
1506c46a77dSDavid van Moolenbroek 	r = write(pipe_fd[1], ptr, 1);
1516c46a77dSDavid van Moolenbroek 
1526c46a77dSDavid van Moolenbroek 	if (r == 1) {
1536c46a77dSDavid van Moolenbroek 		/* Don't fill up the pipe. */
1546c46a77dSDavid van Moolenbroek 		if (read(pipe_fd[0], &byte, 1) != 1) e(0);
1556c46a77dSDavid van Moolenbroek 
1566c46a77dSDavid van Moolenbroek 		return 1;
1576c46a77dSDavid van Moolenbroek 	} else if (r != -1 || errno != EFAULT)
1586c46a77dSDavid van Moolenbroek 		e(0);
1596c46a77dSDavid van Moolenbroek 
1606c46a77dSDavid van Moolenbroek 	return 0;
1616c46a77dSDavid van Moolenbroek }
1626c46a77dSDavid van Moolenbroek 
1636c46a77dSDavid van Moolenbroek /*
1646c46a77dSDavid van Moolenbroek  * Perform I/O using mmap(2) and check the returned results against the
1656c46a77dSDavid van Moolenbroek  * expected result and the image reference data.  Ensure that bytes beyond the
1666c46a77dSDavid van Moolenbroek  * device end are either zero (on the remainder of the last page) or
1676c46a77dSDavid van Moolenbroek  * inaccessible on pages entirely beyond the device end.
1686c46a77dSDavid van Moolenbroek  */
1696c46a77dSDavid van Moolenbroek static void
io_peek(size_t pos,size_t len,size_t expected)1706c46a77dSDavid van Moolenbroek io_peek(size_t pos, size_t len, size_t expected)
1716c46a77dSDavid van Moolenbroek {
1726c46a77dSDavid van Moolenbroek 	size_t n, delta, mapped_size;
1736c46a77dSDavid van Moolenbroek 	char *ptr;
1746c46a77dSDavid van Moolenbroek 
1756c46a77dSDavid van Moolenbroek 	assert(test_peek);
1766c46a77dSDavid van Moolenbroek 
1776c46a77dSDavid van Moolenbroek 	delta = pos % page_size;
1786c46a77dSDavid van Moolenbroek 
1796c46a77dSDavid van Moolenbroek 	pos -= delta;
1806c46a77dSDavid van Moolenbroek 	len += delta;
1816c46a77dSDavid van Moolenbroek 
1826c46a77dSDavid van Moolenbroek 	len = roundup(len, page_size);
1836c46a77dSDavid van Moolenbroek 
1846c46a77dSDavid van Moolenbroek 	/* Don't bother with the given expected value.  Recompute it. */
1856c46a77dSDavid van Moolenbroek 	if (pos < dev_size)
1866c46a77dSDavid van Moolenbroek 		expected = MIN(dev_size - pos, len);
1876c46a77dSDavid van Moolenbroek 	else
1886c46a77dSDavid van Moolenbroek 		expected = 0;
1896c46a77dSDavid van Moolenbroek 
1906c46a77dSDavid van Moolenbroek 	mapped_size = roundup(dev_size, page_size);
1916c46a77dSDavid van Moolenbroek 
1926c46a77dSDavid van Moolenbroek 	assert(!(len % page_size));
1936c46a77dSDavid van Moolenbroek 
1946c46a77dSDavid van Moolenbroek 	ptr = mmap(NULL, len, PROT_READ, MAP_PRIVATE | MAP_FILE, dev_fd,
1956c46a77dSDavid van Moolenbroek 	    (off_t)pos);
1966c46a77dSDavid van Moolenbroek 
1976c46a77dSDavid van Moolenbroek 	/*
1986c46a77dSDavid van Moolenbroek 	 * As of writing, VM allows memory mapping at any offset and for any
1996c46a77dSDavid van Moolenbroek 	 * length.  At least for block devices, VM should probably be changed
2006c46a77dSDavid van Moolenbroek 	 * to throw ENXIO for any pages beyond the file end, which in turn
2016c46a77dSDavid van Moolenbroek 	 * renders all the SIGBUS tests below obsolete.
2026c46a77dSDavid van Moolenbroek 	 */
2036c46a77dSDavid van Moolenbroek 	if (ptr == MAP_FAILED) {
2046c46a77dSDavid van Moolenbroek 		if (pos + len <= mapped_size) e(0);
2056c46a77dSDavid van Moolenbroek 		if (errno != ENXIO) e(0);
2066c46a77dSDavid van Moolenbroek 
2076c46a77dSDavid van Moolenbroek 		return;
2086c46a77dSDavid van Moolenbroek 	}
2096c46a77dSDavid van Moolenbroek 
2106c46a77dSDavid van Moolenbroek 	mmap_ptr = ptr;
2116c46a77dSDavid van Moolenbroek 	mmap_size = len;
2126c46a77dSDavid van Moolenbroek 
2136c46a77dSDavid van Moolenbroek 	/*
2146c46a77dSDavid van Moolenbroek 	 * Any page that contains any valid part of the mapped device should be
2156c46a77dSDavid van Moolenbroek 	 * readable and have correct contents for that part.  If the last valid
2166c46a77dSDavid van Moolenbroek 	 * page extends beyond the mapped device, its remainder should be zero.
2176c46a77dSDavid van Moolenbroek 	 */
2186c46a77dSDavid van Moolenbroek 	if (pos < dev_size) {
2196c46a77dSDavid van Moolenbroek 		/* The valid part should have the expected device contents. */
2206c46a77dSDavid van Moolenbroek 		if (memcmp(&dev_ref[pos], ptr, expected)) e(0);
2216c46a77dSDavid van Moolenbroek 
2226c46a77dSDavid van Moolenbroek 		/* The remainder, if any, should be zero. */
2236c46a77dSDavid van Moolenbroek 		for (n = expected; n % page_size; n++)
2246c46a77dSDavid van Moolenbroek 			if (ptr[n] != 0) e(0);
2256c46a77dSDavid van Moolenbroek 	}
2266c46a77dSDavid van Moolenbroek 
2276c46a77dSDavid van Moolenbroek 	/*
2286c46a77dSDavid van Moolenbroek 	 * Any page entirely beyond EOF should not be mapped in.  In order to
2296c46a77dSDavid van Moolenbroek 	 * ensure that is_readable() works, also test pages that are mapped in.
2306c46a77dSDavid van Moolenbroek 	 */
2316c46a77dSDavid van Moolenbroek 	for (n = pos; n < pos + len; n += page_size)
2326c46a77dSDavid van Moolenbroek 		if (is_readable(&ptr[n - pos]) != (n < mapped_size)) e(0);
2336c46a77dSDavid van Moolenbroek 
2346c46a77dSDavid van Moolenbroek 	munmap(ptr, len);
2356c46a77dSDavid van Moolenbroek 
2366c46a77dSDavid van Moolenbroek 	mmap_ptr = NULL;
2376c46a77dSDavid van Moolenbroek }
2386c46a77dSDavid van Moolenbroek 
2396c46a77dSDavid van Moolenbroek /*
2406c46a77dSDavid van Moolenbroek  * Perform one of the supported end-of-file access attempts using one I/O
2416c46a77dSDavid van Moolenbroek  * operation.
2426c46a77dSDavid van Moolenbroek  */
2436c46a77dSDavid van Moolenbroek static void
do_one_io(int where,void (* io_proc)(size_t,size_t,size_t))2446c46a77dSDavid van Moolenbroek do_one_io(int where, void (* io_proc)(size_t, size_t, size_t))
2456c46a77dSDavid van Moolenbroek {
2466c46a77dSDavid van Moolenbroek 	size_t start, bytes;
2476c46a77dSDavid van Moolenbroek 
2486c46a77dSDavid van Moolenbroek 	switch (where) {
2496c46a77dSDavid van Moolenbroek 	case BEFORE_EOF:
2506c46a77dSDavid van Moolenbroek 		bytes = lrand48() % (dev_size - 1) + 1;
2516c46a77dSDavid van Moolenbroek 
2526c46a77dSDavid van Moolenbroek 		io_proc(dev_size - bytes - 1, bytes, bytes);
2536c46a77dSDavid van Moolenbroek 
2546c46a77dSDavid van Moolenbroek 		break;
2556c46a77dSDavid van Moolenbroek 
2566c46a77dSDavid van Moolenbroek 	case UPTO_EOF:
2576c46a77dSDavid van Moolenbroek 		bytes = lrand48() % dev_size + 1;
2586c46a77dSDavid van Moolenbroek 
2596c46a77dSDavid van Moolenbroek 		io_proc(dev_size - bytes, bytes, bytes);
2606c46a77dSDavid van Moolenbroek 
2616c46a77dSDavid van Moolenbroek 		break;
2626c46a77dSDavid van Moolenbroek 
2636c46a77dSDavid van Moolenbroek 	case ACROSS_EOF:
2646c46a77dSDavid van Moolenbroek 		start = lrand48() % (dev_size - 1) + 1;
2656c46a77dSDavid van Moolenbroek 		bytes = dev_size - start + 1;
2666c46a77dSDavid van Moolenbroek 		assert(start < dev_size && start + bytes > dev_size);
2676c46a77dSDavid van Moolenbroek 		bytes += lrand48() % (dev_size - bytes + 1);
2686c46a77dSDavid van Moolenbroek 
2696c46a77dSDavid van Moolenbroek 		io_proc(start, bytes, dev_size - start);
2706c46a77dSDavid van Moolenbroek 
2716c46a77dSDavid van Moolenbroek 		break;
2726c46a77dSDavid van Moolenbroek 
2736c46a77dSDavid van Moolenbroek 	case ONEPAST_EOF:
2746c46a77dSDavid van Moolenbroek 		bytes = lrand48() % (dev_size - 1) + 1;
2756c46a77dSDavid van Moolenbroek 
2766c46a77dSDavid van Moolenbroek 		io_proc(dev_size - bytes + 1, bytes, bytes - 1);
2776c46a77dSDavid van Moolenbroek 
2786c46a77dSDavid van Moolenbroek 		break;
2796c46a77dSDavid van Moolenbroek 
2806c46a77dSDavid van Moolenbroek 	case FROM_EOF:
2816c46a77dSDavid van Moolenbroek 		bytes = lrand48() % dev_size + 1;
2826c46a77dSDavid van Moolenbroek 
2836c46a77dSDavid van Moolenbroek 		io_proc(dev_size, bytes, 0);
2846c46a77dSDavid van Moolenbroek 
2856c46a77dSDavid van Moolenbroek 		break;
2866c46a77dSDavid van Moolenbroek 
2876c46a77dSDavid van Moolenbroek 	case BEYOND_EOF:
2886c46a77dSDavid van Moolenbroek 		start = dev_size + lrand48() % dev_size + 1;
2896c46a77dSDavid van Moolenbroek 		bytes = lrand48() % dev_size + 1;
2906c46a77dSDavid van Moolenbroek 
2916c46a77dSDavid van Moolenbroek 		io_proc(start, bytes, 0);
2926c46a77dSDavid van Moolenbroek 
2936c46a77dSDavid van Moolenbroek 		break;
2946c46a77dSDavid van Moolenbroek 
2956c46a77dSDavid van Moolenbroek 	default:
2966c46a77dSDavid van Moolenbroek 		assert(0);
2976c46a77dSDavid van Moolenbroek 	}
2986c46a77dSDavid van Moolenbroek }
2996c46a77dSDavid van Moolenbroek 
3006c46a77dSDavid van Moolenbroek /*
3016c46a77dSDavid van Moolenbroek  * Perform I/O operations, testing all the supported end-of-file access
3026c46a77dSDavid van Moolenbroek  * attempts in a random order so as to detect possible problems with caching.
3036c46a77dSDavid van Moolenbroek  */
3046c46a77dSDavid van Moolenbroek static void
do_io(void (* io_proc)(size_t,size_t,size_t))3056c46a77dSDavid van Moolenbroek do_io(void (* io_proc)(size_t, size_t, size_t))
3066c46a77dSDavid van Moolenbroek {
3076c46a77dSDavid van Moolenbroek 	static const int list[] = { BEFORE_EOF, UPTO_EOF, ACROSS_EOF,
3086c46a77dSDavid van Moolenbroek 	    ONEPAST_EOF, FROM_EOF, BEYOND_EOF };
3096c46a77dSDavid van Moolenbroek 	static const int count = sizeof(list) / sizeof(list[0]);
3106c46a77dSDavid van Moolenbroek 	int i, where[count];
3116c46a77dSDavid van Moolenbroek 
3126c46a77dSDavid van Moolenbroek 	scramble(where, list, count);
3136c46a77dSDavid van Moolenbroek 
3146c46a77dSDavid van Moolenbroek 	for (i = 0; i < count; i++)
3156c46a77dSDavid van Moolenbroek 		do_one_io(where[i], io_proc);
3166c46a77dSDavid van Moolenbroek }
3176c46a77dSDavid van Moolenbroek 
3186c46a77dSDavid van Moolenbroek /*
3196c46a77dSDavid van Moolenbroek  * Set up an image file of the given size, assign it to a VND, and open the
3206c46a77dSDavid van Moolenbroek  * resulting block device.  The size is size_t because we keep a reference copy
3216c46a77dSDavid van Moolenbroek  * of its entire contents in memory.
3226c46a77dSDavid van Moolenbroek  */
3236c46a77dSDavid van Moolenbroek static void
setup_image(size_t size)3246c46a77dSDavid van Moolenbroek setup_image(size_t size)
3256c46a77dSDavid van Moolenbroek {
3266c46a77dSDavid van Moolenbroek 	struct part_geom part;
3276c46a77dSDavid van Moolenbroek 	size_t off;
3286c46a77dSDavid van Moolenbroek 	ssize_t bytes;
3296c46a77dSDavid van Moolenbroek 	int fd, status;
3306c46a77dSDavid van Moolenbroek 
3316c46a77dSDavid van Moolenbroek 	dev_size = size;
3326c46a77dSDavid van Moolenbroek 	if ((dev_buf = malloc(dev_size)) == NULL) e(0);
3336c46a77dSDavid van Moolenbroek 	if ((dev_ref = malloc(dev_size)) == NULL) e(0);
3346c46a77dSDavid van Moolenbroek 
3356c46a77dSDavid van Moolenbroek 	if ((fd = open("image", O_CREAT | O_TRUNC | O_RDWR, 0644)) < 0) e(0);
3366c46a77dSDavid van Moolenbroek 
3376c46a77dSDavid van Moolenbroek 	fill_buf(dev_ref, dev_size);
3386c46a77dSDavid van Moolenbroek 
3396c46a77dSDavid van Moolenbroek 	for (off = 0; off < dev_size; off += bytes) {
3406c46a77dSDavid van Moolenbroek 		bytes = write(fd, &dev_ref[off], dev_size - off);
3416c46a77dSDavid van Moolenbroek 
3426c46a77dSDavid van Moolenbroek 		if (bytes <= 0) e(0);
3436c46a77dSDavid van Moolenbroek 	}
3446c46a77dSDavid van Moolenbroek 
3456c46a77dSDavid van Moolenbroek 	close(fd);
3466c46a77dSDavid van Moolenbroek 
3476c46a77dSDavid van Moolenbroek 	status = system(VNCONFIG " vnd0 image 2>/dev/null");
3486c46a77dSDavid van Moolenbroek 	if (!WIFEXITED(status)) e(0);
3496c46a77dSDavid van Moolenbroek 	if (WEXITSTATUS(status) != 0) {
3506c46a77dSDavid van Moolenbroek 		printf("skipped\n"); /* most likely cause: vnd0 is in use */
3516c46a77dSDavid van Moolenbroek 		cleanup();
3526c46a77dSDavid van Moolenbroek 		exit(0);
3536c46a77dSDavid van Moolenbroek 	}
3546c46a77dSDavid van Moolenbroek 
3556c46a77dSDavid van Moolenbroek 	need_cleanup = 1;
3566c46a77dSDavid van Moolenbroek 
3576c46a77dSDavid van Moolenbroek 	if ((dev_fd = open("/dev/vnd0", O_RDWR)) < 0) e(0);
3586c46a77dSDavid van Moolenbroek 
3596c46a77dSDavid van Moolenbroek 	if (ioctl(dev_fd, DIOCGETP, &part) < 0) e(0);
3606c46a77dSDavid van Moolenbroek 
3616c46a77dSDavid van Moolenbroek 	if (part.size != dev_size) e(0);
3626c46a77dSDavid van Moolenbroek }
3636c46a77dSDavid van Moolenbroek 
3646c46a77dSDavid van Moolenbroek /*
3656c46a77dSDavid van Moolenbroek  * Clean up the VND we set up previously.  This function is also called in case
3666c46a77dSDavid van Moolenbroek  * of an unexpected exit.
3676c46a77dSDavid van Moolenbroek  */
3686c46a77dSDavid van Moolenbroek static void
cleanup_device(void)3696c46a77dSDavid van Moolenbroek cleanup_device(void)
3706c46a77dSDavid van Moolenbroek {
3716c46a77dSDavid van Moolenbroek 	int status;
3726c46a77dSDavid van Moolenbroek 
3736c46a77dSDavid van Moolenbroek 	if (!need_cleanup)
3746c46a77dSDavid van Moolenbroek 		return;
3756c46a77dSDavid van Moolenbroek 
3766c46a77dSDavid van Moolenbroek 	if (mmap_ptr != NULL) {
3776c46a77dSDavid van Moolenbroek 		munmap(mmap_ptr, mmap_size);
3786c46a77dSDavid van Moolenbroek 
3796c46a77dSDavid van Moolenbroek 		mmap_ptr = NULL;
3806c46a77dSDavid van Moolenbroek 	}
3816c46a77dSDavid van Moolenbroek 
3826c46a77dSDavid van Moolenbroek 	if (dev_fd >= 0)
3836c46a77dSDavid van Moolenbroek 		close(dev_fd);
3846c46a77dSDavid van Moolenbroek 
3856c46a77dSDavid van Moolenbroek 	status = system(VNCONFIG " -u vnd0 2>/dev/null");
3866c46a77dSDavid van Moolenbroek 	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0);
3876c46a77dSDavid van Moolenbroek 
3886c46a77dSDavid van Moolenbroek 	need_cleanup = 0;
3896c46a77dSDavid van Moolenbroek }
3906c46a77dSDavid van Moolenbroek 
3916c46a77dSDavid van Moolenbroek /*
3926c46a77dSDavid van Moolenbroek  * Signal handler for exceptions.
3936c46a77dSDavid van Moolenbroek  */
3946c46a77dSDavid van Moolenbroek static void
got_signal(int __unused sig)3956c46a77dSDavid van Moolenbroek got_signal(int __unused sig)
3966c46a77dSDavid van Moolenbroek {
3976c46a77dSDavid van Moolenbroek 
3986c46a77dSDavid van Moolenbroek 	cleanup_device();
3996c46a77dSDavid van Moolenbroek 
4006c46a77dSDavid van Moolenbroek 	exit(1);
4016c46a77dSDavid van Moolenbroek }
4026c46a77dSDavid van Moolenbroek 
4036c46a77dSDavid van Moolenbroek /*
4046c46a77dSDavid van Moolenbroek  * Clean up the VND and image file we set up previously.
4056c46a77dSDavid van Moolenbroek  */
4066c46a77dSDavid van Moolenbroek static void
cleanup_image(void)4076c46a77dSDavid van Moolenbroek cleanup_image(void)
4086c46a77dSDavid van Moolenbroek {
4096c46a77dSDavid van Moolenbroek 	size_t off;
4106c46a77dSDavid van Moolenbroek 	ssize_t bytes;
4116c46a77dSDavid van Moolenbroek 	int fd;
4126c46a77dSDavid van Moolenbroek 
4136c46a77dSDavid van Moolenbroek 	cleanup_device();
4146c46a77dSDavid van Moolenbroek 
4156c46a77dSDavid van Moolenbroek 	if ((fd = open("image", O_RDONLY, 0644)) < 0) e(0);
4166c46a77dSDavid van Moolenbroek 
4176c46a77dSDavid van Moolenbroek 	for (off = 0; off < dev_size; off += bytes) {
4186c46a77dSDavid van Moolenbroek 		bytes = read(fd, &dev_buf[off], dev_size - off);
4196c46a77dSDavid van Moolenbroek 
4206c46a77dSDavid van Moolenbroek 		if (bytes <= 0) e(0);
4216c46a77dSDavid van Moolenbroek 	}
4226c46a77dSDavid van Moolenbroek 
4236c46a77dSDavid van Moolenbroek 	close(fd);
4246c46a77dSDavid van Moolenbroek 
4256c46a77dSDavid van Moolenbroek 	/* Have all changes written back to the device? */
4266c46a77dSDavid van Moolenbroek 	if (memcmp(dev_buf, dev_ref, dev_size)) e(0);
4276c46a77dSDavid van Moolenbroek 
4286c46a77dSDavid van Moolenbroek 	unlink("image");
4296c46a77dSDavid van Moolenbroek 
4306c46a77dSDavid van Moolenbroek 	free(dev_buf);
4316c46a77dSDavid van Moolenbroek 	free(dev_ref);
4326c46a77dSDavid van Moolenbroek }
4336c46a77dSDavid van Moolenbroek 
4346c46a77dSDavid van Moolenbroek /*
4356c46a77dSDavid van Moolenbroek  * Run the full test for a block device with the given size.
4366c46a77dSDavid van Moolenbroek  */
4376c46a77dSDavid van Moolenbroek static void
do_test(size_t size)4386c46a77dSDavid van Moolenbroek do_test(size_t size)
4396c46a77dSDavid van Moolenbroek {
4406c46a77dSDavid van Moolenbroek 	int i;
4416c46a77dSDavid van Moolenbroek 
4426c46a77dSDavid van Moolenbroek 	/*
4436c46a77dSDavid van Moolenbroek 	 * Using the three I/O primitives (read, write, peek), we run four
4446c46a77dSDavid van Moolenbroek 	 * sequences, mainly to test the effects of blocks being cached or not.
4456c46a77dSDavid van Moolenbroek 	 * We set up a new image for each sequence, because -if everything goes
4466c46a77dSDavid van Moolenbroek 	 * right- closing the device file also clears all cached blocks for it,
4476c46a77dSDavid van Moolenbroek 	 * in both the root file system's cache and the VM cache.  Note that we
4486c46a77dSDavid van Moolenbroek 	 * currently do not even attempt to push the blocks out of the root FS'
4496c46a77dSDavid van Moolenbroek 	 * cache in order to test retrieval from the VM cache, since this would
4506c46a77dSDavid van Moolenbroek 	 * involve doing a LOT of extra I/O.
4516c46a77dSDavid van Moolenbroek 	 */
4526c46a77dSDavid van Moolenbroek 	for (i = 0; i < 4; i++) {
4536c46a77dSDavid van Moolenbroek 		setup_image(size);
4546c46a77dSDavid van Moolenbroek 
4556c46a77dSDavid van Moolenbroek 		switch (i) {
4566c46a77dSDavid van Moolenbroek 		case 0:
4576c46a77dSDavid van Moolenbroek 			do_io(io_read);
4586c46a77dSDavid van Moolenbroek 
4596c46a77dSDavid van Moolenbroek 			/* FALLTHROUGH */
4606c46a77dSDavid van Moolenbroek 		case 1:
4616c46a77dSDavid van Moolenbroek 			do_io(io_write);
4626c46a77dSDavid van Moolenbroek 
4636c46a77dSDavid van Moolenbroek 			do_io(io_read);
4646c46a77dSDavid van Moolenbroek 
4656c46a77dSDavid van Moolenbroek 			break;
4666c46a77dSDavid van Moolenbroek 
4676c46a77dSDavid van Moolenbroek 		case 2:
4686c46a77dSDavid van Moolenbroek 			do_io(io_peek);
4696c46a77dSDavid van Moolenbroek 
4706c46a77dSDavid van Moolenbroek 			/* FALLTHROUGH */
4716c46a77dSDavid van Moolenbroek 
4726c46a77dSDavid van Moolenbroek 		case 3:
4736c46a77dSDavid van Moolenbroek 			do_io(io_write);
4746c46a77dSDavid van Moolenbroek 
4756c46a77dSDavid van Moolenbroek 			do_io(io_peek);
4766c46a77dSDavid van Moolenbroek 
4776c46a77dSDavid van Moolenbroek 			break;
4786c46a77dSDavid van Moolenbroek 		}
4796c46a77dSDavid van Moolenbroek 
4806c46a77dSDavid van Moolenbroek 		cleanup_image();
4816c46a77dSDavid van Moolenbroek 	}
4826c46a77dSDavid van Moolenbroek }
4836c46a77dSDavid van Moolenbroek 
4846c46a77dSDavid van Moolenbroek /*
4856c46a77dSDavid van Moolenbroek  * Test program for end-of-file conditions during block device I/O.
4866c46a77dSDavid van Moolenbroek  */
4876c46a77dSDavid van Moolenbroek int
main(void)4886c46a77dSDavid van Moolenbroek main(void)
4896c46a77dSDavid van Moolenbroek {
4906c46a77dSDavid van Moolenbroek 	static const unsigned int blocks[] = { 1, 4, 3, 5, 2 };
4916c46a77dSDavid van Moolenbroek 	struct statvfs buf;
4926c46a77dSDavid van Moolenbroek 	int i, j;
4936c46a77dSDavid van Moolenbroek 
4946c46a77dSDavid van Moolenbroek 	start(85);
4956c46a77dSDavid van Moolenbroek 
496*0a6a1f1dSLionel Sambuc 	setuid(geteuid());
497*0a6a1f1dSLionel Sambuc 
4986c46a77dSDavid van Moolenbroek 	signal(SIGINT, got_signal);
4996c46a77dSDavid van Moolenbroek 	signal(SIGABRT, got_signal);
5006c46a77dSDavid van Moolenbroek 	signal(SIGSEGV, got_signal);
5016c46a77dSDavid van Moolenbroek 	signal(SIGBUS, got_signal);
5026c46a77dSDavid van Moolenbroek 	atexit(cleanup_device);
5036c46a77dSDavid van Moolenbroek 
5046c46a77dSDavid van Moolenbroek 	srand48(time(NULL));
5056c46a77dSDavid van Moolenbroek 
5066c46a77dSDavid van Moolenbroek 	if (pipe(pipe_fd) != 0) e(0);
5076c46a77dSDavid van Moolenbroek 
5086c46a77dSDavid van Moolenbroek 	/*
5096c46a77dSDavid van Moolenbroek 	 * Get the system page size, and align all memory mapping offsets and
5106c46a77dSDavid van Moolenbroek 	 * sizes accordingly.
5116c46a77dSDavid van Moolenbroek 	 */
5126c46a77dSDavid van Moolenbroek 	page_size = sysconf(_SC_PAGESIZE);
5136c46a77dSDavid van Moolenbroek 
5146c46a77dSDavid van Moolenbroek 	/*
5156c46a77dSDavid van Moolenbroek 	 * Get the root file system block size.  In the current MINIX3 system
5166c46a77dSDavid van Moolenbroek 	 * architecture, the root file system's block size determines the
5176c46a77dSDavid van Moolenbroek 	 * transfer granularity for I/O on unmounted block devices.  If this
5186c46a77dSDavid van Moolenbroek 	 * block size is not a multiple of the page size, we are (currently!)
5196c46a77dSDavid van Moolenbroek 	 * not expecting memory-mapped block devices to work.
5206c46a77dSDavid van Moolenbroek 	 */
5216c46a77dSDavid van Moolenbroek 	if (statvfs("/", &buf) < 0) e(0);
5226c46a77dSDavid van Moolenbroek 
5236c46a77dSDavid van Moolenbroek 	block_size = buf.f_bsize;
5246c46a77dSDavid van Moolenbroek 
5256c46a77dSDavid van Moolenbroek 	test_peek = !(block_size % page_size);
5266c46a77dSDavid van Moolenbroek 
5276c46a77dSDavid van Moolenbroek 	for (i = 0; i < ITERATIONS; i++) {
5286c46a77dSDavid van Moolenbroek 		/*
5296c46a77dSDavid van Moolenbroek 		 * The 'blocks' array is scrambled so as to detect any blocks
5306c46a77dSDavid van Moolenbroek 		 * left in the VM cache (or not) across runs, just in case.
5316c46a77dSDavid van Moolenbroek 		 */
5326c46a77dSDavid van Moolenbroek 		for (j = 0; j < sizeof(blocks) / sizeof(blocks[0]); j++) {
5336c46a77dSDavid van Moolenbroek 			do_test(blocks[j] * block_size + SECTOR_SIZE);
5346c46a77dSDavid van Moolenbroek 
5356c46a77dSDavid van Moolenbroek 			do_test(blocks[j] * block_size);
5366c46a77dSDavid van Moolenbroek 
5376c46a77dSDavid van Moolenbroek 			do_test(blocks[j] * block_size - SECTOR_SIZE);
5386c46a77dSDavid van Moolenbroek 		}
5396c46a77dSDavid van Moolenbroek 	}
5406c46a77dSDavid van Moolenbroek 
5416c46a77dSDavid van Moolenbroek 	quit();
5426c46a77dSDavid van Moolenbroek }
543