aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNatanael Copa <ncopa@alpinelinux.org>2013-06-26 09:48:55 +0000
committerNatanael Copa <ncopa@alpinelinux.org>2013-06-26 09:48:55 +0000
commit638e4f7ceb5b5a8b9f9c7c3206fcd9e7c39d2bee (patch)
tree7cc05ee0beae82098b3364ab13935ef887a11bb4
parent50869d41a1af768fb0c39ff2d059a8bec102bc91 (diff)
downloadalpine_aports-638e4f7ceb5b5a8b9f9c7c3206fcd9e7c39d2bee.tar.bz2
alpine_aports-638e4f7ceb5b5a8b9f9c7c3206fcd9e7c39d2bee.tar.xz
alpine_aports-638e4f7ceb5b5a8b9f9c7c3206fcd9e7c39d2bee.zip
main/xen: fix xsa57 (CVE-2013-2211)
ref #2117 fixes #2118
-rw-r--r--main/xen/APKBUILD6
-rw-r--r--main/xen/xsa57.patch333
2 files changed, 338 insertions, 1 deletions
diff --git a/main/xen/APKBUILD b/main/xen/APKBUILD
index d0afed1e64..b85ef834d1 100644
--- a/main/xen/APKBUILD
+++ b/main/xen/APKBUILD
@@ -3,7 +3,7 @@
3# Maintainer: William Pitcock <nenolod@dereferenced.org> 3# Maintainer: William Pitcock <nenolod@dereferenced.org>
4pkgname=xen 4pkgname=xen
5pkgver=4.2.2 5pkgver=4.2.2
6pkgrel=3 6pkgrel=4
7pkgdesc="Xen hypervisor" 7pkgdesc="Xen hypervisor"
8url="http://www.xen.org/" 8url="http://www.xen.org/"
9arch="x86 x86_64" 9arch="x86 x86_64"
@@ -29,6 +29,7 @@ source="http://bits.xensource.com/oss-xen/release/$pkgver/$pkgname-$pkgver.tar.g
29 xsa54.patch 29 xsa54.patch
30 xsa55.patch 30 xsa55.patch
31 xsa56.patch 31 xsa56.patch
32 xsa57.patch
32 33
33 fix-pod2man-choking.patch 34 fix-pod2man-choking.patch
34 35
@@ -158,6 +159,7 @@ b3e3a57d189a4f86c9766eaf3b5207f4 xsa48-4.2.patch
158a8393d1ec6b886ea72ffe624a04ee10a xsa54.patch 159a8393d1ec6b886ea72ffe624a04ee10a xsa54.patch
15942cd104f2a33d67938a63a6372cff573 xsa55.patch 16042cd104f2a33d67938a63a6372cff573 xsa55.patch
160e70b9128ffc2175cea314a533a7d8457 xsa56.patch 161e70b9128ffc2175cea314a533a7d8457 xsa56.patch
1627475158130474ee062a4eb878259af61 xsa57.patch
161c1d1a415415b0192e5dae9032962bf61 fix-pod2man-choking.patch 163c1d1a415415b0192e5dae9032962bf61 fix-pod2man-choking.patch
16295d8af17bf844d41a015ff32aae51ba1 xenstored.initd 16495d8af17bf844d41a015ff32aae51ba1 xenstored.initd
163b017ccdd5e1c27bbf1513e3569d4ff07 xenstored.confd 165b017ccdd5e1c27bbf1513e3569d4ff07 xenstored.confd
@@ -184,6 +186,7 @@ dc23077028584e71a08dd0dc9e81552c76744a5ce9d39df5958a95ae9cf3107b xsa48-4.2.patc
1845d94946b3c9cba52aae2bffd4b0ebb11d09181650b5322a3c85170674a05f6b7 xsa54.patch 1865d94946b3c9cba52aae2bffd4b0ebb11d09181650b5322a3c85170674a05f6b7 xsa54.patch
185ac3ebaf3ec37e28ba08e23d63626d7aaccf0a3f282dd0af9c24cc4df3fd8fae0 xsa55.patch 187ac3ebaf3ec37e28ba08e23d63626d7aaccf0a3f282dd0af9c24cc4df3fd8fae0 xsa55.patch
186a691c5f5332a42c0d38ddb4dc037eb902f01ba31033b64c47d02909a8de0257d xsa56.patch 188a691c5f5332a42c0d38ddb4dc037eb902f01ba31033b64c47d02909a8de0257d xsa56.patch
189b6a5106848541972519cc529859d9ff3083c79367276c7031560fa4ce6f9f770 xsa57.patch
187b4e7d43364a06b2cb04527db3e9567524bc489fef475709fd8493ebf1e62406d fix-pod2man-choking.patch 190b4e7d43364a06b2cb04527db3e9567524bc489fef475709fd8493ebf1e62406d fix-pod2man-choking.patch
18881d335946c81311c86e2f2112b773a568a5a530c0db9802b2fe559e71bb8b381 xenstored.initd 19181d335946c81311c86e2f2112b773a568a5a530c0db9802b2fe559e71bb8b381 xenstored.initd
189ea9171e71ab3d33061979bcf3bb737156192aa4b0be4d1234438ced75b6fdef3 xenstored.confd 192ea9171e71ab3d33061979bcf3bb737156192aa4b0be4d1234438ced75b6fdef3 xenstored.confd
@@ -210,6 +213,7 @@ b64a965fab8534958e453c493211ed3a6555aafb90d18f6d56a45b41d3086a0029aee85b6b6eb93b
210c9010be637d4f96ef03c880e1ef28228f762c5980108380a105bd190b631a882c8dff81e9421246d88d597e72f69ad1a8c672be6ddd06936acfcacd4575a2650 xsa54.patch 213c9010be637d4f96ef03c880e1ef28228f762c5980108380a105bd190b631a882c8dff81e9421246d88d597e72f69ad1a8c672be6ddd06936acfcacd4575a2650 xsa54.patch
211b4f43095163146a29ae258575bb03bd45f5a315d3cca7434a0b88c18eb1b6e1cf17ef13b4ac428a08797271a3dbc756d3f705a990991c8d2fc96f0f272c3665a xsa55.patch 214b4f43095163146a29ae258575bb03bd45f5a315d3cca7434a0b88c18eb1b6e1cf17ef13b4ac428a08797271a3dbc756d3f705a990991c8d2fc96f0f272c3665a xsa55.patch
21226a1c2cc92ddd4c1ab6712b0e41a0135d0e76a7fe3a14b651fb0235e352e5a24077414371acccb93058b7ce4d882b667386811170ba74570c53165837bcd983d xsa56.patch 21526a1c2cc92ddd4c1ab6712b0e41a0135d0e76a7fe3a14b651fb0235e352e5a24077414371acccb93058b7ce4d882b667386811170ba74570c53165837bcd983d xsa56.patch
2165ccc1654d9f0270485495f9fc913e41663ddbda602ffe049e0a9c3247c6246690b7ec4165482f96921c5253a2a5205ca384048339996e611c07ab60a6a75cf6a xsa57.patch
213ffb1113fcec0853b690c177655c7d1136388efdebf0d7f625b80481b98eadd3e9ef461442ced53e11acf0e347800a2b0a41e18b05065b5d04bffdd8a4e127cec fix-pod2man-choking.patch 217ffb1113fcec0853b690c177655c7d1136388efdebf0d7f625b80481b98eadd3e9ef461442ced53e11acf0e347800a2b0a41e18b05065b5d04bffdd8a4e127cec fix-pod2man-choking.patch
214792b062e8a16a2efd3cb4662d379d1500527f2a7ca9228d7831c2bd34f3b9141df949153ea05463a7758c3e3dd9a4182492ad5505fa38e298ecf8c99db77b4ee xenstored.initd 218792b062e8a16a2efd3cb4662d379d1500527f2a7ca9228d7831c2bd34f3b9141df949153ea05463a7758c3e3dd9a4182492ad5505fa38e298ecf8c99db77b4ee xenstored.initd
215100cf4112f401f45c1e4e885a5074698c484b40521262f6268fad286498e95f4c51e746f0e94eb43a590bb8e813a397bb53801ccacebec9541020799d8d70514 xenstored.confd 219100cf4112f401f45c1e4e885a5074698c484b40521262f6268fad286498e95f4c51e746f0e94eb43a590bb8e813a397bb53801ccacebec9541020799d8d70514 xenstored.confd
diff --git a/main/xen/xsa57.patch b/main/xen/xsa57.patch
new file mode 100644
index 0000000000..178b818890
--- /dev/null
+++ b/main/xen/xsa57.patch
@@ -0,0 +1,333 @@
1libxl: Restrict permissions on PV console device xenstore nodes
2
3Matthew Daley has observed that the PV console protocol places sensitive host
4state into a guest writeable xenstore locations, this includes:
5
6 - The pty used to communicate between the console backend daemon and its
7 client, allowing the guest administrator to read and write arbitrary host
8 files.
9 - The output file, allowing the guest administrator to write arbitrary host
10 files or to target arbitrary qemu chardevs which include sockets, udp, ptr,
11 pipes etc (see -chardev in qemu(1) for a more complete list).
12 - The maximum buffer size, allowing the guest administrator to consume more
13 resources than the host administrator has configured.
14 - The backend to use (qemu vs xenconsoled), potentially allowing the guest
15 administrator to confuse host software.
16
17So we arrange to make the sensitive keys in the xenstore frontend directory
18read only for the guest. This is safe since the xenstore permissions model,
19unlike POSIX directory permissions, does not allow the guest to remove and
20recreate a node if it has write access to the containing directory.
21
22There are a few associated wrinkles:
23
24 - The primary PV console is "special". It's xenstore node is not under the
25 usual /devices/ subtree and it does not use the customary xenstore state
26 machine protocol. Unfortunately its directory is used for other things,
27 including the vnc-port node, which we do not want the guest to be able to
28 write to. Rather than trying to track down all the possible secondary uses
29 of this directory just make it r/o to the guest. All newly created
30 subdirectories inherit these permissions and so are now safe by default.
31
32 - The other serial consoles do use the customary xenstore state machine and
33 therefore need write access to at least the "protocol" and "state" nodes,
34 however they may also want to use arbitrary "feature-foo" nodes (although
35 I'm not aware of any) and therefore we cannot simply lock down the entire
36 frontend directory. Instead we add support to libxl__device_generic_add for
37 frontend keys which are explicitly read only and use that to lock down the
38 sensitive keys.
39
40 - Minios' console frontend wants to write the "type" node, which it has no
41 business doing since this is a host/toolstack level decision. This fails
42 now that the node has become read only to the PV guest. Since the toolstack
43 already writes this node just remove the attempt to set it.
44
45This is CVE-XXXX-XXX / XSA-57
46
47Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
48
49Conflicts:
50 tools/libxl/libxl.c (no vtpm, free front_ro on error in
51 libxl__device_console_add)
52
53diff --git a/extras/mini-os/console/xenbus.c b/extras/mini-os/console/xenbus.c
54index 77de82a..e65baf7 100644
55--- a/extras/mini-os/console/xenbus.c
56+++ b/extras/mini-os/console/xenbus.c
57@@ -122,12 +122,6 @@ again:
58 goto abort_transaction;
59 }
60
61- err = xenbus_printf(xbt, nodename, "type", "%s", "ioemu");
62- if (err) {
63- message = "writing type";
64- goto abort_transaction;
65- }
66-
67 snprintf(path, sizeof(path), "%s/state", nodename);
68 err = xenbus_switch_state(xbt, path, XenbusStateConnected);
69 if (err) {
70diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c
71index a6e9601..32d788a 100644
72--- a/tools/libxl/libxl.c
73+++ b/tools/libxl/libxl.c
74@@ -1920,8 +1920,9 @@ static void device_disk_add(libxl__egc *egc, uint32_t domid,
75 flexarray_append(front, disk->is_cdrom ? "cdrom" : "disk");
76
77 libxl__device_generic_add(gc, t, device,
78- libxl__xs_kvs_of_flexarray(gc, back, back->count),
79- libxl__xs_kvs_of_flexarray(gc, front, front->count));
80+ libxl__xs_kvs_of_flexarray(gc, back, back->count),
81+ libxl__xs_kvs_of_flexarray(gc, front, front->count),
82+ NULL);
83
84 rc = libxl__xs_transaction_commit(gc, &t);
85 if (!rc) break;
86@@ -2633,8 +2634,9 @@ void libxl__device_nic_add(libxl__egc *egc, uint32_t domid,
87 flexarray_append(front, libxl__sprintf(gc,
88 LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nic->mac)));
89 libxl__device_generic_add(gc, XBT_NULL, device,
90- libxl__xs_kvs_of_flexarray(gc, back, back->count),
91- libxl__xs_kvs_of_flexarray(gc, front, front->count));
92+ libxl__xs_kvs_of_flexarray(gc, back, back->count),
93+ libxl__xs_kvs_of_flexarray(gc, front, front->count),
94+ NULL);
95
96 aodev->dev = device;
97 aodev->action = DEVICE_CONNECT;
98@@ -2830,7 +2832,7 @@ int libxl__device_console_add(libxl__gc *gc, uint32_t domid,
99 libxl__device_console *console,
100 libxl__domain_build_state *state)
101 {
102- flexarray_t *front;
103+ flexarray_t *front, *ro_front;
104 flexarray_t *back;
105 libxl__device device;
106 int rc;
107@@ -2845,6 +2847,11 @@ int libxl__device_console_add(libxl__gc *gc, uint32_t domid,
108 rc = ERROR_NOMEM;
109 goto out;
110 }
111+ ro_front = flexarray_make(16, 1);
112+ if (!ro_front) {
113+ rc = ERROR_NOMEM;
114+ goto out;
115+ }
116 back = flexarray_make(16, 1);
117 if (!back) {
118 rc = ERROR_NOMEM;
119@@ -2871,21 +2878,24 @@ int libxl__device_console_add(libxl__gc *gc, uint32_t domid,
120
121 flexarray_append(front, "backend-id");
122 flexarray_append(front, libxl__sprintf(gc, "%d", console->backend_domid));
123- flexarray_append(front, "limit");
124- flexarray_append(front, libxl__sprintf(gc, "%d", LIBXL_XENCONSOLE_LIMIT));
125- flexarray_append(front, "type");
126+
127+ flexarray_append(ro_front, "limit");
128+ flexarray_append(ro_front, libxl__sprintf(gc, "%d", LIBXL_XENCONSOLE_LIMIT));
129+ flexarray_append(ro_front, "type");
130 if (console->consback == LIBXL__CONSOLE_BACKEND_XENCONSOLED)
131- flexarray_append(front, "xenconsoled");
132+ flexarray_append(ro_front, "xenconsoled");
133 else
134- flexarray_append(front, "ioemu");
135- flexarray_append(front, "output");
136- flexarray_append(front, console->output);
137+ flexarray_append(ro_front, "ioemu");
138+ flexarray_append(ro_front, "output");
139+ flexarray_append(ro_front, console->output);
140+ flexarray_append(ro_front, "tty");
141+ flexarray_append(ro_front, "");
142
143 if (state) {
144- flexarray_append(front, "port");
145- flexarray_append(front, libxl__sprintf(gc, "%"PRIu32, state->console_port));
146- flexarray_append(front, "ring-ref");
147- flexarray_append(front, libxl__sprintf(gc, "%lu", state->console_mfn));
148+ flexarray_append(ro_front, "port");
149+ flexarray_append(ro_front, libxl__sprintf(gc, "%"PRIu32, state->console_port));
150+ flexarray_append(ro_front, "ring-ref");
151+ flexarray_append(ro_front, libxl__sprintf(gc, "%lu", state->console_mfn));
152 } else {
153 flexarray_append(front, "state");
154 flexarray_append(front, libxl__sprintf(gc, "%d", 1));
155@@ -2894,11 +2904,13 @@ int libxl__device_console_add(libxl__gc *gc, uint32_t domid,
156 }
157
158 libxl__device_generic_add(gc, XBT_NULL, &device,
159- libxl__xs_kvs_of_flexarray(gc, back, back->count),
160- libxl__xs_kvs_of_flexarray(gc, front, front->count));
161+ libxl__xs_kvs_of_flexarray(gc, back, back->count),
162+ libxl__xs_kvs_of_flexarray(gc, front, front->count),
163+ libxl__xs_kvs_of_flexarray(gc, ro_front, ro_front->count));
164 rc = 0;
165 out_free:
166 flexarray_free(back);
167+ flexarray_free(ro_front);
168 flexarray_free(front);
169 out:
170 return rc;
171@@ -2982,8 +2994,9 @@ int libxl__device_vkb_add(libxl__gc *gc, uint32_t domid,
172 flexarray_append(front, libxl__sprintf(gc, "%d", 1));
173
174 libxl__device_generic_add(gc, XBT_NULL, &device,
175- libxl__xs_kvs_of_flexarray(gc, back, back->count),
176- libxl__xs_kvs_of_flexarray(gc, front, front->count));
177+ libxl__xs_kvs_of_flexarray(gc, back, back->count),
178+ libxl__xs_kvs_of_flexarray(gc, front, front->count),
179+ NULL);
180 rc = 0;
181 out_free:
182 flexarray_free(back);
183@@ -3096,8 +3109,9 @@ int libxl__device_vfb_add(libxl__gc *gc, uint32_t domid, libxl_device_vfb *vfb)
184 flexarray_append_pair(front, "state", libxl__sprintf(gc, "%d", 1));
185
186 libxl__device_generic_add(gc, XBT_NULL, &device,
187- libxl__xs_kvs_of_flexarray(gc, back, back->count),
188- libxl__xs_kvs_of_flexarray(gc, front, front->count));
189+ libxl__xs_kvs_of_flexarray(gc, back, back->count),
190+ libxl__xs_kvs_of_flexarray(gc, front, front->count),
191+ NULL);
192 rc = 0;
193 out_free:
194 flexarray_free(front);
195diff --git a/tools/libxl/libxl_device.c b/tools/libxl/libxl_device.c
196index c3283f1..1c04a21 100644
197--- a/tools/libxl/libxl_device.c
198+++ b/tools/libxl/libxl_device.c
199@@ -84,11 +84,12 @@ out:
200 }
201
202 int libxl__device_generic_add(libxl__gc *gc, xs_transaction_t t,
203- libxl__device *device, char **bents, char **fents)
204+ libxl__device *device, char **bents, char **fents, char **ro_fents)
205 {
206 libxl_ctx *ctx = libxl__gc_owner(gc);
207 char *frontend_path, *backend_path;
208 struct xs_permissions frontend_perms[2];
209+ struct xs_permissions ro_frontend_perms[2];
210 struct xs_permissions backend_perms[2];
211 int create_transaction = t == XBT_NULL;
212
213@@ -100,22 +101,37 @@ int libxl__device_generic_add(libxl__gc *gc, xs_transaction_t t,
214 frontend_perms[1].id = device->backend_domid;
215 frontend_perms[1].perms = XS_PERM_READ;
216
217- backend_perms[0].id = device->backend_domid;
218- backend_perms[0].perms = XS_PERM_NONE;
219- backend_perms[1].id = device->domid;
220- backend_perms[1].perms = XS_PERM_READ;
221+ ro_frontend_perms[0].id = backend_perms[0].id = device->backend_domid;
222+ ro_frontend_perms[0].perms = backend_perms[0].perms = XS_PERM_NONE;
223+ ro_frontend_perms[1].id = backend_perms[1].id = device->domid;
224+ ro_frontend_perms[1].perms = backend_perms[1].perms = XS_PERM_READ;
225
226 retry_transaction:
227 if (create_transaction)
228 t = xs_transaction_start(ctx->xsh);
229 /* FIXME: read frontend_path and check state before removing stuff */
230
231- if (fents) {
232+ if (fents || ro_fents) {
233 xs_rm(ctx->xsh, t, frontend_path);
234 xs_mkdir(ctx->xsh, t, frontend_path);
235- xs_set_permissions(ctx->xsh, t, frontend_path, frontend_perms, ARRAY_SIZE(frontend_perms));
236+ /* Console 0 is a special case. It doesn't use the regular PV
237+ * state machine but also the frontend directory has
238+ * historically contained other information, such as the
239+ * vnc-port, which we don't want the guest fiddling with.
240+ */
241+ if (device->kind == LIBXL__DEVICE_KIND_CONSOLE && device->devid == 0)
242+ xs_set_permissions(ctx->xsh, t, frontend_path,
243+ ro_frontend_perms, ARRAY_SIZE(ro_frontend_perms));
244+ else
245+ xs_set_permissions(ctx->xsh, t, frontend_path,
246+ frontend_perms, ARRAY_SIZE(frontend_perms));
247 xs_write(ctx->xsh, t, libxl__sprintf(gc, "%s/backend", frontend_path), backend_path, strlen(backend_path));
248- libxl__xs_writev(gc, t, frontend_path, fents);
249+ if (fents)
250+ libxl__xs_writev_perms(gc, t, frontend_path, fents,
251+ frontend_perms, ARRAY_SIZE(frontend_perms));
252+ if (ro_fents)
253+ libxl__xs_writev_perms(gc, t, frontend_path, ro_fents,
254+ ro_frontend_perms, ARRAY_SIZE(ro_frontend_perms));
255 }
256
257 if (bents) {
258diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
259index 13fa509..ae96a74 100644
260--- a/tools/libxl/libxl_internal.h
261+++ b/tools/libxl/libxl_internal.h
262@@ -516,6 +516,11 @@ _hidden char **libxl__xs_kvs_of_flexarray(libxl__gc *gc, flexarray_t *array, int
263 /* treats kvs as pairs of keys and values and writes each to dir. */
264 _hidden int libxl__xs_writev(libxl__gc *gc, xs_transaction_t t,
265 const char *dir, char **kvs);
266+/* as writev but also sets the permissions on each path */
267+_hidden int libxl__xs_writev_perms(libxl__gc *gc, xs_transaction_t t,
268+ const char *dir, char *kvs[],
269+ struct xs_permissions *perms,
270+ unsigned int num_perms);
271 /* _atonce creates a transaction and writes all keys at once */
272 _hidden int libxl__xs_writev_atonce(libxl__gc *gc,
273 const char *dir, char **kvs);
274@@ -930,7 +935,7 @@ _hidden int libxl__device_console_add(libxl__gc *gc, uint32_t domid,
275 libxl__domain_build_state *state);
276
277 _hidden int libxl__device_generic_add(libxl__gc *gc, xs_transaction_t t,
278- libxl__device *device, char **bents, char **fents);
279+ libxl__device *device, char **bents, char **fents, char **ro_fents);
280 _hidden char *libxl__device_backend_path(libxl__gc *gc, libxl__device *device);
281 _hidden char *libxl__device_frontend_path(libxl__gc *gc, libxl__device *device);
282 _hidden int libxl__parse_backend_path(libxl__gc *gc, const char *path,
283diff --git a/tools/libxl/libxl_pci.c b/tools/libxl/libxl_pci.c
284index 48986f3..d373b4d 100644
285--- a/tools/libxl/libxl_pci.c
286+++ b/tools/libxl/libxl_pci.c
287@@ -106,7 +106,8 @@ int libxl__create_pci_backend(libxl__gc *gc, uint32_t domid,
288
289 libxl__device_generic_add(gc, XBT_NULL, &device,
290 libxl__xs_kvs_of_flexarray(gc, back, back->count),
291- libxl__xs_kvs_of_flexarray(gc, front, front->count));
292+ libxl__xs_kvs_of_flexarray(gc, front, front->count),
293+ NULL);
294
295 out:
296 if (back)
297diff --git a/tools/libxl/libxl_xshelp.c b/tools/libxl/libxl_xshelp.c
298index 52af484..d7eaa66 100644
299--- a/tools/libxl/libxl_xshelp.c
300+++ b/tools/libxl/libxl_xshelp.c
301@@ -41,8 +41,10 @@ char **libxl__xs_kvs_of_flexarray(libxl__gc *gc, flexarray_t *array, int length)
302 return kvs;
303 }
304
305-int libxl__xs_writev(libxl__gc *gc, xs_transaction_t t,
306- const char *dir, char *kvs[])
307+int libxl__xs_writev_perms(libxl__gc *gc, xs_transaction_t t,
308+ const char *dir, char *kvs[],
309+ struct xs_permissions *perms,
310+ unsigned int num_perms)
311 {
312 libxl_ctx *ctx = libxl__gc_owner(gc);
313 char *path;
314@@ -56,11 +58,19 @@ int libxl__xs_writev(libxl__gc *gc, xs_transaction_t t,
315 if (path && kvs[i + 1]) {
316 int length = strlen(kvs[i + 1]);
317 xs_write(ctx->xsh, t, path, kvs[i + 1], length);
318+ if (perms)
319+ xs_set_permissions(ctx->xsh, t, path, perms, num_perms);
320 }
321 }
322 return 0;
323 }
324
325+int libxl__xs_writev(libxl__gc *gc, xs_transaction_t t,
326+ const char *dir, char *kvs[])
327+{
328+ return libxl__xs_writev_perms(gc, t, dir, kvs, NULL, 0);
329+}
330+
331 int libxl__xs_writev_atonce(libxl__gc *gc,
332 const char *dir, char *kvs[])
333 {