xref: /openbmc/qemu/fsdev/9p-iov-marshal.c (revision 423be09ab9492735924e73a2d36069784441ebc6)
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': {
87*79660687SMarc-André Lureau             uint16_t val = 0, *valp;
882209bd05SWei Liu             valp = va_arg(ap, uint16_t *);
892209bd05SWei Liu             copied = v9fs_unpack(&val, out_sg, out_num, offset, sizeof(val));
90*79660687SMarc-André Lureau             if (copied <= 0) {
91*79660687SMarc-André Lureau                 break;
92*79660687SMarc-André Lureau             }
932209bd05SWei Liu             if (bswap) {
942209bd05SWei Liu                 *valp = le16_to_cpu(val);
952209bd05SWei Liu             } else {
962209bd05SWei Liu                 *valp = val;
972209bd05SWei Liu             }
982209bd05SWei Liu             break;
992209bd05SWei Liu         }
1002209bd05SWei Liu         case 'd': {
101*79660687SMarc-André Lureau             uint32_t val = 0, *valp;
1022209bd05SWei Liu             valp = va_arg(ap, uint32_t *);
1032209bd05SWei Liu             copied = v9fs_unpack(&val, out_sg, out_num, offset, sizeof(val));
104*79660687SMarc-André Lureau             if (copied <= 0) {
105*79660687SMarc-André Lureau                 break;
106*79660687SMarc-André Lureau             }
1072209bd05SWei Liu             if (bswap) {
1082209bd05SWei Liu                 *valp = le32_to_cpu(val);
1092209bd05SWei Liu             } else {
1102209bd05SWei Liu                 *valp = val;
1112209bd05SWei Liu             }
1122209bd05SWei Liu             break;
1132209bd05SWei Liu         }
1142209bd05SWei Liu         case 'q': {
115*79660687SMarc-André Lureau             uint64_t val = 0, *valp;
1162209bd05SWei Liu             valp = va_arg(ap, uint64_t *);
1172209bd05SWei Liu             copied = v9fs_unpack(&val, out_sg, out_num, offset, sizeof(val));
118*79660687SMarc-André Lureau             if (copied <= 0) {
119*79660687SMarc-André Lureau                 break;
120*79660687SMarc-André Lureau             }
1212209bd05SWei Liu             if (bswap) {
1222209bd05SWei Liu                 *valp = le64_to_cpu(val);
1232209bd05SWei Liu             } else {
1242209bd05SWei Liu                 *valp = val;
1252209bd05SWei Liu             }
1262209bd05SWei Liu             break;
1272209bd05SWei Liu         }
1282209bd05SWei Liu         case 's': {
1292209bd05SWei Liu             V9fsString *str = va_arg(ap, V9fsString *);
1302209bd05SWei Liu             copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap,
1312209bd05SWei Liu                                         "w", &str->size);
1322209bd05SWei Liu             if (copied > 0) {
1332209bd05SWei Liu                 offset += copied;
1342209bd05SWei Liu                 str->data = g_malloc(str->size + 1);
1352209bd05SWei Liu                 copied = v9fs_unpack(str->data, out_sg, out_num, offset,
1362209bd05SWei Liu                                      str->size);
137ba42ebb8SLi Qiang                 if (copied >= 0) {
1382209bd05SWei Liu                     str->data[str->size] = 0;
1392209bd05SWei Liu                 } else {
1402209bd05SWei Liu                     v9fs_string_free(str);
1412209bd05SWei Liu                 }
1422209bd05SWei Liu             }
1432209bd05SWei Liu             break;
1442209bd05SWei Liu         }
1452209bd05SWei Liu         case 'Q': {
1462209bd05SWei Liu             V9fsQID *qidp = va_arg(ap, V9fsQID *);
1472209bd05SWei Liu             copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap,
1482209bd05SWei Liu                                         "bdq", &qidp->type, &qidp->version,
1492209bd05SWei Liu                                         &qidp->path);
1502209bd05SWei Liu             break;
1512209bd05SWei Liu         }
1522209bd05SWei Liu         case 'S': {
1532209bd05SWei Liu             V9fsStat *statp = va_arg(ap, V9fsStat *);
1542209bd05SWei Liu             copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap,
1552209bd05SWei Liu                                         "wwdQdddqsssssddd",
1562209bd05SWei Liu                                         &statp->size, &statp->type,
1572209bd05SWei Liu                                         &statp->dev, &statp->qid,
1582209bd05SWei Liu                                         &statp->mode, &statp->atime,
1592209bd05SWei Liu                                         &statp->mtime, &statp->length,
1602209bd05SWei Liu                                         &statp->name, &statp->uid,
1612209bd05SWei Liu                                         &statp->gid, &statp->muid,
1622209bd05SWei Liu                                         &statp->extension,
1632209bd05SWei Liu                                         &statp->n_uid, &statp->n_gid,
1642209bd05SWei Liu                                         &statp->n_muid);
1652209bd05SWei Liu             break;
1662209bd05SWei Liu         }
1672209bd05SWei Liu         case 'I': {
1682209bd05SWei Liu             V9fsIattr *iattr = va_arg(ap, V9fsIattr *);
1692209bd05SWei Liu             copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap,
1702209bd05SWei Liu                                         "ddddqqqqq",
1712209bd05SWei Liu                                         &iattr->valid, &iattr->mode,
1722209bd05SWei Liu                                         &iattr->uid, &iattr->gid,
1732209bd05SWei Liu                                         &iattr->size, &iattr->atime_sec,
1742209bd05SWei Liu                                         &iattr->atime_nsec,
1752209bd05SWei Liu                                         &iattr->mtime_sec,
1762209bd05SWei Liu                                         &iattr->mtime_nsec);
1772209bd05SWei Liu             break;
1782209bd05SWei Liu         }
1792209bd05SWei Liu         default:
18057a0aa6bSGreg Kurz             g_assert_not_reached();
1812209bd05SWei Liu         }
1822209bd05SWei Liu         if (copied < 0) {
1832209bd05SWei Liu             return copied;
1842209bd05SWei Liu         }
1852209bd05SWei Liu         offset += copied;
1862209bd05SWei Liu     }
1872209bd05SWei Liu 
1882209bd05SWei Liu     return offset - old_offset;
1892209bd05SWei Liu }
1902209bd05SWei Liu 
v9fs_iov_unmarshal(struct iovec * out_sg,int out_num,size_t offset,int bswap,const char * fmt,...)1910e2082d9SWei Liu ssize_t v9fs_iov_unmarshal(struct iovec *out_sg, int out_num, size_t offset,
1922209bd05SWei Liu                            int bswap, const char *fmt, ...)
1932209bd05SWei Liu {
1940e2082d9SWei Liu     ssize_t ret;
1952209bd05SWei Liu     va_list ap;
1960e2082d9SWei Liu 
1970e2082d9SWei Liu     va_start(ap, fmt);
1980e2082d9SWei Liu     ret = v9fs_iov_vunmarshal(out_sg, out_num, offset, bswap, fmt, ap);
1990e2082d9SWei Liu     va_end(ap);
2000e2082d9SWei Liu 
2010e2082d9SWei Liu     return ret;
2020e2082d9SWei Liu }
2030e2082d9SWei Liu 
v9fs_iov_vmarshal(struct iovec * in_sg,int in_num,size_t offset,int bswap,const char * fmt,va_list ap)2040e2082d9SWei Liu ssize_t v9fs_iov_vmarshal(struct iovec *in_sg, int in_num, size_t offset,
2050e2082d9SWei Liu                           int bswap, const char *fmt, va_list ap)
2060e2082d9SWei Liu {
2070e2082d9SWei Liu     int i;
2082209bd05SWei Liu     ssize_t copied = 0;
2092209bd05SWei Liu     size_t old_offset = offset;
2102209bd05SWei Liu 
2112209bd05SWei Liu     for (i = 0; fmt[i]; i++) {
2122209bd05SWei Liu         switch (fmt[i]) {
2132209bd05SWei Liu         case 'b': {
2142209bd05SWei Liu             uint8_t val = va_arg(ap, int);
2152209bd05SWei Liu             copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val));
2162209bd05SWei Liu             break;
2172209bd05SWei Liu         }
2182209bd05SWei Liu         case 'w': {
219b442642dSPeter Maydell             uint16_t val = va_arg(ap, int);
2202209bd05SWei Liu             if (bswap) {
221b442642dSPeter Maydell                 val = cpu_to_le16(val);
2222209bd05SWei Liu             }
2232209bd05SWei Liu             copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val));
2242209bd05SWei Liu             break;
2252209bd05SWei Liu         }
2262209bd05SWei Liu         case 'd': {
227b442642dSPeter Maydell             uint32_t val = va_arg(ap, uint32_t);
2282209bd05SWei Liu             if (bswap) {
229b442642dSPeter Maydell                 val = cpu_to_le32(val);
2302209bd05SWei Liu             }
2312209bd05SWei Liu             copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val));
2322209bd05SWei Liu             break;
2332209bd05SWei Liu         }
2342209bd05SWei Liu         case 'q': {
235b442642dSPeter Maydell             uint64_t val = va_arg(ap, uint64_t);
2362209bd05SWei Liu             if (bswap) {
237b442642dSPeter Maydell                 val = cpu_to_le64(val);
2382209bd05SWei Liu             }
2392209bd05SWei Liu             copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val));
2402209bd05SWei Liu             break;
2412209bd05SWei Liu         }
2422209bd05SWei Liu         case 's': {
2432209bd05SWei Liu             V9fsString *str = va_arg(ap, V9fsString *);
2442209bd05SWei Liu             copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap,
2452209bd05SWei Liu                                       "w", str->size);
2462209bd05SWei Liu             if (copied > 0) {
2472209bd05SWei Liu                 offset += copied;
2482209bd05SWei Liu                 copied = v9fs_pack(in_sg, in_num, offset, str->data, str->size);
2492209bd05SWei Liu             }
2502209bd05SWei Liu             break;
2512209bd05SWei Liu         }
2522209bd05SWei Liu         case 'Q': {
2532209bd05SWei Liu             V9fsQID *qidp = va_arg(ap, V9fsQID *);
2542209bd05SWei Liu             copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap, "bdq",
2552209bd05SWei Liu                                       qidp->type, qidp->version,
2562209bd05SWei Liu                                       qidp->path);
2572209bd05SWei Liu             break;
2582209bd05SWei Liu         }
2592209bd05SWei Liu         case 'S': {
2602209bd05SWei Liu             V9fsStat *statp = va_arg(ap, V9fsStat *);
2612209bd05SWei Liu             copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap,
2622209bd05SWei Liu                                       "wwdQdddqsssssddd",
2632209bd05SWei Liu                                       statp->size, statp->type, statp->dev,
2642209bd05SWei Liu                                       &statp->qid, statp->mode, statp->atime,
2652209bd05SWei Liu                                       statp->mtime, statp->length,
2662209bd05SWei Liu                                       &statp->name,
2672209bd05SWei Liu                                       &statp->uid, &statp->gid, &statp->muid,
2682209bd05SWei Liu                                       &statp->extension, statp->n_uid,
2692209bd05SWei Liu                                       statp->n_gid, statp->n_muid);
2702209bd05SWei Liu             break;
2712209bd05SWei Liu         }
2722209bd05SWei Liu         case 'A': {
2732209bd05SWei Liu             V9fsStatDotl *statp = va_arg(ap, V9fsStatDotl *);
2742209bd05SWei Liu             copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap,
2752209bd05SWei Liu                                       "qQdddqqqqqqqqqqqqqqq",
2762209bd05SWei Liu                                       statp->st_result_mask,
2772209bd05SWei Liu                                       &statp->qid, statp->st_mode,
2782209bd05SWei Liu                                       statp->st_uid, statp->st_gid,
2792209bd05SWei Liu                                       statp->st_nlink, statp->st_rdev,
2802209bd05SWei Liu                                       statp->st_size, statp->st_blksize,
2812209bd05SWei Liu                                       statp->st_blocks, statp->st_atime_sec,
2822209bd05SWei Liu                                       statp->st_atime_nsec,
2832209bd05SWei Liu                                       statp->st_mtime_sec,
2842209bd05SWei Liu                                       statp->st_mtime_nsec,
2852209bd05SWei Liu                                       statp->st_ctime_sec,
2862209bd05SWei Liu                                       statp->st_ctime_nsec,
2872209bd05SWei Liu                                       statp->st_btime_sec,
2882209bd05SWei Liu                                       statp->st_btime_nsec, statp->st_gen,
2892209bd05SWei Liu                                       statp->st_data_version);
2902209bd05SWei Liu             break;
2912209bd05SWei Liu         }
2922209bd05SWei Liu         default:
29357a0aa6bSGreg Kurz             g_assert_not_reached();
2942209bd05SWei Liu         }
2952209bd05SWei Liu         if (copied < 0) {
2962209bd05SWei Liu             return copied;
2972209bd05SWei Liu         }
2982209bd05SWei Liu         offset += copied;
2992209bd05SWei Liu     }
3002209bd05SWei Liu 
3012209bd05SWei Liu     return offset - old_offset;
3022209bd05SWei Liu }
3030e2082d9SWei Liu 
v9fs_iov_marshal(struct iovec * in_sg,int in_num,size_t offset,int bswap,const char * fmt,...)3040e2082d9SWei Liu ssize_t v9fs_iov_marshal(struct iovec *in_sg, int in_num, size_t offset,
3050e2082d9SWei Liu                          int bswap, const char *fmt, ...)
3060e2082d9SWei Liu {
3070e2082d9SWei Liu     ssize_t ret;
3080e2082d9SWei Liu     va_list ap;
3090e2082d9SWei Liu 
3100e2082d9SWei Liu     va_start(ap, fmt);
3110e2082d9SWei Liu     ret = v9fs_iov_vmarshal(in_sg, in_num, offset, bswap, fmt, ap);
3120e2082d9SWei Liu     va_end(ap);
3130e2082d9SWei Liu 
3140e2082d9SWei Liu     return ret;
3150e2082d9SWei Liu }
316