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_vunmarshal(struct iovec *out_sg, int out_num, size_t offset, 80 int bswap, const char *fmt, va_list ap) 81 { 82 int i; 83 ssize_t copied = 0; 84 size_t old_offset = offset; 85 86 for (i = 0; fmt[i]; i++) { 87 switch (fmt[i]) { 88 case 'b': { 89 uint8_t *valp = va_arg(ap, uint8_t *); 90 copied = v9fs_unpack(valp, out_sg, out_num, offset, sizeof(*valp)); 91 break; 92 } 93 case 'w': { 94 uint16_t val, *valp; 95 valp = va_arg(ap, uint16_t *); 96 copied = v9fs_unpack(&val, out_sg, out_num, offset, sizeof(val)); 97 if (bswap) { 98 *valp = le16_to_cpu(val); 99 } else { 100 *valp = val; 101 } 102 break; 103 } 104 case 'd': { 105 uint32_t val, *valp; 106 valp = va_arg(ap, uint32_t *); 107 copied = v9fs_unpack(&val, out_sg, out_num, offset, sizeof(val)); 108 if (bswap) { 109 *valp = le32_to_cpu(val); 110 } else { 111 *valp = val; 112 } 113 break; 114 } 115 case 'q': { 116 uint64_t val, *valp; 117 valp = va_arg(ap, uint64_t *); 118 copied = v9fs_unpack(&val, out_sg, out_num, offset, sizeof(val)); 119 if (bswap) { 120 *valp = le64_to_cpu(val); 121 } else { 122 *valp = val; 123 } 124 break; 125 } 126 case 's': { 127 V9fsString *str = va_arg(ap, V9fsString *); 128 copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap, 129 "w", &str->size); 130 if (copied > 0) { 131 offset += copied; 132 str->data = g_malloc(str->size + 1); 133 copied = v9fs_unpack(str->data, out_sg, out_num, offset, 134 str->size); 135 if (copied > 0) { 136 str->data[str->size] = 0; 137 } else { 138 v9fs_string_free(str); 139 } 140 } 141 break; 142 } 143 case 'Q': { 144 V9fsQID *qidp = va_arg(ap, V9fsQID *); 145 copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap, 146 "bdq", &qidp->type, &qidp->version, 147 &qidp->path); 148 break; 149 } 150 case 'S': { 151 V9fsStat *statp = va_arg(ap, V9fsStat *); 152 copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap, 153 "wwdQdddqsssssddd", 154 &statp->size, &statp->type, 155 &statp->dev, &statp->qid, 156 &statp->mode, &statp->atime, 157 &statp->mtime, &statp->length, 158 &statp->name, &statp->uid, 159 &statp->gid, &statp->muid, 160 &statp->extension, 161 &statp->n_uid, &statp->n_gid, 162 &statp->n_muid); 163 break; 164 } 165 case 'I': { 166 V9fsIattr *iattr = va_arg(ap, V9fsIattr *); 167 copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap, 168 "ddddqqqqq", 169 &iattr->valid, &iattr->mode, 170 &iattr->uid, &iattr->gid, 171 &iattr->size, &iattr->atime_sec, 172 &iattr->atime_nsec, 173 &iattr->mtime_sec, 174 &iattr->mtime_nsec); 175 break; 176 } 177 default: 178 break; 179 } 180 if (copied < 0) { 181 return copied; 182 } 183 offset += copied; 184 } 185 186 return offset - old_offset; 187 } 188 189 ssize_t v9fs_iov_unmarshal(struct iovec *out_sg, int out_num, size_t offset, 190 int bswap, const char *fmt, ...) 191 { 192 ssize_t ret; 193 va_list ap; 194 195 va_start(ap, fmt); 196 ret = v9fs_iov_vunmarshal(out_sg, out_num, offset, bswap, fmt, ap); 197 va_end(ap); 198 199 return ret; 200 } 201 202 ssize_t v9fs_iov_vmarshal(struct iovec *in_sg, int in_num, size_t offset, 203 int bswap, const char *fmt, va_list ap) 204 { 205 int i; 206 ssize_t copied = 0; 207 size_t old_offset = offset; 208 209 for (i = 0; fmt[i]; i++) { 210 switch (fmt[i]) { 211 case 'b': { 212 uint8_t val = va_arg(ap, int); 213 copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val)); 214 break; 215 } 216 case 'w': { 217 uint16_t val; 218 if (bswap) { 219 cpu_to_le16w(&val, va_arg(ap, int)); 220 } else { 221 val = va_arg(ap, int); 222 } 223 copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val)); 224 break; 225 } 226 case 'd': { 227 uint32_t val; 228 if (bswap) { 229 cpu_to_le32w(&val, va_arg(ap, uint32_t)); 230 } else { 231 val = va_arg(ap, uint32_t); 232 } 233 copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val)); 234 break; 235 } 236 case 'q': { 237 uint64_t val; 238 if (bswap) { 239 cpu_to_le64w(&val, va_arg(ap, uint64_t)); 240 } else { 241 val = va_arg(ap, uint64_t); 242 } 243 copied = v9fs_pack(in_sg, in_num, offset, &val, sizeof(val)); 244 break; 245 } 246 case 's': { 247 V9fsString *str = va_arg(ap, V9fsString *); 248 copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap, 249 "w", str->size); 250 if (copied > 0) { 251 offset += copied; 252 copied = v9fs_pack(in_sg, in_num, offset, str->data, str->size); 253 } 254 break; 255 } 256 case 'Q': { 257 V9fsQID *qidp = va_arg(ap, V9fsQID *); 258 copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap, "bdq", 259 qidp->type, qidp->version, 260 qidp->path); 261 break; 262 } 263 case 'S': { 264 V9fsStat *statp = va_arg(ap, V9fsStat *); 265 copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap, 266 "wwdQdddqsssssddd", 267 statp->size, statp->type, statp->dev, 268 &statp->qid, statp->mode, statp->atime, 269 statp->mtime, statp->length, 270 &statp->name, 271 &statp->uid, &statp->gid, &statp->muid, 272 &statp->extension, statp->n_uid, 273 statp->n_gid, statp->n_muid); 274 break; 275 } 276 case 'A': { 277 V9fsStatDotl *statp = va_arg(ap, V9fsStatDotl *); 278 copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap, 279 "qQdddqqqqqqqqqqqqqqq", 280 statp->st_result_mask, 281 &statp->qid, statp->st_mode, 282 statp->st_uid, statp->st_gid, 283 statp->st_nlink, statp->st_rdev, 284 statp->st_size, statp->st_blksize, 285 statp->st_blocks, statp->st_atime_sec, 286 statp->st_atime_nsec, 287 statp->st_mtime_sec, 288 statp->st_mtime_nsec, 289 statp->st_ctime_sec, 290 statp->st_ctime_nsec, 291 statp->st_btime_sec, 292 statp->st_btime_nsec, statp->st_gen, 293 statp->st_data_version); 294 break; 295 } 296 default: 297 break; 298 } 299 if (copied < 0) { 300 return copied; 301 } 302 offset += copied; 303 } 304 305 return offset - old_offset; 306 } 307 308 ssize_t v9fs_iov_marshal(struct iovec *in_sg, int in_num, size_t offset, 309 int bswap, const char *fmt, ...) 310 { 311 ssize_t ret; 312 va_list ap; 313 314 va_start(ap, fmt); 315 ret = v9fs_iov_vmarshal(in_sg, in_num, offset, bswap, fmt, ap); 316 va_end(ap); 317 318 return ret; 319 } 320