Full readonly check

Both in core and in engines. To the extent possible, this should catch
even fio errors.

Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
diff --git a/engines/guasi.c b/engines/guasi.c
index 7db09c3..69b2cd2 100644
--- a/engines/guasi.c
+++ b/engines/guasi.c
@@ -124,6 +124,8 @@
 {
 	struct guasi_data *ld = td->io_ops->data;
 
+	fio_ro_check(td, io_u);
+
 	GDBG_PRINT(("fio_guasi_queue(%p)\n", io_u));
 	if (ld->queued_nr == (int) td->o.iodepth)
 		return FIO_Q_BUSY;
diff --git a/engines/libaio.c b/engines/libaio.c
index f8990c1..8f67711 100644
--- a/engines/libaio.c
+++ b/engines/libaio.c
@@ -86,6 +86,8 @@
 {
 	struct libaio_data *ld = td->io_ops->data;
 
+	fio_ro_check(td, io_u);
+
 	if (ld->iocbs_nr == (int) td->o.iodepth)
 		return FIO_Q_BUSY;
 
diff --git a/engines/mmap.c b/engines/mmap.c
index f3d55c1..604f8b0 100644
--- a/engines/mmap.c
+++ b/engines/mmap.c
@@ -19,6 +19,8 @@
 	struct fio_file *f = io_u->file;
 	unsigned long long real_off = io_u->offset - f->file_offset;
 
+	fio_ro_check(td, io_u);
+
 	if (io_u->ddir == DDIR_READ)
 		memcpy(io_u->xfer_buf, f->mmap + real_off, io_u->xfer_buflen);
 	else if (io_u->ddir == DDIR_WRITE)
diff --git a/engines/net.c b/engines/net.c
index 0adc006..552ad0b 100644
--- a/engines/net.c
+++ b/engines/net.c
@@ -198,6 +198,8 @@
 	struct netio_data *nd = td->io_ops->data;
 	int ret;
 
+	fio_ro_check(td, io_u);
+
 	if (io_u->ddir == DDIR_WRITE) {
 		if (nd->use_splice)
 			ret = fio_netio_splice_out(td, io_u);
diff --git a/engines/null.c b/engines/null.c
index 823d40d..bbb4e8a 100644
--- a/engines/null.c
+++ b/engines/null.c
@@ -56,6 +56,8 @@
 {
 	struct null_data *nd = td->io_ops->data;
 
+	fio_ro_check(td, io_u);
+
 	if (td->io_ops->flags & FIO_SYNCIO)
 		return FIO_Q_COMPLETED;
 	if (nd->events)
diff --git a/engines/posixaio.c b/engines/posixaio.c
index 88ed402..9ff05c4 100644
--- a/engines/posixaio.c
+++ b/engines/posixaio.c
@@ -148,6 +148,8 @@
 	struct aiocb *aiocb = &io_u->aiocb;
 	int ret;
 
+	fio_ro_check(td, io_u);
+
 	if (io_u->ddir == DDIR_READ)
 		ret = aio_read(aiocb);
 	else if (io_u->ddir == DDIR_WRITE)
diff --git a/engines/sg.c b/engines/sg.c
index cc50d6b..790019e 100644
--- a/engines/sg.c
+++ b/engines/sg.c
@@ -242,6 +242,8 @@
 	struct sg_io_hdr *hdr = &io_u->hdr;
 	int ret;
 
+	fio_ro_check(td, io_u);
+
 	ret = fio_sgio_doio(td, io_u, io_u->ddir == DDIR_SYNC);
 
 	if (ret < 0)
diff --git a/engines/skeleton_external.c b/engines/skeleton_external.c
index 9000acc..26dbedf 100644
--- a/engines/skeleton_external.c
+++ b/engines/skeleton_external.c
@@ -65,6 +65,11 @@
 static int fio_skeleton_queue(struct thread_data *td, struct io_u *io_u)
 {
 	/*
+	 * Double sanity check to catch errant write on a readonly setup
+	 */
+	fio_ro_check(td, io_u);
+
+	/*
 	 * Could return FIO_Q_QUEUED for a queued request,
 	 * FIO_Q_COMPLETED for a completed request, and FIO_Q_BUSY
 	 * if we could queue no more at this point (you'd have to
diff --git a/engines/splice.c b/engines/splice.c
index 440196b..2344fdd 100644
--- a/engines/splice.c
+++ b/engines/splice.c
@@ -185,6 +185,8 @@
 	struct spliceio_data *sd = td->io_ops->data;
 	int ret;
 
+	fio_ro_check(td, io_u);
+
 	if (io_u->ddir == DDIR_READ) {
 		if (sd->vmsplice_to_user) {
 			ret = fio_splice_read(td, io_u);
diff --git a/engines/sync.c b/engines/sync.c
index ee8e3c3..597ee01 100644
--- a/engines/sync.c
+++ b/engines/sync.c
@@ -35,6 +35,8 @@
 	struct fio_file *f = io_u->file;
 	int ret;
 
+	fio_ro_check(td, io_u);
+
 	if (io_u->ddir == DDIR_READ)
 		ret = read(f->fd, io_u->xfer_buf, io_u->xfer_buflen);
 	else if (io_u->ddir == DDIR_WRITE)
diff --git a/engines/syslet-rw.c b/engines/syslet-rw.c
index 7e407d7..e0dddd8 100644
--- a/engines/syslet-rw.c
+++ b/engines/syslet-rw.c
@@ -253,6 +253,8 @@
 {
 	struct syslet_data *sd = td->io_ops->data;
 
+	fio_ro_check(td, io_u);
+
 	if (sd->tail) {
 		sd->tail->next = &io_u->req.atom;
 		sd->tail = &io_u->req.atom;
diff --git a/fio.h b/fio.h
index 20d1408..1b392a0 100644
--- a/fio.h
+++ b/fio.h
@@ -13,6 +13,7 @@
 #include <string.h>
 #include <getopt.h>
 #include <inttypes.h>
+#include <assert.h>
 
 #include "compiler/compiler.h"
 #include "list.h"
@@ -670,6 +671,11 @@
 #define td_rw(td)		(((td)->o.td_ddir & TD_DDIR_RW) == TD_DDIR_RW)
 #define td_random(td)		((td)->o.td_ddir & TD_DDIR_RAND)
 
+static inline void fio_ro_check(struct thread_data *td, struct io_u *io_u)
+{
+	assert(!(io_u->ddir == DDIR_WRITE && !td_write(td)));
+}
+
 #define BLOCKS_PER_MAP		(8 * sizeof(long))
 #define TO_MAP_BLOCK(td, f, b)	((b) - ((f)->file_offset / (td)->o.rw_min_bs))
 #define RAND_MAP_IDX(td, f, b)	(TO_MAP_BLOCK(td, f, b) / BLOCKS_PER_MAP)
diff --git a/ioengines.c b/ioengines.c
index 8e6fae2..cf8c2f1 100644
--- a/ioengines.c
+++ b/ioengines.c
@@ -157,6 +157,8 @@
 
 int td_io_prep(struct thread_data *td, struct io_u *io_u)
 {
+	fio_ro_check(td, io_u);
+
 	if (td->io_ops->prep)
 		return td->io_ops->prep(td, io_u);
 
@@ -182,13 +184,13 @@
 {
 	int ret;
 
+	fio_ro_check(td, io_u);
+
 	assert((io_u->flags & IO_U_F_FLIGHT) == 0);
 	io_u->flags |= IO_U_F_FLIGHT;
 
 	assert(io_u->file->flags & FIO_FILE_OPEN);
 
-	assert(!(io_u->ddir == DDIR_WRITE && !td_write(td)));
-
 	io_u->error = 0;
 	io_u->resid = 0;