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