# HG changeset patch
# User cl349@xxxxxxxxxxxxxxxxxxxx
# Node ID 57a5441b323b747b90db089c303e8e79ae7a36b0
# Parent 43f224c33281d981b08f8bd350fdd89d86ae7f38
xenstored updates.
- add trace file support.
- update permissions code.
- trigger watches on children of nodes being removed.
- update test cases.
Signed-off-by: Rusty Russel <rusty@xxxxxxxxxxxxxxx>
Signed-off-by: Christian Limpach <Christian.Limpach@xxxxxxxxxxxx>
diff -r 43f224c33281 -r 57a5441b323b tools/xenstore/xenstored_core.c
--- a/tools/xenstore/xenstored_core.c Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/xenstored_core.c Tue Jul 12 10:16:33 2005
@@ -52,6 +52,7 @@
static bool verbose;
static LIST_HEAD(connections);
+static int tracefd = -1;
#ifdef TESTING
static bool failtest = false;
@@ -149,6 +150,86 @@
}
}
+static void trace_io(const struct connection *conn,
+ const char *prefix,
+ const struct buffered_data *data)
+{
+ char string[64];
+ unsigned int i;
+
+ if (tracefd < 0)
+ return;
+
+ write(tracefd, prefix, strlen(prefix));
+ sprintf(string, " %p ", conn);
+ write(tracefd, string, strlen(string));
+ write(tracefd, sockmsg_string(data->hdr.msg.type),
+ strlen(sockmsg_string(data->hdr.msg.type)));
+ write(tracefd, " (", 2);
+ for (i = 0; i < data->hdr.msg.len; i++) {
+ if (data->buffer[i] == '\0')
+ write(tracefd, " ", 1);
+ else
+ write(tracefd, data->buffer + i, 1);
+ }
+ write(tracefd, ")\n", 2);
+}
+
+void trace_create(const void *data, const char *type)
+{
+ char string[64];
+ if (tracefd < 0)
+ return;
+
+ write(tracefd, "CREATE ", strlen("CREATE "));
+ write(tracefd, type, strlen(type));
+ sprintf(string, " %p\n", data);
+ write(tracefd, string, strlen(string));
+}
+
+void trace_destroy(const void *data, const char *type)
+{
+ char string[64];
+ if (tracefd < 0)
+ return;
+
+ write(tracefd, "DESTROY ", strlen("DESTROY "));
+ write(tracefd, type, strlen(type));
+ sprintf(string, " %p\n", data);
+ write(tracefd, string, strlen(string));
+}
+
+void trace_watch_timeout(const struct connection *conn, const char *node,
const char *token)
+{
+ char string[64];
+ if (tracefd < 0)
+ return;
+ write(tracefd, "WATCH_TIMEOUT ", strlen("WATCH_TIMEOUT "));
+ sprintf(string, " %p ", conn);
+ write(tracefd, string, strlen(string));
+ write(tracefd, " (", 2);
+ write(tracefd, node, strlen(node));
+ write(tracefd, " ", 1);
+ write(tracefd, token, strlen(token));
+ write(tracefd, ")\n", 2);
+}
+
+static void trace_blocked(const struct connection *conn,
+ const struct buffered_data *data)
+{
+ char string[64];
+
+ if (tracefd < 0)
+ return;
+
+ write(tracefd, "BLOCKED", strlen("BLOCKED"));
+ sprintf(string, " %p (", conn);
+ write(tracefd, string, strlen(string));
+ write(tracefd, sockmsg_string(data->hdr.msg.type),
+ strlen(sockmsg_string(data->hdr.msg.type)));
+ write(tracefd, ")\n", 2);
+}
+
static bool write_message(struct connection *conn)
{
int ret;
@@ -186,6 +267,7 @@
if (out->used != out->hdr.msg.len)
return true;
+ trace_io(conn, "OUT", out);
conn->out = NULL;
talloc_free(out);
@@ -213,6 +295,7 @@
close(conn->fd);
}
list_del(&conn->list);
+ trace_destroy(conn, "connection");
return 0;
}
@@ -756,9 +839,9 @@
static bool new_directory(struct connection *conn,
const char *node, void *data, unsigned int datalen)
{
- struct xs_permissions perms;
+ struct xs_permissions *perms;
char *permstr;
- unsigned int len;
+ unsigned int num, len;
int *fd;
char *dir = node_dir(conn->transaction, node);
@@ -768,11 +851,12 @@
/* Set destructor so we clean up if neccesary. */
talloc_set_destructor(dir, destroy_path);
- /* Default permisisons: we own it, noone else has permission. */
- perms.id = conn->id;
- perms.perms = XS_PERM_NONE;
-
- permstr = perms_to_strings(dir, &perms, 1, &len);
+ perms = get_perms(conn->transaction, get_parent(node), &num);
+ /* Domains own what they create. */
+ if (conn->id)
+ perms->id = conn->id;
+
+ permstr = perms_to_strings(dir, perms, num, &len);
fd = talloc_open(node_permfile(conn->transaction, node),
O_WRONLY|O_CREAT|O_EXCL, 0640);
if (!fd || !xs_write_all(*fd, permstr, len))
@@ -805,7 +889,8 @@
return send_error(conn, EINVAL);
node = canonicalize(conn, vec[0]);
- if (!within_transaction(conn->transaction, node))
+ if (/*suppress error on write outside transaction*/ 0 &&
+ !within_transaction(conn->transaction, node))
return send_error(conn, EROFS);
if (transaction_block(conn, node))
@@ -850,9 +935,9 @@
commit_tempfile(tmppath);
}
- add_change_node(conn->transaction, node);
+ add_change_node(conn->transaction, node, false);
send_ack(conn, XS_WRITE);
- fire_watches(conn->transaction, node);
+ fire_watches(conn->transaction, node, false);
return false;
}
@@ -871,9 +956,9 @@
if (!new_directory(conn, node, NULL, 0))
return send_error(conn, errno);
- add_change_node(conn->transaction, node);
+ add_change_node(conn->transaction, node, false);
send_ack(conn, XS_MKDIR);
- fire_watches(conn->transaction, node);
+ fire_watches(conn->transaction, node, false);
return false;
}
@@ -902,10 +987,9 @@
if (rename(path, tmppath) != 0)
return send_error(conn, errno);
- add_change_node(conn->transaction, node);
+ add_change_node(conn->transaction, node, true);
send_ack(conn, XS_RM);
- /* FIXME: traverse and fire watches for ALL of them! */
- fire_watches(conn->transaction, node);
+ fire_watches(conn->transaction, node, true);
return false;
}
@@ -961,9 +1045,9 @@
if (!set_perms(conn->transaction, node, perms, num))
return send_error(conn, errno);
- add_change_node(conn->transaction, node);
+ add_change_node(conn->transaction, node, false);
send_ack(conn, XS_SET_PERMS);
- fire_watches(conn->transaction, node);
+ fire_watches(conn->transaction, node, false);
return false;
}
@@ -1006,8 +1090,12 @@
/* Everything hangs off auto-free context, freed at exit. */
exit(0);
+ case XS_DEBUG:
+ if (streq(in->buffer, "print")) {
+ xprintf("debug: %s", in->buffer + get_string(in, 0));
+ return false;
+ }
#ifdef TESTING
- case XS_DEBUG: {
/* For testing, we allow them to set id. */
if (streq(in->buffer, "setid")) {
conn->id = atoi(in->buffer + get_string(in, 0));
@@ -1018,9 +1106,8 @@
send_ack(conn, XS_DEBUG);
failtest = true;
}
+#endif /* TESTING */
return false;
- }
-#endif /* TESTING */
case XS_WATCH:
return do_watch(conn, in);
@@ -1092,6 +1179,7 @@
talloc_free(conn->in);
conn->in = in;
in = NULL;
+ trace_blocked(conn, conn->in);
}
end:
@@ -1145,6 +1233,7 @@
if (in->used != in->hdr.msg.len)
return;
+ trace_io(conn, "IN ", in);
consider_message(conn);
return;
@@ -1212,6 +1301,7 @@
list_add_tail(&new->list, &connections);
talloc_set_destructor(new, destroy_conn);
+ trace_create(new, "connection");
return new;
}
@@ -1299,6 +1389,7 @@
static struct option options[] = { { "no-fork", 0, NULL, 'N' },
{ "verbose", 0, NULL, 'V' },
{ "output-pid", 0, NULL, 'P' },
+ { "trace-file", 1, NULL, 'T' },
{ NULL, 0, NULL, 0 } };
int main(int argc, char *argv[])
@@ -1309,7 +1400,7 @@
bool dofork = true;
bool outputpid = false;
- while ((opt = getopt_long(argc, argv, "DV", options, NULL)) != -1) {
+ while ((opt = getopt_long(argc, argv, "DVT:", options, NULL)) != -1) {
switch (opt) {
case 'N':
dofork = false;
@@ -1319,6 +1410,13 @@
break;
case 'P':
outputpid = true;
+ break;
+ case 'T':
+ tracefd = open(optarg, O_WRONLY|O_CREAT|O_APPEND, 0600);
+ if (tracefd < 0)
+ barf_perror("Could not open tracefile %s",
+ optarg);
+ write(tracefd, "\n***\n", strlen("\n***\n"));
break;
}
}
diff -r 43f224c33281 -r 57a5441b323b tools/xenstore/xenstored_core.h
--- a/tools/xenstore/xenstored_core.h Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/xenstored_core.h Tue Jul 12 10:16:33 2005
@@ -143,4 +143,9 @@
/* Read entire contents of a talloced fd. */
void *read_all(int *fd, unsigned int *size);
+/* Tracing infrastructure. */
+void trace_create(const void *data, const char *type);
+void trace_destroy(const void *data, const char *type);
+void trace_watch_timeout(const struct connection *conn, const char *node,
const char *token);
+
#endif /* _XENSTORED_CORE_H */
diff -r 43f224c33281 -r 57a5441b323b
tools/xenstore/testsuite/10domain-homedir.sh
--- a/tools/xenstore/testsuite/10domain-homedir.sh Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/testsuite/10domain-homedir.sh Tue Jul 12 10:16:33 2005
@@ -10,3 +10,11 @@
contents
entry1" ]
+# Place a watch using a relative path: expect relative answer.
+[ "`echo 'introduce 1 100 7 /home
+1 mkdir foo
+1 watch foo token 0
+write /home/foo/bar create contents
+1 waitwatch
+1 ackwatch token' | ./xs_test 2>&1`" = "handle is 1
+1:foo/bar:token" ]
diff -r 43f224c33281 -r 57a5441b323b tools/xenstore/xenstored_watch.h
--- a/tools/xenstore/xenstored_watch.h Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/xenstored_watch.h Tue Jul 12 10:16:33 2005
@@ -32,8 +32,8 @@
/* Look through our watches: if any of them have an event, queue it. */
void queue_next_event(struct connection *conn);
-/* Fire all watches. */
-void fire_watches(struct transaction *trans, const char *node);
+/* Fire all watches: recurse means all the children are effected (ie. rm) */
+void fire_watches(struct transaction *trans, const char *node, bool recurse);
/* Find shortest timeout: if any, reduce tv (may already be set). */
void shortest_watch_ack_timeout(struct timeval *tv);
diff -r 43f224c33281 -r 57a5441b323b tools/xenstore/xenstored_transaction.h
--- a/tools/xenstore/xenstored_transaction.h Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/xenstored_transaction.h Tue Jul 12 10:16:33 2005
@@ -40,7 +40,7 @@
char *node_dir_inside_transaction(struct transaction *t, const char *node);
/* This node was changed: can fail and longjmp. */
-void add_change_node(struct transaction *trans, const char *node);
+void add_change_node(struct transaction *trans, const char *node, bool
recurse);
/* Get shortest timeout: leave tv unset if none. */
void shortest_transaction_timeout(struct timeval *tv);
diff -r 43f224c33281 -r 57a5441b323b tools/xenstore/xenstored_transaction.c
--- a/tools/xenstore/xenstored_transaction.c Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/xenstored_transaction.c Tue Jul 12 10:16:33 2005
@@ -41,6 +41,9 @@
/* The name of the node. */
char *node;
+
+ /* And the children? (ie. rm) */
+ bool recurse;
};
struct transaction
@@ -119,7 +122,7 @@
/* Callers get a change node (which can fail) and only commit after they've
* finished. This way they don't have to unwind eg. a write. */
-void add_change_node(struct transaction *trans, const char *node)
+void add_change_node(struct transaction *trans, const char *node, bool recurse)
{
struct changed_node *i;
@@ -132,7 +135,7 @@
i = talloc(trans, struct changed_node);
i->node = talloc_strdup(i, node);
- INIT_LIST_HEAD(&i->list);
+ i->recurse = recurse;
list_add_tail(&i->list, &trans->changes);
}
@@ -176,6 +179,7 @@
struct transaction *trans = _transaction;
list_del(&trans->list);
+ trace_destroy(trans, "transaction");
return destroy_path(trans->divert);
}
@@ -263,13 +267,14 @@
timerclear(&transaction->timeout);
transaction->destined_to_fail = false;
list_add_tail(&transaction->list, &transactions);
- conn->transaction = transaction;
talloc_set_destructor(transaction, destroy_transaction);
+ trace_create(transaction, "transaction");
if (!copy_dir(dir, transaction->divert))
return send_error(conn, errno);
talloc_steal(conn, transaction);
+ conn->transaction = transaction;
return send_ack(transaction->conn, XS_TRANSACTION_START);
}
@@ -292,7 +297,7 @@
/* Fire off the watches for everything that changed. */
list_for_each_entry(i, &trans->changes, list)
- fire_watches(NULL, i->node);
+ fire_watches(NULL, i->node, i->recurse);
return true;
}
diff -r 43f224c33281 -r 57a5441b323b tools/xenstore/xs.h
--- a/tools/xenstore/xs.h Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/xs.h Tue Jul 12 10:16:33 2005
@@ -47,7 +47,7 @@
/* Get the value of a single file, nul terminated.
* Returns a malloced value: call free() on it after use.
- * len indicates length in bytes, not including the nul.
+ * len indicates length in bytes, not including terminator.
*/
void *xs_read(struct xs_handle *h, const char *path, unsigned int *len);
@@ -103,7 +103,7 @@
*/
bool xs_acknowledge_watch(struct xs_handle *h, const char *token);
-/* Remove a watch on a node.
+/* Remove a watch on a node: implicitly acks any outstanding watch.
* Returns false on failure (no watch on that node).
*/
bool xs_unwatch(struct xs_handle *h, const char *path, const char *token);
diff -r 43f224c33281 -r 57a5441b323b tools/xenstore/testsuite/07watch.sh
--- a/tools/xenstore/testsuite/07watch.sh Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/testsuite/07watch.sh Tue Jul 12 10:16:33 2005
@@ -77,13 +77,22 @@
1 waitwatch
1 unwatch /dir token2' | ./xs_test 2>&1`" = "1:/dir/test2:token2" ]
-# unwatch while watch pending.
+# unwatch while watch pending. Next watcher gets the event.
[ "`echo -e '1 watch /dir token1 0
2 watch /dir token2 1
write /dir/test create contents
2 unwatch /dir token2
1 waitwatch
1 ackwatch token1' | ./xs_test 2>&1`" = "1:/dir/test:token1" ]
+
+# unwatch while watch pending. Should clear this so we get next event.
+[ "`echo -e '1 watch /dir token1 0
+write /dir/test create contents
+1 unwatch /dir token1
+1 watch /dir/test token2 0
+write /dir/test none contents2
+1 waitwatch
+1 ackwatch token2' | ./xs_test 2>&1`" = "1:/dir/test:token2" ]
# check we only get notified once.
[ "`echo -e '1 watch /test token 100
@@ -118,3 +127,28 @@
1 waitwatch' | ./xs_test 2>&1`" = "1:/test/subnode:token
1:/test/subnode/subnode:token
1:waitwatch timeout" ]
+
+# Watch event must have happened before we registered interest.
+[ "`echo -e '1 watch / token 100
+2 write /test/subnode create contents2
+2 watch / token2 0
+1 waitwatch
+1 ackwatch token
+2 waitwatch' | ./xs_test 2>&1`" = "1:/test/subnode:token
+2:waitwatch timeout" ]
+
+# Rm fires notification on child.
+[ "`echo -e '1 watch /test/subnode token 100
+2 rm /test
+1 waitwatch
+1 ackwatch token' | ./xs_test 2>&1`" = "1:/test/subnode:token" ]
+
+# Watch should not double-send after we ack, even if we did something in
between.
+[ "`echo -e '1 watch /test2 token 100
+2 write /test2/foo create contents2
+1 waitwatch
+1 read /test2/foo
+1 ackwatch token
+1 waitwatch' | ./xs_test 2>&1`" = "1:/test2/foo:token
+1:contents2
+1:waitwatch timeout" ]
diff -r 43f224c33281 -r 57a5441b323b tools/xenstore/xs_random.c
--- a/tools/xenstore/xs_random.c Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/xs_random.c Tue Jul 12 10:16:33 2005
@@ -201,7 +201,6 @@
unsigned long size;
struct stat st;
- /* No permfile: we didn't bother, return defaults. */
if (lstat(filename, &st) != 0)
return NULL;
@@ -211,18 +210,8 @@
permfile = talloc_asprintf(path, "%s.perms", filename);
perms = grab_file(permfile, &size);
- if (!perms) {
- ret = new(struct xs_permissions);
- ret[0].id = 0;
- /* Default for root is readable. */
- if (streq(path, "/"))
- ret[0].perms = XS_PERM_READ;
- else
- ret[0].perms = XS_PERM_NONE;
- *num = 1;
- release_file(perms, size);
- return ret;
- }
+ if (!perms)
+ barf("Grabbing permissions for %s", permfile);
*num = xs_count_strings(perms, size);
ret = new_array(struct xs_permissions, *num);
@@ -231,6 +220,39 @@
release_file(perms, size);
return ret;
}
+
+static void do_command(const char *cmd)
+{
+ int ret;
+
+ ret = system(cmd);
+ if (ret == -1 || !WIFEXITED(ret) || WEXITSTATUS(ret) != 0)
+ barf_perror("Failed '%s': %i", cmd, ret);
+}
+
+static void init_perms(const char *filename)
+{
+ struct stat st;
+ char *permfile, *command;
+
+ if (lstat(filename, &st) != 0)
+ barf_perror("Failed to stat %s", filename);
+
+ if (S_ISDIR(st.st_mode))
+ permfile = talloc_asprintf(filename, "%s/.perms", filename);
+ else
+ permfile = talloc_asprintf(filename, "%s.perms", filename);
+
+ /* Leave permfile if it already exists. */
+ if (lstat(permfile, &st) == 0)
+ return;
+
+ /* Copy permissions from parent */
+ command = talloc_asprintf(filename, "cp %.*s/.perms %s",
+ strrchr(filename, '/') - filename,
+ filename, permfile);
+ do_command(command);
+}
static bool file_set_perms(struct file_ops_info *info,
const char *path,
@@ -318,6 +340,7 @@
if (write(fd, data, len) != (int)len)
barf_perror("Bad write to %s", filename);
+ init_perms(filename);
close(fd);
return true;
}
@@ -339,16 +362,8 @@
errno = saved_errno;
return false;
}
+ init_perms(dirname);
return true;
-}
-
-static void do_command(const char *cmd)
-{
- int ret;
-
- ret = system(cmd);
- if (ret == -1 || !WIFEXITED(ret) || WEXITSTATUS(ret) != 0)
- barf_perror("Failed '%s': %i", cmd, ret);
}
static bool file_rm(struct file_ops_info *info, const char *path)
@@ -969,8 +984,11 @@
static void setup_file_ops(const char *dir)
{
+ char *cmd = talloc_asprintf(NULL, "echo -n r0 > %s/.perms", dir);
if (mkdir(dir, 0700) != 0)
barf_perror("Creating directory %s", dir);
+ do_command(cmd);
+ talloc_free(cmd);
}
static void setup_xs_ops(void)
diff -r 43f224c33281 -r 57a5441b323b tools/xenstore/testsuite/08transaction.sh
--- a/tools/xenstore/testsuite/08transaction.sh Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/testsuite/08transaction.sh Tue Jul 12 10:16:33 2005
@@ -52,3 +52,28 @@
1 dir /
1 commit' | ./xs_test 2>&1`" = "1:dir
FATAL: 1: commit: Connection timed out" ]
+
+# Events inside transactions don't trigger watches until (successful) commit.
+[ "`echo -e '1 watch / token 100
+2 start /
+2 mkdir /dir/sub
+1 waitwatch' | ./xs_test 2>&1`" = "1:waitwatch timeout" ]
+[ "`echo -e '1 watch / token 100
+2 start /
+2 mkdir /dir/sub
+2 abort
+1 waitwatch' | ./xs_test 2>&1`" = "1:waitwatch timeout" ]
+[ "`echo -e '1 watch / token 100
+2 start /
+2 mkdir /dir/sub
+2 commit
+1 waitwatch
+1 ackwatch token' | ./xs_test 2>&1`" = "1:/dir/sub:token" ]
+
+# Rm inside transaction works like rm outside: children get notified.
+[ "`echo -e '1 watch /dir/sub token 100
+2 start /
+2 rm /dir
+2 commit
+1 waitwatch
+1 ackwatch token' | ./xs_test 2>&1`" = "1:/dir/sub:token" ]
diff -r 43f224c33281 -r 57a5441b323b tools/xenstore/xenstored_domain.c
--- a/tools/xenstore/xenstored_domain.c Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/xenstored_domain.c Tue Jul 12 10:16:33 2005
@@ -273,7 +273,7 @@
domain = talloc(in, struct domain);
domain->domid = atoi(vec[0]);
domain->port = atoi(vec[2]);
- if (!domain->port || !domain->domid || !is_valid_nodename(vec[3]))
+ if ((domain->port <= 0) || !is_valid_nodename(vec[3]))
return send_error(conn, EINVAL);
domain->path = talloc_strdup(domain, vec[3]);
domain->page = xc_map_foreign_range(*xc_handle, domain->domid,
@@ -349,7 +349,7 @@
return send_error(conn, EINVAL);
domid = atoi(domid_str);
- if (domid == 0)
+ if (domid == DOMID_SELF)
domain = conn->domain;
else
domain = find_domain_by_domid(domid);
diff -r 43f224c33281 -r 57a5441b323b tools/xenstore/Makefile
--- a/tools/xenstore/Makefile Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/Makefile Tue Jul 12 10:16:33 2005
@@ -87,7 +87,7 @@
stresstest: xs_stress xs_watch_stress xenstored_test
rm -rf $(TESTDIR)/store
- export $(TESTENV); PID=`./xenstored_test --output-pid`; ./xs_stress
5000; ret=$$?; kill $$PID; exit $$ret
+ export $(TESTENV); PID=`./xenstored_test --output-pid
--trace-file=/tmp/trace`; ./xs_stress 5000; ret=$$?; kill $$PID; exit $$ret
rm -rf $(TESTDIR)/store
export $(TESTENV); PID=`./xenstored_test --output-pid`;
./xs_watch_stress; ret=$$?; kill $$PID; exit $$ret
diff -r 43f224c33281 -r 57a5441b323b
tools/xenstore/testsuite/06dirpermissions.sh
--- a/tools/xenstore/testsuite/06dirpermissions.sh Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/testsuite/06dirpermissions.sh Tue Jul 12 10:16:33 2005
@@ -3,17 +3,17 @@
# Root directory: owned by tool, everyone has read access.
[ "`echo -e 'getperm /' | ./xs_test 2>&1`" = "0 READ" ]
-# Create directory: we own it, noone has access.
+# Create directory: inherits from root.
[ "`echo -e 'mkdir /dir' | ./xs_test 2>&1`" = "" ]
-[ "`echo -e 'getperm /dir' | ./xs_test 2>&1`" = "0 NONE" ]
+[ "`echo -e 'getperm /dir' | ./xs_test 2>&1`" = "0 READ" ]
+[ "`echo -e 'setid 1\ngetperm /dir' | ./xs_test 2>&1`" = "0 READ" ]
+[ "`echo -e 'setid 1\ndir /dir' | ./xs_test 2>&1`" = "" ]
+[ "`echo -e 'setid 1\nwrite /dir/test create contents2' | ./xs_test 2>&1`" =
"FATAL: write: Permission denied" ]
+
+# Remove everyone's read access to directoy.
+[ "`echo -e 'setperm /dir 0 NONE' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'setid 1\ndir /dir' | ./xs_test 2>&1`" = "FATAL: dir: Permission
denied" ]
[ "`echo -e 'setid 1\nread /dir/test create contents2' | ./xs_test 2>&1`" =
"FATAL: read: Permission denied" ]
-[ "`echo -e 'setid 1\nwrite /dir/test create contents2' | ./xs_test 2>&1`" =
"FATAL: write: Permission denied" ]
-
-# Grant everyone read access to directoy.
-[ "`echo -e 'setperm /dir 0 READ' | ./xs_test 2>&1`" = "" ]
-[ "`echo -e 'setid 1\ngetperm /dir' | ./xs_test 2>&1`" = "0 READ" ]
-[ "`echo -e 'setid 1\ndir /dir' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'setid 1\nwrite /dir/test create contents2' | ./xs_test 2>&1`" =
"FATAL: write: Permission denied" ]
# Grant everyone write access to directory.
@@ -21,6 +21,8 @@
[ "`echo -e 'setid 1\ngetperm /dir' | ./xs_test 2>&1`" = "FATAL: getperm:
Permission denied" ]
[ "`echo -e 'setid 1\ndir /dir' | ./xs_test 2>&1`" = "FATAL: dir: Permission
denied" ]
[ "`echo -e 'setid 1\nwrite /dir/test create contents' | ./xs_test 2>&1`" = ""
]
+[ "`echo -e 'getperm /dir/test' | ./xs_test 2>&1`" = "1 WRITE" ]
+[ "`echo -e 'setperm /dir/test 0 NONE' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'read /dir/test' | ./xs_test 2>&1`" = "contents" ]
# Grant everyone both read and write access.
@@ -29,6 +31,7 @@
[ "`echo -e 'setid 1\ndir /dir' | ./xs_test 2>&1`" = "test" ]
[ "`echo -e 'setid 1\nwrite /dir/test2 create contents' | ./xs_test 2>&1`" =
"" ]
[ "`echo -e 'setid 1\nread /dir/test2' | ./xs_test 2>&1`" = "contents" ]
+[ "`echo -e 'setid 1\nsetperm /dir/test2 1 NONE' | ./xs_test 2>&1`" = "" ]
# Change so that user 1 owns it, noone else can do anything.
[ "`echo -e 'setperm /dir 1 NONE' | ./xs_test 2>&1`" = "" ]
@@ -59,3 +62,14 @@
test3" ]
[ "`echo -e 'write /dir/test4 create contents' | ./xs_test 2>&1`" = "" ]
+# Inherited by child.
+[ "`echo -e 'mkdir /dir/subdir' | ./xs_test 2>&1`" = "" ]
+[ "`echo -e 'getperm /dir/subdir' | ./xs_test 2>&1`" = "1 NONE" ]
+[ "`echo -e 'write /dir/subfile excl contents' | ./xs_test 2>&1`" = "" ]
+[ "`echo -e 'getperm /dir/subfile' | ./xs_test 2>&1`" = "1 NONE" ]
+
+# But for domains, they own it.
+[ "`echo -e 'setperm /dir/subdir 2 READ/WRITE' | ./xs_test 2>&1`" = "" ]
+[ "`echo -e 'getperm /dir/subdir' | ./xs_test 2>&1`" = "2 READ/WRITE" ]
+[ "`echo -e 'setid 3\nwrite /dir/subdir/subfile excl contents' | ./xs_test
2>&1`" = "" ]
+[ "`echo -e 'getperm /dir/subdir/subfile' | ./xs_test 2>&1`" = "3 READ/WRITE" ]
diff -r 43f224c33281 -r 57a5441b323b tools/xenstore/testsuite/12readonly.sh
--- a/tools/xenstore/testsuite/12readonly.sh Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/testsuite/12readonly.sh Tue Jul 12 10:16:33 2005
@@ -14,7 +14,7 @@
start /
abort' | ./xs_test --readonly 2>&1`" = "test
contents
-0 NONE" ]
+0 READ" ]
# These don't work
[ "`echo 'write /test2 create contents' | ./xs_test --readonly 2>&1`" =
"FATAL: write: Read-only file system" ]
diff -r 43f224c33281 -r 57a5441b323b
tools/xenstore/testsuite/05filepermissions.sh
--- a/tools/xenstore/testsuite/05filepermissions.sh Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/testsuite/05filepermissions.sh Tue Jul 12 10:16:33 2005
@@ -4,17 +4,17 @@
[ "`echo -e 'getperm /test' | ./xs_test 2>&1`" = "FATAL: getperm: No such file
or directory" ]
[ "`echo -e 'getperm /dir/test' | ./xs_test 2>&1`" = "FATAL: getperm: No such
file or directory" ]
-# Create file: we own it, noone has access.
+# Create file: inherits from root (0 READ)
[ "`echo -e 'write /test excl contents' | ./xs_test 2>&1`" = "" ]
-[ "`echo -e 'getperm /test' | ./xs_test 2>&1`" = "0 NONE" ]
+[ "`echo -e 'getperm /test' | ./xs_test 2>&1`" = "0 READ" ]
+[ "`echo -e 'setid 1\ngetperm /test' | ./xs_test 2>&1`" = "0 READ" ]
+[ "`echo -e 'setid 1\nread /test' | ./xs_test 2>&1`" = "contents" ]
+[ "`echo -e 'setid 1\nwrite /test none contents2' | ./xs_test 2>&1`" = "FATAL:
write: Permission denied" ]
+
+# Take away read access to file.
+[ "`echo -e 'setperm /test 0 NONE' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'setid 1\ngetperm /test' | ./xs_test 2>&1`" = "FATAL: getperm:
Permission denied" ]
[ "`echo -e 'setid 1\nread /test' | ./xs_test 2>&1`" = "FATAL: read:
Permission denied" ]
-[ "`echo -e 'setid 1\nwrite /test none contents2' | ./xs_test 2>&1`" = "FATAL:
write: Permission denied" ]
-
-# Grant everyone read access to file.
-[ "`echo -e 'setperm /test 0 READ' | ./xs_test 2>&1`" = "" ]
-[ "`echo -e 'setid 1\ngetperm /test' | ./xs_test 2>&1`" = "0 READ" ]
-[ "`echo -e 'setid 1\nread /test' | ./xs_test 2>&1`" = "contents" ]
[ "`echo -e 'setid 1\nwrite /test none contents2' | ./xs_test 2>&1`" = "FATAL:
write: Permission denied" ]
# Grant everyone write access to file.
diff -r 43f224c33281 -r 57a5441b323b tools/xenstore/utils.c
--- a/tools/xenstore/utils.c Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/utils.c Tue Jul 12 10:16:33 2005
@@ -13,17 +13,15 @@
void xprintf(const char *fmt, ...)
{
- static FILE *out = NULL;
- va_list args;
- if (!out)
- out = fopen("/dev/console", "w");
+ static FILE *out = NULL;
+ va_list args;
if (!out)
out = stderr;
- va_start(args, fmt);
- vfprintf(out, fmt, args);
- va_end(args);
- fflush(out);
+ va_start(args, fmt);
+ vfprintf(out, fmt, args);
+ va_end(args);
+ fflush(out);
}
void barf(const char *fmt, ...)
@@ -61,14 +59,14 @@
void *_realloc_array(void *ptr, size_t size, size_t num)
{
- if (num >= SIZE_MAX/size)
- return NULL;
- return realloc_nofail(ptr, size * num);
+ if (num >= SIZE_MAX/size)
+ return NULL;
+ return realloc_nofail(ptr, size * num);
}
void *realloc_nofail(void *ptr, size_t size)
{
- ptr = realloc(ptr, size);
+ ptr = realloc(ptr, size);
if (ptr)
return ptr;
barf("realloc of %zu failed", size);
diff -r 43f224c33281 -r 57a5441b323b tools/xenstore/xenstored_watch.c
--- a/tools/xenstore/xenstored_watch.c Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/xenstored_watch.c Tue Jul 12 10:16:33 2005
@@ -23,12 +23,14 @@
#include <stdlib.h>
#include <sys/time.h>
#include <time.h>
+#include <assert.h>
#include "talloc.h"
#include "list.h"
#include "xenstored_watch.h"
#include "xs_lib.h"
#include "utils.h"
#include "xenstored_test.h"
+#include "xenstored_domain.h"
/* FIXME: time out unacked watches. */
@@ -40,13 +42,17 @@
/* The watch we are firing for (watch->events) */
struct list_head list;
- /* Watch we are currently attached to. */
- struct watch *watch;
+ /* Watches we need to fire for (watches[0]->events == this). */
+ struct watch **watches;
+ unsigned int num_watches;
struct timeval timeout;
/* Name of node which changed. */
char *node;
+
+ /* For remove, we trigger on all the children of this node too. */
+ bool recurse;
};
struct watch
@@ -56,6 +62,9 @@
/* Current outstanding events applying to this watch. */
struct list_head events;
+
+ /* Is this relative to connnection's implicit path? */
+ bool relative;
char *token;
char *node;
@@ -84,6 +93,7 @@
void queue_next_event(struct connection *conn)
{
struct watch_event *event;
+ const char *node;
char *buffer;
unsigned int len;
@@ -107,53 +117,63 @@
/* If we decide to cancel, we will reset this. */
conn->waiting_for_ack = true;
+ /* If we deleted /foo and they're watching /foo/bar, that's what we
+ * tell them has changed. */
+ if (!is_child(event->node, event->watches[0]->node)) {
+ assert(event->recurse);
+ node = event->watches[0]->node;
+ } else
+ node = event->node;
+
+ /* If watch placed using relative path, give them relative answer. */
+ if (event->watches[0]->relative) {
+ node += strlen(get_implicit_path(conn));
+ if (node[0] == '/') /* Could be "". */
+ node++;
+ }
+
/* Create reply from path and token */
- len = strlen(event->node) + 1 + strlen(event->watch->token) + 1;
+ len = strlen(node) + 1 + strlen(event->watches[0]->token) + 1;
buffer = talloc_array(conn, char, len);
- strcpy(buffer, event->node);
- strcpy(buffer+strlen(event->node)+1, event->watch->token);
+ strcpy(buffer, node);
+ strcpy(buffer+strlen(node)+1, event->watches[0]->token);
send_reply(conn, XS_WATCH_EVENT, buffer, len);
talloc_free(buffer);
}
-/* Watch on DIR applies to DIR, DIR/FILE, but not DIRLONG. */
-static bool watch_applies(const struct watch *watch, const char *node)
-{
- return is_child(node, watch->node);
-}
-
-static struct watch *find_watch(const char *node)
-{
- struct watch *watch;
-
- list_for_each_entry(watch, &watches, list) {
- if (watch_applies(watch, node))
- return watch;
- }
- return NULL;
-}
-
-static struct watch *find_next_watch(struct watch *watch, const char *node)
-{
- list_for_each_entry_continue(watch, &watches, list) {
- if (watch_applies(watch, node))
- return watch;
- }
- return NULL;
+static struct watch **find_watches(const char *node, bool recurse,
+ unsigned int *num)
+{
+ struct watch *i;
+ struct watch **ret = NULL;
+
+ *num = 0;
+
+ /* We include children too if this is an rm. */
+ list_for_each_entry(i, &watches, list) {
+ if (is_child(node, i->node) ||
+ (recurse && is_child(i->node, node))) {
+ (*num)++;
+ ret = talloc_realloc(node, ret, struct watch *, *num);
+ ret[*num - 1] = i;
+ }
+ }
+ return ret;
}
/* FIXME: we fail to fire on out of memory. Should drop connections. */
-void fire_watches(struct transaction *trans, const char *node)
-{
- struct watch *watch;
- struct watch_event *event;
+void fire_watches(struct transaction *trans, const char *node, bool recurse)
+{
+ struct watch **watches;
+ struct watch_event *event;
+ unsigned int num_watches;
/* During transactions, don't fire watches. */
if (trans)
return;
- watch = find_watch(node);
- if (!watch)
+ watches = find_watches(node, recurse, &num_watches);
+ if (!watches)
return;
/* Create and fill in info about event. */
@@ -161,16 +181,19 @@
event->node = talloc_strdup(event, node);
/* Tie event to this watch. */
- event->watch = watch;
- list_add_tail(&event->list, &watch->events);
+ event->watches = watches;
+ talloc_steal(event, watches);
+ event->num_watches = num_watches;
+ event->recurse = recurse;
+ list_add_tail(&event->list, &watches[0]->events);
/* Warn if not finished after thirty seconds. */
gettimeofday(&event->timeout, NULL);
event->timeout.tv_sec += 30;
/* If connection not doing anything, queue this. */
- if (!watch->conn->out)
- queue_next_event(watch->conn);
+ if (!watches[0]->conn->out)
+ queue_next_event(watches[0]->conn);
}
/* We're done with this event: see if anyone else wants it. */
@@ -178,18 +201,41 @@
{
list_del(&event->list);
- /* Remove from this watch, and find next watch to put this on. */
- event->watch = find_next_watch(event->watch, event->node);
- if (!event->watch) {
+ event->num_watches--;
+ event->watches++;
+ if (!event->num_watches) {
talloc_free(event);
return;
}
- list_add_tail(&event->list, &event->watch->events);
+ list_add_tail(&event->list, &event->watches[0]->events);
/* If connection not doing anything, queue this. */
- if (!event->watch->conn->out)
- queue_next_event(event->watch->conn);
+ if (!event->watches[0]->conn->out)
+ queue_next_event(event->watches[0]->conn);
+}
+
+static void remove_watch_from_events(struct watch *dying_watch)
+{
+ struct watch *watch;
+ struct watch_event *event;
+ unsigned int i;
+
+ list_for_each_entry(watch, &watches, list) {
+ list_for_each_entry(event, &watch->events, list) {
+ for (i = 0; i < event->num_watches; i++) {
+ if (event->watches[i] != dying_watch)
+ continue;
+
+ assert(i != 0);
+ memmove(event->watches+i,
+ event->watches+i+1,
+ (event->num_watches - (i+1))
+ * sizeof(struct watch *));
+ event->num_watches--;
+ }
+ }
+ }
}
static int destroy_watch(void *_watch)
@@ -203,6 +249,11 @@
/* Remove from global list. */
list_del(&watch->list);
+
+ /* Other events which match this watch must be cleared. */
+ remove_watch_from_events(watch);
+
+ trace_destroy(watch, "watch");
return 0;
}
@@ -251,6 +302,8 @@
xprintf("Warning: timeout on watch event %s"
" token %s\n",
i->node, watch->token);
+ trace_watch_timeout(watch->conn, i->node,
+ watch->token);
timerclear(&i->timeout);
}
}
@@ -261,10 +314,12 @@
{
struct watch *watch;
char *vec[3];
+ bool relative;
if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec))
return send_error(conn, EINVAL);
+ relative = !strstarts(vec[0], "/");
vec[0] = canonicalize(conn, vec[0]);
if (!check_node_perms(conn, vec[0], XS_PERM_READ))
return send_error(conn, errno);
@@ -274,22 +329,27 @@
watch->token = talloc_strdup(watch, vec[1]);
watch->conn = conn;
watch->priority = strtoul(vec[2], NULL, 0);
+ watch->relative = relative;
INIT_LIST_HEAD(&watch->events);
insert_watch(watch);
talloc_set_destructor(watch, destroy_watch);
+ trace_create(watch, "watch");
return send_ack(conn, XS_WATCH);
}
bool do_watch_ack(struct connection *conn, const char *token)
{
struct watch_event *event;
+
+ if (!token)
+ return send_error(conn, EINVAL);
if (!conn->waiting_for_ack)
return send_error(conn, ENOENT);
event = get_first_event(conn);
- if (!streq(event->watch->token, token))
+ if (!streq(event->watches[0]->token, token))
return send_error(conn, EINVAL);
move_event_onwards(event);
@@ -305,6 +365,9 @@
if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec))
return send_error(conn, EINVAL);
+ /* We don't need to worry if we're waiting for an ack for the
+ * watch we're deleting: conn->waiting_for_ack was reset by
+ * this command in consider_message anyway. */
node = canonicalize(conn, vec[0]);
list_for_each_entry(watch, &watches, list) {
if (watch->conn != conn)
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|