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