+static int xs_node_get_perms(XsNode **n, struct walk_op *op)
+{
+ GList **perms = op->op_opaque;
+
+ assert(op->inplace);
+ assert(*n);
+
+ *perms = g_list_copy_deep((*n)->perms, do_perm_copy, NULL);
+ return 0;
+}
+
+static void parse_perm(const char *perm, char *letter, unsigned int *dom_id)
+{
+ unsigned int n = sscanf(perm, "%c%u", letter, dom_id);
+
+ assert(n == 2);
+}
+
+static bool can_access(unsigned int dom_id, GList *perms, const char *letters)
+{
+ unsigned int i, n;
+ char perm_letter;
+ unsigned int perm_dom_id;
+ bool access;
+
+ if (dom_id == 0) {
+ return true;
+ }
+
+ n = g_list_length(perms);
+ assert(n >= 1);
+
+ /*
+ * The dom_id of the first perm is the owner, and the owner always has
+ * read-write access.
+ */
+ parse_perm(g_list_nth_data(perms, 0), &perm_letter, &perm_dom_id);
+ if (dom_id == perm_dom_id) {
+ return true;
+ }
+
+ /*
+ * The letter of the first perm specified the default access for all other
+ * domains.
+ */
+ access = !!strchr(letters, perm_letter);
+ for (i = 1; i < n; i++) {
+ parse_perm(g_list_nth_data(perms, i), &perm_letter, &perm_dom_id);
+ if (dom_id != perm_dom_id) {
+ continue;
+ }
+ access = !!strchr(letters, perm_letter);
+ }
+
+ return access;
+}
+
+static int xs_node_set_perms(XsNode **n, struct walk_op *op)
+{
+ GList *perms = op->op_opaque;
+
+ if (op->dom_id) {
+ unsigned int perm_dom_id;
+ char perm_letter;
+
+ /* A guest may not change permissions on nodes it does not own */
+ if (!can_access(op->dom_id, (*n)->perms, "")) {
+ return EPERM;
+ }
+
+ /* A guest may not change the owner of a node it owns. */
+ parse_perm(perms->data, &perm_letter, &perm_dom_id);
+ if (perm_dom_id != op->dom_id) {
+ return EPERM;
+ }
+
+ if (g_list_length(perms) > XS_MAX_PERMS_PER_NODE) {
+ return ENOSPC;
+ }
+ }
+
+ /* We *are* the node to be written. Either this or a copy. */
+ if (!op->inplace) {
+ XsNode *old = *n;
+ *n = xs_node_copy(old);
+ xs_node_unref(old);
+ }
+
+ if ((*n)->perms) {
+ g_list_free_full((*n)->perms, g_free);
+ }
+ (*n)->perms = g_list_copy_deep(perms, do_perm_copy, NULL);
+ if (op->tx_id != XBT_NULL) {
+ (*n)->modified_in_tx = true;
+ }
+ return 0;
+}
+