mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
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@rustcorp.com.au> Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
This commit is contained in:
1 parent
247bc37e83
commit
0f36f0e095
17 files changed
+392
-124
No files matched your search
@@ -87,7 +87,7 @@ randomcheck: xs_random xenstored_test
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -4,19 +4,19 @@
|
||||
[ "`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 '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 '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 write access to file.
|
||||
[ "`echo -e 'setperm /test 0 WRITE' | ./xs_test 2>&1`" = "" ]
|
||||
[ "`echo -e 'setid 1\ngetperm /test' | ./xs_test 2>&1`" = "FATAL: getperm: Permission denied" ]
|
||||
|
||||
@@ -3,24 +3,26 @@
|
||||
# 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 '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 '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 write access to directory.
|
||||
[ "`echo -e 'setperm /dir 0 WRITE' | ./xs_test 2>&1`" = "" ]
|
||||
[ "`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 @@ test2
|
||||
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" ]
|
||||
@@ -77,7 +77,7 @@ write /dir/test create contents
|
||||
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
|
||||
@@ -85,6 +85,15 @@ write /dir/test create contents
|
||||
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
|
||||
2 write /test create contents2
|
||||
@@ -118,3 +127,28 @@ write /dir/test create contents
|
||||
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" ]
|
||||
@@ -52,3 +52,28 @@ dir /' | ./xs_test`" = "" ]
|
||||
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" ]
|
||||
@@ -10,3 +10,11 @@ dir /home' | ./xs_test 2>&1`" = "handle is 1
|
||||
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" ]
|
||||
@@ -14,7 +14,7 @@ commit
|
||||
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" ]
|
||||
|
||||
+10
-12
@@ -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 barf_perror(const char *fmt, ...)
|
||||
|
||||
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);
|
||||
|
||||
+118
-20
@@ -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 char *sockmsg_string(enum xsd_sockmsg_type type)
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ static bool write_message(struct connection *conn)
|
||||
if (out->used != out->hdr.msg.len)
|
||||
return true;
|
||||
|
||||
trace_io(conn, "OUT", out);
|
||||
conn->out = NULL;
|
||||
talloc_free(out);
|
||||
|
||||
@@ -213,6 +295,7 @@ static int destroy_conn(void *_conn)
|
||||
close(conn->fd);
|
||||
}
|
||||
list_del(&conn->list);
|
||||
trace_destroy(conn, "connection");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -756,9 +839,9 @@ static bool do_read(struct connection *conn, const char *node)
|
||||
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 @@ static bool new_directory(struct connection *conn,
|
||||
/* 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;
|
||||
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, 1, &len);
|
||||
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 @@ static bool do_write(struct connection *conn, struct buffered_data *in)
|
||||
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 @@ static bool do_write(struct connection *conn, struct buffered_data *in)
|
||||
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 @@ static bool do_mkdir(struct connection *conn, const char *node)
|
||||
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 @@ static bool do_rm(struct connection *conn, const char *node)
|
||||
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 @@ static bool do_set_perms(struct connection *conn, struct buffered_data *in)
|
||||
|
||||
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 @@ static bool process_message(struct connection *conn, struct buffered_data *in)
|
||||
/* 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 @@ static bool process_message(struct connection *conn, struct buffered_data *in)
|
||||
send_ack(conn, XS_DEBUG);
|
||||
failtest = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif /* TESTING */
|
||||
return false;
|
||||
|
||||
case XS_WATCH:
|
||||
return do_watch(conn, in);
|
||||
@@ -1092,6 +1179,7 @@ static void consider_message(struct connection *conn)
|
||||
talloc_free(conn->in);
|
||||
conn->in = in;
|
||||
in = NULL;
|
||||
trace_blocked(conn, conn->in);
|
||||
}
|
||||
|
||||
end:
|
||||
@@ -1145,6 +1233,7 @@ void handle_input(struct connection *conn)
|
||||
if (in->used != in->hdr.msg.len)
|
||||
return;
|
||||
|
||||
trace_io(conn, "IN ", in);
|
||||
consider_message(conn);
|
||||
return;
|
||||
|
||||
@@ -1212,6 +1301,7 @@ struct connection *new_connection(connwritefn_t *write, connreadfn_t *read)
|
||||
|
||||
list_add_tail(&new->list, &connections);
|
||||
talloc_set_destructor(new, destroy_conn);
|
||||
trace_create(new, "connection");
|
||||
return new;
|
||||
}
|
||||
|
||||
@@ -1299,6 +1389,7 @@ void dump_connection(void)
|
||||
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 @@ int main(int argc, char *argv[])
|
||||
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;
|
||||
@@ -1320,6 +1411,13 @@ int main(int argc, char *argv[])
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (optind != argc)
|
||||
|
||||
@@ -143,4 +143,9 @@ int destroy_path(void *path);
|
||||
/* 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 */
|
||||
@@ -273,7 +273,7 @@ bool do_introduce(struct connection *conn, struct buffered_data *in)
|
||||
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 @@ bool do_get_domain_path(struct connection *conn, const char *domid_str)
|
||||
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);
|
||||
|
||||
@@ -41,6 +41,9 @@ struct changed_node
|
||||
|
||||
/* The name of the node. */
|
||||
char *node;
|
||||
|
||||
/* And the children? (ie. rm) */
|
||||
bool recurse;
|
||||
};
|
||||
|
||||
struct transaction
|
||||
@@ -119,7 +122,7 @@ bool transaction_block(struct connection *conn, const char *node)
|
||||
|
||||
/* 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 @@ void add_change_node(struct transaction *trans, const char *node)
|
||||
|
||||
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 @@ static int destroy_transaction(void *_transaction)
|
||||
struct transaction *trans = _transaction;
|
||||
|
||||
list_del(&trans->list);
|
||||
trace_destroy(trans, "transaction");
|
||||
return destroy_path(trans->divert);
|
||||
}
|
||||
|
||||
@@ -263,13 +267,14 @@ bool do_transaction_start(struct connection *conn, const char *node)
|
||||
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 @@ static bool commit_transaction(struct transaction *trans)
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ struct transaction *transaction_covering_node(const char *node);
|
||||
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);
|
||||
|
||||
@@ -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 @@ struct watch_event
|
||||
/* 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
|
||||
@@ -57,6 +63,9 @@ struct watch
|
||||
/* 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;
|
||||
struct connection *conn;
|
||||
@@ -84,6 +93,7 @@ static struct watch_event *get_first_event(struct connection *conn)
|
||||
void queue_next_event(struct connection *conn)
|
||||
{
|
||||
struct watch_event *event;
|
||||
const char *node;
|
||||
char *buffer;
|
||||
unsigned int len;
|
||||
|
||||
@@ -107,53 +117,63 @@ void queue_next_event(struct connection *conn)
|
||||
/* 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)
|
||||
static struct watch **find_watches(const char *node, bool recurse,
|
||||
unsigned int *num)
|
||||
{
|
||||
return is_child(node, watch->node);
|
||||
}
|
||||
struct watch *i;
|
||||
struct watch **ret = NULL;
|
||||
|
||||
static struct watch *find_watch(const char *node)
|
||||
{
|
||||
struct watch *watch;
|
||||
*num = 0;
|
||||
|
||||
list_for_each_entry(watch, &watches, list) {
|
||||
if (watch_applies(watch, node))
|
||||
return watch;
|
||||
/* 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 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;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* FIXME: we fail to fire on out of memory. Should drop connections. */
|
||||
void fire_watches(struct transaction *trans, const char *node)
|
||||
void fire_watches(struct transaction *trans, const char *node, bool recurse)
|
||||
{
|
||||
struct watch *watch;
|
||||
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 @@ void fire_watches(struct transaction *trans, const char *node)
|
||||
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 @@ static void move_event_onwards(struct watch_event *event)
|
||||
{
|
||||
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 @@ static int destroy_watch(void *_watch)
|
||||
|
||||
/* 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 @@ void check_watch_ack_timeout(void)
|
||||
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 @@ bool do_watch(struct connection *conn, struct buffered_data *in)
|
||||
{
|
||||
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,10 +329,12 @@ bool do_watch(struct connection *conn, struct buffered_data *in)
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -285,11 +342,14 @@ 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 @@ bool do_unwatch(struct connection *conn, struct buffered_data *in)
|
||||
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)
|
||||
|
||||
@@ -32,8 +32,8 @@ bool is_watch_event(struct connection *conn, struct buffered_data *out);
|
||||
/* 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);
|
||||
|
||||
+2
-2
@@ -47,7 +47,7 @@ char **xs_directory(struct xs_handle *h, const char *path, unsigned int *num);
|
||||
|
||||
/* 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 @@ char **xs_read_watch(struct xs_handle *h);
|
||||
*/
|
||||
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);
|
||||
|
||||
+40
-22
@@ -201,7 +201,6 @@ static struct xs_permissions *file_get_perms(struct file_ops_info *info,
|
||||
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 @@ static struct xs_permissions *file_get_perms(struct file_ops_info *info,
|
||||
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);
|
||||
@@ -232,6 +221,39 @@ static struct xs_permissions *file_get_perms(struct file_ops_info *info,
|
||||
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,
|
||||
struct xs_permissions *perms,
|
||||
@@ -318,6 +340,7 @@ static bool file_write(struct file_ops_info *info,
|
||||
if (write(fd, data, len) != (int)len)
|
||||
barf_perror("Bad write to %s", filename);
|
||||
|
||||
init_perms(filename);
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
@@ -339,18 +362,10 @@ static bool file_mkdir(struct file_ops_info *info, const char *path)
|
||||
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)
|
||||
{
|
||||
char *filename = path_to_name(info, path);
|
||||
@@ -969,8 +984,11 @@ static void cleanup(const char *dir)
|
||||
|
||||
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)
|
||||
|
||||