# HG changeset patch
# User kaf24@xxxxxxxxxxxxxxxxxxxx
# Node ID a9ee400a5da98acc8da566a24ecae05902b2bed2
# Parent b60643391488ccc7b3d0c304c1a3a522a18671eb
1) More testing: include tests which I forgot in previous patch, remove
xs_watch_stress, reduce cycles in "make check" random test.
2) xs_crashme: corrupt random packets going to xenstored, watch it
crash.
3) Handle second input from before we finished output on first one.
4) Fix bug where one-arg operations are given zero args.
5) Fix bug where SET_PERMS fails after blocking on transaction.
6) Fix memory leak when DIRECTORY op given no argument.
7) Fail on first memory leak, for better testing.
8) Fix missing waiting_for_ack initialization for new connections.
9) Ensure all input and output is handled for domains so we don't stall.
10) Fix overrun bug in xs_count_strings on non-nul-terminated strings.
11) New test for clients which write without waiting for response.
Signed-off-by: Rusty Russell <rusty@xxxxxxxxxxxxxxx>
diff -r b60643391488 -r a9ee400a5da9 tools/xenstore/Makefile
--- a/tools/xenstore/Makefile Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/Makefile Mon Aug 8 09:13:19 2005
@@ -41,9 +41,9 @@
xs_test: xs_test.o xs_lib.o utils.o
xs_random: xs_random.o xs_test_lib.o xs_lib.o talloc.o utils.o
xs_stress: xs_stress.o xs_test_lib.o xs_lib.o talloc.o utils.o
-xs_watch_stress: xs_watch_stress.o xs_test_lib.o xs_lib.o talloc.o utils.o
+xs_crashme: xs_crashme.o xs_lib.o talloc.o utils.o
-xs_test.o xs_stress.o xs_watch_stress.o xenstored_core_test.o
xenstored_watch_test.o xenstored_transaction_test.o xenstored_domain_test.o
xs_random.o xs_test_lib.o talloc_test.o fake_libxc.o: CFLAGS=$(BASECFLAGS)
$(TESTFLAGS)
+xs_test.o xs_stress.o xenstored_core_test.o xenstored_watch_test.o
xenstored_transaction_test.o xenstored_domain_test.o xs_random.o xs_test_lib.o
talloc_test.o fake_libxc.o xs_crashme.o: CFLAGS=$(BASECFLAGS) $(TESTFLAGS)
xenstored_%_test.o: xenstored_%.c
$(COMPILE.c) -o $@ $<
@@ -65,7 +65,7 @@
clean: testsuite-clean
rm -f *.o *.opic *.a
- rm -f xen xenstored xs_random xs_stress xs_watch_stress
+ rm -f xen xenstored xs_random xs_stress xs_crashme
rm -f xs_test xenstored_test xs_dom0_test
-$(RM) $(PROG_DEP)
@@ -96,14 +96,18 @@
$(TESTENV) ./xs_random --fast /tmp/xs_random 100000 $(RANDSEED) && echo
$(TESTENV) ./xs_random --fail /tmp/xs_random 10000 $(RANDSEED)
+crashme: xs_crashme xenstored_test
+ rm -rf $(TESTDIR)/store $(TESTDIR)/transactions /tmp/xs_crashme.vglog*
/tmp/trace
+ export $(TESTENV); ./xs_crashme 5000 $(RANDSEED) 2>/dev/null
+ if [ -n "`cat /tmp/xs_crashme.vglog*`" ]; then echo Valgrind
complained; cat /tmp/xs_crashme.vglog*; exit 1; fi
+ rm -rf $(TESTDIR)/store $(TESTDIR)/transactions /tmp/xs_crashme.vglog*
/tmp/trace
+
randomcheck-fast: xs_random xenstored_test
- @$(TESTENV) ./xs_random --fast /tmp/xs_random 10000 $(RANDSEED)
+ @$(TESTENV) ./xs_random --fast /tmp/xs_random 2000 $(RANDSEED)
-stresstest: xs_stress xs_watch_stress xenstored_test
+stresstest: xs_stress xenstored_test
rm -rf $(TESTDIR)/store $(TESTDIR)/transactions
export $(TESTENV); PID=`./xenstored_test --output-pid
--trace-file=/tmp/trace`; ./xs_stress 5000; ret=$$?; kill $$PID; exit $$ret
- rm -rf $(TESTDIR)/store $(TESTDIR)/transactions
- export $(TESTENV); PID=`./xenstored_test --output-pid`;
./xs_watch_stress; ret=$$?; kill $$PID; exit $$ret
xs_dom0_test: xs_dom0_test.o utils.o
$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -lxc -o $@
diff -r b60643391488 -r a9ee400a5da9 tools/xenstore/utils.c
--- a/tools/xenstore/utils.c Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/utils.c Mon Aug 8 09:13:19 2005
@@ -84,9 +84,6 @@
void daemonize(void)
{
pid_t pid;
- int fd;
- size_t len;
- char buf[100];
/* Separate from our parent via fork, so init inherits us. */
if ((pid = fork()) < 0)
@@ -104,18 +101,6 @@
chdir("/");
/* Discard our parent's old-fashioned umask prejudices. */
umask(0);
-
- fd = open("/var/run/xenstored.pid", O_RDWR | O_CREAT);
- if (fd == -1) {
- exit(1);
- }
-
- if (lockf(fd, F_TLOCK, 0) == -1) {
- exit(1);
- }
-
- len = sprintf(buf, "%d\n", getpid());
- write(fd, buf, len);
}
diff -r b60643391488 -r a9ee400a5da9 tools/xenstore/xenstored_core.c
--- a/tools/xenstore/xenstored_core.c Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/xenstored_core.c Mon Aug 8 09:13:19 2005
@@ -252,6 +252,7 @@
int ret;
struct buffered_data *out = conn->out;
+ assert(conn->state != BLOCKED);
if (out->inhdr) {
if (verbose)
xprintf("Writing msg %s (%s) out to %p\n",
@@ -289,6 +290,10 @@
talloc_free(out);
queue_next_event(conn);
+
+ /* No longer busy? */
+ if (!conn->out)
+ conn->state = OK;
return true;
}
@@ -492,6 +497,8 @@
conn->waiting_reply = bdata;
} else
conn->out = bdata;
+ assert(conn->state != BLOCKED);
+ conn->state = BUSY;
}
/* Some routines (write, mkdir, etc) just need a non-error return */
@@ -544,7 +551,7 @@
/* We expect one arg in the input: return NULL otherwise. */
static const char *onearg(struct buffered_data *in)
{
- if (get_string(in, 0) != in->used)
+ if (!in->used || get_string(in, 0) != in->used)
return NULL;
return in->buffer;
}
@@ -807,7 +814,7 @@
static void send_directory(struct connection *conn, const char *node)
{
- char *path, *reply = talloc_strdup(node, "");
+ char *path, *reply;
unsigned int reply_len = 0;
DIR **dir;
struct dirent *dirent;
@@ -825,6 +832,7 @@
return;
}
+ reply = talloc_strdup(node, "");
while ((dirent = readdir(*dir)) != NULL) {
int len = strlen(dirent->d_name) + 1;
@@ -1082,7 +1090,7 @@
static void do_set_perms(struct connection *conn, struct buffered_data *in)
{
unsigned int num;
- char *node;
+ char *node, *permstr;
struct xs_permissions *perms;
num = xs_count_strings(in->buffer, in->used);
@@ -1093,7 +1101,7 @@
/* First arg is node name. */
node = canonicalize(conn, in->buffer);
- in->buffer += strlen(in->buffer) + 1;
+ permstr = in->buffer + strlen(in->buffer) + 1;
num--;
if (!within_transaction(conn->transaction, node)) {
@@ -1111,7 +1119,7 @@
}
perms = talloc_array(node, struct xs_permissions, num);
- if (!xs_strings_to_perms(perms, num, in->buffer)) {
+ if (!xs_strings_to_perms(perms, num, permstr)) {
send_error(conn, errno);
return;
}
@@ -1280,8 +1288,10 @@
talloc_free(in);
talloc_set_fail_handler(NULL, NULL);
if (talloc_total_blocks(NULL)
- != talloc_total_blocks(talloc_autofree_context()) + 1)
+ != talloc_total_blocks(talloc_autofree_context()) + 1) {
talloc_report_full(NULL, stderr);
+ abort();
+ }
}
/* Errors in reading or allocating here mean we get out of sync, so we
@@ -1305,8 +1315,10 @@
return;
if (in->hdr.msg.len > PATH_MAX) {
+#ifndef TESTING
syslog(LOG_DAEMON, "Client tried to feed us %i",
in->hdr.msg.len);
+#endif
goto bad_client;
}
@@ -1357,6 +1369,7 @@
consider_message(i);
}
break;
+ case BUSY:
case OK:
break;
}
@@ -1382,6 +1395,7 @@
new->state = OK;
new->blocked_by = NULL;
new->out = new->waiting_reply = NULL;
+ new->waiting_for_ack = NULL;
new->fd = -1;
new->id = 0;
new->domain = NULL;
@@ -1461,6 +1475,7 @@
printf(" state = %s\n",
i->state == OK ? "OK"
: i->state == BLOCKED ? "BLOCKED"
+ : i->state == BUSY ? "BUSY"
: "INVALID");
if (i->id)
printf(" id = %i\n", i->id);
@@ -1631,6 +1646,7 @@
max = initialize_set(&inset, &outset, *sock, *ro_sock, event_fd);
/* Main loop. */
+ /* FIXME: Rewrite so noone can starve. */
for (;;) {
struct connection *i;
struct timeval *tvp = NULL, tv;
@@ -1675,10 +1691,22 @@
}
}
- /* Flush output for domain connections, */
- list_for_each_entry(i, &connections, list)
- if (i->domain && i->out)
+ /* Handle all possible I/O for domain connections. */
+ more:
+ list_for_each_entry(i, &connections, list) {
+ if (!i->domain)
+ continue;
+
+ if (domain_can_read(i)) {
+ handle_input(i);
+ goto more;
+ }
+
+ if (domain_can_write(i)) {
handle_output(i);
+ goto more;
+ }
+ }
if (tvp) {
check_transaction_timeout();
diff -r b60643391488 -r a9ee400a5da9 tools/xenstore/xenstored_core.h
--- a/tools/xenstore/xenstored_core.h Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/xenstored_core.h Mon Aug 8 09:13:19 2005
@@ -51,6 +51,8 @@
{
/* Blocked by transaction. */
BLOCKED,
+ /* Doing action, not listening */
+ BUSY,
/* Completed */
OK,
};
@@ -65,7 +67,7 @@
/* Who am I? 0 for socket connections. */
domid_t id;
- /* Blocked on transaction? */
+ /* Blocked on transaction? Busy? */
enum state state;
/* Node we are waiting for (if state == BLOCKED) */
diff -r b60643391488 -r a9ee400a5da9 tools/xenstore/xenstored_domain.c
--- a/tools/xenstore/xenstored_domain.c Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/xenstored_domain.c Mon Aug 8 09:13:19 2005
@@ -227,32 +227,27 @@
return NULL;
}
+/* We scan all domains rather than use the information given here. */
void handle_event(int event_fd)
{
u16 port;
- struct domain *domain;
if (read(event_fd, &port, sizeof(port)) != sizeof(port))
barf_perror("Failed to read from event fd");
-
- /* We have to handle *all* the data available before we ack:
- * careful that handle_input/handle_output can destroy conn.
- */
- while ((domain = find_domain(port)) != NULL) {
- if (domain->conn->state == OK
- && buffer_has_input(domain->input))
- handle_input(domain->conn);
- else if (domain->conn->out
- && buffer_has_output_room(domain->output))
- handle_output(domain->conn);
- else
- break;
- }
-
#ifndef TESTING
if (write(event_fd, &port, sizeof(port)) != sizeof(port))
barf_perror("Failed to write to event fd");
#endif
+}
+
+bool domain_can_read(struct connection *conn)
+{
+ return conn->state == OK && buffer_has_input(conn->domain->input);
+}
+
+bool domain_can_write(struct connection *conn)
+{
+ return conn->out && buffer_has_output_room(conn->domain->output);
}
static struct domain *new_domain(void *context, domid_t domid,
diff -r b60643391488 -r a9ee400a5da9 tools/xenstore/xenstored_domain.h
--- a/tools/xenstore/xenstored_domain.h Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/xenstored_domain.h Mon Aug 8 09:13:19 2005
@@ -40,4 +40,8 @@
/* Read existing connection information from store. */
void restore_existing_connections(void);
+/* Can connection attached to domain read/write. */
+bool domain_can_read(struct connection *conn);
+bool domain_can_write(struct connection *conn);
+
#endif /* _XENSTORED_DOMAIN_H */
diff -r b60643391488 -r a9ee400a5da9 tools/xenstore/xs.c
--- a/tools/xenstore/xs.c Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/xs.c Mon Aug 8 09:13:19 2005
@@ -204,13 +204,19 @@
return NULL;
}
- assert(msg.type == type);
+ if (msg.type != type) {
+ free(ret);
+ saved_errno = EBADF;
+ goto close_fd;
+
+ }
return ret;
fail:
/* We're in a bad state, so close fd. */
saved_errno = errno;
sigaction(SIGPIPE, &oldact, NULL);
+close_fd:
close(h->fd);
h->fd = -1;
errno = saved_errno;
diff -r b60643391488 -r a9ee400a5da9 tools/xenstore/xs_lib.c
--- a/tools/xenstore/xs_lib.c Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/xs_lib.c Mon Aug 8 09:13:19 2005
@@ -152,8 +152,9 @@
unsigned int num;
const char *p;
- for (p = strings, num = 0; p < strings + len; p += strlen(p) + 1)
- num++;
+ for (p = strings, num = 0; p < strings + len; p++)
+ if (*p == '\0')
+ num++;
return num;
}
diff -r b60643391488 -r a9ee400a5da9 tools/xenstore/xs_random.c
--- a/tools/xenstore/xs_random.c Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/xs_random.c Mon Aug 8 09:13:19 2005
@@ -349,19 +349,12 @@
{
char *dirname = path_to_name(info, path);
- /* Same effective order as daemon, so error returns are right. */
- if (mkdir(dirname, 0700) != 0) {
- if (errno != ENOENT && errno != ENOTDIR)
- write_ok(info, path);
- return false;
- }
-
- if (!write_ok(info, path)) {
- int saved_errno = errno;
- rmdir(dirname);
- errno = saved_errno;
- return false;
- }
+ if (!write_ok(info, path))
+ return false;
+
+ if (mkdir(dirname, 0700) != 0)
+ return false;
+
init_perms(dirname);
return true;
}
@@ -984,13 +977,15 @@
static void setup_file_ops(const char *dir)
{
- char *cmd = talloc_asprintf(NULL, "echo -n r0 > %s/.perms", dir);
+ struct xs_permissions perm = { .id = 0, .perms = XS_PERM_READ };
+ struct file_ops_info *h = file_handle(dir);
if (mkdir(dir, 0700) != 0)
barf_perror("Creating directory %s", dir);
- if (mkdir(talloc_asprintf(cmd, "%s/tool", dir), 0700) != 0)
+ if (mkdir(talloc_asprintf(h, "%s/tool", dir), 0700) != 0)
barf_perror("Creating directory %s/tool", dir);
- do_command(cmd);
- talloc_free(cmd);
+ if (!file_set_perms(h, talloc_strdup(h, "/"), &perm, 1))
+ barf_perror("Setting root perms in %s", dir);
+ file_close(h);
}
static void setup_xs_ops(void)
diff -r b60643391488 -r a9ee400a5da9 tools/xenstore/xs_test.c
--- a/tools/xenstore/xs_test.c Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/xs_test.c Mon Aug 8 09:13:19 2005
@@ -28,13 +28,13 @@
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
+#include <sys/mman.h>
#include <fnmatch.h>
#include <stdarg.h>
#include <string.h>
#include <getopt.h>
#include <ctype.h>
#include <sys/time.h>
-#include <sys/mman.h>
#include "utils.h"
#include "xs_lib.h"
#include "list.h"
@@ -43,7 +43,7 @@
static struct xs_handle *handles[10] = { NULL };
-static unsigned int timeout_ms = 50;
+static unsigned int timeout_ms = 200;
static bool timeout_suppressed = true;
static bool readonly = false;
static bool print_input = false;
@@ -213,6 +213,8 @@
" notimeout\n"
" readonly\n"
" readwrite\n"
+ " noackwrite <path> <flags> <value>...\n"
+ " readack\n"
" dump\n");
}
@@ -365,6 +367,45 @@
failed(handle);
}
+static void do_noackwrite(unsigned int handle,
+ char *path, const char *flags, char *data)
+{
+ struct xsd_sockmsg msg;
+
+ /* Format: Flags (as string), path, data. */
+ if (streq(flags, "none"))
+ flags = XS_WRITE_NONE;
+ else if (streq(flags, "create"))
+ flags = XS_WRITE_CREATE;
+ else if (streq(flags, "excl"))
+ flags = XS_WRITE_CREATE_EXCL;
+ else
+ barf("noackwrite flags 'none', 'create' or 'excl' only");
+
+ msg.len = strlen(path) + 1 + strlen(flags) + 1 + strlen(data);
+ msg.type = XS_WRITE;
+ if (!write_all_choice(handles[handle]->fd, &msg, sizeof(msg)))
+ failed(handle);
+ if (!write_all_choice(handles[handle]->fd, path, strlen(path) + 1))
+ failed(handle);
+ if (!write_all_choice(handles[handle]->fd, flags, strlen(flags) + 1))
+ failed(handle);
+ if (!write_all_choice(handles[handle]->fd, data, strlen(data)))
+ failed(handle);
+ /* Do not wait for ack. */
+}
+
+static void do_readack(unsigned int handle)
+{
+ enum xsd_sockmsg_type type;
+ char *ret;
+
+ ret = read_reply(handles[handle]->fd, &type, NULL);
+ if (!ret)
+ failed(handle);
+ free(ret);
+}
+
static void do_setid(unsigned int handle, char *id)
{
if (!xs_bool(xs_debug_command(handles[handle], "setid", id,
@@ -466,6 +507,25 @@
failed(handle);
}
+static void set_timeout(void)
+{
+ struct itimerval timeout;
+
+ timeout.it_value.tv_sec = timeout_ms / 1000;
+ timeout.it_value.tv_usec = (timeout_ms * 1000) % 1000000;
+ timeout.it_interval.tv_sec = timeout.it_interval.tv_usec = 0;
+ setitimer(ITIMER_REAL, &timeout, NULL);
+}
+
+static void disarm_timeout(void)
+{
+ struct itimerval timeout;
+
+ timeout.it_value.tv_sec = 0;
+ timeout.it_value.tv_usec = 0;
+ setitimer(ITIMER_REAL, &timeout, NULL);
+}
+
static void do_waitwatch(unsigned int handle)
{
char **vec;
@@ -474,14 +534,17 @@
fd_set set;
if (xs_fileno(handles[handle]) != -2) {
+ /* Manually select here so we can time out gracefully. */
FD_ZERO(&set);
FD_SET(xs_fileno(handles[handle]), &set);
+ disarm_timeout();
if (select(xs_fileno(handles[handle])+1, &set,
NULL, NULL, &tv) == 0) {
errno = ETIMEDOUT;
failed(handle);
return;
}
+ set_timeout();
}
vec = xs_read_watch(handles[handle]);
@@ -529,6 +592,9 @@
{
unsigned int i;
int fd;
+
+ /* This mechanism is v. slow w. valgrind running. */
+ timeout_ms = 5000;
/* We poll, so ignore signal */
signal(SIGUSR2, SIG_IGN);
@@ -669,24 +735,6 @@
write(STDOUT_FILENO, command, strlen(command));
write(STDOUT_FILENO, " timeout\n", strlen(" timeout\n"));
exit(1);
-}
-
-static void set_timeout(void)
-{
- struct itimerval timeout;
-
- timeout.it_interval.tv_sec = timeout_ms / 1000;
- timeout.it_interval.tv_usec = (timeout_ms * 1000) % 1000000;
- setitimer(ITIMER_REAL, &timeout, NULL);
-}
-
-static void disarm_timeout(void)
-{
- struct itimerval timeout;
-
- timeout.it_interval.tv_sec = 0;
- timeout.it_interval.tv_usec = 0;
- setitimer(ITIMER_REAL, &timeout, NULL);
}
static void do_command(unsigned int default_handle, char *line)
@@ -779,7 +827,11 @@
readonly = false;
xs_daemon_close(handles[handle]);
handles[handle] = NULL;
- } else
+ } else if (streq(command, "noackwrite"))
+ do_noackwrite(handle, arg(line,1), arg(line,2), arg(line,3));
+ else if (streq(command, "readack"))
+ do_readack(handle);
+ else
barf("Unknown command %s", command);
fflush(stdout);
disarm_timeout();
diff -r b60643391488 -r a9ee400a5da9 tools/xenstore/testsuite/01simple.test
--- /dev/null Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/testsuite/01simple.test Mon Aug 8 09:13:19 2005
@@ -0,0 +1,4 @@
+# Create an entry, read it.
+write /test create contents
+expect contents
+read /test
diff -r b60643391488 -r a9ee400a5da9 tools/xenstore/testsuite/02directory.test
--- /dev/null Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/testsuite/02directory.test Mon Aug 8 09:13:19 2005
@@ -0,0 +1,34 @@
+# Root directory has only tool dir in it.
+expect tool
+dir /
+
+# Create a file.
+write /test create contents
+
+# Directory shows it.
+expect test
+expect tool
+dir /
+
+# Make a new directory, check it's there
+mkdir /dir
+expect dir
+expect test
+expect tool
+dir /
+
+# Check it's empty.
+dir /dir
+
+# Create a file, check it exists.
+write /dir/test2 create contents2
+expect test2
+dir /dir
+expect contents2
+read /dir/test2
+
+# Creating dir over the top should fail.
+expect mkdir failed: File exists
+mkdir /dir
+expect mkdir failed: File exists
+mkdir /dir/test2
diff -r b60643391488 -r a9ee400a5da9 tools/xenstore/testsuite/03write.test
--- /dev/null Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/testsuite/03write.test Mon Aug 8 09:13:19 2005
@@ -0,0 +1,20 @@
+# Write without create fails.
+expect write failed: No such file or directory
+write /test none contents
+
+# Exclusive write succeeds
+write /test excl contents
+expect contents
+read /test
+
+# Exclusive write fails to overwrite.
+expect write failed: File exists
+write /test excl contents
+
+# Non-exclusive overwrite succeeds.
+write /test none contents2
+expect contents2
+read /test
+write /test create contents3
+expect contents3
+read /test
diff -r b60643391488 -r a9ee400a5da9 tools/xenstore/testsuite/04rm.test
--- /dev/null Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/testsuite/04rm.test Mon Aug 8 09:13:19 2005
@@ -0,0 +1,18 @@
+# Remove non-existant fails.
+expect rm failed: No such file or directory
+rm /test
+expect rm failed: No such file or directory
+rm /dir/test
+
+# Create file and remove it
+write /test excl contents
+rm /test
+
+# Create directory and remove it.
+mkdir /dir
+rm /dir
+
+# Create directory, create file, remove all.
+mkdir /dir
+write /dir/test excl contents
+rm /dir
diff -r b60643391488 -r a9ee400a5da9
tools/xenstore/testsuite/05filepermissions.test
--- /dev/null Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/testsuite/05filepermissions.test Mon Aug 8 09:13:19 2005
@@ -0,0 +1,81 @@
+# Fail to get perms on non-existent file.
+expect getperm failed: No such file or directory
+getperm /test
+expect getperm failed: No such file or directory
+getperm /dir/test
+
+# Create file: inherits from root (0 READ)
+write /test excl contents
+expect 0 READ
+getperm /test
+setid 1
+expect 0 READ
+getperm /test
+expect contents
+read /test
+expect write failed: Permission denied
+write /test none contents
+
+# Take away read access to file.
+setid 0
+setperm /test 0 NONE
+setid 1
+expect getperm failed: Permission denied
+getperm /test
+expect read failed: Permission denied
+read /test
+expect write failed: Permission denied
+write /test none contents
+
+# Grant everyone write access to file.
+setid 0
+setperm /test 0 WRITE
+setid 1
+expect getperm failed: Permission denied
+getperm /test
+expect read failed: Permission denied
+read /test
+write /test none contents2
+setid 0
+expect contents2
+read /test
+
+# Grant everyone both read and write access.
+setperm /test 0 READ/WRITE
+setid 1
+expect 0 READ/WRITE
+getperm /test
+expect contents2
+read /test
+write /test none contents3
+expect contents3
+read /test
+
+# Change so that user 1 owns it, noone else can do anything.
+setid 0
+setperm /test 1 NONE
+setid 1
+expect 1 NONE
+getperm /test
+expect contents3
+read /test
+write /test none contents4
+
+# User 2 can do nothing.
+setid 2
+expect setperm failed: Permission denied
+setperm /test 2 NONE
+expect getperm failed: Permission denied
+getperm /test
+expect read failed: Permission denied
+read /test
+expect write failed: Permission denied
+write /test none contents4
+
+# Tools can always access things.
+setid 0
+expect 1 NONE
+getperm /test
+expect contents4
+read /test
+write /test none contents5
diff -r b60643391488 -r a9ee400a5da9
tools/xenstore/testsuite/06dirpermissions.test
--- /dev/null Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/testsuite/06dirpermissions.test Mon Aug 8 09:13:19 2005
@@ -0,0 +1,119 @@
+# Root directory: owned by tool, everyone has read access.
+expect 0 READ
+getperm /
+
+# Create directory: inherits from root.
+mkdir /dir
+expect 0 READ
+getperm /dir
+setid 1
+expect 0 READ
+getperm /dir
+dir /dir
+expect write failed: Permission denied
+write /dir/test create contents2
+
+# Remove everyone's read access to directoy.
+setid 0
+setperm /dir 0 NONE
+setid 1
+expect dir failed: Permission denied
+dir /dir
+expect read failed: Permission denied
+read /dir/test create contents2
+expect write failed: Permission denied
+write /dir/test create contents2
+
+# Grant everyone write access to directory.
+setid 0
+setperm /dir 0 WRITE
+setid 1
+expect getperm failed: Permission denied
+getperm /dir
+expect dir failed: Permission denied
+dir /dir
+write /dir/test create contents
+setid 0
+expect 1 WRITE
+getperm /dir/test
+setperm /dir/test 0 NONE
+expect contents
+read /dir/test
+
+# Grant everyone both read and write access.
+setperm /dir 0 READ/WRITE
+setid 1
+expect 0 READ/WRITE
+getperm /dir
+expect test
+dir /dir
+write /dir/test2 create contents
+expect contents
+read /dir/test2
+setperm /dir/test2 1 NONE
+
+# Change so that user 1 owns it, noone else can do anything.
+setid 0
+setperm /dir 1 NONE
+expect 1 NONE
+getperm /dir
+expect test
+expect test2
+dir /dir
+write /dir/test3 create contents
+
+# User 2 can do nothing. Can't even tell if file exists.
+setid 2
+expect setperm failed: Permission denied
+setperm /dir 2 NONE
+expect getperm failed: Permission denied
+getperm /dir
+expect dir failed: Permission denied
+dir /dir
+expect read failed: Permission denied
+read /dir/test
+expect read failed: Permission denied
+read /dir/test2
+expect read failed: Permission denied
+read /dir/test3
+expect read failed: Permission denied
+read /dir/test4
+expect write failed: Permission denied
+write /dir/test none contents
+expect write failed: Permission denied
+write /dir/test create contents
+expect write failed: Permission denied
+write /dir/test excl contents
+expect write failed: Permission denied
+write /dir/test4 none contents
+expect write failed: Permission denied
+write /dir/test4 create contents
+expect write failed: Permission denied
+write /dir/test4 excl contents
+
+# Tools can always access things.
+setid 0
+expect 1 NONE
+getperm /dir
+expect test
+expect test2
+expect test3
+dir /dir
+write /dir/test4 create contents
+
+# Inherited by child.
+mkdir /dir/subdir
+expect 1 NONE
+getperm /dir/subdir
+write /dir/subfile excl contents
+expect 1 NONE
+getperm /dir/subfile
+
+# But for domains, they own it.
+setperm /dir/subdir 2 READ/WRITE
+expect 2 READ/WRITE
+getperm /dir/subdir
+setid 3
+write /dir/subdir/subfile excl contents
+expect 3 READ/WRITE
+getperm /dir/subdir/subfile
diff -r b60643391488 -r a9ee400a5da9 tools/xenstore/testsuite/07watch.test
--- /dev/null Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/testsuite/07watch.test Mon Aug 8 09:13:19 2005
@@ -0,0 +1,194 @@
+# Watch something, write to it, check watch has fired.
+write /test create contents
+
+1 watch /test token
+2 write /test create contents2
+expect 1:/test:token
+1 waitwatch
+1 ackwatch token
+1 close
+
+# Check that reads don't set it off.
+1 watch /test token
+expect 2:contents2
+2 read /test
+expect 1: waitwatch failed: Connection timed out
+1 waitwatch
+1 close
+
+# mkdir, setperm and rm should (also tests watching dirs)
+mkdir /dir
+1 watch /dir token
+2 mkdir /dir/newdir
+expect 1:/dir/newdir:token
+1 waitwatch
+1 ackwatch token
+2 setperm /dir/newdir 0 READ
+expect 1:/dir/newdir:token
+1 waitwatch
+1 ackwatch token
+2 rm /dir/newdir
+expect 1:/dir/newdir:token
+1 waitwatch
+1 ackwatch token
+1 close
+2 close
+
+# We don't get a watch from our own commands.
+watch /dir token
+mkdir /dir/newdir
+expect waitwatch failed: Connection timed out
+waitwatch
+close
+
+# ignore watches while doing commands, should work.
+watch /dir token
+1 write /dir/test create contents
+expect contents
+read /dir/test
+expect /dir/test:token
+waitwatch
+ackwatch token
+close
+
+# watch priority test: all simultaneous
+1 watch /dir token1
+3 watch /dir token3
+2 watch /dir token2
+write /dir/test create contents
+expect 3:/dir/test:token3
+3 waitwatch
+3 ackwatch token3
+expect 2:/dir/test:token2
+2 waitwatch
+2 ackwatch token2
+expect 1:/dir/test:token1
+1 waitwatch
+1 ackwatch token1
+1 close
+2 close
+3 close
+
+# If one dies (without acking), the other should still get ack.
+1 watch /dir token1
+2 watch /dir token2
+write /dir/test create contents
+expect 2:/dir/test:token2
+2 waitwatch
+2 close
+expect 1:/dir/test:token1
+1 waitwatch
+1 ackwatch token1
+1 close
+
+# If one dies (without reading at all), the other should still get ack.
+1 watch /dir token1
+2 watch /dir token2
+write /dir/test create contents
+2 close
+expect 1:/dir/test:token1
+1 waitwatch
+1 ackwatch token1
+1 close
+2 close
+
+# unwatch
+1 watch /dir token1
+1 unwatch /dir token1
+1 watch /dir token2
+2 write /dir/test2 create contents
+expect 1:/dir/test2:token2
+1 waitwatch
+1 unwatch /dir token2
+1 close
+2 close
+
+# unwatch while watch pending. Other watcher still gets the event.
+1 watch /dir token1
+2 watch /dir token2
+write /dir/test create contents
+2 unwatch /dir token2
+expect 1:/dir/test:token1
+1 waitwatch
+1 ackwatch token1
+1 close
+2 close
+
+# unwatch while watch pending. Should clear this so we get next event.
+1 watch /dir token1
+write /dir/test create contents
+1 unwatch /dir token1
+1 watch /dir/test token2
+write /dir/test none contents2
+expect 1:/dir/test:token2
+1 waitwatch
+1 ackwatch token2
+
+# check we only get notified once.
+1 watch /test token
+2 write /test create contents2
+expect 1:/test:token
+1 waitwatch
+1 ackwatch token
+expect 1: waitwatch failed: Connection timed out
+1 waitwatch
+1 close
+
+# watches are queued in order.
+1 watch / token
+2 write /test1 create contents
+2 write /test2 create contents
+2 write /test3 create contents
+expect 1:/test1:token
+1 waitwatch
+1 ackwatch token
+expect 1:/test2:token
+1 waitwatch
+1 ackwatch token
+expect 1:/test3:token
+1 waitwatch
+1 ackwatch token
+1 close
+
+# Creation of subpaths should be covered correctly.
+1 watch / token
+2 write /test/subnode create contents2
+2 write /test/subnode/subnode create contents2
+expect 1:/test/subnode:token
+1 waitwatch
+1 ackwatch token
+expect 1:/test/subnode/subnode:token
+1 waitwatch
+1 ackwatch token
+expect 1: waitwatch failed: Connection timed out
+1 waitwatch
+1 close
+
+# Watch event must have happened before we registered interest.
+1 watch / token
+2 write /test/subnode create contents2
+1 watch / token2 0
+expect 1:/test/subnode:token
+1 waitwatch
+1 ackwatch token
+expect 1: waitwatch failed: Connection timed out
+1 waitwatch
+1 close
+
+# Rm fires notification on child.
+1 watch /test/subnode token
+2 rm /test
+expect 1:/test/subnode:token
+1 waitwatch
+1 ackwatch token
+
+# Watch should not double-send after we ack, even if we did something in
between.
+1 watch /test2 token
+2 write /test2/foo create contents2
+expect 1:/test2/foo:token
+1 waitwatch
+expect 1:contents2
+1 read /test2/foo
+1 ackwatch token
+expect 1: waitwatch failed: Connection timed out
+1 waitwatch
diff -r b60643391488 -r a9ee400a5da9
tools/xenstore/testsuite/08transaction.slowtest
--- /dev/null Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/testsuite/08transaction.slowtest Mon Aug 8 09:13:19 2005
@@ -0,0 +1,21 @@
+# Test transaction timeouts. Take a second each.
+
+mkdir /test
+write /test/entry1 create contents
+
+# Transactions can take as long as the want...
+start /test
+sleep 1100
+rm /test/entry1
+commit
+dir /test
+
+# ... as long as noone is waiting.
+1 start /test
+notimeout
+2 mkdir /test/dir
+1 mkdir /test/dir
+expect 1:dir
+1 dir /test
+expect 1: commit failed: Connection timed out
+1 commit
diff -r b60643391488 -r a9ee400a5da9 tools/xenstore/testsuite/08transaction.test
--- /dev/null Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/testsuite/08transaction.test Mon Aug 8 09:13:19 2005
@@ -0,0 +1,96 @@
+# Test transactions.
+
+mkdir /test
+
+# Simple transaction: create a file inside transaction.
+1 start /test
+1 write /test/entry1 create contents
+2 dir /test
+expect 1:entry1
+1 dir /test
+1 commit
+expect 2:contents
+2 read /test/entry1
+
+rm /test/entry1
+
+# Create a file and abort transaction.
+1 start /test
+1 write /test/entry1 create contents
+2 dir /test
+expect 1:entry1
+1 dir /test
+1 abort
+2 dir /test
+
+write /test/entry1 create contents
+# Delete in transaction, commit
+1 start /test
+1 rm /test/entry1
+expect 2:entry1
+2 dir /test
+1 dir /test
+1 commit
+2 dir /test
+
+# Delete in transaction, abort.
+write /test/entry1 create contents
+1 start /test
+1 rm /test/entry1
+expect 2:entry1
+2 dir /test
+1 dir /test
+1 abort
+expect 2:entry1
+2 dir /test
+
+# Events inside transactions don't trigger watches until (successful) commit.
+mkdir /test/dir
+1 watch /test token
+2 start /test
+2 mkdir /test/dir/sub
+expect 1: waitwatch failed: Connection timed out
+1 waitwatch
+2 close
+1 close
+
+1 watch /test token
+2 start /test
+2 mkdir /test/dir/sub
+2 abort
+expect 1: waitwatch failed: Connection timed out
+1 waitwatch
+1 close
+
+1 watch /test token
+2 start /test
+2 mkdir /test/dir/sub
+2 commit
+expect 1:/test/dir/sub:token
+1 waitwatch
+1 ackwatch token
+1 close
+
+# Rm inside transaction works like rm outside: children get notified.
+1 watch /test/dir/sub token
+2 start /test
+2 rm /test/dir
+2 commit
+expect 1:/test/dir/sub:token
+1 waitwatch
+1 ackwatch token
+1 close
+
+# Multiple events from single transaction don't trigger assert
+1 watch /test token
+2 start /test
+2 write /test/1 create contents
+2 write /test/2 create contents
+2 commit
+expect 1:/test/1:token
+1 waitwatch
+1 ackwatch token
+expect 1:/test/2:token
+1 waitwatch
+1 ackwatch token
+1 close
diff -r b60643391488 -r a9ee400a5da9 tools/xenstore/testsuite/09domain.test
--- /dev/null Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/testsuite/09domain.test Mon Aug 8 09:13:19 2005
@@ -0,0 +1,19 @@
+# Test domain communication.
+
+# Create a domain, write an entry.
+expect handle is 1
+introduce 1 100 7 /my/home
+1 write /entry1 create contents
+expect entry1
+expect tool
+dir /
+close
+
+# Release that domain.
+release 1
+close
+
+# Introduce and release by same connection.
+expect handle is 2
+introduce 1 100 7 /my/home
+release 1
diff -r b60643391488 -r a9ee400a5da9
tools/xenstore/testsuite/10domain-homedir.test
--- /dev/null Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/testsuite/10domain-homedir.test Mon Aug 8 09:13:19 2005
@@ -0,0 +1,19 @@
+# Test domain "implicit" paths.
+
+# Create a domain, write an entry using implicit path, read using implicit
+mkdir /home
+expect handle is 1
+introduce 1 100 7 /home
+1 write entry1 create contents
+expect contents
+read /home/entry1
+expect entry1
+dir /home
+
+# Place a watch using a relative path: expect relative answer.
+1 mkdir foo
+1 watch foo token
+write /home/foo/bar create contents
+expect 1:foo/bar:token
+1 waitwatch
+1 ackwatch token
diff -r b60643391488 -r a9ee400a5da9
tools/xenstore/testsuite/11domain-watch.test
--- /dev/null Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/testsuite/11domain-watch.test Mon Aug 8 09:13:19 2005
@@ -0,0 +1,52 @@
+# Test watching from a domain.
+
+# Watch something, write to it, check watch has fired.
+write /test create contents
+mkdir /dir
+
+expect handle is 1
+introduce 1 100 7 /my/home
+1 watch /test token
+write /test create contents2
+expect 1:/test:token
+1 waitwatch
+1 ackwatch token
+1 unwatch /test token
+release 1
+1 close
+
+# ignore watches while doing commands, should work.
+expect handle is 1
+introduce 1 100 7 /my/home
+1 watch /dir token
+write /dir/test create contents
+1 write /dir/test2 create contents2
+1 write /dir/test3 create contents3
+1 write /dir/test4 create contents4
+expect 1:/dir/test:token
+1 waitwatch
+1 ackwatch token
+release 1
+1 close
+
+# unwatch
+expect handle is 1
+introduce 1 100 7 /my/home
+1 watch /dir token1
+1 unwatch /dir token1
+1 watch /dir token2
+write /dir/test2 create contents
+expect 1:/dir/test2:token2
+1 waitwatch
+1 unwatch /dir token2
+release 1
+1 close
+
+# unwatch while watch pending.
+expect handle is 1
+introduce 1 100 7 /my/home
+1 watch /dir token1
+write /dir/test2 create contents
+1 unwatch /dir token1
+release 1
+1 close
diff -r b60643391488 -r a9ee400a5da9 tools/xenstore/testsuite/12readonly.test
--- /dev/null Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/testsuite/12readonly.test Mon Aug 8 09:13:19 2005
@@ -0,0 +1,41 @@
+# Test that read only connection can't alter store.
+
+write /test create contents
+
+readonly
+expect test
+expect tool
+dir /
+
+expect contents
+read /test
+expect 0 READ
+getperm /test
+watch /test token
+unwatch /test token
+start /
+commit
+start /
+abort
+
+# These don't work
+expect write failed: Read-only file system
+write /test2 create contents
+expect write failed: Read-only file system
+write /test create contents
+expect setperm failed: Read-only file system
+setperm /test 100 NONE
+expect setperm failed: Read-only file system
+setperm /test 100 NONE
+expect shutdown failed: Read-only file system
+shutdown
+expect introduce failed: Read-only file system
+introduce 1 100 7 /home
+
+# Check that watches work like normal.
+watch / token
+1 readwrite
+1 write /test create contents
+expect /test:token
+waitwatch
+ackwatch token
diff -r b60643391488 -r a9ee400a5da9 tools/xenstore/testsuite/13watch-ack.test
--- /dev/null Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/testsuite/13watch-ack.test Mon Aug 8 09:13:19 2005
@@ -0,0 +1,22 @@
+# This demonstrates a bug where an xs_acknowledge_watch returns
+# EINVAL, because the daemon doesn't track what watch event it sent
+# and relies on it being the "first" watch which has an event.
+# Watches firing after the first event is sent out will change this.
+
+# Create three things to watch.
+mkdir /test
+mkdir /test/1
+mkdir /test/2
+mkdir /test/3
+
+# Watch all three, fire event on 2, read watch, fire event on 1 and 3, ack 2.
+1 watch /test/1 token1
+1 watch /test/2 token2
+1 watch /test/3 token3
+2 write /test/2 create contents2
+expect 1:/test/2:token2
+1 waitwatch
+3 write /test/1 create contents1
+4 write /test/3 create contents3
+1 ackwatch token2
+1 close
diff -r b60643391488 -r a9ee400a5da9
tools/xenstore/testsuite/14complexperms.test
--- /dev/null Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/testsuite/14complexperms.test Mon Aug 8 09:13:19 2005
@@ -0,0 +1,99 @@
+# We should not be able to tell the difference between a node which
+# doesn't exist, and a node we don't have permission on, if we don't
+# have permission on it directory.
+
+mkdir /dir
+setperm /dir 0 NONE
+
+# First when it doesn't exist
+setid 1
+expect *Permission denied
+dir /dir/file
+expect *Permission denied
+read /dir/file
+expect *Permission denied
+write /dir/file none value
+expect *Permission denied
+write /dir/file create value
+expect *Permission denied
+write /dir/file excl value
+expect write failed: Invalid argument
+write /dir/file crap value
+expect *Permission denied
+mkdir /dir/file
+expect *Permission denied
+rm /dir/file
+expect *Permission denied
+rm /dir
+expect *Permission denied
+getperm /dir/file
+expect *Permission denied
+setperm /dir/file 0 NONE
+watch /dir/file token
+setid 0
+write /dir/file create contents
+rm /dir/file
+setid 1
+expect waitwatch failed: Connection timed out
+waitwatch
+unwatch /dir/file token
+expect *No such file or directory
+unwatch /dir/file token
+expect *Permission denied
+start /dir/file
+expect *No such file or directory
+abort
+expect *Permission denied
+start /dir/file
+expect *No such file or directory
+commit
+expect *Permission denied
+introduce 2 100 7 /dir/file
+
+# Now it exists
+setid 0
+write /dir/file create contents
+
+setid 1
+expect *Permission denied
+dir /dir/file
+expect *Permission denied
+read /dir/file
+expect *Permission denied
+write /dir/file none value
+expect *Permission denied
+write /dir/file create value
+expect *Permission denied
+write /dir/file excl value
+expect write failed: Invalid argument
+write /dir/file crap value
+expect *Permission denied
+mkdir /dir/file
+expect *Permission denied
+rm /dir/file
+expect *Permission denied
+rm /dir
+expect *Permission denied
+getperm /dir/file
+expect *Permission denied
+setperm /dir/file 0 NONE
+watch /dir/file token
+setid 0
+write /dir/file create contents
+rm /dir/file
+setid 1
+expect waitwatch failed: Connection timed out
+waitwatch
+unwatch /dir/file token
+expect *No such file or directory
+unwatch /dir/file token
+expect *Permission denied
+start /dir/file
+expect *No such file or directory
+abort
+expect *Permission denied
+start /dir/file
+expect *No such file or directory
+commit
+expect *Permission denied
+introduce 2 100 7 /dir/file
diff -r b60643391488 -r a9ee400a5da9 tools/xenstore/testsuite/15nowait.test
--- /dev/null Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/testsuite/15nowait.test Mon Aug 8 09:13:19 2005
@@ -0,0 +1,25 @@
+# If we don't wait for an ack, we can crash daemon as it never expects to be
+# sending out two replies on top of each other.
+noackwrite /1 create 1
+noackwrite /2 create 2
+noackwrite /3 create 3
+noackwrite /4 create 4
+noackwrite /5 create 5
+readack
+readack
+readack
+readack
+readack
+
+expect handle is 1
+introduce 1 100 7 /my/home
+1 noackwrite /1 create 1
+1 noackwrite /2 create 2
+1 noackwrite /3 create 3
+1 noackwrite /4 create 4
+1 noackwrite /5 create 5
+1 readack
+1 readack
+1 readack
+1 readack
+1 readack
diff -r b60643391488 -r a9ee400a5da9 tools/xenstore/xs_crashme.c
--- /dev/null Mon Aug 8 09:12:22 2005
+++ b/tools/xenstore/xs_crashme.c Mon Aug 8 09:13:19 2005
@@ -0,0 +1,413 @@
+/* Code which randomly corrupts bits going to the daemon.
+ Copyright (C) 2005 Rusty Russell IBM Corporation
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/time.h>
+#include "xs.h"
+#include "talloc.h"
+#include <errno.h>
+#include "xenstored.h"
+
+#define XSTEST
+#define RAND_FREQ 128 /* One char in 32 is corrupted. */
+
+/* jhash.h: Jenkins hash support.
+ *
+ * Copyright (C) 1996 Bob Jenkins (bob_jenkins@xxxxxxxxxxxxxxxx)
+ *
+ * http://burtleburtle.net/bob/hash/
+ *
+ * These are the credits from Bob's sources:
+ *
+ * lookup2.c, by Bob Jenkins, December 1996, Public Domain.
+ * hash(), hash2(), hash3, and mix() are externally useful functions.
+ * Routines to test the hash are included if SELF_TEST is defined.
+ * You can use this free for any purpose. It has no warranty.
+ *
+ * Copyright (C) 2003 David S. Miller (davem@xxxxxxxxxx)
+ *
+ * I've modified Bob's hash to be useful in the Linux kernel, and
+ * any bugs present are surely my fault. -DaveM
+ */
+
+/* NOTE: Arguments are modified. */
+#define __jhash_mix(a, b, c) \
+{ \
+ a -= b; a -= c; a ^= (c>>13); \
+ b -= c; b -= a; b ^= (a<<8); \
+ c -= a; c -= b; c ^= (b>>13); \
+ a -= b; a -= c; a ^= (c>>12); \
+ b -= c; b -= a; b ^= (a<<16); \
+ c -= a; c -= b; c ^= (b>>5); \
+ a -= b; a -= c; a ^= (c>>3); \
+ b -= c; b -= a; b ^= (a<<10); \
+ c -= a; c -= b; c ^= (b>>15); \
+}
+
+/* The golden ration: an arbitrary value */
+#define JHASH_GOLDEN_RATIO 0x9e3779b9
+
+/* The most generic version, hashes an arbitrary sequence
+ * of bytes. No alignment or length assumptions are made about
+ * the input key.
+ */
+static inline u32 jhash(const void *key, u32 length, u32 initval)
+{
+ u32 a, b, c, len;
+ const u8 *k = key;
+
+ len = length;
+ a = b = JHASH_GOLDEN_RATIO;
+ c = initval;
+
+ while (len >= 12) {
+ a += (k[0] +((u32)k[1]<<8) +((u32)k[2]<<16) +((u32)k[3]<<24));
+ b += (k[4] +((u32)k[5]<<8) +((u32)k[6]<<16) +((u32)k[7]<<24));
+ c += (k[8] +((u32)k[9]<<8) +((u32)k[10]<<16)+((u32)k[11]<<24));
+
+ __jhash_mix(a,b,c);
+
+ k += 12;
+ len -= 12;
+ }
+
+ c += length;
+ switch (len) {
+ case 11: c += ((u32)k[10]<<24);
+ case 10: c += ((u32)k[9]<<16);
+ case 9 : c += ((u32)k[8]<<8);
+ case 8 : b += ((u32)k[7]<<24);
+ case 7 : b += ((u32)k[6]<<16);
+ case 6 : b += ((u32)k[5]<<8);
+ case 5 : b += k[4];
+ case 4 : a += ((u32)k[3]<<24);
+ case 3 : a += ((u32)k[2]<<16);
+ case 2 : a += ((u32)k[1]<<8);
+ case 1 : a += k[0];
+ };
+
+ __jhash_mix(a,b,c);
+
+ return c;
+}
+
+/* A special optimized version that handles 1 or more of u32s.
+ * The length parameter here is the number of u32s in the key.
+ */
+static inline u32 jhash2(u32 *k, u32 length, u32 initval)
+{
+ u32 a, b, c, len;
+
+ a = b = JHASH_GOLDEN_RATIO;
+ c = initval;
+ len = length;
+
+ while (len >= 3) {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ __jhash_mix(a, b, c);
+ k += 3; len -= 3;
+ }
+
+ c += length * 4;
+
+ switch (len) {
+ case 2 : b += k[1];
+ case 1 : a += k[0];
+ };
+
+ __jhash_mix(a,b,c);
+
+ return c;
+}
+
+
+/* A special ultra-optimized versions that knows they are hashing exactly
+ * 3, 2 or 1 word(s).
+ *
+ * NOTE: In partilar the "c += length; __jhash_mix(a,b,c);" normally
+ * done at the end is not done here.
+ */
+static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval)
+{
+ a += JHASH_GOLDEN_RATIO;
+ b += JHASH_GOLDEN_RATIO;
+ c += initval;
+
+ __jhash_mix(a, b, c);
+
+ return c;
+}
+
+static inline u32 jhash_2words(u32 a, u32 b, u32 initval)
+{
+ return jhash_3words(a, b, 0, initval);
+}
+
+static inline u32 jhash_1word(u32 a, u32 initval)
+{
+ return jhash_3words(a, 0, 0, initval);
+}
+
+static unsigned int get_randomness(int *state)
+{
+ return jhash_1word((*state)++, *state * 1103515243);
+}
+
+static int state;
+
+/* Lengthening headers is pointless: other end will just wait for more
+ * data and timeout. We merely shorten the length. */
+static void corrupt_header(char *output, const struct xsd_sockmsg *msg,
+ unsigned int *next_bit)
+{
+ struct xsd_sockmsg newmsg = *msg;
+
+ while (*next_bit < sizeof(*msg)) {
+ if (newmsg.len)
+ newmsg.len = get_randomness(&state) % newmsg.len;
+ *next_bit += get_randomness(&state) % RAND_FREQ;
+ }
+ memcpy(output, &newmsg, sizeof(newmsg));
+}
+
+#define read_all_choice read_all
+static bool write_all_choice(int fd, const void *data, unsigned int len)
+{
+ char corrupt_data[len];
+ bool ret;
+ static unsigned int next_bit;
+
+ if (len == sizeof(struct xsd_sockmsg)
+ && ((unsigned long)data % __alignof__(struct xsd_sockmsg)) == 0)
+ corrupt_header(corrupt_data, data, &next_bit);
+ else {
+ memcpy(corrupt_data, data, len);
+ while (next_bit < len * CHAR_BIT) {
+ corrupt_data[next_bit/CHAR_BIT]
+ ^= (1 << (next_bit%CHAR_BIT));
+ next_bit += get_randomness(&state) % RAND_FREQ;
+ }
+ }
+
+ ret = xs_write_all(fd, corrupt_data, len);
+ next_bit -= len * CHAR_BIT;
+ return ret;
+}
+
+#include "xs.c"
+
+static char *random_path(void)
+{
+ unsigned int i;
+ char *ret = NULL;
+
+ if (get_randomness(&state) % 20 == 0)
+ return talloc_strdup(NULL, "/");
+
+ for (i = 0; i < 1 || (get_randomness(&state) % 2); i++) {
+ ret = talloc_asprintf_append(ret, "/%i",
+ get_randomness(&state) % 15);
+ }
+ return ret;
+}
+
+static int random_flags(int *state)
+{
+ switch (get_randomness(state) % 4) {
+ case 0:
+ return 0;
+ case 1:
+ return O_CREAT;
+ case 2:
+ return O_CREAT|O_EXCL;
+ default:
+ return get_randomness(state);
+ }
+}
+
+/* Do the next operation, return the results. */
+static void do_next_op(struct xs_handle *h, bool verbose)
+{
+ char *name;
+ unsigned int num;
+
+ if (verbose)
+ printf("State %i: ", state);
+
+ name = random_path();
+ switch (get_randomness(&state) % 9) {
+ case 0:
+ if (verbose)
+ printf("DIR %s\n", name);
+ free(xs_directory(h, name, &num));
+ break;
+ case 1:
+ if (verbose)
+ printf("READ %s\n", name);
+ free(xs_read(h, name, &num));
+ break;
+ case 2: {
+ int flags = random_flags(&state);
+ char *contents = talloc_asprintf(NULL, "%i",
+ get_randomness(&state));
+ unsigned int len = get_randomness(&state)%(strlen(contents)+1);
+ if (verbose)
+ printf("WRITE %s %s %.*s\n", name,
+ flags == O_CREAT ? "O_CREAT"
+ : flags == (O_CREAT|O_EXCL) ? "O_CREAT|O_EXCL"
+ : flags == 0 ? "0" : "CRAPFLAGS",
+ len, contents);
+ xs_write(h, name, contents, len, flags);
+ break;
+ }
+ case 3:
+ if (verbose)
+ printf("MKDIR %s\n", name);
+ xs_mkdir(h, name);
+ break;
+ case 4:
+ if (verbose)
+ printf("RM %s\n", name);
+ xs_rm(h, name);
+ break;
+ case 5:
+ if (verbose)
+ printf("GETPERMS %s\n", name);
+ free(xs_get_permissions(h, name, &num));
+ break;
+ case 6: {
+ unsigned int i, num = get_randomness(&state)%8;
+ struct xs_permissions perms[num];
+
+ if (verbose)
+ printf("SETPERMS %s: ", name);
+ for (i = 0; i < num; i++) {
+ perms[i].id = get_randomness(&state)%8;
+ perms[i].perms = get_randomness(&state)%4;
+ if (verbose)
+ printf("%i%c ", perms[i].id,
+ perms[i].perms == XS_PERM_WRITE ? 'W'
+ : perms[i].perms == XS_PERM_READ ? 'R'
+ : perms[i].perms ==
+ (XS_PERM_READ|XS_PERM_WRITE) ? 'B'
+ : 'N');
+ }
+ if (verbose)
+ printf("\n");
+ xs_set_permissions(h, name, perms, num);
+ break;
+ }
+ case 7: {
+ if (verbose)
+ printf("START %s\n", name);
+ xs_transaction_start(h, name);
+ break;
+ }
+ case 8: {
+ bool abort = (get_randomness(&state) % 2);
+
+ if (verbose)
+ printf("STOP %s\n", abort ? "ABORT" : "COMMIT");
+ xs_transaction_end(h, abort);
+ break;
+ }
+ default:
+ barf("Impossible randomness");
+ }
+}
+
+static struct xs_handle *h;
+static void alarmed(int sig __attribute__((unused)))
+{
+ /* We force close on timeout. */
+ close(h->fd);
+}
+
+static int start_daemon(void)
+{
+ int fds[2];
+ int daemon_pid;
+
+ /* Start daemon. */
+ pipe(fds);
+ if ((daemon_pid = fork())) {
+ /* Child writes PID when its ready: we wait for that. */
+ char buffer[20];
+ close(fds[1]);
+ if (read(fds[0], buffer, sizeof(buffer)) < 0)
+ barf("Failed to summon daemon");
+ close(fds[0]);
+ return daemon_pid;
+ } else {
+ dup2(fds[1], STDOUT_FILENO);
+ close(fds[0]);
+#if 1
+ execlp("valgrind", "valgrind",
"--log-file=/tmp/xs_crashme.vglog", "-q", "./xenstored_test", "--output-pid",
+ "--no-fork", "--trace-file=/tmp/trace", NULL);
+#else
+ execlp("./xenstored_test", "xenstored_test", "--output-pid",
+ "--no-fork", NULL);
+#endif
+ exit(1);
+ }
+}
+
+
+int main(int argc, char **argv)
+{
+ unsigned int i;
+ int pid;
+
+ if (argc != 3 && argc != 4)
+ barf("Usage: xs_crashme <iterations> <seed> [pid]");
+
+ if (argc == 3)
+ pid = start_daemon();
+ else
+ pid = atoi(argv[3]);
+
+ state = atoi(argv[2]);
+ h = xs_daemon_open();
+ if (!h)
+ barf_perror("Opening connection to daemon");
+ signal(SIGALRM, alarmed);
+ for (i = 0; i < (unsigned)atoi(argv[1]); i++) {
+ alarm(1);
+ do_next_op(h, false);
+ if (i % (atoi(argv[1]) / 72 ?: 1) == 0) {
+ printf(".");
+ fflush(stdout);
+ }
+ if (kill(pid, 0) != 0)
+ barf_perror("Pinging daemon on iteration %i", i);
+ if (h->fd < 0) {
+ xs_daemon_close(h);
+ h = xs_daemon_open();
+ if (!h)
+ barf_perror("Connecting on iteration %i", i);
+ }
+ }
+ kill(pid, SIGTERM);
+ return 0;
+}
+
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|