Extend --readonly

- Never allow open of a data file with writeable bits sets, and that
  includes extend as well.

- Skip any writes in replay logs (iolog/iolog2/blktrace)

Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
diff --git a/blktrace.c b/blktrace.c
index 58f28ce..64e49f5 100644
--- a/blktrace.c
+++ b/blktrace.c
@@ -233,7 +233,7 @@
 {
 	unsigned long long ttime, delay;
 	struct blk_io_trace t;
-	unsigned long ios[2];
+	unsigned long ios[2], skipped_writes;
 	unsigned int cpu;
 	unsigned int rw_bs[2];
 	struct fifo *fifo;
@@ -253,6 +253,7 @@
 	ttime = 0;
 	ios[0] = ios[1] = 0;
 	rw_bs[0] = rw_bs[1] = 0;
+	skipped_writes = 0;
 	do {
 		int ret = trace_fifo_get(td, fifo, fd, &t, sizeof(t));
 
@@ -290,7 +291,10 @@
 		delay = 0;
 		if (cpu == t.cpu)
 			delay = t.time - ttime;
-		handle_trace(td, &t, delay, ios, rw_bs);
+		if ((t.action & BLK_TC_ACT(BLK_TC_WRITE)) && read_only)
+			skipped_writes++;
+		else
+			handle_trace(td, &t, delay, ios, rw_bs);
 		ttime = t.time;
 		cpu = t.cpu;
 	} while (1);
@@ -298,6 +302,9 @@
 	fifo_free(fifo);
 	close(fd);
 
+	if (skipped_writes)
+		log_err("fio: <%s> skips replay of %lu writes due to read-only\n", td->o.name, skipped_writes);
+
 	if (!ios[DDIR_READ] && !ios[DDIR_WRITE]) {
 		log_err("fio: found no ios in blktrace data\n");
 		return 1;
diff --git a/filesetup.c b/filesetup.c
index ff1385c..47ddacf 100644
--- a/filesetup.c
+++ b/filesetup.c
@@ -18,6 +18,11 @@
 	unsigned int bs;
 	char *b;
 
+	if (read_only) {
+		log_err("fio: refusing extend of file due to read-only\n");
+		return 0;
+	}
+
 	/*
 	 * check if we need to lay the file out complete again. fio
 	 * does that for operations involving reads, or for writes
@@ -228,6 +233,8 @@
 		flags |= O_SYNC;
 
 	if (td_write(td)) {
+		assert(!read_only);
+
 		flags |= O_RDWR;
 
 		if (f->filetype == FIO_TYPE_FILE)
@@ -238,7 +245,7 @@
 		else
 			f->fd = open(f->file_name, flags, 0600);
 	} else {
-		if (f->filetype == FIO_TYPE_CHAR)
+		if (f->filetype == FIO_TYPE_CHAR && !read_only)
 			flags |= O_RDWR;
 		else
 			flags |= O_RDONLY;
diff --git a/fio.h b/fio.h
index 5177795..20d1408 100644
--- a/fio.h
+++ b/fio.h
@@ -661,6 +661,7 @@
 extern int temp_stall_ts;
 extern unsigned long long mlock_size;
 extern unsigned long page_mask, page_size;
+extern int read_only;
 
 extern struct thread_data *threads;
 
diff --git a/init.c b/init.c
index 2a1ee14..89c66af 100644
--- a/init.c
+++ b/init.c
@@ -24,7 +24,6 @@
 static char **ini_file;
 static int max_jobs = MAX_JOBS;
 static int dump_cmdline;
-static int read_only;
 
 struct thread_data def_thread;
 struct thread_data *threads = NULL;
@@ -36,6 +35,7 @@
 FILE *f_err = NULL;
 
 int write_bw_log = 0;
+int read_only = 0;
 
 static int def_timeout = 0;
 static int write_lat_log = 0;
diff --git a/log.c b/log.c
index 759771a..66b8f5b 100644
--- a/log.c
+++ b/log.c
@@ -233,9 +233,14 @@
 			
 		if (rw == DDIR_READ)
 			reads++;
-		else if (rw == DDIR_WRITE)
+		else if (rw == DDIR_WRITE) {
 			writes++;
-		else if (rw != DDIR_SYNC && rw != DDIR_INVAL) {
+			/*
+			 * Don't add a write for ro mode
+			 */
+			if (read_only)
+				continue;
+		} else if (rw != DDIR_SYNC && rw != DDIR_INVAL) {
 			log_err("bad ddir: %d\n", rw);
 			continue;
 		}
@@ -263,6 +268,11 @@
 	free(act);
 	free(fname);
 
+	if (writes && read_only) {
+		log_err("fio: <%s> skips replay of %d writes due to read-only\n", td->o.name, writes);
+		writes = 0;
+	}
+
 	if (!reads && !writes)
 		return 1;
 	else if (reads && !writes)
@@ -301,9 +311,14 @@
 		}
 		if (rw == DDIR_READ)
 			reads++;
-		else if (rw == DDIR_WRITE)
+		else if (rw == DDIR_WRITE) {
 			writes++;
-		else if (rw != DDIR_SYNC) {
+			/*
+			 * Don't add a write for ro mode
+			 */
+			if (read_only)
+				continue;
+		} else if (rw != DDIR_SYNC) {
 			log_err("bad ddir: %d\n", rw);
 			continue;
 		}
@@ -322,6 +337,11 @@
 
 	free(str);
 
+	if (writes && read_only) {
+		log_err("fio: <%s> skips replay of %d writes due to read-only\n", td->o.name, writes);
+		writes = 0;
+	}
+
 	if (!reads && !writes)
 		return 1;
 	else if (reads && !writes)