ttypa ttypb ttypc ttypd ttype ttypf \
ttyq0 ttyq1 ttyq2 ttyq3 ttyq4 ttyq5 ttyq6 ttyq7 ttyq8 ttyq9 \
ttyqa ttyqb ttyqc ttyqd ttyqe ttyqf \
- eth klog random rescue
+ eth klog random rescue filter
;;
0:|1:-\?)
cat >&2 <<EOF
kbd # Make /dev/kbd
kbdaux # Make /dev/kbdaux
rescue # Make /dev/rescue
+ filter # Make /dev/filter
video # Make /dev/video
std # All standard devices
EOF
$e mknod klog c 15 0
$e chmod 600 klog
;;
+ filter)
+ # filter driver
+ $e mknod filter b 11 0
+ $e chmod 644 filter
+ ;;
*)
echo "$0: don't know about $dev" >&2
ex=1
cd ./dpeth && $(MAKE) $@
cd ./log && $(MAKE) $@
cd ./bios_wini && $(MAKE) $@
+ cd ./filter && $(MAKE) $@
cd ./random && $(MAKE) $@
cd ./readclock && $(MAKE) $@
cd ./dp8390 && $(MAKE) $@
--- /dev/null
+# Makefile for filter driver
+DRIVER = filter
+
+# programs, flags, etc.
+CC = cc
+CFLAGS = -DDEBUG=1 -DDEBUG2=0
+LDFLAGS =
+LIBS = -lsys
+
+OBJ = main.o sum.o driver.o util.o optset.o crc.o md5.o
+
+# build local binary
+all build: $(DRIVER)
+$(DRIVER): $(OBJ)
+ $(CC) -o $@ $(LDFLAGS) $(OBJ) $(LIBS)
+
+# install with other drivers
+install: /usr/sbin/$(DRIVER)
+/usr/sbin/$(DRIVER): $(DRIVER)
+ install -o root -c $? $@
+
+# clean up local files
+clean:
+ rm -f *.o *.bak $(DRIVER)
+
+depend:
+ mkdep "$(CC) -E $(CPPFLAGS)" *.c > .depend
+
+# Include generated dependencies.
+include .depend
+
--- /dev/null
+/* CRC32 implementation taken from cksum.c */
+
+/* Copyright 1991 by Vincent Archer
+ * You may freely redistribute this software, in source or binary
+ * form, provided that you do not alter this copyright mention in any
+ * way.
+ */
+
+#include <sys/types.h>
+
+unsigned long crctab[] = {
+ 0x7fffffff,
+ 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e,
+ 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+ 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d,
+ 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0,
+ 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63,
+ 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa,
+ 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75,
+ 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180,
+ 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+ 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87,
+ 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5,
+ 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
+ 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4,
+ 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b,
+ 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea,
+ 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541,
+ 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc,
+ 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f,
+ 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
+ 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e,
+ 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c,
+ 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+ 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b,
+ 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2,
+ 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671,
+ 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8,
+ 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767,
+ 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+ 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6,
+ 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795,
+ 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b,
+ 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
+ 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82,
+ 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d,
+ 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8,
+ 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff,
+ 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee,
+ 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d,
+ 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
+ 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c,
+ 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02,
+ 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+unsigned long compute_crc( unsigned char *b, size_t n)
+{
+ int i;
+ unsigned long s = 0;
+ int aux = 0;
+
+ while (n-- > 0) {
+ /* Compute the index to the crc table */
+ i = (s >> 24) ^ ((unsigned int) (*b++));
+
+ if (i == 0) {
+ /* Replace an intermediate zero with the next value
+ * from the sequence */
+ i = aux++;
+ if (aux >= sizeof(crctab) / sizeof(crctab[0])) aux = 0;
+ }
+
+ /* New checksum value */
+ s = (s << 8) ^ crctab[i];
+ }
+ return(s);
+}
+
--- /dev/null
+#ifndef _CRC_H
+#define _CRC_H
+
+extern unsigned long compute_crc(unsigned char *b, size_t n);
+
+#endif /* _CRC_H */
--- /dev/null
+/* Filter driver - lowest layer - disk driver management */
+
+#include "inc.h"
+
+/* Drivers. */
+static struct {
+ char *label;
+ int minor;
+ endpoint_t endpt;
+
+ int problem; /* one of BD_* */
+ int error; /* one of E*, only relevant if problem>0 */
+ int retries;
+ int kills;
+} driver[2];
+
+/* State variables. */
+static endpoint_t self_ep;
+static asynmsg_t amsgtable[2];
+
+static int size_known = 0;
+static u64_t disk_size;
+
+static int problem_stats[BD_LAST] = { 0 };
+
+/*===========================================================================*
+ * driver_open *
+ *===========================================================================*/
+static int driver_open(int which)
+{
+ /* Perform an open or close operation on the driver. This is
+ * unfinished code: we should never be doing a blocking sendrec() to
+ * the driver.
+ */
+ message msg;
+ cp_grant_id_t gid;
+ struct partition part;
+ sector_t sectors;
+ int r;
+
+ msg.m_type = DEV_OPEN;
+ msg.DEVICE = driver[which].minor;
+ msg.IO_ENDPT = self_ep;
+ r = sendrec(driver[which].endpt, &msg);
+
+ if (r != OK) {
+ /* Should we restart the driver now? */
+ printf("Filter: driver_open: sendrec returned %d\n", r);
+
+ return RET_REDO;
+ }
+
+ if(msg.m_type != TASK_REPLY || msg.REP_STATUS != OK) {
+ printf("Filter: driver_open: sendrec returned %d, %d\n",
+ msg.m_type, msg.REP_STATUS);
+
+ return RET_REDO;
+ }
+
+ /* Take the opportunity to retrieve the hard disk size. */
+ gid = cpf_grant_direct(driver[which].endpt,
+ (vir_bytes) &part, sizeof(part), CPF_WRITE);
+ if(!GRANT_VALID(gid))
+ panic(__FILE__, "invalid grant", gid);
+
+ msg.m_type = DEV_IOCTL_S;
+ msg.REQUEST = DIOCGETP;
+ msg.DEVICE = driver[which].minor;
+ msg.IO_ENDPT = self_ep;
+ msg.IO_GRANT = (char *) gid;
+
+ r = sendrec(driver[which].endpt, &msg);
+
+ cpf_revoke(gid);
+
+ if (r != OK || msg.m_type != TASK_REPLY || msg.REP_STATUS != OK) {
+ /* Not sure what to do here, either. */
+ printf("Filter: ioctl(DIOCGETP) returned (%d, %d)\n",
+ r, msg.m_type);
+
+ return RET_REDO;
+ }
+
+ if(!size_known) {
+ disk_size = part.size;
+ size_known = 1;
+ sectors = div64u(disk_size, SECTOR_SIZE);
+ if(cmp64(mul64u(sectors, SECTOR_SIZE), disk_size)) {
+ printf("Filter: partition too large\n");
+
+ return RET_REDO;
+ }
+#if DEBUG
+ printf("Filter: partition size: 0x%s / %lu sectors\n",
+ print64(disk_size), sectors);
+#endif
+ } else {
+ if(cmp64(disk_size, part.size)) {
+ printf("Filter: partition size mismatch (%s != %s)\n",
+ print64(part.size), print64(disk_size));
+
+ return RET_REDO;
+ }
+ }
+
+ return OK;
+}
+
+/*===========================================================================*
+ * driver_close *
+ *===========================================================================*/
+static int driver_close(int which)
+{
+ message msg;
+ int r;
+
+ msg.m_type = DEV_CLOSE;
+ msg.DEVICE = driver[which].minor;
+ msg.IO_ENDPT = self_ep;
+ r = sendrec(driver[which].endpt, &msg);
+
+ if (r != OK) {
+ /* Should we restart the driver now? */
+ printf("Filter: driver_close: sendrec returned %d\n", r);
+
+ return RET_REDO;
+ }
+
+ if(msg.m_type != TASK_REPLY || msg.REP_STATUS != OK) {
+ printf("Filter: driver_close: sendrec returned %d, %d\n",
+ msg.m_type, msg.REP_STATUS);
+
+ return RET_REDO;
+ }
+
+ return OK;
+}
+
+/*===========================================================================*
+ * driver_init *
+ *===========================================================================*/
+void driver_init(void)
+{
+ /* Initialize the driver layer. */
+ int r;
+
+ self_ep = getprocnr();
+
+ memset(driver, 0, sizeof(driver));
+
+ /* Endpoints unknown. */
+ driver[DRIVER_MAIN].endpt = NONE;
+ driver[DRIVER_BACKUP].endpt = NONE;
+
+ /* Get disk driver's and this proc's endpoint. */
+ driver[DRIVER_MAIN].label = MAIN_LABEL;
+ driver[DRIVER_MAIN].minor = MAIN_MINOR;
+
+ r = ds_retrieve_u32(driver[DRIVER_MAIN].label,
+ (u32_t *) &driver[DRIVER_MAIN].endpt);
+ if (r != OK) {
+ printf("Filter: failed to get main disk driver's endpoint: "
+ "%d\n", r);
+ bad_driver(DRIVER_MAIN, BD_DEAD, EFAULT);
+ check_driver(DRIVER_MAIN);
+ }
+ else if (driver_open(DRIVER_MAIN) != OK) {
+ panic(__FILE__, "unhandled driver_open failure", NO_NUM);
+ }
+
+ if(USE_MIRROR) {
+ driver[DRIVER_BACKUP].label = BACKUP_LABEL;
+ driver[DRIVER_BACKUP].minor = BACKUP_MINOR;
+
+ if(!strcmp(driver[DRIVER_MAIN].label,
+ driver[DRIVER_BACKUP].label)) {
+ panic(__FILE__, "same driver: not tested", NO_NUM);
+ }
+
+ r = ds_retrieve_u32(driver[DRIVER_BACKUP].label,
+ (u32_t *) &driver[DRIVER_BACKUP].endpt);
+ if (r != OK) {
+ printf("Filter: failed to get backup disk driver's "
+ "endpoint: %d\n", r);
+ bad_driver(DRIVER_BACKUP, BD_DEAD, EFAULT);
+ check_driver(DRIVER_BACKUP);
+ }
+ else if (driver_open(DRIVER_BACKUP) != OK) {
+ panic(__FILE__, "unhandled driver_open failure",
+ NO_NUM);
+ }
+ }
+}
+
+/*===========================================================================*
+ * driver_shutdown *
+ *===========================================================================*/
+void driver_shutdown(void)
+{
+ /* Clean up. */
+
+#if DEBUG
+ printf("Filter: %u driver deaths, %u protocol errors, "
+ "%u data errors\n", problem_stats[BD_DEAD],
+ problem_stats[BD_PROTO], problem_stats[BD_DATA]);
+#endif
+
+ if(driver_close(DRIVER_MAIN) != OK)
+ printf("Filter: DEV_CLOSE failed on shutdown (1)\n");
+
+ if(USE_MIRROR)
+ if(driver_close(DRIVER_BACKUP) != OK)
+ printf("Filter: DEV_CLOSE failed on shutdown (2)\n");
+}
+
+/*===========================================================================*
+ * get_raw_size *
+ *===========================================================================*/
+u64_t get_raw_size(void)
+{
+ /* Return the size of the raw disks as used by the filter driver.
+ */
+
+ return disk_size;
+}
+
+/*===========================================================================*
+ * reset_kills *
+ *===========================================================================*/
+void reset_kills(void)
+{
+ /* Reset kill and retry statistics. */
+ driver[DRIVER_MAIN].kills = 0;
+ driver[DRIVER_MAIN].retries = 0;
+ driver[DRIVER_BACKUP].kills = 0;
+ driver[DRIVER_BACKUP].retries = 0;
+}
+
+/*===========================================================================*
+ * bad_driver *
+ *===========================================================================*/
+int bad_driver(int which, int type, int error)
+{
+ /* A disk driver has died or produced an error. Mark it so that we can
+ * deal with it later, and return RET_REDO to indicate that the
+ * current operation is to be retried. Also store an error code to
+ * return to the user if the situation is unrecoverable.
+ */
+ driver[which].problem = type;
+ driver[which].error = error;
+
+ return RET_REDO;
+}
+
+/*===========================================================================*
+ * new_driver_ep *
+ *===========================================================================*/
+static int new_driver_ep(int which)
+{
+ /* See if a new driver instance has already been started for the given
+ * driver, by retrieving its entry from DS.
+ */
+ int r;
+ endpoint_t endpt;
+
+ r = ds_retrieve_u32(driver[which].label, (u32_t *) &endpt);
+
+ if (r != OK) {
+ printf("Filter: DS query for %s failed\n",
+ driver[which].label);
+
+ return 0;
+ }
+
+ if (endpt == driver[which].endpt) {
+#if DEBUG
+ printf("Filter: same endpoint for %s\n", driver[which].label);
+#endif
+ return 0;
+ }
+
+#if DEBUG
+ printf("Filter: new enpdoint for %s: %d -> %d\n", driver[which].label,
+ driver[which].endpt, endpt);
+#endif
+
+ driver[which].endpt = endpt;
+
+ return 1;
+}
+
+/*===========================================================================*
+ * check_problem *
+ *===========================================================================*/
+static int check_problem(int which, int problem, int retries, int *tell_rs)
+{
+ /* A problem has occurred with a driver. Update statistics, and decide
+ * what to do. If EAGAIN is returned, the driver should be restarted;
+ * any other result will be passed up.
+ */
+
+#if DEBUG
+ printf("Filter: check_driver processing driver %d, problem %d\n",
+ which, problem);
+#endif
+
+ problem_stats[problem]++;
+
+ if(new_driver_ep(which)) {
+#if DEBUG
+ printf("Filter: check_problem: noticed a new driver\n");
+#endif
+
+ if(driver_open(which) == OK) {
+#if DEBUG2
+ printf("Filter: open OK -> no recovery\n");
+#endif
+ return OK;
+ } else {
+#if DEBUG2
+ printf("Filter: open not OK -> recovery\n");
+#endif
+ problem = BD_PROTO;
+ problem_stats[problem]++;
+ }
+ }
+
+ /* If the driver has died, we always need to restart it. If it has
+ * been giving problems, we first retry the request, up to N times,
+ * after which we kill and restart the driver. We restart the driver
+ * up to M times, after which we remove the driver from the mirror
+ * configuration. If we are not set up to do mirroring, we can only
+ * do one thing, and that is continue to limp along with the bad
+ * driver..
+ */
+ switch(problem) {
+ case BD_PROTO:
+ case BD_DATA:
+ driver[which].retries++;
+
+#if DEBUG
+ printf("Filter: disk driver %d has had "
+ "%d/%d retry attempts, %d/%d kills\n", which,
+ driver[which].retries, NR_RETRIES,
+ driver[which].kills, NR_RESTARTS);
+#endif
+
+ if (driver[which].retries < NR_RETRIES) {
+ if(retries == 1) {
+#if DEBUG
+ printf("Filter: not restarting; retrying "
+ "(retries %d/%d, kills %d/%d)\n",
+ driver[which].retries, NR_RETRIES,
+ driver[which].kills, NR_RESTARTS);
+#endif
+ return OK;
+ }
+#if DEBUG
+ printf("Filter: restarting (retries %d/%d, "
+ "kills %d/%d, internal retry %d)\n",
+ driver[which].retries, NR_RETRIES,
+ driver[which].kills, NR_RESTARTS, retries);
+#endif
+ }
+
+#if DEBUG
+ printf("Filter: disk driver %d has reached error "
+ "threshold, restarting driver\n", which);
+#endif
+
+ *tell_rs = 1;
+ break;
+
+ case BD_DEAD:
+ /* Can't kill that which is already dead.. */
+ *tell_rs = 0;
+ break;
+
+ default:
+ panic(__FILE__, "invalid problem", problem);
+ }
+
+ /* At this point, the driver will be restarted. */
+ driver[which].retries = 0;
+ driver[which].kills++;
+
+ if (driver[which].kills < NR_RESTARTS)
+ return EAGAIN;
+
+ /* We've reached the maximum number of restarts for this driver. */
+ if (USE_MIRROR) {
+ printf("Filter: kill threshold reached, disabling mirroring\n");
+
+ USE_MIRROR = 0;
+
+ if (which == DRIVER_MAIN) {
+ driver[DRIVER_MAIN] = driver[DRIVER_BACKUP];
+
+ /* This is not necessary. */
+ strcpy(MAIN_LABEL, BACKUP_LABEL);
+ MAIN_MINOR = BACKUP_MINOR;
+ }
+
+ driver[DRIVER_BACKUP].endpt = NONE;
+
+ return OK;
+ }
+ else {
+ /* We tried, we really did. But now we give up. Tell the user.
+ */
+ printf("Filter: kill threshold reached, returning error\n");
+
+ if (driver[which].error == EAGAIN) return EIO;
+
+ return driver[which].error;
+ }
+}
+
+/*===========================================================================*
+ * restart_driver *
+ *===========================================================================*/
+static void restart_driver(int which, int tell_rs)
+{
+ /* Restart the given driver. Block until the new instance is up.
+ */
+ message msg;
+ endpoint_t endpt;
+ int r, w = 0;
+
+ if (tell_rs) {
+ /* Tell RS to refresh or restart the driver */
+ msg.m_type = RS_REFRESH;
+ msg.RS_CMD_ADDR = driver[which].label;
+ msg.RS_CMD_LEN = strlen(driver[which].label);
+
+#if DEBUG
+ printf("Filter: asking RS to refresh %s..\n",
+ driver[which].label);
+#endif
+
+ r = sendrec(RS_PROC_NR, &msg);
+
+ if (r != OK || msg.m_type != OK)
+ panic(__FILE__, "RS request failed", r);
+
+#if DEBUG
+ printf("Filter: RS call succeeded\n");
+#endif
+ }
+
+ /* Wait until the new driver instance is up, and get its endpoint. */
+#if DEBUG
+ printf("Filter: endpoint update driver %d; old endpoint %d\n",
+ which, driver[which].endpt);
+#endif
+
+ do {
+ if(w) flt_sleep(1);
+ w = 1;
+
+ r = ds_retrieve_u32(driver[which].label, (u32_t *) &endpt);
+
+#if DEBUG2
+ if (r != OK)
+ printf("Filter: DS request failed (%d)\n", r);
+ else if (endpt == driver[which].endpt)
+ printf("Filter: DS returned same endpoint\n");
+ else
+ printf("Filter: DS request OK, new endpoint\n");
+#endif
+ } while (r != OK || endpt == driver[which].endpt);
+
+ driver[which].endpt = endpt;
+}
+
+/*===========================================================================*
+ * check_driver *
+ *===========================================================================*/
+int check_driver(int which)
+{
+ /* See if the given driver has been troublesome, and if so, deal with
+ * it.
+ */
+ int problem, tell_rs;
+ int r, retries = 0;
+
+ problem = driver[which].problem;
+
+ if (problem == BD_NONE)
+ return OK;
+
+ do {
+ if(retries) {
+#if DEBUG
+ printf("Filter: check_driver: retry number %d\n",
+ retries);
+#endif
+ problem = BD_PROTO;
+ }
+ retries++;
+ driver[which].problem = BD_NONE;
+
+ /* Decide what to do: continue operation, restart the driver,
+ * or return an error.
+ */
+ r = check_problem(which, problem, retries, &tell_rs);
+ if (r != EAGAIN)
+ return r;
+
+ /* Restarting the driver it is. First tell RS (if necessary),
+ * then wait for the new driver instance to come up.
+ */
+ restart_driver(which, tell_rs);
+
+ /* Finally, open the device on the new driver */
+ } while (driver_open(which) != OK);
+
+#if DEBUG
+ printf("Filter: check_driver restarted driver %d, endpoint %d\n",
+ which, driver[which].endpt);
+#endif
+
+ return OK;
+}
+
+/*===========================================================================*
+ * flt_senda *
+ *===========================================================================*/
+static int flt_senda(message *mess, int which)
+{
+ /* Send a message to one driver. Can only return OK at the moment. */
+ int r;
+ asynmsg_t *amp;
+
+ /* Fill in the last bits of the message. */
+ mess->DEVICE = driver[which].minor;
+ mess->IO_ENDPT = self_ep;
+
+ /* Send the message asynchronously. */
+ amp = &amsgtable[which];
+ amp->dst = driver[which].endpt;
+ amp->msg = *mess;
+ amp->flags = AMF_VALID;
+ r = senda(amsgtable, 2);
+
+ if(r != OK)
+ panic(__FILE__, "senda returned error", r);
+
+ return r;
+}
+
+/*===========================================================================*
+ * check_senda *
+ *===========================================================================*/
+static int check_senda(int which)
+{
+ /* Check whether an earlier senda resulted in an error indicating the
+ * message never got delivered. Only in that case can we reliably say
+ * that the driver died. Return BD_DEAD in this case, and BD_PROTO
+ * otherwise.
+ */
+ asynmsg_t *amp;
+
+ amp = &amsgtable[which];
+
+ if ((amp->flags & AMF_DONE) &&
+ (amp->result == EDEADSRCDST || amp->result == EDSTDIED)) {
+
+ return BD_DEAD;
+ }
+
+ return BD_PROTO;
+}
+
+/*===========================================================================*
+ * flt_receive *
+ *===========================================================================*/
+static int flt_receive(message *mess, int which)
+{
+ /* Receive a message from one or either driver, unless a timeout
+ * occurs. Can only return OK or RET_REDO.
+ */
+ int r;
+
+ for (;;) {
+ r = receive(ANY, mess);
+ if(r != OK)
+ panic(__FILE__, "receive returned error", r);
+
+ if(mess->m_source == CLOCK && is_notify(mess->m_type)) {
+ if (mess->NOTIFY_TIMESTAMP < flt_alarm(-1)) {
+#if DEBUG
+ printf("Filter: SKIPPING old alarm "
+ "notification\n");
+#endif
+ continue;
+ }
+
+#if DEBUG
+ printf("Filter: timeout waiting for disk driver %d "
+ "reply!\n", which);
+#endif
+
+ /* If we're waiting for either driver,
+ * both are at fault.
+ */
+ if (which < 0) {
+ bad_driver(DRIVER_MAIN,
+ check_senda(DRIVER_MAIN), EFAULT);
+
+ return bad_driver(DRIVER_BACKUP,
+ check_senda(DRIVER_BACKUP), EFAULT);
+ }
+
+ /* Otherwise, just report the one not replying as dead.
+ */
+ return bad_driver(which, check_senda(which), EFAULT);
+ }
+
+ if (mess->m_source != driver[DRIVER_MAIN].endpt &&
+ mess->m_source != driver[DRIVER_BACKUP].endpt) {
+#if DEBUG
+ printf("Filter: got STRAY message %d from %d\n",
+ mess->m_type, mess->m_source);
+#endif
+
+ continue;
+ }
+
+ /* We are waiting for a reply from one specific driver. */
+ if (which >= 0) {
+ /* If the message source is that driver, good. */
+ if (mess->m_source == driver[which].endpt)
+ break;
+
+ /* This should probably be treated as a real protocol
+ * error. We do not abort any receives (not even paired
+ * receives) except because of timeouts. Getting here
+ * means a driver replied at least the timeout period
+ * later than expected, which should be enough reason
+ * to kill it really. The other explanation is that it
+ * is actually violating the protocol and sending bogus
+ * messages...
+ */
+#if DEBUG
+ printf("Filter: got UNEXPECTED reply from %d\n",
+ mess->m_source);
+#endif
+
+ continue;
+ }
+
+ /* We got a message from one of the drivers, and we didn't
+ * care which one we wanted to receive from. A-OK.
+ */
+ break;
+ }
+
+ return OK;
+}
+
+/*===========================================================================*
+ * flt_sendrec *
+ *===========================================================================*/
+static int flt_sendrec(message *mess, int which)
+{
+ int r;
+
+ r = flt_senda(mess, which);
+ if(r != OK)
+ return r;
+
+ if(check_senda(which) == BD_DEAD) {
+ return bad_driver(which, BD_DEAD, EFAULT);
+ }
+
+ /* Set alarm. */
+ flt_alarm(DRIVER_TIMEOUT);
+
+ r = flt_receive(mess, which);
+
+ /* Clear the alarm. */
+ flt_alarm(0);
+ return r;
+}
+
+/*===========================================================================*
+ * do_sendrec_both *
+ *===========================================================================*/
+static int do_sendrec_both(message *m1, message *m2)
+{
+ /* If USEE_MIRROR is set, call flt_sendrec() to both drivers.
+ * Otherwise, only call flt_sendrec() to the main driver.
+ * This function will only return either OK or RET_REDO.
+ */
+ int r, which = -1;
+ message ma, mb;
+
+ /* If the two disks use the same driver, call flt_sendrec() twice
+ * sequentially. Such a setup is not very useful though.
+ */
+ if (!strcmp(driver[DRIVER_MAIN].label, driver[DRIVER_BACKUP].label)) {
+ if ((r = flt_sendrec(m1, DRIVER_MAIN)) != OK) return r;
+ return flt_sendrec(m2, DRIVER_BACKUP);
+ }
+
+ /* If the two disks use different drivers, call flt_senda()
+ * twice, and then flt_receive(), and distinguish the return
+ * messages by means of m_source.
+ */
+ if ((r = flt_senda(m1, DRIVER_MAIN)) != OK) return r;
+ if ((r = flt_senda(m2, DRIVER_BACKUP)) != OK) return r;
+
+ /* Set alarm. */
+ flt_alarm(DRIVER_TIMEOUT);
+
+ /* The message received by the 1st flt_receive() may not be
+ * from DRIVER_MAIN.
+ */
+ if ((r = flt_receive(&ma, -1)) != OK) {
+ flt_alarm(0);
+ return r;
+ }
+
+ if (ma.m_source == driver[DRIVER_MAIN].endpt) {
+ which = DRIVER_BACKUP;
+ } else if (ma.m_source == driver[DRIVER_BACKUP].endpt) {
+ which = DRIVER_MAIN;
+ } else {
+ panic(__FILE__, "message from unexpected source",
+ ma.m_source);
+ }
+
+ r = flt_receive(&mb, which);
+
+ /* Clear the alarm. */
+ flt_alarm(0);
+
+ if(r != OK)
+ return r;
+
+ if (ma.m_source == driver[DRIVER_MAIN].endpt) {
+ *m1 = ma;
+ *m2 = mb;
+ } else {
+ *m1 = mb;
+ *m2 = ma;
+ }
+
+ return OK;
+}
+
+/*===========================================================================*
+ * do_sendrec_one *
+ *===========================================================================*/
+static int do_sendrec_one(message *m1, message *m2)
+{
+ /* Only talk to the main driver. If something goes wrong, it will
+ * be fixed elsewhere.
+ * This function will only return either OK or RET_REDO.
+ */
+
+ return flt_sendrec(m1, DRIVER_MAIN);
+}
+
+/*===========================================================================*
+ * paired_sendrec *
+ *===========================================================================*/
+static int paired_sendrec(message *m1, message *m2, int both)
+{
+ /* Sendrec with the disk driver. If the disk driver is down, and was
+ * restarted, redo the request, until the driver works fine, or can't
+ * be restarted again.
+ */
+ int r;
+
+#if DEBUG2
+ printf("paired_sendrec(%d) - <%d,%x:%x,%d> - %x,%x\n",
+ both, m1->m_type, m1->HIGHPOS, m1->POSITION, m1->COUNT,
+ m1->IO_GRANT, m2->IO_GRANT);
+#endif
+
+ if (both)
+ r = do_sendrec_both(m1, m2);
+ else
+ r = do_sendrec_one(m1, m2);
+
+#if DEBUG2
+ if (r != OK)
+ printf("paired_sendrec about to return %d\n", r);
+#endif
+
+ return r;
+}
+
+/*===========================================================================*
+ * paired_grant *
+ *===========================================================================*/
+static void paired_grant(char *buf1, char *buf2, size_t size, int request,
+ cp_grant_id_t *gids, int both)
+{
+ /* Create memory grants. If USE_MIRROR, grant to both drivers,
+ * otherwise only to the main one.
+ */
+ cp_grant_id_t gid;
+ int access;
+
+ access = (request == FLT_WRITE) ? CPF_READ : CPF_WRITE;
+
+ if(driver[DRIVER_MAIN].endpt > 0) {
+ gid = cpf_grant_direct(driver[DRIVER_MAIN].endpt,
+ (vir_bytes) buf1, size, access);
+ if(!GRANT_VALID(gid))
+ panic(__FILE__, "invalid grant", gid);
+ gids[0] = gid;
+ }
+
+ if (both) {
+ if(driver[DRIVER_BACKUP].endpt > 0) {
+ gid = cpf_grant_direct(driver[DRIVER_BACKUP].endpt,
+ (vir_bytes) buf2, size, access);
+ if(!GRANT_VALID(gid))
+ panic(__FILE__, "invalid grant", gid);
+ gids[1] = gid;
+ }
+ }
+}
+
+/*===========================================================================*
+ * paired_revoke *
+ *===========================================================================*/
+static void paired_revoke(cp_grant_id_t gid1, cp_grant_id_t gid2, int both)
+{
+ cpf_revoke(gid1);
+
+ if (both)
+ cpf_revoke(gid2);
+}
+
+/*===========================================================================*
+ * read_write *
+ *===========================================================================*/
+int read_write(u64_t pos, char *bufa, char *bufb, size_t *sizep, int request)
+{
+ message m1, m2;
+ cp_grant_id_t gids[2];
+ int r, both;
+
+ gids[0] = gids[1] = GRANT_INVALID;
+
+ /* Send two requests only if mirroring is enabled and the given request
+ * is either FLT_READ2 or FLT_WRITE.
+ */
+ both = (USE_MIRROR && request != FLT_READ);
+
+ m1.m_type = (request == FLT_WRITE) ? DEV_WRITE_S : DEV_READ_S;
+ m1.COUNT = *sizep;
+ m1.POSITION = ex64lo(pos);
+ m1.HIGHPOS = ex64hi(pos);
+ m2 = m1;
+
+ paired_grant(bufa, bufb, *sizep, request, gids, both);
+ m1.IO_GRANT = (char *) gids[0];
+ m2.IO_GRANT = (char *) gids[1];
+
+ r = paired_sendrec(&m1, &m2, both);
+
+ paired_revoke(gids[0], gids[1], both);
+
+ if(r != OK) {
+#if DEBUG
+ if (r != RET_REDO)
+ printf("Filter: paired_sendrec returned %d\n", r);
+#endif
+ return r;
+ }
+
+ if (m1.m_type != TASK_REPLY || m1.REP_STATUS < 0) {
+ printf("Filter: unexpected/invalid reply from main driver: "
+ "(%x, %d)\n", m1.m_type, m1.REP_STATUS);
+
+ return bad_driver(DRIVER_MAIN, BD_PROTO,
+ (m1.m_type == TASK_REPLY) ? m1.REP_STATUS : EFAULT);
+ }
+
+ if (m1.REP_STATUS != *sizep) {
+ printf("Filter: truncated reply %u to I/O request of size "
+ "0x%x at 0x%s; size 0x%s\n",
+ m1.REP_STATUS, *sizep,
+ print64(pos), print64(disk_size));
+
+ /* If the driver returned a value *larger* than we requested,
+ * OR if we did NOT exceed the disk size, then we should
+ * report the driver for acting strangely!
+ */
+ if (m1.REP_STATUS > *sizep ||
+ cmp64(add64u(pos, *sizep), disk_size) < 0)
+ return bad_driver(DRIVER_MAIN, BD_PROTO, EFAULT);
+
+ /* Return the actual size. */
+ *sizep = m1.REP_STATUS;
+ }
+
+ if (both) {
+ if (m2.m_type != TASK_REPLY || m2.REP_STATUS < 0) {
+ printf("Filter: unexpected/invalid reply from "
+ "backup driver (%x, %d)\n",
+ m2.m_type, m2.REP_STATUS);
+
+ return bad_driver(DRIVER_BACKUP, BD_PROTO,
+ m2.m_type == TASK_REPLY ? m2.REP_STATUS :
+ EFAULT);
+ }
+ if (m2.REP_STATUS != *sizep) {
+ printf("Filter: truncated reply from backup driver\n");
+
+ /* As above */
+ if (m2.REP_STATUS > *sizep ||
+ cmp64(add64u(pos, *sizep), disk_size) < 0)
+ return bad_driver(DRIVER_BACKUP, BD_PROTO,
+ EFAULT);
+
+ /* Return the actual size. */
+ if (*sizep >= m2.REP_STATUS)
+ *sizep = m2.REP_STATUS;
+ }
+ }
+
+ return OK;
+}
--- /dev/null
+/* Filter driver - general include file */
+#define _MINIX 1
+#define _SYSTEM 1
+#include <minix/config.h>
+#include <minix/const.h>
+#include <minix/type.h>
+#include <minix/com.h>
+#include <minix/ipc.h>
+#include <sys/ioc_disk.h>
+#include <minix/sysutil.h>
+#include <minix/syslib.h>
+#include <minix/partition.h>
+#include <minix/ds.h>
+#include <minix/callnr.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#define SECTOR_SIZE 512
+
+enum {
+ ST_XOR, /* XOR-based checksums */
+ ST_CRC, /* CRC32-based checksums */
+ ST_MD5 /* MD5-based checksums */
+};
+
+enum {
+ FLT_WRITE, /* write to up to two disks */
+ FLT_READ, /* read from one disk */
+ FLT_READ2 /* read from both disks */
+};
+
+/* Something was wrong and the disk driver has been restarted/refreshed,
+ * so the request needs to be redone.
+ */
+#define RET_REDO 1
+
+/* The cases where the disk driver need to be restarted/refreshed by RS.
+ * BD_DEAD: the disk driver has died. Restart it.
+ * BD_PROTO: a protocol error has occurred. Refresh it.
+ * BD_DATA: a data error has occurred. Refresh it.
+ */
+enum {
+ BD_NONE,
+ BD_DEAD,
+ BD_PROTO,
+ BD_DATA,
+ BD_LAST
+};
+
+#define DRIVER_MAIN 0
+#define DRIVER_BACKUP 1
+
+/* Requests for more than this many bytes need to go through malloc(). */
+#define BUF_SIZE (128 * 1024)
+#define SBUF_SIZE (BUF_SIZE * 2)
+
+#define LABEL_SIZE 32
+
+typedef unsigned long sector_t;
+
+/* main.c */
+extern int USE_CHECKSUM;
+extern int USE_MIRROR;
+extern int BAD_SUM_ERROR;
+extern int USE_SUM_LAYOUT;
+extern int SUM_TYPE;
+extern int SUM_SIZE;
+extern int NR_SUM_SEC;
+extern int NR_RETRIES;
+extern int NR_RESTARTS;
+extern int DRIVER_TIMEOUT;
+
+extern char MAIN_LABEL[LABEL_SIZE];
+extern char BACKUP_LABEL[LABEL_SIZE];
+extern int MAIN_MINOR;
+extern int BACKUP_MINOR;
+
+/* sum.c */
+extern void sum_init(void);
+extern int transfer(u64_t pos, char *buffer, size_t *sizep, int flag_rw);
+extern u64_t convert(u64_t size);
+
+/* driver.c */
+extern void driver_init(void);
+extern void driver_shutdown(void);
+extern u64_t get_raw_size(void);
+extern void reset_kills(void);
+extern int check_driver(int which);
+extern int bad_driver(int which, int type, int error);
+extern int read_write(u64_t pos, char *bufa, char *bufb, size_t *sizep,
+ int flag_rw);
+
+/* util.c */
+extern char *flt_malloc(size_t size, char *sbuf, size_t ssize);
+extern void flt_free(char *buf, size_t size, char *sbuf);
+extern char *print64(u64_t p);
+extern clock_t flt_alarm(clock_t dt);
+extern void flt_sleep(int secs);
--- /dev/null
+/* Filter driver - top layer - block interface */
+
+/* This is a filter driver, which lays above disk driver, and forwards
+ * messages between disk driver and its callers. The filter can detect
+ * corrupted data (toggled by USE_CHECKSUM) and recover it (toggled
+ * by USE_MIRROR). These two functions are independent from each other.
+ * The mirroring function requires two disks, on separate disk drivers.
+ */
+
+#include "inc.h"
+#include "optset.h"
+
+#define _POSIX_SOURCE 1
+#include <signal.h>
+
+/* Global settings. */
+int USE_CHECKSUM = 0; /* enable checksumming */
+int USE_MIRROR = 0; /* enable mirroring */
+
+int BAD_SUM_ERROR = 1; /* bad checksums are considered a driver error */
+
+int USE_SUM_LAYOUT = 0; /* use checksumming layout on disk */
+int NR_SUM_SEC = 8; /* number of checksums per checksum sector */
+
+int SUM_TYPE = 0; /* use XOR, CRC or MD5 */
+int SUM_SIZE = 0; /* size of the stored checksum */
+
+int NR_RETRIES = 3; /* number of times the request will be retried (N) */
+int NR_RESTARTS = 3; /* number of times a driver will be restarted (M) */
+int DRIVER_TIMEOUT = 5; /* timeout in seconds to declare a driver dead (T) */
+
+char MAIN_LABEL[LABEL_SIZE] = ""; /* main disk driver label */
+char BACKUP_LABEL[LABEL_SIZE] = ""; /* backup disk driver label */
+int MAIN_MINOR = -1; /* main partition minor nr */
+int BACKUP_MINOR = -1; /* backup partition minor nr */
+
+struct optset optset_table[] = {
+ { "label0", OPT_STRING, MAIN_LABEL, LABEL_SIZE },
+ { "label1", OPT_STRING, BACKUP_LABEL, LABEL_SIZE },
+ { "minor0", OPT_INT, &MAIN_MINOR, 10 },
+ { "minor1", OPT_INT, &BACKUP_MINOR, 10 },
+ { "sum_sec", OPT_INT, &NR_SUM_SEC, 10 },
+ { "layout", OPT_BOOL, &USE_SUM_LAYOUT, 1 },
+ { "nolayout", OPT_BOOL, &USE_SUM_LAYOUT, 0 },
+ { "sum", OPT_BOOL, &USE_CHECKSUM, 1 },
+ { "nosum", OPT_BOOL, &USE_CHECKSUM, 0 },
+ { "mirror", OPT_BOOL, &USE_MIRROR, 1 },
+ { "nomirror", OPT_BOOL, &USE_MIRROR, 0 },
+ { "xor", OPT_BOOL, &SUM_TYPE, ST_XOR },
+ { "crc", OPT_BOOL, &SUM_TYPE, ST_CRC },
+ { "md5", OPT_BOOL, &SUM_TYPE, ST_MD5 },
+ { "sumerr", OPT_BOOL, &BAD_SUM_ERROR, 1 },
+ { "nosumerr", OPT_BOOL, &BAD_SUM_ERROR, 0 },
+ { "retries", OPT_INT, &NR_RETRIES, 10 },
+ { "N", OPT_INT, &NR_RETRIES, 10 },
+ { "restarts", OPT_INT, &NR_RESTARTS, 10 },
+ { "M", OPT_INT, &NR_RESTARTS, 10 },
+ { "timeout", OPT_INT, &DRIVER_TIMEOUT, 10 },
+ { "T", OPT_INT, &DRIVER_TIMEOUT, 10 },
+ { NULL }
+};
+
+/* Request message. */
+static message m_in;
+static endpoint_t who_e; /* m_source */
+static endpoint_t proc_e; /* IO_ENDPT */
+static cp_grant_id_t grant_id; /* IO_GRANT */
+
+/* Data buffers. */
+static char *buf_array, *buffer; /* contiguous buffer */
+
+/*===========================================================================*
+ * carry *
+ *===========================================================================*/
+static int carry(size_t size, int flag_rw)
+{
+ /* Carry data between caller proc and filter.
+ */
+
+ if (flag_rw == FLT_WRITE)
+ return sys_safecopyfrom(proc_e, grant_id, 0,
+ (vir_bytes) buffer, size, D);
+ else
+ return sys_safecopyto(proc_e, grant_id, 0,
+ (vir_bytes) buffer, size, D);
+}
+
+/*===========================================================================*
+ * vcarry *
+ *===========================================================================*/
+static int vcarry(int grants, iovec_t *iov, int flag_rw, size_t size)
+{
+ /* Carry data between caller proc and filter, through grant-vector.
+ */
+ char *bufp;
+ int i, r;
+ size_t bytes;
+
+ bufp = buffer;
+ for(i = 0; i < grants && size > 0; i++) {
+ bytes = MIN(size, iov[i].iov_size);
+
+ if (flag_rw == FLT_WRITE)
+ r = sys_safecopyfrom(proc_e,
+ (vir_bytes) iov[i].iov_addr, 0,
+ (vir_bytes) bufp, bytes, D);
+ else
+ r = sys_safecopyto(proc_e,
+ (vir_bytes) iov[i].iov_addr, 0,
+ (vir_bytes) bufp, bytes, D);
+
+ if(r != OK)
+ return r;
+
+ bufp += bytes;
+ size -= bytes;
+ }
+
+ return OK;
+}
+
+/*===========================================================================*
+ * do_rdwt *
+ *===========================================================================*/
+static int do_rdwt(int flag_rw)
+{
+ size_t size, size_ret;
+ u64_t pos;
+ int r;
+
+ pos = make64(m_in.POSITION, m_in.HIGHPOS);
+ size = m_in.COUNT;
+
+ if (rem64u(pos, SECTOR_SIZE) != 0 || size % SECTOR_SIZE != 0) {
+ printf("Filter: unaligned request from caller!\n");
+
+ return EINVAL;
+ }
+
+ buffer = flt_malloc(size, buf_array, BUF_SIZE);
+
+ if(flag_rw == FLT_WRITE)
+ carry(size, flag_rw);
+
+ reset_kills();
+
+ for (;;) {
+ size_ret = size;
+ r = transfer(pos, buffer, &size_ret, flag_rw);
+ if(r != RET_REDO)
+ break;
+
+#if DEBUG
+ printf("Filter: transfer yielded RET_REDO, checking drivers\n");
+#endif
+ if((r = check_driver(DRIVER_MAIN)) != OK) break;
+ if((r = check_driver(DRIVER_BACKUP)) != OK) break;
+ }
+
+ if(r == OK && flag_rw == FLT_READ)
+ carry(size_ret, flag_rw);
+
+ flt_free(buffer, size, buf_array);
+ return r != OK ? r : size_ret;
+}
+
+/*===========================================================================*
+ * do_vrdwt *
+ *===========================================================================*/
+static int do_vrdwt(int flag_rw)
+{
+ size_t size, size_ret, bytes;
+ int grants;
+ int r, i;
+ u64_t pos;
+ iovec_t iov_proc[NR_IOREQS];
+
+ /* Extract informations. */
+ grants = m_in.COUNT;
+ if((r = sys_safecopyfrom(who_e, grant_id, 0, (vir_bytes) iov_proc,
+ grants * sizeof(iovec_t), D)) != OK) {
+ panic(__FILE__, "copying in grant vector failed", r);
+ }
+
+ pos = make64(m_in.POSITION, m_in.HIGHPOS);
+ for(size = 0, i = 0; i < grants; i++)
+ size += iov_proc[i].iov_size;
+
+ if (rem64u(pos, SECTOR_SIZE) != 0 || size % SECTOR_SIZE != 0) {
+ printf("Filter: unaligned request from caller!\n");
+ return EINVAL;
+ }
+
+ buffer = flt_malloc(size, buf_array, BUF_SIZE);
+
+ if(flag_rw == FLT_WRITE)
+ vcarry(grants, iov_proc, flag_rw, size);
+
+ reset_kills();
+
+ for (;;) {
+ size_ret = size;
+ r = transfer(pos, buffer, &size_ret, flag_rw);
+ if(r != RET_REDO)
+ break;
+
+#if DEBUG
+ printf("Filter: transfer yielded RET_REDO, checking drivers\n");
+#endif
+ if((r = check_driver(DRIVER_MAIN)) != OK) break;
+ if((r = check_driver(DRIVER_BACKUP)) != OK) break;
+ }
+
+ if(r != OK) {
+ flt_free(buffer, size, buf_array);
+ return r;
+ }
+
+ if(flag_rw == FLT_READ)
+ vcarry(grants, iov_proc, flag_rw, size_ret);
+
+ /* Set the result-iovec. */
+ for(i = 0; i < grants && size_ret > 0; i++) {
+ bytes = MIN(size_ret, iov_proc[i].iov_size);
+
+ iov_proc[i].iov_size -= bytes;
+ size_ret -= bytes;
+ }
+
+ /* Copy the caller's grant-table back. */
+ if((r = sys_safecopyto(who_e, grant_id, 0, (vir_bytes) iov_proc,
+ grants * sizeof(iovec_t), D)) != OK) {
+ panic(__FILE__, "copying out grant vector failed", r);
+ }
+
+ flt_free(buffer, size, buf_array);
+ return OK;
+}
+
+/*===========================================================================*
+ * do_ioctl *
+ *===========================================================================*/
+static int do_ioctl(message *m)
+{
+ struct partition sizepart;
+
+ switch(m->REQUEST) {
+ case DIOCSETP:
+ case DIOCTIMEOUT:
+ case DIOCOPENCT:
+ /* These do not make sense for us. */
+ return EINVAL;
+
+ case DIOCGETP:
+ memset(&sizepart, 0, sizeof(sizepart));
+
+ /* The presented disk size is the raw partition size,
+ * corrected for space needed for checksums.
+ */
+ sizepart.size = convert(get_raw_size());
+
+ if(sys_safecopyto(proc_e, (vir_bytes) grant_id, 0,
+ (vir_bytes) &sizepart,
+ sizeof(struct partition), D) != OK) {
+ printf("Filter: DIOCGETP safecopyto failed\n");
+ return EIO;
+ }
+ break;
+
+ default:
+ printf("Filter: unknown ioctl request: %d!\n", m->REQUEST);
+ return EINVAL;
+ }
+
+ return OK;
+}
+
+/*===========================================================================*
+ * parse_arguments *
+ *===========================================================================*/
+static int parse_arguments(int argc, char *argv[])
+{
+
+ if(argc != 2)
+ return EINVAL;
+
+ optset_parse(optset_table, argv[1]);
+
+ if (MAIN_LABEL[0] == 0 || MAIN_MINOR < 0 || MAIN_MINOR > 255)
+ return EINVAL;
+ if (USE_MIRROR && (BACKUP_LABEL[0] == 0 ||
+ BACKUP_MINOR < 0 || BACKUP_MINOR > 255))
+ return EINVAL;
+
+ /* Checksumming implies a checksum layout. */
+ if (USE_CHECKSUM)
+ USE_SUM_LAYOUT = 1;
+
+ /* Determine the checksum size for the chosen checksum type. */
+ switch (SUM_TYPE) {
+ case ST_XOR:
+ SUM_SIZE = 16; /* compatibility */
+ break;
+ case ST_CRC:
+ SUM_SIZE = 4;
+ break;
+ case ST_MD5:
+ SUM_SIZE = 16;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ if (NR_SUM_SEC <= 0 || SUM_SIZE * NR_SUM_SEC > SECTOR_SIZE)
+ return EINVAL;
+
+#if DEBUG
+ printf("Filter starting. Configuration:\n");
+ printf(" USE_CHECKSUM : %3s ", USE_CHECKSUM ? "yes" : "no");
+ printf(" USE_MIRROR : %3s\n", USE_MIRROR ? "yes" : "no");
+
+ if (USE_CHECKSUM) {
+ printf(" BAD_SUM_ERROR : %3s ",
+ BAD_SUM_ERROR ? "yes" : "no");
+ printf(" NR_SUM_SEC : %3d\n", NR_SUM_SEC);
+
+ printf(" SUM_TYPE : ");
+
+ switch (SUM_TYPE) {
+ case ST_XOR: printf("xor"); break;
+ case ST_CRC: printf("crc"); break;
+ case ST_MD5: printf("md5"); break;
+ }
+
+ printf(" SUM_SIZE : %3d\n", SUM_SIZE);
+ }
+ else printf(" USE_SUM_LAYOUT : %3s\n", USE_SUM_LAYOUT ? "yes" : "no");
+
+ printf(" N : %3dx M : %3dx T : %3ds\n",
+ NR_RETRIES, NR_RESTARTS, DRIVER_TIMEOUT);
+
+ printf(" MAIN_LABEL / MAIN_MINOR : %19s / %d\n",
+ MAIN_LABEL, MAIN_MINOR);
+ if (USE_MIRROR) {
+ printf(" BACKUP_LABEL / BACKUP_MINOR : %15s / %d\n",
+ BACKUP_LABEL, BACKUP_MINOR);
+ }
+
+#endif
+
+ /* Convert timeout seconds to ticks. */
+ DRIVER_TIMEOUT *= sys_hz();
+
+ return OK;
+}
+
+/*===========================================================================*
+ * got_signal *
+ *===========================================================================*/
+static void got_signal(void)
+{
+ sigset_t set;
+
+ /* See if PM sent us a SIGTERM. */
+ if (getsigset(&set) != 0) return;
+
+ if (!sigismember(&set, SIGTERM)) return;
+
+ /* If so, shut down this driver. */
+#if DEBUG
+ printf("Filter: shutdown...\n");
+#endif
+
+ driver_shutdown();
+
+ exit(0);
+}
+
+/*===========================================================================*
+ * main *
+ *===========================================================================*/
+int main(int argc, char *argv[])
+{
+ message m_out;
+ int r;
+
+ r = parse_arguments(argc, argv);
+ if(r != OK) {
+ printf("Filter: wrong argument!\n");
+ return 1;
+ }
+
+ if ((buf_array = alloc_contig(BUF_SIZE, 0, NULL)) == NULL)
+ panic(__FILE__, "no memory available", NO_NUM);
+
+ sum_init();
+
+ driver_init();
+
+ for (;;) {
+ /* Wait for request. */
+ if(receive(ANY, &m_in) != OK) {
+ panic(__FILE__, "receive failed", NO_NUM);
+ }
+
+#if DEBUG2
+ printf("Filter: got request %d from %d\n",
+ m_in.m_type, m_in.m_source);
+#endif
+
+ if (is_notify(m_in.m_type) && m_in.m_source == PM_PROC_NR)
+ got_signal();
+
+ who_e = m_in.m_source;
+ proc_e = m_in.IO_ENDPT;
+ grant_id = (cp_grant_id_t) m_in.IO_GRANT;
+
+ /* Forword the request message to the drivers. */
+ switch(m_in.m_type) {
+ case DEV_OPEN: /* open/close is a noop for filter. */
+ case DEV_CLOSE: r = OK; break;
+ case DEV_READ_S: r = do_rdwt(FLT_READ); break;
+ case DEV_WRITE_S: r = do_rdwt(FLT_WRITE); break;
+ case DEV_GATHER_S: r = do_vrdwt(FLT_READ); break;
+ case DEV_SCATTER_S: r = do_vrdwt(FLT_WRITE); break;
+ case DEV_IOCTL_S: r = do_ioctl(&m_in); break;
+
+ default:
+ printf("Filter: ignoring unknown request %d from %d\n",
+ m_in.m_type, m_in.m_source);
+ continue;
+ }
+
+#if DEBUG2
+ printf("Filter: replying with code %d\n", r);
+#endif
+
+ /* Send back reply message. */
+ m_out.m_type = TASK_REPLY;
+ m_out.REP_ENDPT = proc_e;
+ m_out.REP_STATUS = r;
+ send(who_e, &m_out);
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+/* This code was modified in 1997 by Jim Kingdon of Cyclic Software to
+ not require an integer type which is exactly 32 bits. This work
+ draws on the changes for the same purpose by Tatu Ylonen
+ <ylo@cs.hut.fi> as part of SSH, but since I didn't actually use
+ that code, there is no copyright issue. I hereby disclaim
+ copyright in any changes I have made; this code remains in the
+ public domain. */
+
+#ifdef TEST
+#include <stdlib.h>
+#endif
+
+#include <string.h> /* for memcpy() and memset() */
+
+#include "md5.h"
+
+/* Little-endian byte-swapping routines. Note that these do not
+ depend on the size of datatypes such as uint32, nor do they require
+ us to detect the endianness of the machine we are running on. It
+ is possible they should be macros for speed, but I would be
+ surprised if they were a performance bottleneck for MD5. */
+
+static uint32
+getu32 (const unsigned char *addr)
+{
+ return (((((unsigned long)addr[3] << 8) | addr[2]) << 8)
+ | addr[1]) << 8 | addr[0];
+}
+
+static void
+putu32 (uint32 data, unsigned char *addr)
+{
+ addr[0] = (unsigned char)data;
+ addr[1] = (unsigned char)(data >> 8);
+ addr[2] = (unsigned char)(data >> 16);
+ addr[3] = (unsigned char)(data >> 24);
+}
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void
+MD5Init (ctx)
+ struct MD5Context *ctx;
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void
+MD5Update (ctx, buf, len)
+ struct MD5Context *ctx;
+ unsigned char const *buf;
+ unsigned len;
+{
+ uint32 t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = (t + ((uint32)len << 3)) & 0xffffffff) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if ( t ) {
+ unsigned char *p = ctx->in + t;
+
+ t = 64-t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ MD5Transform (ctx->buf, ctx->in);
+ buf += t;
+ len -= t;
+ }
+
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ MD5Transform (ctx->buf, ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void
+MD5Final (digest, ctx)
+ unsigned char digest[16];
+ struct MD5Context *ctx;
+{
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ MD5Transform (ctx->buf, ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count-8);
+ }
+
+ /* Append length in bits and transform */
+ putu32(ctx->bits[0], ctx->in + 56);
+ putu32(ctx->bits[1], ctx->in + 60);
+
+ MD5Transform (ctx->buf, ctx->in);
+ putu32(ctx->buf[0], digest);
+ putu32(ctx->buf[1], digest + 4);
+ putu32(ctx->buf[2], digest + 8);
+ putu32(ctx->buf[3], digest + 12);
+ memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w &= 0xffffffff, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void
+MD5Transform (buf, inraw)
+ uint32 buf[4];
+ const unsigned char inraw[64];
+{
+ register uint32 a, b, c, d;
+ uint32 in[16];
+ int i;
+
+ for (i = 0; i < 16; ++i)
+ in[i] = getu32 (inraw + 4 * i);
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+#endif
+
+#ifdef TEST
+/* Simple test program. Can use it to manually run the tests from
+ RFC1321 for example. */
+#include <stdio.h>
+
+int
+main (int argc, char **argv)
+{
+ struct MD5Context context;
+ unsigned char checksum[16];
+ int i;
+ int j;
+
+ if (argc < 2)
+ {
+ fprintf (stderr, "usage: %s string-to-hash\n", argv[0]);
+ exit (1);
+ }
+ for (j = 1; j < argc; ++j)
+ {
+ printf ("MD5 (\"%s\") = ", argv[j]);
+ MD5Init (&context);
+ MD5Update (&context, (unsigned char *)argv[j], strlen (argv[j]));
+ MD5Final (checksum, &context);
+ for (i = 0; i < 16; i++)
+ {
+ printf ("%02x", (unsigned int) checksum[i]);
+ }
+ printf ("\n");
+ }
+ return 0;
+}
+#endif /* TEST */
--- /dev/null
+/* See md5.c for explanation and copyright information. */
+
+#ifndef MD5_H
+#define MD5_H
+
+/* Unlike previous versions of this code, uint32 need not be exactly
+ 32 bits, merely 32 bits or more. Choosing a data type which is 32
+ bits instead of 64 is not important; speed is considerably more
+ important. ANSI guarantees that "unsigned long" will be big enough,
+ and always using it seems to have few disadvantages. */
+typedef unsigned long uint32;
+
+struct MD5Context {
+ uint32 buf[4];
+ uint32 bits[2];
+ unsigned char in[64];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context,
+ unsigned char const *buf, unsigned len);
+void MD5Final(unsigned char digest[16],
+ struct MD5Context *context);
+void MD5Transform(uint32 buf[4], const unsigned char in[64]);
+
+#endif /* !MD5_H */
--- /dev/null
+/* This file provides functionality to parse strings of comma-separated
+ * options, each being either a single key name or a key=value pair, where the
+ * value may be enclosed in quotes. A table of optset entries is provided to
+ * determine which options are recognized, how to parse their values, and where
+ * to store those. Unrecognized options are silently ignored; improperly
+ * formatted options are silently set to reasonably acceptable values.
+ *
+ * The entry points into this file are:
+ * optset_parse parse the given options string using the given table
+ *
+ * Created:
+ * May 2009 (D.C. van Moolenbroek)
+ */
+
+#define _MINIX 1
+#include <stdlib.h>
+#include <string.h>
+#include <minix/config.h>
+#include <minix/const.h>
+
+#include "optset.h"
+
+FORWARD _PROTOTYPE( void optset_parse_entry, (struct optset *entry,
+ char *ptr, int len) );
+
+/*===========================================================================*
+ * optset_parse_entry *
+ *===========================================================================*/
+PRIVATE void optset_parse_entry(entry, ptr, len)
+struct optset *entry;
+char *ptr;
+int len;
+{
+/* Parse and store the value of a single option.
+ */
+ char *dst;
+ int val;
+
+ switch (entry->os_type) {
+ case OPT_BOOL:
+ *((int *) entry->os_ptr) = entry->os_val;
+
+ break;
+
+ case OPT_STRING:
+ if (len >= entry->os_val)
+ len = entry->os_val - 1;
+
+ dst = (char *) entry->os_ptr;
+
+ if (len > 0)
+ memcpy(dst, ptr, len);
+ dst[len] = 0;
+
+ break;
+
+ case OPT_INT:
+ if (len > 0)
+ val = strtol(ptr, NULL, entry->os_val);
+ else
+ val = 0;
+
+ *((int *) entry->os_ptr) = val;
+
+ break;
+ }
+}
+
+/*===========================================================================*
+ * optset_parse *
+ *===========================================================================*/
+PUBLIC void optset_parse(table, string)
+struct optset *table;
+char *string;
+{
+/* Parse a string of options, using the provided table of optset entries.
+ */
+ char *p, *kptr, *vptr;
+ int i, klen, vlen;
+
+ for (p = string; *p; ) {
+ /* Get the key name for the field. */
+ for (kptr = p, klen = 0; *p && *p != '=' && *p != ','; p++, klen++);
+
+ if (*p == '=') {
+ /* The field has an associated value. */
+ vptr = ++p;
+
+ /* If the first character after the '=' is a quote character,
+ * find a matching quote character followed by either a comma
+ * or the terminating null character, and use the string in
+ * between. Otherwise, use the string up to the next comma or
+ * the terminating null character.
+ */
+ if (*p == '\'' || *p == '"') {
+ p++;
+
+ for (vlen = 0; *p && (*p != *vptr ||
+ (p[1] && p[1] != ',')); p++, vlen++);
+
+ if (*p) p++;
+ vptr++;
+ }
+ else
+ for (vlen = 0; *p && *p != ','; p++, vlen++);
+ }
+ else {
+ vptr = NULL;
+ vlen = 0;
+ }
+
+ if (*p == ',') p++;
+
+ /* Find a matching entry for this key in the given table. If found,
+ * call optset_parse_entry() on it. Silently ignore the option
+ * otherwise.
+ */
+ for (i = 0; table[i].os_name != NULL; i++) {
+ if (strlen(table[i].os_name) == klen &&
+ !strncasecmp(table[i].os_name, kptr, klen)) {
+
+ optset_parse_entry(&table[i], vptr, vlen);
+
+ break;
+ }
+ }
+ }
+}
--- /dev/null
+#ifndef _OPTSET_H
+#define _OPTSET_H
+
+enum {
+ OPT_BOOL,
+ OPT_STRING,
+ OPT_INT
+};
+
+/* An entry for the parser of an options set. The 'os_name' field must point
+ * to a string, which is treated case-insensitively; the last entry of a table
+ * must have NULL name. The 'os_type' field must be set to one of the OPT_
+ * values defined above. The 'os_ptr' field must point to the field that is to
+ * receive the value of a recognized option. For OPT_STRING, it must point to a
+ * string of a size set in 'os_val'; the resulting string may be truncated, but
+ * will always be null-terminated. For OPT_BOOL, it must point to an int which
+ * will be set to the value in 'os_val' if the option is present. For OPT_INT,
+ * it must point to an int which will be set to the provided option value;
+ * 'os_val' is then a base passed to strtol().
+ */
+struct optset {
+ char *os_name;
+ int os_type;
+ void *os_ptr;
+ int os_val;
+};
+
+_PROTOTYPE( void optset_parse, (struct optset *table, char *string) );
+
+#endif /* _OPTSET_H */
--- /dev/null
+/* Filter driver - middle layer - checksumming */
+
+#include "inc.h"
+#include "crc.h"
+#include "md5.h"
+
+#define GROUP_SIZE (SECTOR_SIZE * NR_SUM_SEC)
+#define SEC2SUM_NR(nr) ((nr)/NR_SUM_SEC*(NR_SUM_SEC+1) + NR_SUM_SEC)
+#define LOG2PHYS(nr) ((nr)/NR_SUM_SEC*(NR_SUM_SEC+1) + (nr)%NR_SUM_SEC)
+
+#define POS2SEC(nr) div64u((nr), SECTOR_SIZE)
+#define SEC2POS(nr) mul64u((nr), SECTOR_SIZE)
+
+/* Data buffers. */
+static char *ext_array, *ext_buffer; /* interspersed buffer */
+static char *rb0_array; /* write readback buffer for disk 0 */
+static char *rb1_array; /* write readback buffer for disk 1 */
+
+/*===========================================================================*
+ * sum_init *
+ *===========================================================================*/
+void sum_init(void)
+{
+ /* Initialize buffers. */
+
+ ext_array = alloc_contig(SBUF_SIZE, 0, NULL);
+ rb0_array = alloc_contig(SBUF_SIZE, 0, NULL);
+ rb1_array = alloc_contig(SBUF_SIZE, 0, NULL);
+
+ if (ext_array == NULL || rb0_array == NULL || rb1_array == NULL)
+ panic(__FILE__, "no memory available", NO_NUM);
+}
+
+/*===========================================================================*
+ * calc_sum *
+ *===========================================================================*/
+static void calc_sum(unsigned sector, char *data, char *sum)
+{
+ /* Compute the checksum for a sector. The sector number must be part
+ * of the checksum in some way.
+ */
+ unsigned long crc, *p, *q;
+ int i, j;
+ struct MD5Context ctx;
+
+ switch(SUM_TYPE) {
+ case ST_XOR:
+ /* Basic XOR checksum */
+ p = (unsigned long *) data;
+
+ memset(sum, 0, SUM_SIZE);
+ for(i = 0; i < SECTOR_SIZE / SUM_SIZE; i++) {
+ q = (unsigned long *) sum;
+ for(j = 0; j < SUM_SIZE / sizeof(*p); j++) {
+ *q ^= *p;
+ q++;
+ p++;
+ }
+ }
+ q = (unsigned long *) sum;
+ *q ^= sector;
+
+ break;
+
+ case ST_CRC:
+ /* CRC32 checksum */
+
+ crc = compute_crc((unsigned char *) data, SECTOR_SIZE);
+
+ q = (unsigned long *) sum;
+
+ *q = crc ^ sector;
+
+ break;
+
+ case ST_MD5:
+ /* MD5 checksum */
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, (unsigned char *) data, SECTOR_SIZE);
+ MD5Update(&ctx, (unsigned char *) §or, sizeof(sector));
+ MD5Final((unsigned char *) sum, &ctx);
+
+ break;
+
+ default:
+ panic(__FILE__, "invalid checksum type", SUM_TYPE);
+ }
+}
+
+/*===========================================================================*
+ * read_sectors *
+ *===========================================================================*/
+static int read_sectors(char *buf, sector_t phys_sector, int count)
+{
+ /* Read 'count' sectors starting at 'phys_sector' into 'buf'. If an
+ * EOF occurs, zero-fill the remaining part of the buffer.
+ */
+ size_t size, wsize;
+ int r;
+
+ size = wsize = count * SECTOR_SIZE;
+
+ r = read_write(SEC2POS(phys_sector), buf, buf, &size, FLT_READ);
+
+ if (r != OK)
+ return r;
+
+ if (size != wsize) {
+#if DEBUG
+ printf("Filter: EOF reading sector %lu\n", phys_sector);
+#endif
+
+ memset(buf + size, 0, wsize - size);
+ }
+
+ return OK;
+}
+
+/*===========================================================================*
+ * make_group_sum *
+ *===========================================================================*/
+static void make_group_sum(char *bufp, char *sump, sector_t sector, int index,
+ int count)
+{
+ /* Compute checksums for 'count' sectors within a group, starting at
+ * sector 'index' into the group, which has logical sector number
+ * 'sector'. The 'bufp' pointer points to the same first sector to
+ * start checksumming; 'sump' is a pointer to the checksum sector.
+ */
+
+ sump += index * SUM_SIZE;
+
+ while (count--) {
+ calc_sum(sector, bufp, sump);
+
+ bufp += SECTOR_SIZE;
+
+ sump += SUM_SIZE;
+ sector++;
+ }
+}
+
+/*===========================================================================*
+ * check_group_sum *
+ *===========================================================================*/
+static int check_group_sum(char *bufp, char *sump, sector_t sector, int index,
+ int count)
+{
+ /* Check checksums in a group. Parameters are the same as in
+ * make_group_sum(). Return OK if all checksums check out, or RET_REDO
+ * upon failure.
+ */
+ char sum_buffer[SECTOR_SIZE];
+
+ sump += index * SUM_SIZE;
+
+ while (count--) {
+ calc_sum(sector, bufp, sum_buffer);
+
+ if (memcmp(sum_buffer, sump, SUM_SIZE)) {
+ printf("Filter: BAD CHECKSUM at sector %lu\n", sector);
+
+ if (BAD_SUM_ERROR)
+ return bad_driver(DRIVER_MAIN, BD_DATA, EIO);
+ }
+
+ bufp += SECTOR_SIZE;
+ sump += SUM_SIZE;
+ sector++;
+ }
+
+ return OK;
+}
+
+/*===========================================================================*
+ * make_sum *
+ *===========================================================================*/
+static int make_sum(sector_t current_sector, sector_t sectors_left)
+{
+ /* Compute checksums over all data in the buffer with expanded data.
+ * As side effect, possibly read in first and last checksum sectors
+ * and data to fill the gap between the last data sector and the last
+ * checksum sector.
+ */
+ sector_t sector_in_group, group_left;
+ size_t size, gap;
+ char *extp;
+ int r;
+
+ /* See the description of the extended buffer in transfer(). A number
+ * of points are relevant for this function in particular:
+ *
+ * 1) If the "xx" head of the buffer does not cover an entire group,
+ * we need to copy in the first checksum sector so that we can
+ * modify it.
+ * 2) We can generate checksums for the full "yyyyy" groups without
+ * copying in the corresponding checksum sectors first, because
+ * those sectors will be overwritten entirely anyway.
+ * 3) We copy in not only the checksum sector for the group containing
+ * the "zzz" tail data, but also all the data between "zzz" and the
+ * last checksum sector. This allows us to write all the data in
+ * the buffer in one operation. In theory, we could verify the
+ * checksum of the data in this gap for extra early failure
+ * detection, but we currently do not do this.
+ *
+ * If points 1 and 3 cover the same group (implying a small, unaligned
+ * write operation), the read operation is done only once. Whether
+ * point 1 or 3 is skipped depends on whether there is a gap before
+ * the checksum sector.
+ */
+
+ sector_in_group = current_sector % NR_SUM_SEC;
+ group_left = NR_SUM_SEC - sector_in_group;
+
+ extp = ext_buffer;
+
+ /* This loop covers points 1 and 2. */
+ while (sectors_left >= group_left) {
+ size = group_left * SECTOR_SIZE;
+
+ if (sector_in_group > 0) {
+ if ((r = read_sectors(extp + size,
+ LOG2PHYS(current_sector) + group_left,
+ 1)) != OK)
+ return r;
+ }
+ else memset(extp + size, 0, SECTOR_SIZE);
+
+ make_group_sum(extp, extp + size, current_sector,
+ sector_in_group, group_left);
+
+ extp += size + SECTOR_SIZE;
+
+ sectors_left -= group_left;
+ current_sector += group_left;
+
+ sector_in_group = 0;
+ group_left = NR_SUM_SEC;
+ }
+
+ /* The remaining code covers point 3. */
+ if (sectors_left > 0) {
+ size = sectors_left * SECTOR_SIZE;
+
+ if (group_left != NR_SUM_SEC - sector_in_group)
+ panic(__FILE__, "group_left assertion", 0);
+
+ gap = group_left - sectors_left;
+
+ if (gap <= 0)
+ panic(__FILE__, "gap assertion", 0);
+
+ if ((r = read_sectors(extp + size,
+ LOG2PHYS(current_sector) + sectors_left,
+ gap + 1)) != OK)
+ return r;
+
+ make_group_sum(extp, extp + size + gap * SECTOR_SIZE,
+ current_sector, sector_in_group, sectors_left);
+ }
+
+ return OK;
+}
+
+/*===========================================================================*
+ * check_sum *
+ *===========================================================================*/
+static int check_sum(sector_t current_sector, size_t bytes_left)
+{
+ /* Check checksums of all data in the buffer with expanded data.
+ * Return OK if all checksums are okay, or RET_REDO upon failure.
+ */
+ sector_t sector_in_group;
+ size_t size, groupbytes_left;
+ int count;
+ char *extp;
+
+ extp = ext_buffer;
+
+ sector_in_group = current_sector % NR_SUM_SEC;
+ groupbytes_left = (NR_SUM_SEC - sector_in_group) * SECTOR_SIZE;
+
+ while (bytes_left > 0) {
+ size = MIN(bytes_left, groupbytes_left);
+ count = size / SECTOR_SIZE;
+
+ if (check_group_sum(extp, extp + groupbytes_left,
+ current_sector, sector_in_group, count))
+ return RET_REDO;
+
+ extp += size + SECTOR_SIZE;
+
+ bytes_left -= MIN(size + SECTOR_SIZE, bytes_left);
+ current_sector += count;
+
+ sector_in_group = 0;
+ groupbytes_left = GROUP_SIZE;
+ }
+
+ return OK;
+}
+
+/*===========================================================================*
+ * check_write *
+ *===========================================================================*/
+static int check_write(u64_t pos, size_t size)
+{
+ /* Read back the data just written, from both disks if mirroring is
+ * enabled, and check the result against the original. Return OK on
+ * success; report the malfunctioning driver and return RET_REDO
+ * otherwise.
+ */
+ char *rb0_buffer, *rb1_buffer;
+ size_t orig_size;
+ int r;
+
+ if (size == 0)
+ return OK;
+
+ rb0_buffer = rb1_buffer =
+ flt_malloc(size, rb0_array, SBUF_SIZE);
+ if (USE_MIRROR)
+ rb1_buffer = flt_malloc(size, rb1_array, SBUF_SIZE);
+
+ orig_size = size;
+
+ r = read_write(pos, rb0_buffer, rb1_buffer, &size, FLT_READ2);
+
+ if (r != OK) {
+ if (USE_MIRROR) flt_free(rb1_buffer, orig_size, rb1_array);
+ flt_free(rb0_buffer, orig_size, rb0_array);
+
+ return r;
+ }
+
+ /* If we get a size smaller than what we requested, then we somehow
+ * succeeded in writing past the disk end, and now fail to read it all
+ * back. This is not an error, and we just compare the part that we
+ * did manage to read back in.
+ */
+
+ if (memcmp(ext_buffer, rb0_buffer, size)) {
+#if DEBUG
+ printf("Filter: readback from disk 0 failed (size %d)\n",
+ size);
+#endif
+
+ return bad_driver(DRIVER_MAIN, BD_DATA, EFAULT);
+ }
+
+ if (USE_MIRROR && memcmp(ext_buffer, rb1_buffer, size)) {
+#if DEBUG
+ printf("Filter: readback from disk 1 failed (size %d)\n",
+ size);
+#endif
+
+ return bad_driver(DRIVER_BACKUP, BD_DATA, EFAULT);
+ }
+
+ if (USE_MIRROR) flt_free(rb1_buffer, orig_size, rb1_array);
+ flt_free(rb0_buffer, orig_size, rb0_array);
+
+ return OK;
+}
+
+/*===========================================================================*
+ * expand *
+ *===========================================================================*/
+static void expand(sector_t first_sector, char *buffer, sector_t sectors_left)
+{
+ /* Expand the contiguous data in 'buffer' to interspersed format in
+ * 'ext_buffer'. The checksum areas are not touched.
+ */
+ char *srcp, *dstp;
+ sector_t group_left;
+ size_t size;
+ int count;
+
+ srcp = buffer;
+ dstp = ext_buffer;
+
+ group_left = NR_SUM_SEC - first_sector % NR_SUM_SEC;
+
+ while (sectors_left > 0) {
+ count = MIN(sectors_left, group_left);
+ size = count * SECTOR_SIZE;
+
+ memcpy(dstp, srcp, size);
+
+ srcp += size;
+ dstp += size + SECTOR_SIZE;
+
+ sectors_left -= count;
+ group_left = NR_SUM_SEC;
+ }
+}
+
+/*===========================================================================*
+ * collapse *
+ *===========================================================================*/
+static void collapse(sector_t first_sector, char *buffer, size_t *sizep)
+{
+ /* Collapse the interspersed data in 'ext_buffer' to contiguous format
+ * in 'buffer'. As side effect, adjust the given size to reflect the
+ * resulting contiguous data size.
+ */
+ char *srcp, *dstp;
+ size_t size, bytes_left, groupbytes_left;
+
+ srcp = ext_buffer;
+ dstp = buffer;
+
+ bytes_left = *sizep;
+ groupbytes_left =
+ (NR_SUM_SEC - first_sector % NR_SUM_SEC) * SECTOR_SIZE;
+
+ while (bytes_left > 0) {
+ size = MIN(bytes_left, groupbytes_left);
+
+ memcpy(dstp, srcp, size);
+
+ srcp += size + SECTOR_SIZE;
+ dstp += size;
+
+ bytes_left -= MIN(size + SECTOR_SIZE, bytes_left);
+ groupbytes_left = GROUP_SIZE;
+ }
+
+ *sizep = dstp - buffer;
+}
+
+/*===========================================================================*
+ * expand_sizes *
+ *===========================================================================*/
+static size_t expand_sizes(sector_t first_sector, sector_t nr_sectors,
+ size_t *req_size)
+{
+ /* Compute the size of the data area including interspersed checksum
+ * sectors (req_size) and the size of the data area including
+ * interspersed and trailing checksum sectors (the return value).
+ */
+ sector_t last_sector, sum_sector, phys_sector;
+
+ last_sector = LOG2PHYS(first_sector + nr_sectors - 1);
+
+ sum_sector = SEC2SUM_NR(first_sector + nr_sectors - 1);
+
+ phys_sector = LOG2PHYS(first_sector);
+
+ *req_size = (last_sector - phys_sector + 1) * SECTOR_SIZE;
+
+ return (sum_sector - phys_sector + 1) * SECTOR_SIZE;
+}
+
+/*===========================================================================*
+ * collapse_size *
+ *===========================================================================*/
+static void collapse_size(sector_t first_sector, size_t *sizep)
+{
+ /* Compute the size of the contiguous user data written to disk, given
+ * the result size of the write operation with interspersed checksums.
+ */
+ sector_t sector_in_group;
+ size_t sectors_from_group_base, nr_sum_secs, nr_data_secs;
+
+ sector_in_group = first_sector % NR_SUM_SEC;
+
+ sectors_from_group_base = *sizep / SECTOR_SIZE + sector_in_group;
+
+ nr_sum_secs = sectors_from_group_base / (NR_SUM_SEC+1);
+
+ nr_data_secs = sectors_from_group_base - sector_in_group - nr_sum_secs;
+
+ *sizep = nr_data_secs * SECTOR_SIZE;
+}
+
+/*===========================================================================*
+ * transfer *
+ *===========================================================================*/
+int transfer(u64_t pos, char *buffer, size_t *sizep, int flag_rw)
+{
+ /* Transfer data in interspersed-checksum format. When writing, first
+ * compute checksums, and read back the written data afterwards. When
+ * reading, check the stored checksums afterwards.
+ */
+ sector_t first_sector, nr_sectors;
+ size_t ext_size, req_size, res_size;
+ u64_t phys_pos;
+ int r;
+
+ /* If we don't use checksums or even checksum layout, simply pass on
+ * the request to the drivers as is.
+ */
+ if (!USE_SUM_LAYOUT)
+ return read_write(pos, buffer, buffer, sizep, flag_rw);
+
+ /* The extended buffer (for checksumming) essentially looks like this:
+ *
+ * ------------------------------
+ * |xx|C|yyyyy|C|yyyyy|C|zzz |C|
+ * ------------------------------
+ *
+ * In this example, "xxyyyyyyyyyyzzz" is our actual data. The data is
+ * split up into groups, so that each group is followed by a checksum
+ * sector C containing the checksums for all data sectors in that
+ * group. The head and tail of the actual data may cover parts of
+ * groups; the remaining data (nor their checksums) are not to be
+ * modified.
+ *
+ * The entire buffer is written or read in one operation: the
+ * read_write() call below. In order to write, we may first have to
+ * read some data; see the description in make_sum().
+ *
+ * Some points of interest here:
+ * - We need a buffer large enough to hold the all user and non-user
+ * data, from the first "xx" to the last checksum sector. This size
+ * is ext_size.
+ * - For writing, we need to expand the user-provided data from
+ * contiguous layout to interspersed format. The size of the user
+ * data after expansion is req_size.
+ * - For reading, we need to collapse the user-requested data from
+ * interspersed to contiguous format. For writing, we still need to
+ * compute the contiguous result size to return to the user.
+ * - In both cases, the result size may be different from the
+ * requested write size, because an EOF (as in, disk end) may occur
+ * and the resulting size is less than the requested size.
+ * - If we only follow the checksum layout, and do not do any
+ * checksumming, ext_size is reduced to req_size.
+ */
+
+ first_sector = POS2SEC(pos);
+ nr_sectors = *sizep / SECTOR_SIZE;
+ phys_pos = SEC2POS(LOG2PHYS(first_sector));
+
+#if DEBUG2
+ printf("Filter: transfer: pos 0x%lx:0x%lx -> phys_pos 0x%lx:0x%lx\n",
+ ex64hi(pos), ex64lo(pos), ex64hi(phys_pos), ex64lo(phys_pos));
+#endif
+
+ /* Compute the size for the buffer and for the user data after
+ * expansion.
+ */
+ ext_size = expand_sizes(first_sector, nr_sectors, &req_size);
+
+ if (!USE_CHECKSUM)
+ ext_size = req_size;
+
+ ext_buffer = flt_malloc(ext_size, ext_array, SBUF_SIZE);
+
+ if (flag_rw == FLT_WRITE) {
+ expand(first_sector, buffer, nr_sectors);
+
+ if (USE_CHECKSUM && make_sum(first_sector, nr_sectors))
+ return RET_REDO;
+ }
+
+ /* Perform the actual I/O. */
+ res_size = ext_size;
+ r = read_write(phys_pos, ext_buffer, ext_buffer, &res_size, flag_rw);
+
+#if DEBUG2
+ printf("Filter: transfer: read_write(%x:%x, %u, %d) = %d, %u\n",
+ ex64hi(phys_pos), ex64lo(phys_pos), ext_size, flag_rw, r,
+ res_size);
+#endif
+
+ if (r != OK) {
+ flt_free(ext_buffer, ext_size, ext_array);
+
+ return r;
+ }
+
+ /* Limit the resulting size to the user data part of the buffer.
+ * The resulting size may already be less, due to an EOF.
+ */
+ *sizep = MIN(req_size, res_size);
+
+ if (flag_rw == FLT_WRITE) {
+ if (USE_CHECKSUM && check_write(phys_pos, res_size))
+ return RET_REDO;
+
+ collapse_size(first_sector, sizep);
+ }
+ else { /* FLT_READ */
+ if (USE_CHECKSUM && check_sum(first_sector, *sizep))
+ return RET_REDO;
+
+ collapse(first_sector, buffer, sizep);
+ }
+
+ flt_free(ext_buffer, ext_size, ext_array);
+
+ return OK;
+}
+
+/*===========================================================================*
+ * convert *
+ *===========================================================================*/
+u64_t convert(u64_t size)
+{
+ /* Given a raw disk size, subtract the amount of disk space used for
+ * checksums, resulting in the user-visible disk size.
+ */
+ sector_t sectors;
+
+ if (!USE_SUM_LAYOUT)
+ return size;
+
+ sectors = POS2SEC(size);
+
+ return SEC2POS(sectors / (NR_SUM_SEC + 1) * NR_SUM_SEC);
+}
--- /dev/null
+/* Filter driver - utility functions */
+
+#include "inc.h"
+#include <sys/mman.h>
+#include <signal.h>
+
+static clock_t next_alarm;
+
+/*===========================================================================*
+ * flt_malloc *
+ *===========================================================================*/
+char *flt_malloc(size_t size, char *sbuf, size_t ssize)
+{
+ /* Allocate a buffer for 'size' bytes. If 'size' is equal to or less
+ * than 'ssize', return the static buffer 'sbuf', otherwise, use
+ * malloc() to allocate memory dynamically.
+ */
+ char *p;
+
+ if (size <= ssize)
+ return sbuf;
+
+ p = alloc_contig(size, 0, NULL);
+ if (p == NULL)
+ panic(__FILE__, "out of memory", size);
+
+ return p;
+}
+
+/*===========================================================================*
+ * flt_free *
+ *===========================================================================*/
+void flt_free(char *buf, size_t size, char *sbuf)
+{
+ /* Free a buffer previously allocated with flt_malloc().
+ */
+
+ if(buf != sbuf)
+ munmap(buf, size);
+}
+
+/*===========================================================================*
+ * print64 *
+ *===========================================================================*/
+char *print64(u64_t p)
+{
+#define NB 10
+ static int n = 0;
+ static char buf[NB][100];
+ u32_t lo = ex64lo(p), hi = ex64hi(p);
+ n = (n+1) % NB;
+ if(!hi) sprintf(buf[n], "%lx", lo);
+ else sprintf(buf[n], "%lx%08lx", hi, lo);
+ return buf[n];
+}
+
+/*===========================================================================*
+ * flt_alarm *
+ *===========================================================================*/
+clock_t flt_alarm(clock_t dt)
+{
+ int r;
+
+ if(dt < 0)
+ return next_alarm;
+
+ r = sys_setalarm(dt, 0);
+
+ if(r != OK)
+ panic(__FILE__, "sys_setalarm failed", r);
+
+ if(dt == 0) {
+ if(!next_alarm)
+ panic(__FILE__, "clearing unset alarm", r);
+ next_alarm = 0;
+ } else {
+ if(next_alarm)
+ panic(__FILE__, "overwriting alarm", r);
+ if ((r = getuptime(&next_alarm)) != OK)
+ panic(__FILE__, "getuptime failed", r);
+ next_alarm += dt;
+ }
+
+ return next_alarm;
+}
+
+/*===========================================================================*
+ * got_alarm *
+ *===========================================================================*/
+static void got_alarm(int sig)
+{
+ /* Do nothing. */
+}
+
+/*===========================================================================*
+ * flt_sleep *
+ *===========================================================================*/
+void flt_sleep(int secs)
+{
+ /* Sleep for the given number of seconds. Don't use sleep(), as that
+ * will end up calling select() to VFS. This implementation could be
+ * improved.
+ */
+
+ signal(SIGALRM, got_alarm);
+ alarm(secs);
+
+ pause();
+}
;
uid 0;
};
+
+driver filter
+{
+ system
+ SETALARM # 24
+ TIMES # 25
+ GETINFO # 26
+ SAFECOPYFROM # 31
+ SAFECOPYTO # 32
+ SETGRANT # 34
+ SYSCTL # 44
+ ;
+ ipc
+ SYSTEM PM VFS RS DS VM
+ at_wini
+ bios_wini
+ ;
+ control
+ at_wini
+ bios_wini
+ ;
+};
#define RESCUE_MAJOR 9 /* major device for rescue */
+#define FILTER_MAJOR 11 /* major device for filter driver */
+
#define LOG_MAJOR 15 /* major device for log driver */
# define IS_KLOG_DEV 0 /* minor device for /dev/klog */
DT(0, no_dev, 0, NONE, DMAP_MUTABLE, "") /* 8 = /dev/c1 */
DT(0, 0, 0, 0, DMAP_MUTABLE, "") /* 9 = not used */
DT(0, no_dev, 0, 0, DMAP_MUTABLE, "") /*10 = /dev/c2 */
- DT(0, 0, 0, 0, DMAP_MUTABLE, "") /*11 = not used */
+ DT(0, no_dev, 0, 0, DMAP_MUTABLE, "") /*11 = /dev/filter*/
DT(0, no_dev, 0, NONE, DMAP_MUTABLE, "") /*12 = /dev/c3 */
DT(0, no_dev, 0, NONE, DMAP_MUTABLE, "") /*13 = /dev/audio */
DT(0, 0, 0, 0, DMAP_MUTABLE, "") /*14 = not used */