--- disk-utils/Makefile.~1~	Mon Aug 30 22:34:13 1999
+++ disk-utils/Makefile	Mon Aug 30 22:32:24 1999
@@ -16,13 +16,13 @@
 
 SBIN= 		mkfs mkswap
 
-USRBIN=		fdformat setfdprm
+USRBIN=		fdformat setfdprm raw
 
 ETC=		fdprm
 
 ifneq "$(CPU)" "sparc"
 # fsck and mkfs will compile, but there is no kernel support on sparc
-MAN8:=$(MAN8) fsck.minix.8 mkfs.8 mkfs.minix.8
+MAN8:=$(MAN8) fsck.minix.8 mkfs.8 mkfs.minix.8 raw.8
 SBIN:=$(SBIN) fsck.minix mkfs.minix
 endif
 
--- disk-utils/raw.8.~1~	Mon Aug 30 22:35:02 1999
+++ disk-utils/raw.8	Mon Aug 30 23:00:21 1999
@@ -0,0 +1,87 @@
+.\" -*- nroff -*-
+.TH RAW 8 "Aug 1999" "Version 0.1"
+.SH NAME
+raw \- bind a Linux raw character device
+.SH SYNOPSIS
+.B raw
+.I /dev/raw<N> <major> <minor>
+.PP
+.B raw
+.I /dev/raw<N> /dev/<blockdev>
+.PP
+.B raw
+.B \-q
+.I /dev/raw<N>
+.PP
+.B raw
+.B \-qa
+.SH DESCRIPTION
+.B raw
+is used to bind a Linux raw character device to a block device.  Any
+block device may be used: at the time of binding, the device driver does
+not even have to be accessible (it may be loaded on demand as a kernel
+module later).
+.PP
+.B raw
+is used in two modes: it either sets raw device bindings, or it queries
+existing bindings.  When setting a raw device,
+.I /dev/raw<N>
+is the device name of an existing raw device node in the filesystem.
+The block device to which it is to be bound can be specified either in
+terms of its
+.I major
+and
+.I minor
+device numbers, or as a path name
+.I /dev/<blockdev>
+to an existing block device file.
+.PP
+The bindings already in existance can be queried with the 
+.I \-q
+option, with is used either with a raw device filename to query that one
+device, or with the 
+.I \-a
+option to query all bound raw devices.
+.PP
+Once bound to a block device, a raw device can be opened, read and
+written, just like the block device it is bound to.  However, the raw
+device does not behave exactly like the block device.  In particular,
+access to the raw device bypasses the kernel's block buffer cache
+entirely: all I/O is done directly to and from the address space of the
+process performing the I/O.  If the underlying block device driver can
+support DMA, then no data copying at all is required to complete the
+I/O.
+.PP
+Because raw I/O involves direct hardware access to a process's memory, a
+few extra restrictions must be observed.  All I/Os must be correctly
+aligned in memory and on disk: they must start at a sector offset on
+disk, they must be an exact number of sectors long, and the data buffer
+in virtual memory must also be aligned to a multiple of the sector
+size.  The sector size is 512 bytes for most devices.
+.SH OPTIONS
+.TP
+.B -q
+Set query mode.
+.B raw
+will query an existing binding instead of setting a new one.
+.TP
+.B -a
+With
+.B -q
+, specifies that all bound raw devices should be queried.
+.TP
+.B -h
+provides a usage summary.
+.SH BUGS
+The Linux 
+.B dd
+(1) command does not currently align its buffers correctly, and so
+cannot be used on raw devices.
+.PP
+Raw I/O devices do not maintain cache coherency with the Linux block
+device buffer cache.  If you use raw I/O to overwrite data already in
+the buffer cache, the buffer cache will no longer correspond to the
+contents of the actual storage device underneath.  This is deliberate,
+but is regarded either a bug or a feature depending on who you ask!
+.SH AUTHOR
+Stephen Tweedie (sct@redhat.com)
--- disk-utils/raw.c.~2~	Mon Aug 30 22:34:48 1999
+++ disk-utils/raw.c	Mon Aug 30 22:36:18 1999
@@ -0,0 +1,217 @@
+/*
+ * raw.c: User mode tool to bind and query raw character devices.
+ *
+ * Stephen Tweedie, 1999
+ *
+ * This file may be redistributed under the terms of the GNU General
+ * Public License, version 2.
+ * 
+ * Copyright Red Hat Software, 1999
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/sysmacros.h>
+#include <linux/raw.h>
+#include <linux/major.h>
+
+
+char *	progname;
+int	do_query = 0;
+int	do_query_all = 0;
+
+int	master_fd;
+int	raw_minor;
+
+void open_raw_ctl(void);
+int  query(int minor, int quiet);
+int  bind (int minor, int block_major, int block_minor);
+
+
+static void usage(int err) 
+{
+	fprintf(stderr,
+		"Usage:\n"
+		"  %s /dev/rawN <major> <minor>\n"
+		"  %s /dev/rawN /dev/<blockdev>\n"
+		"  %s -q /dev/rawN\n"
+		"  %s -qa\n",
+		progname, progname, progname, progname);
+	exit(err);
+}
+
+
+int main(int argc, char *argv[])
+{
+	char c;
+	char * raw_name;
+	char * block_name;
+	int  err;
+	int  block_major, block_minor;	
+	int  i;
+
+	struct stat statbuf;
+	
+	progname = argv[0];
+	
+	while ((c = getopt(argc, argv, "ahq")) != EOF) {
+		switch (c) {
+		case 'a':
+			do_query_all = 1;
+			break;
+		case 'h':
+			usage(0);
+		case 'q':
+			do_query = 1;
+			break;
+		default:
+			usage(1);
+		}
+	}
+	
+	/*
+	 * Check for, and open, the master raw device, /dev/raw
+	 */
+	
+	open_raw_ctl();
+	
+	if (do_query_all) {
+		if (optind < argc)
+			usage(1);
+		for (i=1; i<255; i++)
+			query(i, 1);
+		exit(0);
+	}
+	
+	/*
+	 * It's a bind or a single query.  Either way we need a raw device.
+	 */
+
+	if (optind >= argc)
+		usage(1);
+	raw_name = argv[optind++];
+
+	err = stat(raw_name, &statbuf);
+	if (err) {
+		fprintf (stderr, "Cannot locate raw device '%s' (%s)\n",
+			 raw_name, strerror(errno));
+		exit(2);
+	}
+	
+	if (!S_ISCHR(statbuf.st_mode)) {
+		fprintf (stderr, "raw device '%s' is not a character dev\n",
+			 raw_name);
+		exit(2);
+	}
+	if (major(statbuf.st_rdev) != RAW_MAJOR) {
+		fprintf (stderr, "Device '%s' is not a raw dev\n",
+			 raw_name);
+		exit(2);
+	}
+
+	raw_minor = minor(statbuf.st_rdev);
+
+	if (do_query)
+		return query(raw_minor, 0);
+	
+	/* 
+	 * It's not a query, so we still have some parsing to do.  Have
+	 * we been given a block device filename or a major/minor pair? 
+	 */
+
+	switch (argc - optind) {
+	case 1:
+		block_name = argv[optind];
+		err = stat(block_name, &statbuf);
+		if (err) {
+			fprintf (stderr,
+				 "Cannot locate block device '%s' (%s)\n",
+				 block_name, strerror(errno));
+			exit(2);
+		}
+		
+		if (!S_ISBLK(statbuf.st_mode)) {
+			fprintf (stderr, "Device '%s' is not a block dev\n",
+				 block_name);
+			exit(2);
+		}
+		
+		block_major = major(statbuf.st_rdev);
+		block_minor = minor(statbuf.st_rdev);
+		break;
+				
+	case 2:
+		block_major = strtol(argv[optind], 0, 0);
+		block_minor = strtol(argv[optind+1], 0, 0);
+		break;
+		
+	default:
+		usage(1);
+	}
+	
+	return bind(raw_minor, block_major, block_minor);
+	return 0;
+	
+}
+
+
+void open_raw_ctl(void)
+{
+	master_fd = open("/dev/raw", O_RDWR, 0);
+	if (master_fd < 0) {
+		fprintf (stderr, 
+			 "Cannot open master raw device '/dev/raw' (%s)\n",
+			 strerror(errno));
+		exit(2);
+	}
+}
+
+int query(int minor, int quiet)
+{
+	struct raw_config_request rq;
+	int err;
+	
+	rq.raw_minor = minor;
+	err = ioctl(master_fd, RAW_GETBIND, &rq);
+	if (err < 0) {
+		if (quiet && errno == ENODEV)
+			return 3;
+		fprintf (stderr, 
+			 "Error querying raw device (%s)\n",
+			 strerror(errno));
+		exit(3);
+	}
+	if (quiet && !rq.block_major && !rq.block_minor)
+		return 0;
+	printf ("/dev/raw%d:	bound to major %d, minor %d\n",
+		minor, (int) rq.block_major, (int) rq.block_minor);
+	return 0;
+}
+
+int bind(int minor, int block_major, int block_minor)
+{
+	struct raw_config_request rq;
+	int err;
+	
+	rq.raw_minor   = minor;
+	rq.block_major = block_major;
+	rq.block_minor = block_minor;
+	err = ioctl(master_fd, RAW_SETBIND, &rq);
+	if (err < 0) {
+		fprintf (stderr, 
+			 "Error setting raw device (%s)\n",
+			 strerror(errno));
+		exit(3);
+	}
+	printf ("/dev/raw%d:	bound to major %d, minor %d\n",
+		raw_minor, (int) rq.block_major, (int) rq.block_minor);
+	return 0;
+}
+