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