9p-proxy.c (35621B)
1/* 2 * 9p Proxy callback 3 * 4 * Copyright IBM, Corp. 2011 5 * 6 * Authors: 7 * M. Mohan Kumar <mohan@in.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 * Not so fast! You might want to read the 9p developer docs first: 15 * https://wiki.qemu.org/Documentation/9p 16 */ 17 18#include "qemu/osdep.h" 19#include <sys/socket.h> 20#include <sys/un.h> 21#include "qemu-common.h" 22#include "9p.h" 23#include "qapi/error.h" 24#include "qemu/cutils.h" 25#include "qemu/error-report.h" 26#include "qemu/option.h" 27#include "fsdev/qemu-fsdev.h" 28#include "9p-proxy.h" 29 30typedef struct V9fsProxy { 31 int sockfd; 32 QemuMutex mutex; 33 struct iovec in_iovec; 34 struct iovec out_iovec; 35} V9fsProxy; 36 37/* 38 * Return received file descriptor on success in *status. 39 * errno is also returned on *status (which will be < 0) 40 * return < 0 on transport error. 41 */ 42static int v9fs_receivefd(int sockfd, int *status) 43{ 44 struct iovec iov; 45 struct msghdr msg; 46 struct cmsghdr *cmsg; 47 int retval, data, fd; 48 union MsgControl msg_control; 49 50 iov.iov_base = &data; 51 iov.iov_len = sizeof(data); 52 53 memset(&msg, 0, sizeof(msg)); 54 msg.msg_iov = &iov; 55 msg.msg_iovlen = 1; 56 msg.msg_control = &msg_control; 57 msg.msg_controllen = sizeof(msg_control); 58 59 do { 60 retval = recvmsg(sockfd, &msg, 0); 61 } while (retval < 0 && errno == EINTR); 62 if (retval <= 0) { 63 return retval; 64 } 65 /* 66 * data is set to V9FS_FD_VALID, if ancillary data is sent. If this 67 * request doesn't need ancillary data (fd) or an error occurred, 68 * data is set to negative errno value. 69 */ 70 if (data != V9FS_FD_VALID) { 71 *status = data; 72 return 0; 73 } 74 /* 75 * File descriptor (fd) is sent in the ancillary data. Check if we 76 * indeed received it. One of the reasons to fail to receive it is if 77 * we exceeded the maximum number of file descriptors! 78 */ 79 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { 80 if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)) || 81 cmsg->cmsg_level != SOL_SOCKET || 82 cmsg->cmsg_type != SCM_RIGHTS) { 83 continue; 84 } 85 fd = *((int *)CMSG_DATA(cmsg)); 86 *status = fd; 87 return 0; 88 } 89 *status = -ENFILE; /* Ancillary data sent but not received */ 90 return 0; 91} 92 93static ssize_t socket_read(int sockfd, void *buff, size_t size) 94{ 95 ssize_t retval, total = 0; 96 97 while (size) { 98 retval = read(sockfd, buff, size); 99 if (retval == 0) { 100 return -EIO; 101 } 102 if (retval < 0) { 103 if (errno == EINTR) { 104 continue; 105 } 106 return -errno; 107 } 108 size -= retval; 109 buff += retval; 110 total += retval; 111 } 112 return total; 113} 114 115/* Converts proxy_statfs to VFS statfs structure */ 116static void prstatfs_to_statfs(struct statfs *stfs, ProxyStatFS *prstfs) 117{ 118 memset(stfs, 0, sizeof(*stfs)); 119 stfs->f_type = prstfs->f_type; 120 stfs->f_bsize = prstfs->f_bsize; 121 stfs->f_blocks = prstfs->f_blocks; 122 stfs->f_bfree = prstfs->f_bfree; 123 stfs->f_bavail = prstfs->f_bavail; 124 stfs->f_files = prstfs->f_files; 125 stfs->f_ffree = prstfs->f_ffree; 126 stfs->f_fsid.__val[0] = prstfs->f_fsid[0] & 0xFFFFFFFFU; 127 stfs->f_fsid.__val[1] = prstfs->f_fsid[1] >> 32 & 0xFFFFFFFFU; 128 stfs->f_namelen = prstfs->f_namelen; 129 stfs->f_frsize = prstfs->f_frsize; 130} 131 132/* Converts proxy_stat structure to VFS stat structure */ 133static void prstat_to_stat(struct stat *stbuf, ProxyStat *prstat) 134{ 135 memset(stbuf, 0, sizeof(*stbuf)); 136 stbuf->st_dev = prstat->st_dev; 137 stbuf->st_ino = prstat->st_ino; 138 stbuf->st_nlink = prstat->st_nlink; 139 stbuf->st_mode = prstat->st_mode; 140 stbuf->st_uid = prstat->st_uid; 141 stbuf->st_gid = prstat->st_gid; 142 stbuf->st_rdev = prstat->st_rdev; 143 stbuf->st_size = prstat->st_size; 144 stbuf->st_blksize = prstat->st_blksize; 145 stbuf->st_blocks = prstat->st_blocks; 146 stbuf->st_atim.tv_sec = prstat->st_atim_sec; 147 stbuf->st_atim.tv_nsec = prstat->st_atim_nsec; 148 stbuf->st_mtime = prstat->st_mtim_sec; 149 stbuf->st_mtim.tv_nsec = prstat->st_mtim_nsec; 150 stbuf->st_ctime = prstat->st_ctim_sec; 151 stbuf->st_ctim.tv_nsec = prstat->st_ctim_nsec; 152} 153 154/* 155 * Response contains two parts 156 * {header, data} 157 * header.type == T_ERROR, data -> -errno 158 * header.type == T_SUCCESS, data -> response 159 * size of errno/response is given by header.size 160 * returns < 0, on transport error. response is 161 * valid only if status >= 0. 162 */ 163static int v9fs_receive_response(V9fsProxy *proxy, int type, 164 int *status, void *response) 165{ 166 int retval; 167 ProxyHeader header; 168 struct iovec *reply = &proxy->in_iovec; 169 170 *status = 0; 171 reply->iov_len = 0; 172 retval = socket_read(proxy->sockfd, reply->iov_base, PROXY_HDR_SZ); 173 if (retval < 0) { 174 return retval; 175 } 176 reply->iov_len = PROXY_HDR_SZ; 177 retval = proxy_unmarshal(reply, 0, "dd", &header.type, &header.size); 178 assert(retval == 4 * 2); 179 /* 180 * if response size > PROXY_MAX_IO_SZ, read the response but ignore it and 181 * return -ENOBUFS 182 */ 183 if (header.size > PROXY_MAX_IO_SZ) { 184 int count; 185 while (header.size > 0) { 186 count = MIN(PROXY_MAX_IO_SZ, header.size); 187 count = socket_read(proxy->sockfd, reply->iov_base, count); 188 if (count < 0) { 189 return count; 190 } 191 header.size -= count; 192 } 193 *status = -ENOBUFS; 194 return 0; 195 } 196 197 retval = socket_read(proxy->sockfd, 198 reply->iov_base + PROXY_HDR_SZ, header.size); 199 if (retval < 0) { 200 return retval; 201 } 202 reply->iov_len += header.size; 203 /* there was an error during processing request */ 204 if (header.type == T_ERROR) { 205 int ret; 206 ret = proxy_unmarshal(reply, PROXY_HDR_SZ, "d", status); 207 assert(ret == 4); 208 return 0; 209 } 210 211 switch (type) { 212 case T_LSTAT: { 213 ProxyStat prstat; 214 retval = proxy_unmarshal(reply, PROXY_HDR_SZ, 215 "qqqdddqqqqqqqqqq", &prstat.st_dev, 216 &prstat.st_ino, &prstat.st_nlink, 217 &prstat.st_mode, &prstat.st_uid, 218 &prstat.st_gid, &prstat.st_rdev, 219 &prstat.st_size, &prstat.st_blksize, 220 &prstat.st_blocks, 221 &prstat.st_atim_sec, &prstat.st_atim_nsec, 222 &prstat.st_mtim_sec, &prstat.st_mtim_nsec, 223 &prstat.st_ctim_sec, &prstat.st_ctim_nsec); 224 assert(retval == 8 * 3 + 4 * 3 + 8 * 10); 225 prstat_to_stat(response, &prstat); 226 break; 227 } 228 case T_STATFS: { 229 ProxyStatFS prstfs; 230 retval = proxy_unmarshal(reply, PROXY_HDR_SZ, 231 "qqqqqqqqqqq", &prstfs.f_type, 232 &prstfs.f_bsize, &prstfs.f_blocks, 233 &prstfs.f_bfree, &prstfs.f_bavail, 234 &prstfs.f_files, &prstfs.f_ffree, 235 &prstfs.f_fsid[0], &prstfs.f_fsid[1], 236 &prstfs.f_namelen, &prstfs.f_frsize); 237 assert(retval == 8 * 11); 238 prstatfs_to_statfs(response, &prstfs); 239 break; 240 } 241 case T_READLINK: { 242 V9fsString target; 243 v9fs_string_init(&target); 244 retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "s", &target); 245 strcpy(response, target.data); 246 v9fs_string_free(&target); 247 break; 248 } 249 case T_LGETXATTR: 250 case T_LLISTXATTR: { 251 V9fsString xattr; 252 v9fs_string_init(&xattr); 253 retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "s", &xattr); 254 memcpy(response, xattr.data, xattr.size); 255 v9fs_string_free(&xattr); 256 break; 257 } 258 case T_GETVERSION: 259 retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "q", response); 260 assert(retval == 8); 261 break; 262 default: 263 return -1; 264 } 265 if (retval < 0) { 266 *status = retval; 267 } 268 return 0; 269} 270 271/* 272 * return < 0 on transport error. 273 * *status is valid only if return >= 0 274 */ 275static int v9fs_receive_status(V9fsProxy *proxy, 276 struct iovec *reply, int *status) 277{ 278 int retval; 279 ProxyHeader header; 280 281 *status = 0; 282 reply->iov_len = 0; 283 retval = socket_read(proxy->sockfd, reply->iov_base, PROXY_HDR_SZ); 284 if (retval < 0) { 285 return retval; 286 } 287 reply->iov_len = PROXY_HDR_SZ; 288 retval = proxy_unmarshal(reply, 0, "dd", &header.type, &header.size); 289 assert(retval == 4 * 2); 290 retval = socket_read(proxy->sockfd, 291 reply->iov_base + PROXY_HDR_SZ, header.size); 292 if (retval < 0) { 293 return retval; 294 } 295 reply->iov_len += header.size; 296 retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "d", status); 297 assert(retval == 4); 298 return 0; 299} 300 301/* 302 * Proxy->header and proxy->request written to socket by QEMU process. 303 * This request read by proxy helper process 304 * returns 0 on success and -errno on error 305 */ 306static int v9fs_request(V9fsProxy *proxy, int type, void *response, ...) 307{ 308 dev_t rdev; 309 va_list ap; 310 int size = 0; 311 int retval = 0; 312 uint64_t offset; 313 ProxyHeader header = { 0, 0}; 314 struct timespec spec[2]; 315 int flags, mode, uid, gid; 316 V9fsString *name, *value; 317 V9fsString *path, *oldpath; 318 struct iovec *iovec = NULL, *reply = NULL; 319 320 qemu_mutex_lock(&proxy->mutex); 321 322 if (proxy->sockfd == -1) { 323 retval = -EIO; 324 goto err_out; 325 } 326 iovec = &proxy->out_iovec; 327 reply = &proxy->in_iovec; 328 va_start(ap, response); 329 switch (type) { 330 case T_OPEN: 331 path = va_arg(ap, V9fsString *); 332 flags = va_arg(ap, int); 333 retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, flags); 334 if (retval > 0) { 335 header.size = retval; 336 header.type = T_OPEN; 337 } 338 break; 339 case T_CREATE: 340 path = va_arg(ap, V9fsString *); 341 flags = va_arg(ap, int); 342 mode = va_arg(ap, int); 343 uid = va_arg(ap, int); 344 gid = va_arg(ap, int); 345 retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sdddd", path, 346 flags, mode, uid, gid); 347 if (retval > 0) { 348 header.size = retval; 349 header.type = T_CREATE; 350 } 351 break; 352 case T_MKNOD: 353 path = va_arg(ap, V9fsString *); 354 mode = va_arg(ap, int); 355 rdev = va_arg(ap, long int); 356 uid = va_arg(ap, int); 357 gid = va_arg(ap, int); 358 retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddsdq", 359 uid, gid, path, mode, rdev); 360 if (retval > 0) { 361 header.size = retval; 362 header.type = T_MKNOD; 363 } 364 break; 365 case T_MKDIR: 366 path = va_arg(ap, V9fsString *); 367 mode = va_arg(ap, int); 368 uid = va_arg(ap, int); 369 gid = va_arg(ap, int); 370 retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddsd", 371 uid, gid, path, mode); 372 if (retval > 0) { 373 header.size = retval; 374 header.type = T_MKDIR; 375 } 376 break; 377 case T_SYMLINK: 378 oldpath = va_arg(ap, V9fsString *); 379 path = va_arg(ap, V9fsString *); 380 uid = va_arg(ap, int); 381 gid = va_arg(ap, int); 382 retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddss", 383 uid, gid, oldpath, path); 384 if (retval > 0) { 385 header.size = retval; 386 header.type = T_SYMLINK; 387 } 388 break; 389 case T_LINK: 390 oldpath = va_arg(ap, V9fsString *); 391 path = va_arg(ap, V9fsString *); 392 retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss", 393 oldpath, path); 394 if (retval > 0) { 395 header.size = retval; 396 header.type = T_LINK; 397 } 398 break; 399 case T_LSTAT: 400 path = va_arg(ap, V9fsString *); 401 retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path); 402 if (retval > 0) { 403 header.size = retval; 404 header.type = T_LSTAT; 405 } 406 break; 407 case T_READLINK: 408 path = va_arg(ap, V9fsString *); 409 size = va_arg(ap, int); 410 retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, size); 411 if (retval > 0) { 412 header.size = retval; 413 header.type = T_READLINK; 414 } 415 break; 416 case T_STATFS: 417 path = va_arg(ap, V9fsString *); 418 retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path); 419 if (retval > 0) { 420 header.size = retval; 421 header.type = T_STATFS; 422 } 423 break; 424 case T_CHMOD: 425 path = va_arg(ap, V9fsString *); 426 mode = va_arg(ap, int); 427 retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, mode); 428 if (retval > 0) { 429 header.size = retval; 430 header.type = T_CHMOD; 431 } 432 break; 433 case T_CHOWN: 434 path = va_arg(ap, V9fsString *); 435 uid = va_arg(ap, int); 436 gid = va_arg(ap, int); 437 retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sdd", path, uid, gid); 438 if (retval > 0) { 439 header.size = retval; 440 header.type = T_CHOWN; 441 } 442 break; 443 case T_TRUNCATE: 444 path = va_arg(ap, V9fsString *); 445 offset = va_arg(ap, uint64_t); 446 retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sq", path, offset); 447 if (retval > 0) { 448 header.size = retval; 449 header.type = T_TRUNCATE; 450 } 451 break; 452 case T_UTIME: 453 path = va_arg(ap, V9fsString *); 454 spec[0].tv_sec = va_arg(ap, long); 455 spec[0].tv_nsec = va_arg(ap, long); 456 spec[1].tv_sec = va_arg(ap, long); 457 spec[1].tv_nsec = va_arg(ap, long); 458 retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sqqqq", path, 459 spec[0].tv_sec, spec[1].tv_nsec, 460 spec[1].tv_sec, spec[1].tv_nsec); 461 if (retval > 0) { 462 header.size = retval; 463 header.type = T_UTIME; 464 } 465 break; 466 case T_RENAME: 467 oldpath = va_arg(ap, V9fsString *); 468 path = va_arg(ap, V9fsString *); 469 retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss", oldpath, path); 470 if (retval > 0) { 471 header.size = retval; 472 header.type = T_RENAME; 473 } 474 break; 475 case T_REMOVE: 476 path = va_arg(ap, V9fsString *); 477 retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path); 478 if (retval > 0) { 479 header.size = retval; 480 header.type = T_REMOVE; 481 } 482 break; 483 case T_LGETXATTR: 484 size = va_arg(ap, int); 485 path = va_arg(ap, V9fsString *); 486 name = va_arg(ap, V9fsString *); 487 retval = proxy_marshal(iovec, PROXY_HDR_SZ, 488 "dss", size, path, name); 489 if (retval > 0) { 490 header.size = retval; 491 header.type = T_LGETXATTR; 492 } 493 break; 494 case T_LLISTXATTR: 495 size = va_arg(ap, int); 496 path = va_arg(ap, V9fsString *); 497 retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ds", size, path); 498 if (retval > 0) { 499 header.size = retval; 500 header.type = T_LLISTXATTR; 501 } 502 break; 503 case T_LSETXATTR: 504 path = va_arg(ap, V9fsString *); 505 name = va_arg(ap, V9fsString *); 506 value = va_arg(ap, V9fsString *); 507 size = va_arg(ap, int); 508 flags = va_arg(ap, int); 509 retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sssdd", 510 path, name, value, size, flags); 511 if (retval > 0) { 512 header.size = retval; 513 header.type = T_LSETXATTR; 514 } 515 break; 516 case T_LREMOVEXATTR: 517 path = va_arg(ap, V9fsString *); 518 name = va_arg(ap, V9fsString *); 519 retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss", path, name); 520 if (retval > 0) { 521 header.size = retval; 522 header.type = T_LREMOVEXATTR; 523 } 524 break; 525 case T_GETVERSION: 526 path = va_arg(ap, V9fsString *); 527 retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path); 528 if (retval > 0) { 529 header.size = retval; 530 header.type = T_GETVERSION; 531 } 532 break; 533 default: 534 error_report("Invalid type %d", type); 535 retval = -EINVAL; 536 break; 537 } 538 va_end(ap); 539 540 if (retval < 0) { 541 goto err_out; 542 } 543 544 /* marshal the header details */ 545 retval = proxy_marshal(iovec, 0, "dd", header.type, header.size); 546 assert(retval == 4 * 2); 547 header.size += PROXY_HDR_SZ; 548 549 retval = qemu_write_full(proxy->sockfd, iovec->iov_base, header.size); 550 if (retval != header.size) { 551 goto close_error; 552 } 553 554 switch (type) { 555 case T_OPEN: 556 case T_CREATE: 557 /* 558 * A file descriptor is returned as response for 559 * T_OPEN,T_CREATE on success 560 */ 561 if (v9fs_receivefd(proxy->sockfd, &retval) < 0) { 562 goto close_error; 563 } 564 break; 565 case T_MKNOD: 566 case T_MKDIR: 567 case T_SYMLINK: 568 case T_LINK: 569 case T_CHMOD: 570 case T_CHOWN: 571 case T_RENAME: 572 case T_TRUNCATE: 573 case T_UTIME: 574 case T_REMOVE: 575 case T_LSETXATTR: 576 case T_LREMOVEXATTR: 577 if (v9fs_receive_status(proxy, reply, &retval) < 0) { 578 goto close_error; 579 } 580 break; 581 case T_LSTAT: 582 case T_READLINK: 583 case T_STATFS: 584 case T_GETVERSION: 585 if (v9fs_receive_response(proxy, type, &retval, response) < 0) { 586 goto close_error; 587 } 588 break; 589 case T_LGETXATTR: 590 case T_LLISTXATTR: 591 if (!size) { 592 if (v9fs_receive_status(proxy, reply, &retval) < 0) { 593 goto close_error; 594 } 595 } else { 596 if (v9fs_receive_response(proxy, type, &retval, response) < 0) { 597 goto close_error; 598 } 599 } 600 break; 601 } 602 603err_out: 604 qemu_mutex_unlock(&proxy->mutex); 605 return retval; 606 607close_error: 608 close(proxy->sockfd); 609 proxy->sockfd = -1; 610 qemu_mutex_unlock(&proxy->mutex); 611 return -EIO; 612} 613 614static int proxy_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf) 615{ 616 int retval; 617 retval = v9fs_request(fs_ctx->private, T_LSTAT, stbuf, fs_path); 618 if (retval < 0) { 619 errno = -retval; 620 return -1; 621 } 622 return retval; 623} 624 625static ssize_t proxy_readlink(FsContext *fs_ctx, V9fsPath *fs_path, 626 char *buf, size_t bufsz) 627{ 628 int retval; 629 retval = v9fs_request(fs_ctx->private, T_READLINK, buf, fs_path, bufsz); 630 if (retval < 0) { 631 errno = -retval; 632 return -1; 633 } 634 return strlen(buf); 635} 636 637static int proxy_close(FsContext *ctx, V9fsFidOpenState *fs) 638{ 639 return close(fs->fd); 640} 641 642static int proxy_closedir(FsContext *ctx, V9fsFidOpenState *fs) 643{ 644 return closedir(fs->dir.stream); 645} 646 647static int proxy_open(FsContext *ctx, V9fsPath *fs_path, 648 int flags, V9fsFidOpenState *fs) 649{ 650 fs->fd = v9fs_request(ctx->private, T_OPEN, NULL, fs_path, flags); 651 if (fs->fd < 0) { 652 errno = -fs->fd; 653 fs->fd = -1; 654 } 655 return fs->fd; 656} 657 658static int proxy_opendir(FsContext *ctx, 659 V9fsPath *fs_path, V9fsFidOpenState *fs) 660{ 661 int serrno, fd; 662 663 fs->dir.stream = NULL; 664 fd = v9fs_request(ctx->private, T_OPEN, NULL, fs_path, O_DIRECTORY); 665 if (fd < 0) { 666 errno = -fd; 667 return -1; 668 } 669 fs->dir.stream = fdopendir(fd); 670 if (!fs->dir.stream) { 671 serrno = errno; 672 close(fd); 673 errno = serrno; 674 return -1; 675 } 676 return 0; 677} 678 679static void proxy_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) 680{ 681 rewinddir(fs->dir.stream); 682} 683 684static off_t proxy_telldir(FsContext *ctx, V9fsFidOpenState *fs) 685{ 686 return telldir(fs->dir.stream); 687} 688 689static struct dirent *proxy_readdir(FsContext *ctx, V9fsFidOpenState *fs) 690{ 691 return readdir(fs->dir.stream); 692} 693 694static void proxy_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) 695{ 696 seekdir(fs->dir.stream, off); 697} 698 699static ssize_t proxy_preadv(FsContext *ctx, V9fsFidOpenState *fs, 700 const struct iovec *iov, 701 int iovcnt, off_t offset) 702{ 703 ssize_t ret; 704#ifdef CONFIG_PREADV 705 ret = preadv(fs->fd, iov, iovcnt, offset); 706#else 707 ret = lseek(fs->fd, offset, SEEK_SET); 708 if (ret >= 0) { 709 ret = readv(fs->fd, iov, iovcnt); 710 } 711#endif 712 return ret; 713} 714 715static ssize_t proxy_pwritev(FsContext *ctx, V9fsFidOpenState *fs, 716 const struct iovec *iov, 717 int iovcnt, off_t offset) 718{ 719 ssize_t ret; 720 721#ifdef CONFIG_PREADV 722 ret = pwritev(fs->fd, iov, iovcnt, offset); 723#else 724 ret = lseek(fs->fd, offset, SEEK_SET); 725 if (ret >= 0) { 726 ret = writev(fs->fd, iov, iovcnt); 727 } 728#endif 729#ifdef CONFIG_SYNC_FILE_RANGE 730 if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { 731 /* 732 * Initiate a writeback. This is not a data integrity sync. 733 * We want to ensure that we don't leave dirty pages in the cache 734 * after write when writeout=immediate is sepcified. 735 */ 736 sync_file_range(fs->fd, offset, ret, 737 SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE); 738 } 739#endif 740 return ret; 741} 742 743static int proxy_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) 744{ 745 int retval; 746 retval = v9fs_request(fs_ctx->private, T_CHMOD, NULL, fs_path, 747 credp->fc_mode); 748 if (retval < 0) { 749 errno = -retval; 750 } 751 return retval; 752} 753 754static int proxy_mknod(FsContext *fs_ctx, V9fsPath *dir_path, 755 const char *name, FsCred *credp) 756{ 757 int retval; 758 V9fsString fullname; 759 760 v9fs_string_init(&fullname); 761 v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); 762 763 retval = v9fs_request(fs_ctx->private, T_MKNOD, NULL, &fullname, 764 credp->fc_mode, credp->fc_rdev, 765 credp->fc_uid, credp->fc_gid); 766 v9fs_string_free(&fullname); 767 if (retval < 0) { 768 errno = -retval; 769 retval = -1; 770 } 771 return retval; 772} 773 774static int proxy_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, 775 const char *name, FsCred *credp) 776{ 777 int retval; 778 V9fsString fullname; 779 780 v9fs_string_init(&fullname); 781 v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); 782 783 retval = v9fs_request(fs_ctx->private, T_MKDIR, NULL, &fullname, 784 credp->fc_mode, credp->fc_uid, credp->fc_gid); 785 v9fs_string_free(&fullname); 786 if (retval < 0) { 787 errno = -retval; 788 retval = -1; 789 } 790 return retval; 791} 792 793static int proxy_fstat(FsContext *fs_ctx, int fid_type, 794 V9fsFidOpenState *fs, struct stat *stbuf) 795{ 796 int fd; 797 798 if (fid_type == P9_FID_DIR) { 799 fd = dirfd(fs->dir.stream); 800 } else { 801 fd = fs->fd; 802 } 803 return fstat(fd, stbuf); 804} 805 806static int proxy_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, 807 int flags, FsCred *credp, V9fsFidOpenState *fs) 808{ 809 V9fsString fullname; 810 811 v9fs_string_init(&fullname); 812 v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); 813 814 fs->fd = v9fs_request(fs_ctx->private, T_CREATE, NULL, &fullname, flags, 815 credp->fc_mode, credp->fc_uid, credp->fc_gid); 816 v9fs_string_free(&fullname); 817 if (fs->fd < 0) { 818 errno = -fs->fd; 819 fs->fd = -1; 820 } 821 return fs->fd; 822} 823 824static int proxy_symlink(FsContext *fs_ctx, const char *oldpath, 825 V9fsPath *dir_path, const char *name, FsCred *credp) 826{ 827 int retval; 828 V9fsString fullname, target; 829 830 v9fs_string_init(&fullname); 831 v9fs_string_init(&target); 832 833 v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); 834 v9fs_string_sprintf(&target, "%s", oldpath); 835 836 retval = v9fs_request(fs_ctx->private, T_SYMLINK, NULL, &target, &fullname, 837 credp->fc_uid, credp->fc_gid); 838 v9fs_string_free(&fullname); 839 v9fs_string_free(&target); 840 if (retval < 0) { 841 errno = -retval; 842 retval = -1; 843 } 844 return retval; 845} 846 847static int proxy_link(FsContext *ctx, V9fsPath *oldpath, 848 V9fsPath *dirpath, const char *name) 849{ 850 int retval; 851 V9fsString newpath; 852 853 v9fs_string_init(&newpath); 854 v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name); 855 856 retval = v9fs_request(ctx->private, T_LINK, NULL, oldpath, &newpath); 857 v9fs_string_free(&newpath); 858 if (retval < 0) { 859 errno = -retval; 860 retval = -1; 861 } 862 return retval; 863} 864 865static int proxy_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) 866{ 867 int retval; 868 869 retval = v9fs_request(ctx->private, T_TRUNCATE, NULL, fs_path, size); 870 if (retval < 0) { 871 errno = -retval; 872 return -1; 873 } 874 return 0; 875} 876 877static int proxy_rename(FsContext *ctx, const char *oldpath, 878 const char *newpath) 879{ 880 int retval; 881 V9fsString oldname, newname; 882 883 v9fs_string_init(&oldname); 884 v9fs_string_init(&newname); 885 886 v9fs_string_sprintf(&oldname, "%s", oldpath); 887 v9fs_string_sprintf(&newname, "%s", newpath); 888 retval = v9fs_request(ctx->private, T_RENAME, NULL, &oldname, &newname); 889 v9fs_string_free(&oldname); 890 v9fs_string_free(&newname); 891 if (retval < 0) { 892 errno = -retval; 893 } 894 return retval; 895} 896 897static int proxy_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) 898{ 899 int retval; 900 retval = v9fs_request(fs_ctx->private, T_CHOWN, NULL, fs_path, 901 credp->fc_uid, credp->fc_gid); 902 if (retval < 0) { 903 errno = -retval; 904 } 905 return retval; 906} 907 908static int proxy_utimensat(FsContext *s, V9fsPath *fs_path, 909 const struct timespec *buf) 910{ 911 int retval; 912 retval = v9fs_request(s->private, T_UTIME, NULL, fs_path, 913 buf[0].tv_sec, buf[0].tv_nsec, 914 buf[1].tv_sec, buf[1].tv_nsec); 915 if (retval < 0) { 916 errno = -retval; 917 } 918 return retval; 919} 920 921static int proxy_remove(FsContext *ctx, const char *path) 922{ 923 int retval; 924 V9fsString name; 925 v9fs_string_init(&name); 926 v9fs_string_sprintf(&name, "%s", path); 927 retval = v9fs_request(ctx->private, T_REMOVE, NULL, &name); 928 v9fs_string_free(&name); 929 if (retval < 0) { 930 errno = -retval; 931 } 932 return retval; 933} 934 935static int proxy_fsync(FsContext *ctx, int fid_type, 936 V9fsFidOpenState *fs, int datasync) 937{ 938 int fd; 939 940 if (fid_type == P9_FID_DIR) { 941 fd = dirfd(fs->dir.stream); 942 } else { 943 fd = fs->fd; 944 } 945 946 if (datasync) { 947 return qemu_fdatasync(fd); 948 } else { 949 return fsync(fd); 950 } 951} 952 953static int proxy_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf) 954{ 955 int retval; 956 retval = v9fs_request(s->private, T_STATFS, stbuf, fs_path); 957 if (retval < 0) { 958 errno = -retval; 959 return -1; 960 } 961 return retval; 962} 963 964static ssize_t proxy_lgetxattr(FsContext *ctx, V9fsPath *fs_path, 965 const char *name, void *value, size_t size) 966{ 967 int retval; 968 V9fsString xname; 969 970 v9fs_string_init(&xname); 971 v9fs_string_sprintf(&xname, "%s", name); 972 retval = v9fs_request(ctx->private, T_LGETXATTR, value, size, fs_path, 973 &xname); 974 v9fs_string_free(&xname); 975 if (retval < 0) { 976 errno = -retval; 977 } 978 return retval; 979} 980 981static ssize_t proxy_llistxattr(FsContext *ctx, V9fsPath *fs_path, 982 void *value, size_t size) 983{ 984 int retval; 985 retval = v9fs_request(ctx->private, T_LLISTXATTR, value, size, fs_path); 986 if (retval < 0) { 987 errno = -retval; 988 } 989 return retval; 990} 991 992static int proxy_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name, 993 void *value, size_t size, int flags) 994{ 995 int retval; 996 V9fsString xname, xvalue; 997 998 v9fs_string_init(&xname); 999 v9fs_string_sprintf(&xname, "%s", name); 1000 1001 v9fs_string_init(&xvalue); 1002 xvalue.size = size; 1003 xvalue.data = g_malloc(size); 1004 memcpy(xvalue.data, value, size); 1005 1006 retval = v9fs_request(ctx->private, T_LSETXATTR, value, fs_path, &xname, 1007 &xvalue, size, flags); 1008 v9fs_string_free(&xname); 1009 v9fs_string_free(&xvalue); 1010 if (retval < 0) { 1011 errno = -retval; 1012 } 1013 return retval; 1014} 1015 1016static int proxy_lremovexattr(FsContext *ctx, V9fsPath *fs_path, 1017 const char *name) 1018{ 1019 int retval; 1020 V9fsString xname; 1021 1022 v9fs_string_init(&xname); 1023 v9fs_string_sprintf(&xname, "%s", name); 1024 retval = v9fs_request(ctx->private, T_LREMOVEXATTR, NULL, fs_path, &xname); 1025 v9fs_string_free(&xname); 1026 if (retval < 0) { 1027 errno = -retval; 1028 } 1029 return retval; 1030} 1031 1032static int proxy_name_to_path(FsContext *ctx, V9fsPath *dir_path, 1033 const char *name, V9fsPath *target) 1034{ 1035 if (dir_path) { 1036 v9fs_path_sprintf(target, "%s/%s", dir_path->data, name); 1037 } else { 1038 v9fs_path_sprintf(target, "%s", name); 1039 } 1040 return 0; 1041} 1042 1043static int proxy_renameat(FsContext *ctx, V9fsPath *olddir, 1044 const char *old_name, V9fsPath *newdir, 1045 const char *new_name) 1046{ 1047 int ret; 1048 V9fsString old_full_name, new_full_name; 1049 1050 v9fs_string_init(&old_full_name); 1051 v9fs_string_init(&new_full_name); 1052 1053 v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name); 1054 v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name); 1055 1056 ret = proxy_rename(ctx, old_full_name.data, new_full_name.data); 1057 v9fs_string_free(&old_full_name); 1058 v9fs_string_free(&new_full_name); 1059 return ret; 1060} 1061 1062static int proxy_unlinkat(FsContext *ctx, V9fsPath *dir, 1063 const char *name, int flags) 1064{ 1065 int ret; 1066 V9fsString fullname; 1067 v9fs_string_init(&fullname); 1068 1069 v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name); 1070 ret = proxy_remove(ctx, fullname.data); 1071 v9fs_string_free(&fullname); 1072 1073 return ret; 1074} 1075 1076static int proxy_ioc_getversion(FsContext *fs_ctx, V9fsPath *path, 1077 mode_t st_mode, uint64_t *st_gen) 1078{ 1079 int err; 1080 1081 /* Do not try to open special files like device nodes, fifos etc 1082 * we can get fd for regular files and directories only 1083 */ 1084 if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) { 1085 errno = ENOTTY; 1086 return -1; 1087 } 1088 err = v9fs_request(fs_ctx->private, T_GETVERSION, st_gen, path); 1089 if (err < 0) { 1090 errno = -err; 1091 err = -1; 1092 } 1093 return err; 1094} 1095 1096static int connect_namedsocket(const char *path, Error **errp) 1097{ 1098 int sockfd; 1099 struct sockaddr_un helper; 1100 1101 if (strlen(path) >= sizeof(helper.sun_path)) { 1102 error_setg(errp, "socket name too long"); 1103 return -1; 1104 } 1105 sockfd = socket(AF_UNIX, SOCK_STREAM, 0); 1106 if (sockfd < 0) { 1107 error_setg_errno(errp, errno, "failed to create client socket"); 1108 return -1; 1109 } 1110 strcpy(helper.sun_path, path); 1111 helper.sun_family = AF_UNIX; 1112 if (connect(sockfd, (struct sockaddr *)&helper, sizeof(helper)) < 0) { 1113 error_setg_errno(errp, errno, "failed to connect to '%s'", path); 1114 close(sockfd); 1115 return -1; 1116 } 1117 1118 /* remove the socket for security reasons */ 1119 unlink(path); 1120 return sockfd; 1121} 1122 1123static void error_append_socket_sockfd_hint(Error *const *errp) 1124{ 1125 error_append_hint(errp, "Either specify socket=/some/path where /some/path" 1126 " points to a listening AF_UNIX socket or sock_fd=fd" 1127 " where fd is a file descriptor to a connected AF_UNIX" 1128 " socket\n"); 1129} 1130 1131static int proxy_parse_opts(QemuOpts *opts, FsDriverEntry *fs, Error **errp) 1132{ 1133 const char *socket = qemu_opt_get(opts, "socket"); 1134 const char *sock_fd = qemu_opt_get(opts, "sock_fd"); 1135 1136 if (!socket && !sock_fd) { 1137 error_setg(errp, "both socket and sock_fd properties are missing"); 1138 error_append_socket_sockfd_hint(errp); 1139 return -1; 1140 } 1141 if (socket && sock_fd) { 1142 error_setg(errp, "both socket and sock_fd properties are set"); 1143 error_append_socket_sockfd_hint(errp); 1144 return -1; 1145 } 1146 if (socket) { 1147 fs->path = g_strdup(socket); 1148 fs->export_flags |= V9FS_PROXY_SOCK_NAME; 1149 } else { 1150 fs->path = g_strdup(sock_fd); 1151 fs->export_flags |= V9FS_PROXY_SOCK_FD; 1152 } 1153 return 0; 1154} 1155 1156static int proxy_init(FsContext *ctx, Error **errp) 1157{ 1158 V9fsProxy *proxy = g_malloc(sizeof(V9fsProxy)); 1159 int sock_id; 1160 1161 if (ctx->export_flags & V9FS_PROXY_SOCK_NAME) { 1162 sock_id = connect_namedsocket(ctx->fs_root, errp); 1163 } else { 1164 sock_id = atoi(ctx->fs_root); 1165 if (sock_id < 0) { 1166 error_setg(errp, "socket descriptor not initialized"); 1167 } 1168 } 1169 if (sock_id < 0) { 1170 g_free(proxy); 1171 return -1; 1172 } 1173 g_free(ctx->fs_root); 1174 ctx->fs_root = NULL; 1175 1176 proxy->in_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ); 1177 proxy->in_iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ; 1178 proxy->out_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ); 1179 proxy->out_iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ; 1180 1181 ctx->private = proxy; 1182 proxy->sockfd = sock_id; 1183 qemu_mutex_init(&proxy->mutex); 1184 1185 ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT; 1186 ctx->exops.get_st_gen = proxy_ioc_getversion; 1187 return 0; 1188} 1189 1190static void proxy_cleanup(FsContext *ctx) 1191{ 1192 V9fsProxy *proxy = ctx->private; 1193 1194 if (!proxy) { 1195 return; 1196 } 1197 1198 g_free(proxy->out_iovec.iov_base); 1199 g_free(proxy->in_iovec.iov_base); 1200 if (ctx->export_flags & V9FS_PROXY_SOCK_NAME) { 1201 close(proxy->sockfd); 1202 } 1203 g_free(proxy); 1204} 1205 1206FileOperations proxy_ops = { 1207 .parse_opts = proxy_parse_opts, 1208 .init = proxy_init, 1209 .cleanup = proxy_cleanup, 1210 .lstat = proxy_lstat, 1211 .readlink = proxy_readlink, 1212 .close = proxy_close, 1213 .closedir = proxy_closedir, 1214 .open = proxy_open, 1215 .opendir = proxy_opendir, 1216 .rewinddir = proxy_rewinddir, 1217 .telldir = proxy_telldir, 1218 .readdir = proxy_readdir, 1219 .seekdir = proxy_seekdir, 1220 .preadv = proxy_preadv, 1221 .pwritev = proxy_pwritev, 1222 .chmod = proxy_chmod, 1223 .mknod = proxy_mknod, 1224 .mkdir = proxy_mkdir, 1225 .fstat = proxy_fstat, 1226 .open2 = proxy_open2, 1227 .symlink = proxy_symlink, 1228 .link = proxy_link, 1229 .truncate = proxy_truncate, 1230 .rename = proxy_rename, 1231 .chown = proxy_chown, 1232 .utimensat = proxy_utimensat, 1233 .remove = proxy_remove, 1234 .fsync = proxy_fsync, 1235 .statfs = proxy_statfs, 1236 .lgetxattr = proxy_lgetxattr, 1237 .llistxattr = proxy_llistxattr, 1238 .lsetxattr = proxy_lsetxattr, 1239 .lremovexattr = proxy_lremovexattr, 1240 .name_to_path = proxy_name_to_path, 1241 .renameat = proxy_renameat, 1242 .unlinkat = proxy_unlinkat, 1243};