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