xref: /qemu/fsdev/9p-iov-marshal.c (revision 57a0aa6b)
12209bd05SWei Liu /*
22209bd05SWei Liu  * 9p backend
32209bd05SWei Liu  *
42209bd05SWei Liu  * Copyright IBM, Corp. 2010
52209bd05SWei Liu  *
62209bd05SWei Liu  * Authors:
72209bd05SWei Liu  *  Anthony Liguori   <aliguori@us.ibm.com>
82209bd05SWei Liu  *
92209bd05SWei Liu  * This work is licensed under the terms of the GNU GPL, version 2.  See
102209bd05SWei Liu  * the COPYING file in the top-level directory.
112209bd05SWei Liu  *
122209bd05SWei Liu  */
132209bd05SWei Liu 
14fbc04127SPeter Maydell #include "qemu/osdep.h"
152209bd05SWei Liu #include <glib/gprintf.h>
162209bd05SWei Liu #include <utime.h>
172209bd05SWei Liu 
182209bd05SWei Liu #include "9p-iov-marshal.h"
192209bd05SWei Liu #include "qemu/bswap.h"
202209bd05SWei Liu 
v9fs_packunpack(void * addr,struct iovec * sg,int sg_count,size_t offset,size_t size,int pack)212209bd05SWei Liu static ssize_t v9fs_packunpack(void *addr, struct iovec *sg, int sg_count,
222209bd05SWei Liu                                size_t offset, size_t size, int pack)
232209bd05SWei Liu {
242209bd05SWei Liu     int i = 0;
252209bd05SWei Liu     size_t copied = 0;
262209bd05SWei Liu     size_t req_size = size;
272209bd05SWei Liu 
282209bd05SWei Liu 
292209bd05SWei Liu     for (i = 0; size && i < sg_count; i++) {
302209bd05SWei Liu         size_t len;
312209bd05SWei Liu         if (offset >= sg[i].iov_len) {
322209bd05SWei Liu             /* skip this sg */
332209bd05SWei Liu             offset -= sg[i].iov_len;
342209bd05SWei Liu             continue;
352209bd05SWei Liu         } else {
362209bd05SWei Liu             len = MIN(sg[i].iov_len - offset, size);
372209bd05SWei Liu             if (pack) {
382209bd05SWei Liu                 memcpy(sg[i].iov_base + offset, addr, len);
392209bd05SWei Liu             } else {
402209bd05SWei Liu                 memcpy(addr, sg[i].iov_base + offset, len);
412209bd05SWei Liu             }
422209bd05SWei Liu             size -= len;
432209bd05SWei Liu             copied += len;
442209bd05SWei Liu             addr += len;
452209bd05SWei Liu             if (size) {
462209bd05SWei Liu                 offset = 0;
472209bd05SWei Liu                 continue;
482209bd05SWei Liu             }
492209bd05SWei Liu         }
502209bd05SWei Liu     }
512209bd05SWei Liu     if (copied < req_size) {
522209bd05SWei Liu         /*
532209bd05SWei Liu          * We copied less that requested size. error out
542209bd05SWei Liu          */
552209bd05SWei Liu         return -ENOBUFS;
562209bd05SWei Liu     }
572209bd05SWei Liu     return copied;
582209bd05SWei Liu }
592209bd05SWei Liu 
v9fs_unpack(void * dst,struct iovec * out_sg,int out_num,size_t offset,size_t size)602209bd05SWei Liu static ssize_t v9fs_unpack(void *dst, struct iovec *out_sg, int out_num,
612209bd05SWei Liu                            size_t offset, size_t size)
622209bd05SWei Liu {
632209bd05SWei Liu     return v9fs_packunpack(dst, out_sg, out_num, offset, size, 0);
642209bd05SWei Liu }
652209bd05SWei Liu 
v9fs_pack(struct iovec * in_sg,int in_num,size_t offset,const void * src,size_t size)662209bd05SWei Liu ssize_t v9fs_pack(struct iovec *in_sg, int in_num, size_t offset,
672209bd05SWei Liu                   const void *src, size_t size)
682209bd05SWei Liu {
692209bd05SWei Liu     return v9fs_packunpack((void *)src, in_sg, in_num, offset, size, 1);
702209bd05SWei Liu }
712209bd05SWei Liu 
v9fs_iov_vunmarshal(struct iovec * out_sg,int out_num,size_t offset,int bswap,const char * fmt,va_list ap)720e2082d9SWei Liu ssize_t v9fs_iov_vunmarshal(struct iovec *out_sg, int out_num, size_t offset,
730e2082d9SWei Liu                             int bswap, const char *fmt, va_list ap)
742209bd05SWei Liu {
752209bd05SWei Liu     int i;
762209bd05SWei Liu     ssize_t copied = 0;
772209bd05SWei Liu     size_t old_offset = offset;
782209bd05SWei Liu 
792209bd05SWei Liu     for (i = 0; fmt[i]; i++) {
802209bd05SWei Liu         switch (fmt[i]) {
812209bd05SWei Liu         case 'b': {
822209bd05SWei Liu             uint8_t *valp = va_arg(ap, uint8_t *);
832209bd05SWei Liu             copied = v9fs_unpack(valp, out_sg, out_num, offset, sizeof(*valp));
842209bd05SWei Liu             break;
852209bd05SWei Liu         }
862209bd05SWei Liu         case 'w': {
872209bd05SWei Liu             uint16_t val, *valp;
882209bd05SWei Liu             valp = va_arg(ap, uint16_t *);
892209bd05SWei Liu             copied = v9fs_unpack(&val, out_sg, out_num, offset, sizeof(val));
902209bd05SWei Liu             if (bswap) {
912209bd05SWei Liu                 *valp = le16_to_cpu(val);
922209bd05SWei Liu             } else {
932209bd05SWei Liu                 *valp = val;
942209bd05SWei Liu             }
952209bd05SWei Liu             break;
962209bd05SWei Liu         }
972209bd05SWei Liu         case 'd': {
982209bd05SWei Liu             uint32_t val, *valp;
992209bd05SWei Liu             valp = va_arg(ap, uint32_t *);
1002209bd05SWei Liu             copied = v9fs_unpack(&val, out_sg, out_num, offset, sizeof(val));
1012209bd05SWei Liu             if (bswap) {
1022209bd05SWei Liu                 *valp = le32_to_cpu(val);
1032209bd05SWei Liu             } else {
1042209bd05SWei Liu                 *valp = val;
1052209bd05SWei Liu             }
1062209bd05SWei Liu             break;
1072209bd05SWei Liu         }
1082209bd05SWei Liu         case 'q': {
1092209bd05SWei Liu             uint64_t val, *valp;
1102209bd05SWei Liu             valp = va_arg(ap, uint64_t *);
1112209bd05SWei Liu             copied = v9fs_unpack(&val, out_sg, out_num, offset, sizeof(val));
1122209bd05SWei Liu             if (bswap) {
1132209bd05SWei Liu                 *valp = le64_to_cpu(val);
1142209bd05SWei Liu             } else {
1152209bd05SWei Liu                 *valp = val;
1162209bd05SWei Liu             }
1172209bd05SWei Liu             break;
1182209bd05SWei Liu         }
1192209bd05SWei Liu         case 's': {
1202209bd05SWei Liu             V9fsString *str = va_arg(ap, V9fsString *);
1212209bd05SWei Liu             copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap,
1222209bd05SWei Liu                                         "w", &str->size);
1232209bd05SWei Liu             if (copied > 0) {
1242209bd05SWei Liu                 offset += copied;
1252209bd05SWei Liu                 str->data = g_malloc(str->size + 1);
1262209bd05SWei Liu                 copied = v9fs_unpack(str->data, out_sg, out_num, offset,
1272209bd05SWei Liu                                      str->size);
128ba42ebb8SLi Qiang                 if (copied >= 0) {
1292209bd05SWei Liu                     str->data[str->size] = 0;
1302209bd05SWei Liu                 } else {
1312209bd05SWei Liu                     v9fs_string_free(str);
1322209bd05SWei Liu                 }
1332209bd05SWei Liu             }
1342209bd05SWei Liu             break;
1352209bd05SWei Liu         }
1362209bd05SWei Liu         case 'Q': {
1372209bd05SWei Liu             V9fsQID *qidp = va_arg(ap, V9fsQID *);
1382209bd05SWei Liu             copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap,
1392209bd05SWei Liu                                         "bdq", &qidp->type, &qidp->version,
1402209bd05SWei Liu                                         &qidp->path);
1412209bd05SWei Liu             break;
1422209bd05SWei Liu         }
1432209bd05SWei Liu         case 'S': {
1442209bd05SWei Liu             V9fsStat *statp = va_arg(ap, V9fsStat *);
1452209bd05SWei Liu             copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap,
1462209bd05SWei Liu                                         "wwdQdddqsssssddd",
1472209bd05SWei Liu                                         &statp->size, &statp->type,
1482209bd05SWei Liu                                         &statp->dev, &statp->qid,
1492209bd05SWei Liu                                         &statp->mode, &statp->atime,
1502209bd05SWei Liu                                         &statp->mtime, &statp->length,
1512209bd05SWei Liu                                         &statp->name, &statp->uid,
1522209bd05SWei Liu                                         &statp->gid, &statp->muid,
1532209bd05SWei Liu                                         &statp->extension,
1542209bd05SWei Liu                                         &statp->n_uid, &statp->n_gid,
1552209bd05SWei Liu                                         &statp->n_muid);
1562209bd05SWei Liu             break;
1572209bd05SWei Liu         }
1582209bd05SWei Liu         case 'I': {
1592209bd05SWei Liu             V9fsIattr *iattr = va_arg(ap, V9fsIattr *);
1602209bd05SWei Liu             copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap,
1612209bd05SWei Liu                                         "ddddqqqqq",
1622209bd05SWei Liu                                         &iattr->valid, &iattr->mode,
1632209bd05SWei Liu                                         &iattr->uid, &iattr->gid,
1642209bd05SWei Liu                                         &iattr->size, &iattr->atime_sec,
1652209bd05SWei Liu                                         &iattr->atime_nsec,
1662209bd05SWei Liu                                         &iattr->mtime_sec,
1672209bd05SWei Liu                                         &iattr->mtime_nsec);
1682209bd05SWei Liu             break;
1692209bd05SWei Liu         }
1702209bd05SWei Liu         default:
171*57a0aa6bSGreg Kurz             g_assert_not_reached();
1722209bd05SWei Liu         }
1732209bd05SWei Liu         if (copied < 0) {
1742209bd05SWei Liu             return copied;
1752209bd05SWei Liu         }
1762209bd05SWei Liu         offset += copied;
1772209bd05SWei Liu     }
1782209bd05SWei Liu 
1792209bd05SWei Liu     return offset - old_offset;
1802209bd05SWei Liu }
1812209bd05SWei Liu 
v9fs_iov_unmarshal(struct iovec * out_sg,int out_num,size_t offset,int bswap,const char * fmt,...)1820e2082d9SWei Liu ssize_t v9fs_iov_unmarshal(struct iovec *out_sg, int out_num, size_t offset,
1832209bd05SWei Liu                            int bswap, const char *fmt, ...)
1842209bd05SWei Liu {
1850e2082d9SWei Liu     ssize_t ret;
1862209bd05SWei Liu     va_list ap;
1870e2082d9SWei Liu 
1880e2082d9SWei Liu     va_start(ap, fmt);
1890e2082d9SWei Liu     ret = v9fs_iov_vunmarshal(out_sg, out_num, offset, bswap, fmt, ap);
1900e2082d9SWei Liu     va_end(ap);
1910e2082d9SWei Liu 
1920e2082d9SWei Liu     return ret;
1930e2082d9SWei Liu }
1940e2082d9SWei Liu 
v9fs_iov_vmarshal(struct iovec * in_sg,int in_num,size_t offset,int bswap,const char * fmt,va_list ap)1950e2082d9SWei Liu ssize_t v9fs_iov_vmarshal(struct iovec *in_sg, int in_num, size_t offset,
1960e2082d9SWei Liu                           int bswap, const char *fmt, va_list ap)
1970e2082d9SWei Liu {
1980e2082d9SWei Liu     int i;
1992209bd05SWei Liu     ssize_t copied = 0;
2002209bd05SWei Liu     size_t old_offset = offset;
2012209bd05SWei Liu 
2022209bd05SWei Liu     for (i = 0; fmt[i]; i++) {
2032209bd05SWei Liu         switch (fmt[i]) {
2042209bd05SWei Liu         case 'b': {
2052209bd05SWei Liu             uint8_t val = va_arg(ap, int);
2062209bd05SWei Liu             copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val));
2072209bd05SWei Liu             break;
2082209bd05SWei Liu         }
2092209bd05SWei Liu         case 'w': {
210b442642dSPeter Maydell             uint16_t val = va_arg(ap, int);
2112209bd05SWei Liu             if (bswap) {
212b442642dSPeter Maydell                 val = cpu_to_le16(val);
2132209bd05SWei Liu             }
2142209bd05SWei Liu             copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val));
2152209bd05SWei Liu             break;
2162209bd05SWei Liu         }
2172209bd05SWei Liu         case 'd': {
218b442642dSPeter Maydell             uint32_t val = va_arg(ap, uint32_t);
2192209bd05SWei Liu             if (bswap) {
220b442642dSPeter Maydell                 val = cpu_to_le32(val);
2212209bd05SWei Liu             }
2222209bd05SWei Liu             copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val));
2232209bd05SWei Liu             break;
2242209bd05SWei Liu         }
2252209bd05SWei Liu         case 'q': {
226b442642dSPeter Maydell             uint64_t val = va_arg(ap, uint64_t);
2272209bd05SWei Liu             if (bswap) {
228b442642dSPeter Maydell                 val = cpu_to_le64(val);
2292209bd05SWei Liu             }
2302209bd05SWei Liu             copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val));
2312209bd05SWei Liu             break;
2322209bd05SWei Liu         }
2332209bd05SWei Liu         case 's': {
2342209bd05SWei Liu             V9fsString *str = va_arg(ap, V9fsString *);
2352209bd05SWei Liu             copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap,
2362209bd05SWei Liu                                       "w", str->size);
2372209bd05SWei Liu             if (copied > 0) {
2382209bd05SWei Liu                 offset += copied;
2392209bd05SWei Liu                 copied = v9fs_pack(in_sg, in_num, offset, str->data, str->size);
2402209bd05SWei Liu             }
2412209bd05SWei Liu             break;
2422209bd05SWei Liu         }
2432209bd05SWei Liu         case 'Q': {
2442209bd05SWei Liu             V9fsQID *qidp = va_arg(ap, V9fsQID *);
2452209bd05SWei Liu             copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap, "bdq",
2462209bd05SWei Liu                                       qidp->type, qidp->version,
2472209bd05SWei Liu                                       qidp->path);
2482209bd05SWei Liu             break;
2492209bd05SWei Liu         }
2502209bd05SWei Liu         case 'S': {
2512209bd05SWei Liu             V9fsStat *statp = va_arg(ap, V9fsStat *);
2522209bd05SWei Liu             copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap,
2532209bd05SWei Liu                                       "wwdQdddqsssssddd",
2542209bd05SWei Liu                                       statp->size, statp->type, statp->dev,
2552209bd05SWei Liu                                       &statp->qid, statp->mode, statp->atime,
2562209bd05SWei Liu                                       statp->mtime, statp->length,
2572209bd05SWei Liu                                       &statp->name,
2582209bd05SWei Liu                                       &statp->uid, &statp->gid, &statp->muid,
2592209bd05SWei Liu                                       &statp->extension, statp->n_uid,
2602209bd05SWei Liu                                       statp->n_gid, statp->n_muid);
2612209bd05SWei Liu             break;
2622209bd05SWei Liu         }
2632209bd05SWei Liu         case 'A': {
2642209bd05SWei Liu             V9fsStatDotl *statp = va_arg(ap, V9fsStatDotl *);
2652209bd05SWei Liu             copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap,
2662209bd05SWei Liu                                       "qQdddqqqqqqqqqqqqqqq",
2672209bd05SWei Liu                                       statp->st_result_mask,
2682209bd05SWei Liu                                       &statp->qid, statp->st_mode,
2692209bd05SWei Liu                                       statp->st_uid, statp->st_gid,
2702209bd05SWei Liu                                       statp->st_nlink, statp->st_rdev,
2712209bd05SWei Liu                                       statp->st_size, statp->st_blksize,
2722209bd05SWei Liu                                       statp->st_blocks, statp->st_atime_sec,
2732209bd05SWei Liu                                       statp->st_atime_nsec,
2742209bd05SWei Liu                                       statp->st_mtime_sec,
2752209bd05SWei Liu                                       statp->st_mtime_nsec,
2762209bd05SWei Liu                                       statp->st_ctime_sec,
2772209bd05SWei Liu                                       statp->st_ctime_nsec,
2782209bd05SWei Liu                                       statp->st_btime_sec,
2792209bd05SWei Liu                                       statp->st_btime_nsec, statp->st_gen,
2802209bd05SWei Liu                                       statp->st_data_version);
2812209bd05SWei Liu             break;
2822209bd05SWei Liu         }
2832209bd05SWei Liu         default:
284*57a0aa6bSGreg Kurz             g_assert_not_reached();
2852209bd05SWei Liu         }
2862209bd05SWei Liu         if (copied < 0) {
2872209bd05SWei Liu             return copied;
2882209bd05SWei Liu         }
2892209bd05SWei Liu         offset += copied;
2902209bd05SWei Liu     }
2912209bd05SWei Liu 
2922209bd05SWei Liu     return offset - old_offset;
2932209bd05SWei Liu }
2940e2082d9SWei Liu 
v9fs_iov_marshal(struct iovec * in_sg,int in_num,size_t offset,int bswap,const char * fmt,...)2950e2082d9SWei Liu ssize_t v9fs_iov_marshal(struct iovec *in_sg, int in_num, size_t offset,
2960e2082d9SWei Liu                          int bswap, const char *fmt, ...)
2970e2082d9SWei Liu {
2980e2082d9SWei Liu     ssize_t ret;
2990e2082d9SWei Liu     va_list ap;
3000e2082d9SWei Liu 
3010e2082d9SWei Liu     va_start(ap, fmt);
3020e2082d9SWei Liu     ret = v9fs_iov_vmarshal(in_sg, in_num, offset, bswap, fmt, ap);
3030e2082d9SWei Liu     va_end(ap);
3040e2082d9SWei Liu 
3050e2082d9SWei Liu     return ret;
3060e2082d9SWei Liu }
307