xref: /openbmc/qemu/fsdev/9p-iov-marshal.c (revision 2209bd050a9287120b74f8616837293d739c5fb3)
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_unmarshal(struct iovec *out_sg, int out_num, size_t offset,
80                            int bswap, const char *fmt, ...)
81 {
82     int i;
83     va_list ap;
84     ssize_t copied = 0;
85     size_t old_offset = offset;
86 
87     va_start(ap, fmt);
88     for (i = 0; fmt[i]; i++) {
89         switch (fmt[i]) {
90         case 'b': {
91             uint8_t *valp = va_arg(ap, uint8_t *);
92             copied = v9fs_unpack(valp, out_sg, out_num, offset, sizeof(*valp));
93             break;
94         }
95         case 'w': {
96             uint16_t val, *valp;
97             valp = va_arg(ap, uint16_t *);
98             copied = v9fs_unpack(&val, out_sg, out_num, offset, sizeof(val));
99             if (bswap) {
100                 *valp = le16_to_cpu(val);
101             } else {
102                 *valp = val;
103             }
104             break;
105         }
106         case 'd': {
107             uint32_t val, *valp;
108             valp = va_arg(ap, uint32_t *);
109             copied = v9fs_unpack(&val, out_sg, out_num, offset, sizeof(val));
110             if (bswap) {
111                 *valp = le32_to_cpu(val);
112             } else {
113                 *valp = val;
114             }
115             break;
116         }
117         case 'q': {
118             uint64_t val, *valp;
119             valp = va_arg(ap, uint64_t *);
120             copied = v9fs_unpack(&val, out_sg, out_num, offset, sizeof(val));
121             if (bswap) {
122                 *valp = le64_to_cpu(val);
123             } else {
124                 *valp = val;
125             }
126             break;
127         }
128         case 's': {
129             V9fsString *str = va_arg(ap, V9fsString *);
130             copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap,
131                                         "w", &str->size);
132             if (copied > 0) {
133                 offset += copied;
134                 str->data = g_malloc(str->size + 1);
135                 copied = v9fs_unpack(str->data, out_sg, out_num, offset,
136                                      str->size);
137                 if (copied > 0) {
138                     str->data[str->size] = 0;
139                 } else {
140                     v9fs_string_free(str);
141                 }
142             }
143             break;
144         }
145         case 'Q': {
146             V9fsQID *qidp = va_arg(ap, V9fsQID *);
147             copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap,
148                                         "bdq", &qidp->type, &qidp->version,
149                                         &qidp->path);
150             break;
151         }
152         case 'S': {
153             V9fsStat *statp = va_arg(ap, V9fsStat *);
154             copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap,
155                                         "wwdQdddqsssssddd",
156                                         &statp->size, &statp->type,
157                                         &statp->dev, &statp->qid,
158                                         &statp->mode, &statp->atime,
159                                         &statp->mtime, &statp->length,
160                                         &statp->name, &statp->uid,
161                                         &statp->gid, &statp->muid,
162                                         &statp->extension,
163                                         &statp->n_uid, &statp->n_gid,
164                                         &statp->n_muid);
165             break;
166         }
167         case 'I': {
168             V9fsIattr *iattr = va_arg(ap, V9fsIattr *);
169             copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap,
170                                         "ddddqqqqq",
171                                         &iattr->valid, &iattr->mode,
172                                         &iattr->uid, &iattr->gid,
173                                         &iattr->size, &iattr->atime_sec,
174                                         &iattr->atime_nsec,
175                                         &iattr->mtime_sec,
176                                         &iattr->mtime_nsec);
177             break;
178         }
179         default:
180             break;
181         }
182         if (copied < 0) {
183             va_end(ap);
184             return copied;
185         }
186         offset += copied;
187     }
188     va_end(ap);
189 
190     return offset - old_offset;
191 }
192 
193 ssize_t v9fs_iov_marshal(struct iovec *in_sg, int in_num, size_t offset,
194                          int bswap, const char *fmt, ...)
195 {
196     int i;
197     va_list ap;
198     ssize_t copied = 0;
199     size_t old_offset = offset;
200 
201     va_start(ap, fmt);
202     for (i = 0; fmt[i]; i++) {
203         switch (fmt[i]) {
204         case 'b': {
205             uint8_t val = va_arg(ap, int);
206             copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val));
207             break;
208         }
209         case 'w': {
210             uint16_t val;
211             if (bswap) {
212                 cpu_to_le16w(&val, va_arg(ap, int));
213             } else {
214                 val =  va_arg(ap, int);
215             }
216             copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val));
217             break;
218         }
219         case 'd': {
220             uint32_t val;
221             if (bswap) {
222                 cpu_to_le32w(&val, va_arg(ap, uint32_t));
223             } else {
224                 val =  va_arg(ap, uint32_t);
225             }
226             copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val));
227             break;
228         }
229         case 'q': {
230             uint64_t val;
231             if (bswap) {
232                 cpu_to_le64w(&val, va_arg(ap, uint64_t));
233             } else {
234                 val =  va_arg(ap, uint64_t);
235             }
236             copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val));
237             break;
238         }
239         case 's': {
240             V9fsString *str = va_arg(ap, V9fsString *);
241             copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap,
242                                       "w", str->size);
243             if (copied > 0) {
244                 offset += copied;
245                 copied = v9fs_pack(in_sg, in_num, offset, str->data, str->size);
246             }
247             break;
248         }
249         case 'Q': {
250             V9fsQID *qidp = va_arg(ap, V9fsQID *);
251             copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap, "bdq",
252                                       qidp->type, qidp->version,
253                                       qidp->path);
254             break;
255         }
256         case 'S': {
257             V9fsStat *statp = va_arg(ap, V9fsStat *);
258             copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap,
259                                       "wwdQdddqsssssddd",
260                                       statp->size, statp->type, statp->dev,
261                                       &statp->qid, statp->mode, statp->atime,
262                                       statp->mtime, statp->length,
263                                       &statp->name,
264                                       &statp->uid, &statp->gid, &statp->muid,
265                                       &statp->extension, statp->n_uid,
266                                       statp->n_gid, statp->n_muid);
267             break;
268         }
269         case 'A': {
270             V9fsStatDotl *statp = va_arg(ap, V9fsStatDotl *);
271             copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap,
272                                       "qQdddqqqqqqqqqqqqqqq",
273                                       statp->st_result_mask,
274                                       &statp->qid, statp->st_mode,
275                                       statp->st_uid, statp->st_gid,
276                                       statp->st_nlink, statp->st_rdev,
277                                       statp->st_size, statp->st_blksize,
278                                       statp->st_blocks, statp->st_atime_sec,
279                                       statp->st_atime_nsec,
280                                       statp->st_mtime_sec,
281                                       statp->st_mtime_nsec,
282                                       statp->st_ctime_sec,
283                                       statp->st_ctime_nsec,
284                                       statp->st_btime_sec,
285                                       statp->st_btime_nsec, statp->st_gen,
286                                       statp->st_data_version);
287             break;
288         }
289         default:
290             break;
291         }
292         if (copied < 0) {
293             va_end(ap);
294             return copied;
295         }
296         offset += copied;
297     }
298     va_end(ap);
299 
300     return offset - old_offset;
301 }
302