e7db2d3588
This driver can be loaded as an overlay on top of a real block device, and can then be used to generate block-level failures for certain transfer requests. Specifically, a rule-based system allows the user to introduce (overt and silent) data corruption and errors. It exposes itself through /dev/fbd, and a file system can be mounted on top of it. The new fbdctl(8) tool can be used to control the driver; see ``man fbdctl'' for details. It also comes with a test set, located in test/fbdtest.
185 lines
4.8 KiB
C
185 lines
4.8 KiB
C
#include <minix/drivers.h>
|
|
#include <minix/ioctl.h>
|
|
#include <sys/ioc_fbd.h>
|
|
|
|
#include "action.h"
|
|
#include "rule.h"
|
|
|
|
PRIVATE struct fbd_rule rules[MAX_RULES];
|
|
PRIVATE struct fbd_rule *matches[MAX_RULES];
|
|
PRIVATE int nr_matches;
|
|
|
|
/*===========================================================================*
|
|
* rule_ctl *
|
|
*===========================================================================*/
|
|
PUBLIC int rule_ctl(int request, endpoint_t endpt, cp_grant_id_t grant)
|
|
{
|
|
/* Handle an I/O control request regarding rules. */
|
|
fbd_rulenum_t i;
|
|
int r;
|
|
|
|
/* Note that any of the safecopy calls may fail if the ioctl is
|
|
* improperly defined in userland; never panic if they fail!
|
|
*/
|
|
switch (request) {
|
|
case FBDCADDRULE:
|
|
/* Find a free rule slot. */
|
|
for (i = 1; i <= MAX_RULES; i++)
|
|
if (rules[i-1].num == 0)
|
|
break;
|
|
|
|
if (i == MAX_RULES+1)
|
|
return ENOMEM;
|
|
|
|
/* Copy in the rule. */
|
|
if ((r = sys_safecopyfrom(endpt, grant, 0,
|
|
(vir_bytes) &rules[i-1], sizeof(rules[0]),
|
|
D)) != OK)
|
|
return r;
|
|
|
|
/* Mark the rule as active, and return its number. */
|
|
rules[i-1].num = i;
|
|
|
|
return i;
|
|
|
|
case FBDCDELRULE:
|
|
/* Copy in the given rule number. */
|
|
if ((r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &i,
|
|
sizeof(i), D)) != OK)
|
|
return r;
|
|
|
|
/* Fail if the given rule number is not valid or in use.
|
|
* Allow the caller to determine the maximum rule number.
|
|
*/
|
|
if (i <= 0 || i > MAX_RULES) return EINVAL;
|
|
|
|
if (rules[i-1].num != i) return ENOENT;
|
|
|
|
/* Mark the rule as not active. */
|
|
rules[i-1].num = 0;
|
|
|
|
return OK;
|
|
|
|
case FBDCGETRULE:
|
|
/* Copy in just the rule number from the given structure. */
|
|
if ((r = sys_safecopyfrom(endpt, grant,
|
|
offsetof(struct fbd_rule, num), (vir_bytes) &i,
|
|
sizeof(i), D)) != OK)
|
|
return r;
|
|
|
|
/* Fail if the given rule number is not valid or in use.
|
|
* Allow the caller to determine the maximum rule number.
|
|
*/
|
|
if (i <= 0 || i > MAX_RULES) return EINVAL;
|
|
|
|
if (rules[i-1].num != i) return ENOENT;
|
|
|
|
/* Copy out the entire rule as is. */
|
|
return sys_safecopyto(endpt, grant, 0, (vir_bytes) &rules[i-1],
|
|
sizeof(rules[0]), D);
|
|
|
|
default:
|
|
return EINVAL;
|
|
}
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* rule_match *
|
|
*===========================================================================*/
|
|
PRIVATE int rule_match(struct fbd_rule *rule, u64_t pos, size_t size, int flag)
|
|
{
|
|
/* Check whether the given rule matches the given parameters. As side
|
|
* effect, update counters in the rule as appropriate.
|
|
*/
|
|
|
|
/* Ranges must overlap (start < pos+size && end > pos). */
|
|
if (cmp64(rule->start, add64u(pos, size)) >= 0 ||
|
|
(cmp64u(rule->end, 0) && cmp64(rule->end, pos) <= 0))
|
|
return FALSE;
|
|
|
|
/* Flags must match. */
|
|
if (!(rule->flags & flag)) return FALSE;
|
|
|
|
/* This is a match, but is it supposed to trigger yet? */
|
|
if (rule->skip > 0) {
|
|
rule->skip--;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* rule_find *
|
|
*===========================================================================*/
|
|
PUBLIC int rule_find(u64_t pos, size_t size, int flag)
|
|
{
|
|
/* Find all matching rules, and return a hook mask. */
|
|
struct fbd_rule *rule;
|
|
int i, hooks;
|
|
|
|
nr_matches = 0;
|
|
hooks = 0;
|
|
|
|
for (i = 0; i < MAX_RULES; i++) {
|
|
rule = &rules[i];
|
|
|
|
if (rule->num == 0) continue;
|
|
|
|
if (!rule_match(rule, pos, size, flag))
|
|
continue;
|
|
|
|
matches[nr_matches++] = rule;
|
|
|
|
/* If the rule has a limited lifetime, update it now. */
|
|
if (rule->count > 0) {
|
|
rule->count--;
|
|
|
|
/* Disable the rule from future matching. */
|
|
if (rule->count == 0)
|
|
rule->num = 0;
|
|
}
|
|
|
|
hooks |= action_mask(rule);
|
|
}
|
|
|
|
return hooks;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* rule_pre_hook *
|
|
*===========================================================================*/
|
|
PUBLIC void rule_pre_hook(iovec_t *iov, unsigned *count, size_t *size,
|
|
u64_t *pos)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < nr_matches; i++)
|
|
if (action_mask(matches[i]) & PRE_HOOK)
|
|
action_pre_hook(matches[i], iov, count, size, pos);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* rule_io_hook *
|
|
*===========================================================================*/
|
|
PUBLIC void rule_io_hook(char *buf, size_t size, u64_t pos, int flag)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < nr_matches; i++)
|
|
if (action_mask(matches[i]) & IO_HOOK)
|
|
action_io_hook(matches[i], buf, size, pos, flag);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* rule_post_hook *
|
|
*===========================================================================*/
|
|
PUBLIC void rule_post_hook(size_t osize, int *result)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < nr_matches; i++)
|
|
if (action_mask(matches[i]) & POST_HOOK)
|
|
action_post_hook(matches[i], osize, result);
|
|
}
|