[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] xenstored updates.
# 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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |