LIST_HEAD(connections);
int tracefd = -1;
static bool recovery = true;
+bool keep_orphans = false;
static int reopen_log_pipe[2];
static int reopen_log_pipe0_pollfd_idx = -1;
char *tracefile = NULL;
node->perms.p = hdr->perms;
node->acc.domid = node->perms.p[0].id;
node->acc.memory = data.dsize;
- if (domain_adjust_node_perms(conn, node))
+ if (domain_adjust_node_perms(node))
goto error;
/* If owner is gone reset currently accounted memory size. */
void *p;
struct xs_tdb_record_hdr *hdr;
- if (domain_adjust_node_perms(conn, node))
+ if (domain_adjust_node_perms(node))
return errno;
data.dsize = sizeof(*hdr)
return WALK_TREE_RM_CHILDENTRY;
}
-static int _rm(struct connection *conn, const void *ctx, const char *name)
+int rm_node(struct connection *conn, const void *ctx, const char *name)
{
struct node *parent;
char *parentname = get_parent(ctx, name);
if (streq(name, "/"))
return EINVAL;
- ret = _rm(conn, ctx, name);
+ ret = rm_node(conn, ctx, name);
if (ret)
return ret;
" -R, --no-recovery to request that no recovery should be attempted when\n"
" the store is corrupted (debug only),\n"
" -I, --internal-db store database in memory, not on disk\n"
+" -K, --keep-orphans don't delete nodes owned by a domain when the\n"
+" domain is deleted (this is a security risk!)\n"
" -V, --verbose to request verbose execution.\n");
}
{ "timeout", 1, NULL, 'w' },
{ "no-recovery", 0, NULL, 'R' },
{ "internal-db", 0, NULL, 'I' },
+ { "keep-orphans", 0, NULL, 'K' },
{ "verbose", 0, NULL, 'V' },
{ "watch-nb", 1, NULL, 'W' },
{ NULL, 0, NULL, 0 } };
int timeout;
- while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:Q:q:T:RVW:w:", options,
+ while ((opt = getopt_long(argc, argv, "DE:F:HKNPS:t:A:Q:q:T:RVW:w:", options,
NULL)) != -1) {
switch (opt) {
case 'D':
case 'I':
tdb_flags = TDB_INTERNAL|TDB_NOLOCK;
break;
+ case 'K':
+ keep_orphans = true;
+ break;
case 'V':
verbose = true;
break;
xengnttab_unmap(*xgt_handle, interface, 1);
}
+static int domain_tree_remove_sub(const void *ctx, struct connection *conn,
+ struct node *node, void *arg)
+{
+ struct domain *domain = arg;
+ TDB_DATA key;
+ int ret = WALK_TREE_OK;
+
+ if (node->perms.p[0].id != domain->domid)
+ return WALK_TREE_OK;
+
+ if (keep_orphans) {
+ key.dptr = (char *)node->name;
+ key.dsize = strlen(node->name);
+ domain->nbentry--;
+ node->perms.p[0].id = priv_domid;
+ node->acc.memory = 0;
+ domain_entry_inc(NULL, node);
+ if (write_node_raw(NULL, &key, node, true)) {
+ /* That's unfortunate. We only can try to continue. */
+ syslog(LOG_ERR,
+ "error when moving orphaned node %s to dom0\n",
+ node->name);
+ } else
+ trace("orphaned node %s moved to dom0\n", node->name);
+ } else {
+ if (rm_node(NULL, ctx, node->name)) {
+ /* That's unfortunate. We only can try to continue. */
+ syslog(LOG_ERR,
+ "error when deleting orphaned node %s\n",
+ node->name);
+ } else
+ trace("orphaned node %s deleted\n", node->name);
+
+ /* Skip children in all cases in order to avoid more errors. */
+ ret = WALK_TREE_SKIP_CHILDREN;
+ }
+
+ return domain->nbentry > 0 ? ret : WALK_TREE_SUCCESS_STOP;
+}
+
+static void domain_tree_remove(struct domain *domain)
+{
+ int ret;
+ struct walk_funcs walkfuncs = { .enter = domain_tree_remove_sub };
+
+ if (domain->nbentry > 0) {
+ ret = walk_node_tree(domain, NULL, "/", &walkfuncs, domain);
+ if (ret == WALK_TREE_ERROR_STOP)
+ syslog(LOG_ERR,
+ "error when looking for orphaned nodes\n");
+ }
+}
+
static int destroy_domain(void *_domain)
{
struct domain *domain = _domain;
+ domain_tree_remove(domain);
+
list_del(&domain->list);
if (!domain->introduced)
struct domain *d;
unsigned int domid;
- if (!conn)
+ if (!node->perms.p)
return 0;
- domid = node->perms.p ? node->perms.p[0].id : conn->id;
+ domid = node->perms.p[0].id;
- if (conn->transaction) {
+ if (conn && conn->transaction) {
transaction_entry_inc(conn->transaction, domid);
} else {
- d = (domid == conn->id && conn->domain) ? conn->domain
+ d = (conn && domid == conn->id && conn->domain) ? conn->domain
: find_or_alloc_existing_domain(domid);
if (d)
d->nbentry++;
* Remove permissions for no longer existing domains in order to avoid a new
* domain with the same domid inheriting the permissions.
*/
-int domain_adjust_node_perms(struct connection *conn, struct node *node)
+int domain_adjust_node_perms(struct node *node)
{
unsigned int i;
int ret;
- ret = chk_domain_generation(node->perms.p[0].id, node->generation);
-
- /* If the owner doesn't exist any longer give it to priv domain. */
- if (!ret) {
- /*
- * In theory we'd need to update the number of dom0 nodes here,
- * but we could be called for a read of the node. So better
- * avoid the risk to overflow the node count of dom0.
- */
- node->perms.p[0].id = priv_domid;
- }
-
for (i = 1; i < node->perms.num; i++) {
if (node->perms.p[i].perms & XS_PERM_IGNORE)
continue;
struct domain *d;
unsigned int domid;
- if (!conn)
+ if (!node->perms.p)
return;
domid = node->perms.p ? node->perms.p[0].id : conn->id;
- if (conn->transaction) {
+ if (conn && conn->transaction) {
transaction_entry_dec(conn->transaction, domid);
} else {
- d = (domid == conn->id && conn->domain) ? conn->domain
+ d = (conn && domid == conn->id && conn->domain) ? conn->domain
: find_domain_struct(domid);
if (d) {
d->nbentry--;
* exist, as accounting is done either for a domain related to
* the current connection, or for the domain owning a node
* (which is always existing, as the owner of the node is
- * tested to exist and replaced by domid 0 if not).
+ * tested to exist and deleted or replaced by domid 0 if not).
* So not finding the related domain MUST be an error in the
* data base.
*/