2576e9776c6dc725f1928182366f35991a860fb2
[sdk/emulator/qemu.git] / fsdev / 9p-iov-marshal.c
1 /*
2  * 9p backend
3  *
4  * Copyright IBM, Corp. 2010
5  *
6  * Authors:
7  *  Anthony Liguori   <aliguori@us.ibm.com>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2.  See
10  * the COPYING file in the top-level directory.
11  *
12  */
13
14 #include "qemu/osdep.h"
15 #include <glib/gprintf.h>
16 #include <utime.h>
17
18 #if !defined(CONFIG_MARU) || !defined(CONFIG_WIN32)
19 #include <sys/uio.h>
20 #endif
21
22 #include "9p-iov-marshal.h"
23 #include "qemu/bswap.h"
24
25 static ssize_t v9fs_packunpack(void *addr, struct iovec *sg, int sg_count,
26                                size_t offset, size_t size, int pack)
27 {
28     int i = 0;
29     size_t copied = 0;
30     size_t req_size = size;
31
32
33     for (i = 0; size && i < sg_count; i++) {
34         size_t len;
35         if (offset >= sg[i].iov_len) {
36             /* skip this sg */
37             offset -= sg[i].iov_len;
38             continue;
39         } else {
40             len = MIN(sg[i].iov_len - offset, size);
41             if (pack) {
42                 memcpy(sg[i].iov_base + offset, addr, len);
43             } else {
44                 memcpy(addr, sg[i].iov_base + offset, len);
45             }
46             size -= len;
47             copied += len;
48             addr += len;
49             if (size) {
50                 offset = 0;
51                 continue;
52             }
53         }
54     }
55     if (copied < req_size) {
56         /*
57          * We copied less that requested size. error out
58          */
59         return -ENOBUFS;
60     }
61     return copied;
62 }
63
64 static ssize_t v9fs_unpack(void *dst, struct iovec *out_sg, int out_num,
65                            size_t offset, size_t size)
66 {
67     return v9fs_packunpack(dst, out_sg, out_num, offset, size, 0);
68 }
69
70 ssize_t v9fs_pack(struct iovec *in_sg, int in_num, size_t offset,
71                   const void *src, size_t size)
72 {
73     return v9fs_packunpack((void *)src, in_sg, in_num, offset, size, 1);
74 }
75
76 ssize_t v9fs_iov_vunmarshal(struct iovec *out_sg, int out_num, size_t offset,
77                             int bswap, const char *fmt, va_list ap)
78 {
79     int i;
80     ssize_t copied = 0;
81     size_t old_offset = offset;
82
83     for (i = 0; fmt[i]; i++) {
84         switch (fmt[i]) {
85         case 'b': {
86             uint8_t *valp = va_arg(ap, uint8_t *);
87             copied = v9fs_unpack(valp, out_sg, out_num, offset, sizeof(*valp));
88             break;
89         }
90         case 'w': {
91             uint16_t val, *valp;
92             valp = va_arg(ap, uint16_t *);
93             copied = v9fs_unpack(&val, out_sg, out_num, offset, sizeof(val));
94             if (bswap) {
95                 *valp = le16_to_cpu(val);
96             } else {
97                 *valp = val;
98             }
99             break;
100         }
101         case 'd': {
102             uint32_t val, *valp;
103             valp = va_arg(ap, uint32_t *);
104             copied = v9fs_unpack(&val, out_sg, out_num, offset, sizeof(val));
105             if (bswap) {
106                 *valp = le32_to_cpu(val);
107             } else {
108                 *valp = val;
109             }
110             break;
111         }
112         case 'q': {
113             uint64_t val, *valp;
114             valp = va_arg(ap, uint64_t *);
115             copied = v9fs_unpack(&val, out_sg, out_num, offset, sizeof(val));
116             if (bswap) {
117                 *valp = le64_to_cpu(val);
118             } else {
119                 *valp = val;
120             }
121             break;
122         }
123         case 's': {
124             V9fsString *str = va_arg(ap, V9fsString *);
125             copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap,
126                                         "w", &str->size);
127             if (copied > 0) {
128                 offset += copied;
129                 str->data = g_malloc(str->size + 1);
130                 copied = v9fs_unpack(str->data, out_sg, out_num, offset,
131                                      str->size);
132                 if (copied > 0) {
133                     str->data[str->size] = 0;
134                 } else {
135                     v9fs_string_free(str);
136                 }
137             }
138             break;
139         }
140         case 'Q': {
141             V9fsQID *qidp = va_arg(ap, V9fsQID *);
142             copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap,
143                                         "bdq", &qidp->type, &qidp->version,
144                                         &qidp->path);
145             break;
146         }
147         case 'S': {
148             V9fsStat *statp = va_arg(ap, V9fsStat *);
149             copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap,
150                                         "wwdQdddqsssssddd",
151                                         &statp->size, &statp->type,
152                                         &statp->dev, &statp->qid,
153                                         &statp->mode, &statp->atime,
154                                         &statp->mtime, &statp->length,
155                                         &statp->name, &statp->uid,
156                                         &statp->gid, &statp->muid,
157                                         &statp->extension,
158                                         &statp->n_uid, &statp->n_gid,
159                                         &statp->n_muid);
160             break;
161         }
162         case 'I': {
163             V9fsIattr *iattr = va_arg(ap, V9fsIattr *);
164             copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap,
165                                         "ddddqqqqq",
166                                         &iattr->valid, &iattr->mode,
167                                         &iattr->uid, &iattr->gid,
168                                         &iattr->size, &iattr->atime_sec,
169                                         &iattr->atime_nsec,
170                                         &iattr->mtime_sec,
171                                         &iattr->mtime_nsec);
172             break;
173         }
174         default:
175             break;
176         }
177         if (copied < 0) {
178             return copied;
179         }
180         offset += copied;
181     }
182
183     return offset - old_offset;
184 }
185
186 ssize_t v9fs_iov_unmarshal(struct iovec *out_sg, int out_num, size_t offset,
187                            int bswap, const char *fmt, ...)
188 {
189     ssize_t ret;
190     va_list ap;
191
192     va_start(ap, fmt);
193     ret = v9fs_iov_vunmarshal(out_sg, out_num, offset, bswap, fmt, ap);
194     va_end(ap);
195
196     return ret;
197 }
198
199 ssize_t v9fs_iov_vmarshal(struct iovec *in_sg, int in_num, size_t offset,
200                           int bswap, const char *fmt, va_list ap)
201 {
202     int i;
203     ssize_t copied = 0;
204     size_t old_offset = offset;
205
206     for (i = 0; fmt[i]; i++) {
207         switch (fmt[i]) {
208         case 'b': {
209             uint8_t val = va_arg(ap, int);
210             copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val));
211             break;
212         }
213         case 'w': {
214             uint16_t val = va_arg(ap, int);
215             if (bswap) {
216                 val = cpu_to_le16(val);
217             }
218             copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val));
219             break;
220         }
221         case 'd': {
222             uint32_t val = va_arg(ap, uint32_t);
223             if (bswap) {
224                 val = cpu_to_le32(val);
225             }
226             copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val));
227             break;
228         }
229         case 'q': {
230             uint64_t val = va_arg(ap, uint64_t);
231             if (bswap) {
232                 val = cpu_to_le64(val);
233             }
234             copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val));
235             break;
236         }
237         case 's': {
238             V9fsString *str = va_arg(ap, V9fsString *);
239             copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap,
240                                       "w", str->size);
241             if (copied > 0) {
242                 offset += copied;
243                 copied = v9fs_pack(in_sg, in_num, offset, str->data, str->size);
244             }
245             break;
246         }
247         case 'Q': {
248             V9fsQID *qidp = va_arg(ap, V9fsQID *);
249             copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap, "bdq",
250                                       qidp->type, qidp->version,
251                                       qidp->path);
252             break;
253         }
254         case 'S': {
255             V9fsStat *statp = va_arg(ap, V9fsStat *);
256             copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap,
257                                       "wwdQdddqsssssddd",
258                                       statp->size, statp->type, statp->dev,
259                                       &statp->qid, statp->mode, statp->atime,
260                                       statp->mtime, statp->length,
261                                       &statp->name,
262                                       &statp->uid, &statp->gid, &statp->muid,
263                                       &statp->extension, statp->n_uid,
264                                       statp->n_gid, statp->n_muid);
265             break;
266         }
267         case 'A': {
268             V9fsStatDotl *statp = va_arg(ap, V9fsStatDotl *);
269             copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap,
270                                       "qQdddqqqqqqqqqqqqqqq",
271                                       statp->st_result_mask,
272                                       &statp->qid, statp->st_mode,
273                                       statp->st_uid, statp->st_gid,
274                                       statp->st_nlink, statp->st_rdev,
275                                       statp->st_size, statp->st_blksize,
276                                       statp->st_blocks, statp->st_atime_sec,
277                                       statp->st_atime_nsec,
278                                       statp->st_mtime_sec,
279                                       statp->st_mtime_nsec,
280                                       statp->st_ctime_sec,
281                                       statp->st_ctime_nsec,
282                                       statp->st_btime_sec,
283                                       statp->st_btime_nsec, statp->st_gen,
284                                       statp->st_data_version);
285             break;
286         }
287         default:
288             break;
289         }
290         if (copied < 0) {
291             return copied;
292         }
293         offset += copied;
294     }
295
296     return offset - old_offset;
297 }
298
299 ssize_t v9fs_iov_marshal(struct iovec *in_sg, int in_num, size_t offset,
300                          int bswap, const char *fmt, ...)
301 {
302     ssize_t ret;
303     va_list ap;
304
305     va_start(ap, fmt);
306     ret = v9fs_iov_vmarshal(in_sg, in_num, offset, bswap, fmt, ap);
307     va_end(ap);
308
309     return ret;
310 }