]>
Commit | Line | Data |
---|---|---|
684f9120 CS |
1 | /* |
2 | * 9P network client for VirtIO 9P test cases (based on QTest) | |
3 | * | |
4 | * Copyright (c) 2014 SUSE LINUX Products GmbH | |
5 | * | |
6 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
7 | * See the COPYING file in the top-level directory. | |
8 | */ | |
9 | ||
10 | /* | |
11 | * Not so fast! You might want to read the 9p developer docs first: | |
12 | * https://wiki.qemu.org/Documentation/9p | |
13 | */ | |
14 | ||
15 | #include "qemu/osdep.h" | |
16 | #include "virtio-9p-client.h" | |
17 | ||
18 | #define QVIRTIO_9P_TIMEOUT_US (10 * 1000 * 1000) | |
19 | static QGuestAllocator *alloc; | |
20 | ||
21 | void v9fs_set_allocator(QGuestAllocator *t_alloc) | |
22 | { | |
23 | alloc = t_alloc; | |
24 | } | |
25 | ||
569f3b63 CS |
26 | /* |
27 | * Used to auto generate new fids. Start with arbitrary high value to avoid | |
28 | * collision with hard coded fids in basic test code. | |
29 | */ | |
30 | static uint32_t fid_generator = 1000; | |
31 | ||
32 | static uint32_t genfid(void) | |
33 | { | |
34 | return fid_generator++; | |
35 | } | |
36 | ||
37 | /** | |
38 | * Splits the @a in string by @a delim into individual (non empty) strings | |
39 | * and outputs them to @a out. The output array @a out is NULL terminated. | |
40 | * | |
41 | * Output array @a out must be freed by calling split_free(). | |
42 | * | |
43 | * @returns number of individual elements in output array @a out (without the | |
44 | * final NULL terminating element) | |
45 | */ | |
46 | static int split(const char *in, const char *delim, char ***out) | |
47 | { | |
48 | int n = 0, i = 0; | |
49 | char *tmp, *p; | |
50 | ||
51 | tmp = g_strdup(in); | |
52 | for (p = strtok(tmp, delim); p != NULL; p = strtok(NULL, delim)) { | |
53 | if (strlen(p) > 0) { | |
54 | ++n; | |
55 | } | |
56 | } | |
57 | g_free(tmp); | |
58 | ||
59 | *out = g_new0(char *, n + 1); /* last element NULL delimiter */ | |
60 | ||
61 | tmp = g_strdup(in); | |
62 | for (p = strtok(tmp, delim); p != NULL; p = strtok(NULL, delim)) { | |
63 | if (strlen(p) > 0) { | |
64 | (*out)[i++] = g_strdup(p); | |
65 | } | |
66 | } | |
67 | g_free(tmp); | |
68 | ||
69 | return n; | |
70 | } | |
71 | ||
72 | static void split_free(char ***out) | |
73 | { | |
74 | int i; | |
75 | if (!*out) { | |
76 | return; | |
77 | } | |
78 | for (i = 0; (*out)[i]; ++i) { | |
79 | g_free((*out)[i]); | |
80 | } | |
81 | g_free(*out); | |
82 | *out = NULL; | |
83 | } | |
84 | ||
684f9120 CS |
85 | void v9fs_memwrite(P9Req *req, const void *addr, size_t len) |
86 | { | |
87 | qtest_memwrite(req->qts, req->t_msg + req->t_off, addr, len); | |
88 | req->t_off += len; | |
89 | } | |
90 | ||
91 | void v9fs_memskip(P9Req *req, size_t len) | |
92 | { | |
93 | req->r_off += len; | |
94 | } | |
95 | ||
96 | void v9fs_memread(P9Req *req, void *addr, size_t len) | |
97 | { | |
98 | qtest_memread(req->qts, req->r_msg + req->r_off, addr, len); | |
99 | req->r_off += len; | |
100 | } | |
101 | ||
102 | void v9fs_uint8_read(P9Req *req, uint8_t *val) | |
103 | { | |
104 | v9fs_memread(req, val, 1); | |
105 | } | |
106 | ||
107 | void v9fs_uint16_write(P9Req *req, uint16_t val) | |
108 | { | |
109 | uint16_t le_val = cpu_to_le16(val); | |
110 | ||
111 | v9fs_memwrite(req, &le_val, 2); | |
112 | } | |
113 | ||
114 | void v9fs_uint16_read(P9Req *req, uint16_t *val) | |
115 | { | |
116 | v9fs_memread(req, val, 2); | |
117 | le16_to_cpus(val); | |
118 | } | |
119 | ||
120 | void v9fs_uint32_write(P9Req *req, uint32_t val) | |
121 | { | |
122 | uint32_t le_val = cpu_to_le32(val); | |
123 | ||
124 | v9fs_memwrite(req, &le_val, 4); | |
125 | } | |
126 | ||
127 | void v9fs_uint64_write(P9Req *req, uint64_t val) | |
128 | { | |
129 | uint64_t le_val = cpu_to_le64(val); | |
130 | ||
131 | v9fs_memwrite(req, &le_val, 8); | |
132 | } | |
133 | ||
134 | void v9fs_uint32_read(P9Req *req, uint32_t *val) | |
135 | { | |
136 | v9fs_memread(req, val, 4); | |
137 | le32_to_cpus(val); | |
138 | } | |
139 | ||
140 | void v9fs_uint64_read(P9Req *req, uint64_t *val) | |
141 | { | |
142 | v9fs_memread(req, val, 8); | |
143 | le64_to_cpus(val); | |
144 | } | |
145 | ||
146 | /* len[2] string[len] */ | |
147 | uint16_t v9fs_string_size(const char *string) | |
148 | { | |
149 | size_t len = strlen(string); | |
150 | ||
151 | g_assert_cmpint(len, <=, UINT16_MAX - 2); | |
152 | ||
153 | return 2 + len; | |
154 | } | |
155 | ||
156 | void v9fs_string_write(P9Req *req, const char *string) | |
157 | { | |
158 | int len = strlen(string); | |
159 | ||
160 | g_assert_cmpint(len, <=, UINT16_MAX); | |
161 | ||
162 | v9fs_uint16_write(req, (uint16_t) len); | |
163 | v9fs_memwrite(req, string, len); | |
164 | } | |
165 | ||
166 | void v9fs_string_read(P9Req *req, uint16_t *len, char **string) | |
167 | { | |
168 | uint16_t local_len; | |
169 | ||
170 | v9fs_uint16_read(req, &local_len); | |
171 | if (len) { | |
172 | *len = local_len; | |
173 | } | |
174 | if (string) { | |
175 | *string = g_malloc(local_len + 1); | |
176 | v9fs_memread(req, *string, local_len); | |
177 | (*string)[local_len] = 0; | |
178 | } else { | |
179 | v9fs_memskip(req, local_len); | |
180 | } | |
181 | } | |
182 | ||
183 | typedef struct { | |
184 | uint32_t size; | |
185 | uint8_t id; | |
186 | uint16_t tag; | |
187 | } QEMU_PACKED P9Hdr; | |
188 | ||
189 | P9Req *v9fs_req_init(QVirtio9P *v9p, uint32_t size, uint8_t id, | |
190 | uint16_t tag) | |
191 | { | |
192 | P9Req *req = g_new0(P9Req, 1); | |
193 | uint32_t total_size = 7; /* 9P header has well-known size of 7 bytes */ | |
194 | P9Hdr hdr = { | |
195 | .id = id, | |
196 | .tag = cpu_to_le16(tag) | |
197 | }; | |
198 | ||
199 | g_assert_cmpint(total_size, <=, UINT32_MAX - size); | |
200 | total_size += size; | |
201 | hdr.size = cpu_to_le32(total_size); | |
202 | ||
203 | g_assert_cmpint(total_size, <=, P9_MAX_SIZE); | |
204 | ||
205 | req->qts = global_qtest; | |
206 | req->v9p = v9p; | |
207 | req->t_size = total_size; | |
208 | req->t_msg = guest_alloc(alloc, req->t_size); | |
209 | v9fs_memwrite(req, &hdr, 7); | |
210 | req->tag = tag; | |
211 | return req; | |
212 | } | |
213 | ||
214 | void v9fs_req_send(P9Req *req) | |
215 | { | |
216 | QVirtio9P *v9p = req->v9p; | |
217 | ||
218 | req->r_msg = guest_alloc(alloc, P9_MAX_SIZE); | |
219 | req->free_head = qvirtqueue_add(req->qts, v9p->vq, req->t_msg, req->t_size, | |
220 | false, true); | |
221 | qvirtqueue_add(req->qts, v9p->vq, req->r_msg, P9_MAX_SIZE, true, false); | |
222 | qvirtqueue_kick(req->qts, v9p->vdev, v9p->vq, req->free_head); | |
223 | req->t_off = 0; | |
224 | } | |
225 | ||
226 | static const char *rmessage_name(uint8_t id) | |
227 | { | |
228 | return | |
229 | id == P9_RLERROR ? "RLERROR" : | |
230 | id == P9_RVERSION ? "RVERSION" : | |
231 | id == P9_RATTACH ? "RATTACH" : | |
232 | id == P9_RWALK ? "RWALK" : | |
233 | id == P9_RLOPEN ? "RLOPEN" : | |
234 | id == P9_RWRITE ? "RWRITE" : | |
235 | id == P9_RMKDIR ? "RMKDIR" : | |
236 | id == P9_RLCREATE ? "RLCREATE" : | |
237 | id == P9_RSYMLINK ? "RSYMLINK" : | |
238 | id == P9_RLINK ? "RLINK" : | |
239 | id == P9_RUNLINKAT ? "RUNLINKAT" : | |
240 | id == P9_RFLUSH ? "RFLUSH" : | |
241 | id == P9_RREADDIR ? "READDIR" : | |
242 | "<unknown>"; | |
243 | } | |
244 | ||
245 | void v9fs_req_wait_for_reply(P9Req *req, uint32_t *len) | |
246 | { | |
247 | QVirtio9P *v9p = req->v9p; | |
248 | ||
249 | qvirtio_wait_used_elem(req->qts, v9p->vdev, v9p->vq, req->free_head, len, | |
250 | QVIRTIO_9P_TIMEOUT_US); | |
251 | } | |
252 | ||
253 | void v9fs_req_recv(P9Req *req, uint8_t id) | |
254 | { | |
255 | P9Hdr hdr; | |
256 | ||
257 | v9fs_memread(req, &hdr, 7); | |
258 | hdr.size = ldl_le_p(&hdr.size); | |
259 | hdr.tag = lduw_le_p(&hdr.tag); | |
260 | ||
261 | g_assert_cmpint(hdr.size, >=, 7); | |
262 | g_assert_cmpint(hdr.size, <=, P9_MAX_SIZE); | |
263 | g_assert_cmpint(hdr.tag, ==, req->tag); | |
264 | ||
265 | if (hdr.id != id) { | |
266 | g_printerr("Received response %d (%s) instead of %d (%s)\n", | |
267 | hdr.id, rmessage_name(hdr.id), id, rmessage_name(id)); | |
268 | ||
269 | if (hdr.id == P9_RLERROR) { | |
270 | uint32_t err; | |
271 | v9fs_uint32_read(req, &err); | |
272 | g_printerr("Rlerror has errno %d (%s)\n", err, strerror(err)); | |
273 | } | |
274 | } | |
275 | g_assert_cmpint(hdr.id, ==, id); | |
276 | } | |
277 | ||
278 | void v9fs_req_free(P9Req *req) | |
279 | { | |
280 | guest_free(alloc, req->t_msg); | |
281 | guest_free(alloc, req->r_msg); | |
282 | g_free(req); | |
283 | } | |
284 | ||
285 | /* size[4] Rlerror tag[2] ecode[4] */ | |
286 | void v9fs_rlerror(P9Req *req, uint32_t *err) | |
287 | { | |
288 | v9fs_req_recv(req, P9_RLERROR); | |
289 | v9fs_uint32_read(req, err); | |
290 | v9fs_req_free(req); | |
291 | } | |
292 | ||
293 | /* size[4] Tversion tag[2] msize[4] version[s] */ | |
bee8fda2 | 294 | TVersionRes v9fs_tversion(TVersionOpt opt) |
684f9120 CS |
295 | { |
296 | P9Req *req; | |
bee8fda2 | 297 | uint32_t err; |
684f9120 | 298 | uint32_t body_size = 4; |
bee8fda2 CS |
299 | uint16_t string_size; |
300 | uint16_t server_len; | |
301 | g_autofree char *server_version = NULL; | |
302 | ||
303 | g_assert(opt.client); | |
304 | ||
305 | if (!opt.msize) { | |
306 | opt.msize = P9_MAX_SIZE; | |
307 | } | |
308 | ||
309 | if (!opt.tag) { | |
310 | opt.tag = P9_NOTAG; | |
311 | } | |
312 | ||
313 | if (!opt.version) { | |
314 | opt.version = "9P2000.L"; | |
315 | } | |
684f9120 | 316 | |
bee8fda2 | 317 | string_size = v9fs_string_size(opt.version); |
684f9120 CS |
318 | g_assert_cmpint(body_size, <=, UINT32_MAX - string_size); |
319 | body_size += string_size; | |
bee8fda2 | 320 | req = v9fs_req_init(opt.client, body_size, P9_TVERSION, opt.tag); |
684f9120 | 321 | |
bee8fda2 CS |
322 | v9fs_uint32_write(req, opt.msize); |
323 | v9fs_string_write(req, opt.version); | |
684f9120 | 324 | v9fs_req_send(req); |
bee8fda2 CS |
325 | |
326 | if (!opt.requestOnly) { | |
327 | v9fs_req_wait_for_reply(req, NULL); | |
328 | if (opt.expectErr) { | |
329 | v9fs_rlerror(req, &err); | |
330 | g_assert_cmpint(err, ==, opt.expectErr); | |
331 | } else { | |
332 | v9fs_rversion(req, &server_len, &server_version); | |
333 | g_assert_cmpmem(server_version, server_len, | |
334 | opt.version, strlen(opt.version)); | |
335 | } | |
336 | req = NULL; /* request was freed */ | |
337 | } | |
338 | ||
339 | return (TVersionRes) { | |
340 | .req = req, | |
341 | }; | |
684f9120 CS |
342 | } |
343 | ||
344 | /* size[4] Rversion tag[2] msize[4] version[s] */ | |
345 | void v9fs_rversion(P9Req *req, uint16_t *len, char **version) | |
346 | { | |
347 | uint32_t msize; | |
348 | ||
349 | v9fs_req_recv(req, P9_RVERSION); | |
350 | v9fs_uint32_read(req, &msize); | |
351 | ||
352 | g_assert_cmpint(msize, ==, P9_MAX_SIZE); | |
353 | ||
354 | if (len || version) { | |
355 | v9fs_string_read(req, len, version); | |
356 | } | |
357 | ||
358 | v9fs_req_free(req); | |
359 | } | |
360 | ||
361 | /* size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s] n_uname[4] */ | |
74a160ab | 362 | TAttachRes v9fs_tattach(TAttachOpt opt) |
684f9120 | 363 | { |
74a160ab | 364 | uint32_t err; |
684f9120 CS |
365 | const char *uname = ""; /* ignored by QEMU */ |
366 | const char *aname = ""; /* ignored by QEMU */ | |
684f9120 | 367 | |
74a160ab CS |
368 | g_assert(opt.client); |
369 | /* expecting either Rattach or Rlerror, but obviously not both */ | |
370 | g_assert(!opt.expectErr || !opt.rattach.qid); | |
371 | ||
372 | if (!opt.requestOnly) { | |
373 | v9fs_tversion((TVersionOpt) { .client = opt.client }); | |
374 | } | |
375 | ||
376 | if (!opt.n_uname) { | |
377 | opt.n_uname = getuid(); | |
378 | } | |
379 | ||
380 | P9Req *req = v9fs_req_init(opt.client, 4 + 4 + 2 + 2 + 4, P9_TATTACH, | |
381 | opt.tag); | |
382 | ||
383 | v9fs_uint32_write(req, opt.fid); | |
684f9120 CS |
384 | v9fs_uint32_write(req, P9_NOFID); |
385 | v9fs_string_write(req, uname); | |
386 | v9fs_string_write(req, aname); | |
74a160ab | 387 | v9fs_uint32_write(req, opt.n_uname); |
684f9120 | 388 | v9fs_req_send(req); |
74a160ab CS |
389 | |
390 | if (!opt.requestOnly) { | |
391 | v9fs_req_wait_for_reply(req, NULL); | |
392 | if (opt.expectErr) { | |
393 | v9fs_rlerror(req, &err); | |
394 | g_assert_cmpint(err, ==, opt.expectErr); | |
395 | } else { | |
396 | v9fs_rattach(req, opt.rattach.qid); | |
397 | } | |
398 | req = NULL; /* request was freed */ | |
399 | } | |
400 | ||
401 | return (TAttachRes) { | |
402 | .req = req, | |
403 | }; | |
684f9120 CS |
404 | } |
405 | ||
406 | /* size[4] Rattach tag[2] qid[13] */ | |
407 | void v9fs_rattach(P9Req *req, v9fs_qid *qid) | |
408 | { | |
409 | v9fs_req_recv(req, P9_RATTACH); | |
410 | if (qid) { | |
411 | v9fs_memread(req, qid, 13); | |
412 | } | |
413 | v9fs_req_free(req); | |
414 | } | |
415 | ||
416 | /* size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s]) */ | |
569f3b63 | 417 | TWalkRes v9fs_twalk(TWalkOpt opt) |
684f9120 CS |
418 | { |
419 | P9Req *req; | |
420 | int i; | |
421 | uint32_t body_size = 4 + 4 + 2; | |
569f3b63 CS |
422 | uint32_t err; |
423 | char **wnames = NULL; | |
424 | ||
425 | g_assert(opt.client); | |
426 | /* expecting either high- or low-level path, both not both */ | |
427 | g_assert(!opt.path || !(opt.nwname || opt.wnames)); | |
428 | /* expecting either Rwalk or Rlerror, but obviously not both */ | |
429 | g_assert(!opt.expectErr || !(opt.rwalk.nwqid || opt.rwalk.wqid)); | |
684f9120 | 430 | |
569f3b63 CS |
431 | if (!opt.newfid) { |
432 | opt.newfid = genfid(); | |
433 | } | |
434 | ||
435 | if (opt.path) { | |
436 | opt.nwname = split(opt.path, "/", &wnames); | |
437 | opt.wnames = wnames; | |
438 | } | |
439 | ||
440 | for (i = 0; i < opt.nwname; i++) { | |
441 | uint16_t wname_size = v9fs_string_size(opt.wnames[i]); | |
684f9120 CS |
442 | |
443 | g_assert_cmpint(body_size, <=, UINT32_MAX - wname_size); | |
444 | body_size += wname_size; | |
445 | } | |
569f3b63 CS |
446 | req = v9fs_req_init(opt.client, body_size, P9_TWALK, opt.tag); |
447 | v9fs_uint32_write(req, opt.fid); | |
448 | v9fs_uint32_write(req, opt.newfid); | |
449 | v9fs_uint16_write(req, opt.nwname); | |
450 | for (i = 0; i < opt.nwname; i++) { | |
451 | v9fs_string_write(req, opt.wnames[i]); | |
684f9120 CS |
452 | } |
453 | v9fs_req_send(req); | |
569f3b63 CS |
454 | |
455 | if (!opt.requestOnly) { | |
456 | v9fs_req_wait_for_reply(req, NULL); | |
457 | if (opt.expectErr) { | |
458 | v9fs_rlerror(req, &err); | |
459 | g_assert_cmpint(err, ==, opt.expectErr); | |
460 | } else { | |
461 | v9fs_rwalk(req, opt.rwalk.nwqid, opt.rwalk.wqid); | |
462 | } | |
463 | req = NULL; /* request was freed */ | |
464 | } | |
465 | ||
466 | split_free(&wnames); | |
467 | ||
468 | return (TWalkRes) { | |
469 | .newfid = opt.newfid, | |
470 | .req = req, | |
471 | }; | |
684f9120 CS |
472 | } |
473 | ||
474 | /* size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13]) */ | |
475 | void v9fs_rwalk(P9Req *req, uint16_t *nwqid, v9fs_qid **wqid) | |
476 | { | |
477 | uint16_t local_nwqid; | |
478 | ||
479 | v9fs_req_recv(req, P9_RWALK); | |
480 | v9fs_uint16_read(req, &local_nwqid); | |
481 | if (nwqid) { | |
482 | *nwqid = local_nwqid; | |
483 | } | |
484 | if (wqid) { | |
485 | *wqid = g_malloc(local_nwqid * 13); | |
486 | v9fs_memread(req, *wqid, local_nwqid * 13); | |
487 | } | |
488 | v9fs_req_free(req); | |
489 | } | |
490 | ||
491 | /* size[4] Tgetattr tag[2] fid[4] request_mask[8] */ | |
2af5be47 | 492 | TGetAttrRes v9fs_tgetattr(TGetAttrOpt opt) |
684f9120 CS |
493 | { |
494 | P9Req *req; | |
2af5be47 CS |
495 | uint32_t err; |
496 | ||
497 | g_assert(opt.client); | |
498 | /* expecting either Rgetattr or Rlerror, but obviously not both */ | |
499 | g_assert(!opt.expectErr || !opt.rgetattr.attr); | |
500 | ||
501 | if (!opt.request_mask) { | |
502 | opt.request_mask = P9_GETATTR_ALL; | |
503 | } | |
684f9120 | 504 | |
2af5be47 CS |
505 | req = v9fs_req_init(opt.client, 4 + 8, P9_TGETATTR, opt.tag); |
506 | v9fs_uint32_write(req, opt.fid); | |
507 | v9fs_uint64_write(req, opt.request_mask); | |
684f9120 | 508 | v9fs_req_send(req); |
2af5be47 CS |
509 | |
510 | if (!opt.requestOnly) { | |
511 | v9fs_req_wait_for_reply(req, NULL); | |
512 | if (opt.expectErr) { | |
513 | v9fs_rlerror(req, &err); | |
514 | g_assert_cmpint(err, ==, opt.expectErr); | |
515 | } else { | |
516 | v9fs_rgetattr(req, opt.rgetattr.attr); | |
517 | } | |
518 | req = NULL; /* request was freed */ | |
519 | } | |
520 | ||
521 | return (TGetAttrRes) { .req = req }; | |
684f9120 CS |
522 | } |
523 | ||
524 | /* | |
525 | * size[4] Rgetattr tag[2] valid[8] qid[13] mode[4] uid[4] gid[4] nlink[8] | |
526 | * rdev[8] size[8] blksize[8] blocks[8] | |
527 | * atime_sec[8] atime_nsec[8] mtime_sec[8] mtime_nsec[8] | |
528 | * ctime_sec[8] ctime_nsec[8] btime_sec[8] btime_nsec[8] | |
529 | * gen[8] data_version[8] | |
530 | */ | |
531 | void v9fs_rgetattr(P9Req *req, v9fs_attr *attr) | |
532 | { | |
533 | v9fs_req_recv(req, P9_RGETATTR); | |
534 | ||
535 | v9fs_uint64_read(req, &attr->valid); | |
536 | v9fs_memread(req, &attr->qid, 13); | |
537 | v9fs_uint32_read(req, &attr->mode); | |
538 | v9fs_uint32_read(req, &attr->uid); | |
539 | v9fs_uint32_read(req, &attr->gid); | |
540 | v9fs_uint64_read(req, &attr->nlink); | |
541 | v9fs_uint64_read(req, &attr->rdev); | |
542 | v9fs_uint64_read(req, &attr->size); | |
543 | v9fs_uint64_read(req, &attr->blksize); | |
544 | v9fs_uint64_read(req, &attr->blocks); | |
545 | v9fs_uint64_read(req, &attr->atime_sec); | |
546 | v9fs_uint64_read(req, &attr->atime_nsec); | |
547 | v9fs_uint64_read(req, &attr->mtime_sec); | |
548 | v9fs_uint64_read(req, &attr->mtime_nsec); | |
549 | v9fs_uint64_read(req, &attr->ctime_sec); | |
550 | v9fs_uint64_read(req, &attr->ctime_nsec); | |
551 | v9fs_uint64_read(req, &attr->btime_sec); | |
552 | v9fs_uint64_read(req, &attr->btime_nsec); | |
553 | v9fs_uint64_read(req, &attr->gen); | |
554 | v9fs_uint64_read(req, &attr->data_version); | |
555 | ||
556 | v9fs_req_free(req); | |
557 | } | |
558 | ||
559 | /* size[4] Treaddir tag[2] fid[4] offset[8] count[4] */ | |
1ebacc40 | 560 | TReadDirRes v9fs_treaddir(TReadDirOpt opt) |
684f9120 CS |
561 | { |
562 | P9Req *req; | |
1ebacc40 CS |
563 | uint32_t err; |
564 | ||
565 | g_assert(opt.client); | |
566 | /* expecting either Rreaddir or Rlerror, but obviously not both */ | |
567 | g_assert(!opt.expectErr || !(opt.rreaddir.count || | |
568 | opt.rreaddir.nentries || opt.rreaddir.entries)); | |
684f9120 | 569 | |
1ebacc40 CS |
570 | req = v9fs_req_init(opt.client, 4 + 8 + 4, P9_TREADDIR, opt.tag); |
571 | v9fs_uint32_write(req, opt.fid); | |
572 | v9fs_uint64_write(req, opt.offset); | |
573 | v9fs_uint32_write(req, opt.count); | |
684f9120 | 574 | v9fs_req_send(req); |
1ebacc40 CS |
575 | |
576 | if (!opt.requestOnly) { | |
577 | v9fs_req_wait_for_reply(req, NULL); | |
578 | if (opt.expectErr) { | |
579 | v9fs_rlerror(req, &err); | |
580 | g_assert_cmpint(err, ==, opt.expectErr); | |
581 | } else { | |
582 | v9fs_rreaddir(req, opt.rreaddir.count, opt.rreaddir.nentries, | |
583 | opt.rreaddir.entries); | |
584 | } | |
585 | req = NULL; /* request was freed */ | |
586 | } | |
587 | ||
588 | return (TReadDirRes) { .req = req }; | |
684f9120 CS |
589 | } |
590 | ||
591 | /* size[4] Rreaddir tag[2] count[4] data[count] */ | |
592 | void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t *nentries, | |
593 | struct V9fsDirent **entries) | |
594 | { | |
595 | uint32_t local_count; | |
596 | struct V9fsDirent *e = NULL; | |
597 | uint16_t slen; | |
598 | uint32_t n = 0; | |
599 | ||
600 | v9fs_req_recv(req, P9_RREADDIR); | |
601 | v9fs_uint32_read(req, &local_count); | |
602 | ||
603 | if (count) { | |
604 | *count = local_count; | |
605 | } | |
606 | ||
607 | for (int32_t togo = (int32_t)local_count; | |
608 | togo >= 13 + 8 + 1 + 2; | |
609 | togo -= 13 + 8 + 1 + 2 + slen, ++n) | |
610 | { | |
611 | if (!e) { | |
612 | e = g_new(struct V9fsDirent, 1); | |
613 | if (entries) { | |
614 | *entries = e; | |
615 | } | |
616 | } else { | |
617 | e = e->next = g_new(struct V9fsDirent, 1); | |
618 | } | |
619 | e->next = NULL; | |
620 | /* qid[13] offset[8] type[1] name[s] */ | |
621 | v9fs_memread(req, &e->qid, 13); | |
622 | v9fs_uint64_read(req, &e->offset); | |
623 | v9fs_uint8_read(req, &e->type); | |
624 | v9fs_string_read(req, &slen, &e->name); | |
625 | } | |
626 | ||
627 | if (nentries) { | |
628 | *nentries = n; | |
629 | } | |
630 | ||
631 | v9fs_req_free(req); | |
632 | } | |
633 | ||
634 | void v9fs_free_dirents(struct V9fsDirent *e) | |
635 | { | |
636 | struct V9fsDirent *next = NULL; | |
637 | ||
638 | for (; e; e = next) { | |
639 | next = e->next; | |
640 | g_free(e->name); | |
641 | g_free(e); | |
642 | } | |
643 | } | |
644 | ||
645 | /* size[4] Tlopen tag[2] fid[4] flags[4] */ | |
3878ce4c | 646 | TLOpenRes v9fs_tlopen(TLOpenOpt opt) |
684f9120 CS |
647 | { |
648 | P9Req *req; | |
3878ce4c CS |
649 | uint32_t err; |
650 | ||
651 | g_assert(opt.client); | |
652 | /* expecting either Rlopen or Rlerror, but obviously not both */ | |
653 | g_assert(!opt.expectErr || !(opt.rlopen.qid || opt.rlopen.iounit)); | |
684f9120 | 654 | |
3878ce4c CS |
655 | req = v9fs_req_init(opt.client, 4 + 4, P9_TLOPEN, opt.tag); |
656 | v9fs_uint32_write(req, opt.fid); | |
657 | v9fs_uint32_write(req, opt.flags); | |
684f9120 | 658 | v9fs_req_send(req); |
3878ce4c CS |
659 | |
660 | if (!opt.requestOnly) { | |
661 | v9fs_req_wait_for_reply(req, NULL); | |
662 | if (opt.expectErr) { | |
663 | v9fs_rlerror(req, &err); | |
664 | g_assert_cmpint(err, ==, opt.expectErr); | |
665 | } else { | |
666 | v9fs_rlopen(req, opt.rlopen.qid, opt.rlopen.iounit); | |
667 | } | |
668 | req = NULL; /* request was freed */ | |
669 | } | |
670 | ||
671 | return (TLOpenRes) { .req = req }; | |
684f9120 CS |
672 | } |
673 | ||
674 | /* size[4] Rlopen tag[2] qid[13] iounit[4] */ | |
675 | void v9fs_rlopen(P9Req *req, v9fs_qid *qid, uint32_t *iounit) | |
676 | { | |
677 | v9fs_req_recv(req, P9_RLOPEN); | |
678 | if (qid) { | |
679 | v9fs_memread(req, qid, 13); | |
680 | } else { | |
681 | v9fs_memskip(req, 13); | |
682 | } | |
683 | if (iounit) { | |
684 | v9fs_uint32_read(req, iounit); | |
685 | } | |
686 | v9fs_req_free(req); | |
687 | } | |
688 | ||
689 | /* size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count] */ | |
690 | P9Req *v9fs_twrite(QVirtio9P *v9p, uint32_t fid, uint64_t offset, | |
691 | uint32_t count, const void *data, uint16_t tag) | |
692 | { | |
693 | P9Req *req; | |
694 | uint32_t body_size = 4 + 8 + 4; | |
695 | ||
696 | g_assert_cmpint(body_size, <=, UINT32_MAX - count); | |
697 | body_size += count; | |
698 | req = v9fs_req_init(v9p, body_size, P9_TWRITE, tag); | |
699 | v9fs_uint32_write(req, fid); | |
700 | v9fs_uint64_write(req, offset); | |
701 | v9fs_uint32_write(req, count); | |
702 | v9fs_memwrite(req, data, count); | |
703 | v9fs_req_send(req); | |
704 | return req; | |
705 | } | |
706 | ||
707 | /* size[4] Rwrite tag[2] count[4] */ | |
708 | void v9fs_rwrite(P9Req *req, uint32_t *count) | |
709 | { | |
710 | v9fs_req_recv(req, P9_RWRITE); | |
711 | if (count) { | |
712 | v9fs_uint32_read(req, count); | |
713 | } | |
714 | v9fs_req_free(req); | |
715 | } | |
716 | ||
717 | /* size[4] Tflush tag[2] oldtag[2] */ | |
718 | P9Req *v9fs_tflush(QVirtio9P *v9p, uint16_t oldtag, uint16_t tag) | |
719 | { | |
720 | P9Req *req; | |
721 | ||
722 | req = v9fs_req_init(v9p, 2, P9_TFLUSH, tag); | |
723 | v9fs_uint32_write(req, oldtag); | |
724 | v9fs_req_send(req); | |
725 | return req; | |
726 | } | |
727 | ||
728 | /* size[4] Rflush tag[2] */ | |
729 | void v9fs_rflush(P9Req *req) | |
730 | { | |
731 | v9fs_req_recv(req, P9_RFLUSH); | |
732 | v9fs_req_free(req); | |
733 | } | |
734 | ||
735 | /* size[4] Tmkdir tag[2] dfid[4] name[s] mode[4] gid[4] */ | |
736 | P9Req *v9fs_tmkdir(QVirtio9P *v9p, uint32_t dfid, const char *name, | |
737 | uint32_t mode, uint32_t gid, uint16_t tag) | |
738 | { | |
739 | P9Req *req; | |
740 | ||
741 | uint32_t body_size = 4 + 4 + 4; | |
742 | uint16_t string_size = v9fs_string_size(name); | |
743 | ||
744 | g_assert_cmpint(body_size, <=, UINT32_MAX - string_size); | |
745 | body_size += string_size; | |
746 | ||
747 | req = v9fs_req_init(v9p, body_size, P9_TMKDIR, tag); | |
748 | v9fs_uint32_write(req, dfid); | |
749 | v9fs_string_write(req, name); | |
750 | v9fs_uint32_write(req, mode); | |
751 | v9fs_uint32_write(req, gid); | |
752 | v9fs_req_send(req); | |
753 | return req; | |
754 | } | |
755 | ||
756 | /* size[4] Rmkdir tag[2] qid[13] */ | |
757 | void v9fs_rmkdir(P9Req *req, v9fs_qid *qid) | |
758 | { | |
759 | v9fs_req_recv(req, P9_RMKDIR); | |
760 | if (qid) { | |
761 | v9fs_memread(req, qid, 13); | |
762 | } else { | |
763 | v9fs_memskip(req, 13); | |
764 | } | |
765 | v9fs_req_free(req); | |
766 | } | |
767 | ||
768 | /* size[4] Tlcreate tag[2] fid[4] name[s] flags[4] mode[4] gid[4] */ | |
769 | P9Req *v9fs_tlcreate(QVirtio9P *v9p, uint32_t fid, const char *name, | |
770 | uint32_t flags, uint32_t mode, uint32_t gid, | |
771 | uint16_t tag) | |
772 | { | |
773 | P9Req *req; | |
774 | ||
775 | uint32_t body_size = 4 + 4 + 4 + 4; | |
776 | uint16_t string_size = v9fs_string_size(name); | |
777 | ||
778 | g_assert_cmpint(body_size, <=, UINT32_MAX - string_size); | |
779 | body_size += string_size; | |
780 | ||
781 | req = v9fs_req_init(v9p, body_size, P9_TLCREATE, tag); | |
782 | v9fs_uint32_write(req, fid); | |
783 | v9fs_string_write(req, name); | |
784 | v9fs_uint32_write(req, flags); | |
785 | v9fs_uint32_write(req, mode); | |
786 | v9fs_uint32_write(req, gid); | |
787 | v9fs_req_send(req); | |
788 | return req; | |
789 | } | |
790 | ||
791 | /* size[4] Rlcreate tag[2] qid[13] iounit[4] */ | |
792 | void v9fs_rlcreate(P9Req *req, v9fs_qid *qid, uint32_t *iounit) | |
793 | { | |
794 | v9fs_req_recv(req, P9_RLCREATE); | |
795 | if (qid) { | |
796 | v9fs_memread(req, qid, 13); | |
797 | } else { | |
798 | v9fs_memskip(req, 13); | |
799 | } | |
800 | if (iounit) { | |
801 | v9fs_uint32_read(req, iounit); | |
802 | } | |
803 | v9fs_req_free(req); | |
804 | } | |
805 | ||
806 | /* size[4] Tsymlink tag[2] fid[4] name[s] symtgt[s] gid[4] */ | |
807 | P9Req *v9fs_tsymlink(QVirtio9P *v9p, uint32_t fid, const char *name, | |
808 | const char *symtgt, uint32_t gid, uint16_t tag) | |
809 | { | |
810 | P9Req *req; | |
811 | ||
812 | uint32_t body_size = 4 + 4; | |
813 | uint16_t string_size = v9fs_string_size(name) + v9fs_string_size(symtgt); | |
814 | ||
815 | g_assert_cmpint(body_size, <=, UINT32_MAX - string_size); | |
816 | body_size += string_size; | |
817 | ||
818 | req = v9fs_req_init(v9p, body_size, P9_TSYMLINK, tag); | |
819 | v9fs_uint32_write(req, fid); | |
820 | v9fs_string_write(req, name); | |
821 | v9fs_string_write(req, symtgt); | |
822 | v9fs_uint32_write(req, gid); | |
823 | v9fs_req_send(req); | |
824 | return req; | |
825 | } | |
826 | ||
827 | /* size[4] Rsymlink tag[2] qid[13] */ | |
828 | void v9fs_rsymlink(P9Req *req, v9fs_qid *qid) | |
829 | { | |
830 | v9fs_req_recv(req, P9_RSYMLINK); | |
831 | if (qid) { | |
832 | v9fs_memread(req, qid, 13); | |
833 | } else { | |
834 | v9fs_memskip(req, 13); | |
835 | } | |
836 | v9fs_req_free(req); | |
837 | } | |
838 | ||
839 | /* size[4] Tlink tag[2] dfid[4] fid[4] name[s] */ | |
840 | P9Req *v9fs_tlink(QVirtio9P *v9p, uint32_t dfid, uint32_t fid, | |
841 | const char *name, uint16_t tag) | |
842 | { | |
843 | P9Req *req; | |
844 | ||
845 | uint32_t body_size = 4 + 4; | |
846 | uint16_t string_size = v9fs_string_size(name); | |
847 | ||
848 | g_assert_cmpint(body_size, <=, UINT32_MAX - string_size); | |
849 | body_size += string_size; | |
850 | ||
851 | req = v9fs_req_init(v9p, body_size, P9_TLINK, tag); | |
852 | v9fs_uint32_write(req, dfid); | |
853 | v9fs_uint32_write(req, fid); | |
854 | v9fs_string_write(req, name); | |
855 | v9fs_req_send(req); | |
856 | return req; | |
857 | } | |
858 | ||
859 | /* size[4] Rlink tag[2] */ | |
860 | void v9fs_rlink(P9Req *req) | |
861 | { | |
862 | v9fs_req_recv(req, P9_RLINK); | |
863 | v9fs_req_free(req); | |
864 | } | |
865 | ||
866 | /* size[4] Tunlinkat tag[2] dirfd[4] name[s] flags[4] */ | |
867 | P9Req *v9fs_tunlinkat(QVirtio9P *v9p, uint32_t dirfd, const char *name, | |
868 | uint32_t flags, uint16_t tag) | |
869 | { | |
870 | P9Req *req; | |
871 | ||
872 | uint32_t body_size = 4 + 4; | |
873 | uint16_t string_size = v9fs_string_size(name); | |
874 | ||
875 | g_assert_cmpint(body_size, <=, UINT32_MAX - string_size); | |
876 | body_size += string_size; | |
877 | ||
878 | req = v9fs_req_init(v9p, body_size, P9_TUNLINKAT, tag); | |
879 | v9fs_uint32_write(req, dirfd); | |
880 | v9fs_string_write(req, name); | |
881 | v9fs_uint32_write(req, flags); | |
882 | v9fs_req_send(req); | |
883 | return req; | |
884 | } | |
885 | ||
886 | /* size[4] Runlinkat tag[2] */ | |
887 | void v9fs_runlinkat(P9Req *req) | |
888 | { | |
889 | v9fs_req_recv(req, P9_RUNLINKAT); | |
890 | v9fs_req_free(req); | |
891 | } |