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