1 /* 2 * Helper for QEMU Proxy FS Driver 3 * Copyright IBM, Corp. 2011 4 * 5 * Authors: 6 * M. Mohan Kumar <mohan@in.ibm.com> 7 * 8 * This work is licensed under the terms of the GNU GPL, version 2. See 9 * the COPYING file in the top-level directory. 10 */ 11 12 #include <sys/resource.h> 13 #include <getopt.h> 14 #include <syslog.h> 15 #include <sys/capability.h> 16 #include <sys/fsuid.h> 17 #include <sys/vfs.h> 18 #include <sys/ioctl.h> 19 #include <linux/fs.h> 20 #ifdef CONFIG_LINUX_MAGIC_H 21 #include <linux/magic.h> 22 #endif 23 #include "qemu-common.h" 24 #include "qemu_socket.h" 25 #include "qemu-xattr.h" 26 #include "virtio-9p-marshal.h" 27 #include "hw/9pfs/virtio-9p-proxy.h" 28 #include "fsdev/virtio-9p-marshal.h" 29 30 #define PROGNAME "virtfs-proxy-helper" 31 32 #ifndef XFS_SUPER_MAGIC 33 #define XFS_SUPER_MAGIC 0x58465342 34 #endif 35 #ifndef EXT2_SUPER_MAGIC 36 #define EXT2_SUPER_MAGIC 0xEF53 37 #endif 38 #ifndef REISERFS_SUPER_MAGIC 39 #define REISERFS_SUPER_MAGIC 0x52654973 40 #endif 41 #ifndef BTRFS_SUPER_MAGIC 42 #define BTRFS_SUPER_MAGIC 0x9123683E 43 #endif 44 45 static struct option helper_opts[] = { 46 {"fd", required_argument, NULL, 'f'}, 47 {"path", required_argument, NULL, 'p'}, 48 {"nodaemon", no_argument, NULL, 'n'}, 49 {"socket", required_argument, NULL, 's'}, 50 {"uid", required_argument, NULL, 'u'}, 51 {"gid", required_argument, NULL, 'g'}, 52 }; 53 54 static bool is_daemon; 55 static bool get_version; /* IOC getversion IOCTL supported */ 56 57 static void GCC_FMT_ATTR(2, 3) do_log(int loglevel, const char *format, ...) 58 { 59 va_list ap; 60 61 va_start(ap, format); 62 if (is_daemon) { 63 vsyslog(LOG_CRIT, format, ap); 64 } else { 65 vfprintf(stderr, format, ap); 66 } 67 va_end(ap); 68 } 69 70 static void do_perror(const char *string) 71 { 72 if (is_daemon) { 73 syslog(LOG_CRIT, "%s:%s", string, strerror(errno)); 74 } else { 75 fprintf(stderr, "%s:%s\n", string, strerror(errno)); 76 } 77 } 78 79 static int do_cap_set(cap_value_t *cap_value, int size, int reset) 80 { 81 cap_t caps; 82 if (reset) { 83 /* 84 * Start with an empty set and set permitted and effective 85 */ 86 caps = cap_init(); 87 if (caps == NULL) { 88 do_perror("cap_init"); 89 return -1; 90 } 91 if (cap_set_flag(caps, CAP_PERMITTED, size, cap_value, CAP_SET) < 0) { 92 do_perror("cap_set_flag"); 93 goto error; 94 } 95 } else { 96 caps = cap_get_proc(); 97 if (!caps) { 98 do_perror("cap_get_proc"); 99 return -1; 100 } 101 } 102 if (cap_set_flag(caps, CAP_EFFECTIVE, size, cap_value, CAP_SET) < 0) { 103 do_perror("cap_set_flag"); 104 goto error; 105 } 106 if (cap_set_proc(caps) < 0) { 107 do_perror("cap_set_proc"); 108 goto error; 109 } 110 cap_free(caps); 111 return 0; 112 113 error: 114 cap_free(caps); 115 return -1; 116 } 117 118 static int init_capabilities(void) 119 { 120 /* helper needs following capbabilities only */ 121 cap_value_t cap_list[] = { 122 CAP_CHOWN, 123 CAP_DAC_OVERRIDE, 124 CAP_FOWNER, 125 CAP_FSETID, 126 CAP_SETGID, 127 CAP_MKNOD, 128 CAP_SETUID, 129 }; 130 return do_cap_set(cap_list, ARRAY_SIZE(cap_list), 1); 131 } 132 133 static int socket_read(int sockfd, void *buff, ssize_t size) 134 { 135 ssize_t retval, total = 0; 136 137 while (size) { 138 retval = read(sockfd, buff, size); 139 if (retval == 0) { 140 return -EIO; 141 } 142 if (retval < 0) { 143 if (errno == EINTR) { 144 continue; 145 } 146 return -errno; 147 } 148 size -= retval; 149 buff += retval; 150 total += retval; 151 } 152 return total; 153 } 154 155 static int socket_write(int sockfd, void *buff, ssize_t size) 156 { 157 ssize_t retval, total = 0; 158 159 while (size) { 160 retval = write(sockfd, buff, size); 161 if (retval < 0) { 162 if (errno == EINTR) { 163 continue; 164 } 165 return -errno; 166 } 167 size -= retval; 168 buff += retval; 169 total += retval; 170 } 171 return total; 172 } 173 174 static int read_request(int sockfd, struct iovec *iovec, ProxyHeader *header) 175 { 176 int retval; 177 178 /* 179 * read the request header. 180 */ 181 iovec->iov_len = 0; 182 retval = socket_read(sockfd, iovec->iov_base, PROXY_HDR_SZ); 183 if (retval < 0) { 184 return retval; 185 } 186 iovec->iov_len = PROXY_HDR_SZ; 187 retval = proxy_unmarshal(iovec, 0, "dd", &header->type, &header->size); 188 if (retval < 0) { 189 return retval; 190 } 191 /* 192 * We can't process message.size > PROXY_MAX_IO_SZ. 193 * Treat it as fatal error 194 */ 195 if (header->size > PROXY_MAX_IO_SZ) { 196 return -ENOBUFS; 197 } 198 retval = socket_read(sockfd, iovec->iov_base + PROXY_HDR_SZ, header->size); 199 if (retval < 0) { 200 return retval; 201 } 202 iovec->iov_len += header->size; 203 return 0; 204 } 205 206 static int send_fd(int sockfd, int fd) 207 { 208 struct msghdr msg; 209 struct iovec iov; 210 int retval, data; 211 struct cmsghdr *cmsg; 212 union MsgControl msg_control; 213 214 iov.iov_base = &data; 215 iov.iov_len = sizeof(data); 216 217 memset(&msg, 0, sizeof(msg)); 218 msg.msg_iov = &iov; 219 msg.msg_iovlen = 1; 220 /* No ancillary data on error */ 221 if (fd < 0) { 222 /* fd is really negative errno if the request failed */ 223 data = fd; 224 } else { 225 data = V9FS_FD_VALID; 226 msg.msg_control = &msg_control; 227 msg.msg_controllen = sizeof(msg_control); 228 229 cmsg = &msg_control.cmsg; 230 cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); 231 cmsg->cmsg_level = SOL_SOCKET; 232 cmsg->cmsg_type = SCM_RIGHTS; 233 memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); 234 } 235 236 do { 237 retval = sendmsg(sockfd, &msg, 0); 238 } while (retval < 0 && errno == EINTR); 239 if (fd >= 0) { 240 close(fd); 241 } 242 if (retval < 0) { 243 return retval; 244 } 245 return 0; 246 } 247 248 static int send_status(int sockfd, struct iovec *iovec, int status) 249 { 250 ProxyHeader header; 251 int retval, msg_size;; 252 253 if (status < 0) { 254 header.type = T_ERROR; 255 } else { 256 header.type = T_SUCCESS; 257 } 258 header.size = sizeof(status); 259 /* 260 * marshal the return status. We don't check error. 261 * because we are sure we have enough space for the status 262 */ 263 msg_size = proxy_marshal(iovec, 0, "ddd", header.type, 264 header.size, status); 265 retval = socket_write(sockfd, iovec->iov_base, msg_size); 266 if (retval < 0) { 267 return retval; 268 } 269 return 0; 270 } 271 272 /* 273 * from man 7 capabilities, section 274 * Effect of User ID Changes on Capabilities: 275 * 4. If the file system user ID is changed from 0 to nonzero (see setfsuid(2)) 276 * then the following capabilities are cleared from the effective set: 277 * CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_DAC_READ_SEARCH, CAP_FOWNER, CAP_FSETID, 278 * CAP_LINUX_IMMUTABLE (since Linux 2.2.30), CAP_MAC_OVERRIDE, and CAP_MKNOD 279 * (since Linux 2.2.30). If the file system UID is changed from nonzero to 0, 280 * then any of these capabilities that are enabled in the permitted set 281 * are enabled in the effective set. 282 */ 283 static int setfsugid(int uid, int gid) 284 { 285 /* 286 * We still need DAC_OVERRIDE because we don't change 287 * supplementary group ids, and hence may be subjected DAC rules 288 */ 289 cap_value_t cap_list[] = { 290 CAP_DAC_OVERRIDE, 291 }; 292 293 setfsgid(gid); 294 setfsuid(uid); 295 296 if (uid != 0 || gid != 0) { 297 return do_cap_set(cap_list, ARRAY_SIZE(cap_list), 0); 298 } 299 return 0; 300 } 301 302 /* 303 * send response in two parts 304 * 1) ProxyHeader 305 * 2) Response or error status 306 * This function should be called with marshaled response 307 * send_response constructs header part and error part only. 308 * send response sends {ProxyHeader,Response} if the request was success 309 * otherwise sends {ProxyHeader,error status} 310 */ 311 static int send_response(int sock, struct iovec *iovec, int size) 312 { 313 int retval; 314 ProxyHeader header; 315 316 /* 317 * If response size exceeds available iovec->iov_len, 318 * we return ENOBUFS 319 */ 320 if (size > PROXY_MAX_IO_SZ) { 321 size = -ENOBUFS; 322 } 323 324 if (size < 0) { 325 /* 326 * In case of error we would not have got the error encoded 327 * already so encode the error here. 328 */ 329 header.type = T_ERROR; 330 header.size = sizeof(size); 331 proxy_marshal(iovec, PROXY_HDR_SZ, "d", size); 332 } else { 333 header.type = T_SUCCESS; 334 header.size = size; 335 } 336 proxy_marshal(iovec, 0, "dd", header.type, header.size); 337 retval = socket_write(sock, iovec->iov_base, header.size + PROXY_HDR_SZ); 338 if (retval < 0) { 339 return retval;; 340 } 341 return 0; 342 } 343 344 /* 345 * gets generation number 346 * returns -errno on failure and sizeof(generation number) on success 347 */ 348 static int do_getversion(struct iovec *iovec, struct iovec *out_iovec) 349 { 350 uint64_t version; 351 int retval = -ENOTTY; 352 #ifdef FS_IOC_GETVERSION 353 int fd; 354 V9fsString path; 355 #endif 356 357 358 /* no need to issue ioctl */ 359 if (!get_version) { 360 version = 0; 361 retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "q", version); 362 return retval; 363 } 364 #ifdef FS_IOC_GETVERSION 365 retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "s", &path); 366 if (retval < 0) { 367 return retval; 368 } 369 370 fd = open(path.data, O_RDONLY); 371 if (fd < 0) { 372 retval = -errno; 373 goto err_out; 374 } 375 if (ioctl(fd, FS_IOC_GETVERSION, &version) < 0) { 376 retval = -errno; 377 } else { 378 retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "q", version); 379 } 380 close(fd); 381 err_out: 382 v9fs_string_free(&path); 383 #endif 384 return retval; 385 } 386 387 static int do_getxattr(int type, struct iovec *iovec, struct iovec *out_iovec) 388 { 389 int size = 0, offset, retval; 390 V9fsString path, name, xattr; 391 392 v9fs_string_init(&xattr); 393 v9fs_string_init(&path); 394 retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "ds", &size, &path); 395 if (retval < 0) { 396 return retval; 397 } 398 offset = PROXY_HDR_SZ + retval; 399 400 if (size) { 401 xattr.data = g_malloc(size); 402 xattr.size = size; 403 } 404 switch (type) { 405 case T_LGETXATTR: 406 v9fs_string_init(&name); 407 retval = proxy_unmarshal(iovec, offset, "s", &name); 408 if (retval > 0) { 409 retval = lgetxattr(path.data, name.data, xattr.data, size); 410 if (retval < 0) { 411 retval = -errno; 412 } else { 413 xattr.size = retval; 414 } 415 } 416 v9fs_string_free(&name); 417 break; 418 case T_LLISTXATTR: 419 retval = llistxattr(path.data, xattr.data, size); 420 if (retval < 0) { 421 retval = -errno; 422 } else { 423 xattr.size = retval; 424 } 425 break; 426 } 427 if (retval < 0) { 428 goto err_out; 429 } 430 431 if (!size) { 432 proxy_marshal(out_iovec, PROXY_HDR_SZ, "d", retval); 433 retval = sizeof(retval); 434 } else { 435 retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "s", &xattr); 436 } 437 err_out: 438 v9fs_string_free(&xattr); 439 v9fs_string_free(&path); 440 return retval; 441 } 442 443 static void stat_to_prstat(ProxyStat *pr_stat, struct stat *stat) 444 { 445 memset(pr_stat, 0, sizeof(*pr_stat)); 446 pr_stat->st_dev = stat->st_dev; 447 pr_stat->st_ino = stat->st_ino; 448 pr_stat->st_nlink = stat->st_nlink; 449 pr_stat->st_mode = stat->st_mode; 450 pr_stat->st_uid = stat->st_uid; 451 pr_stat->st_gid = stat->st_gid; 452 pr_stat->st_rdev = stat->st_rdev; 453 pr_stat->st_size = stat->st_size; 454 pr_stat->st_blksize = stat->st_blksize; 455 pr_stat->st_blocks = stat->st_blocks; 456 pr_stat->st_atim_sec = stat->st_atim.tv_sec; 457 pr_stat->st_atim_nsec = stat->st_atim.tv_nsec; 458 pr_stat->st_mtim_sec = stat->st_mtim.tv_sec; 459 pr_stat->st_mtim_nsec = stat->st_mtim.tv_nsec; 460 pr_stat->st_ctim_sec = stat->st_ctim.tv_sec; 461 pr_stat->st_ctim_nsec = stat->st_ctim.tv_nsec; 462 } 463 464 static void statfs_to_prstatfs(ProxyStatFS *pr_stfs, struct statfs *stfs) 465 { 466 memset(pr_stfs, 0, sizeof(*pr_stfs)); 467 pr_stfs->f_type = stfs->f_type; 468 pr_stfs->f_bsize = stfs->f_bsize; 469 pr_stfs->f_blocks = stfs->f_blocks; 470 pr_stfs->f_bfree = stfs->f_bfree; 471 pr_stfs->f_bavail = stfs->f_bavail; 472 pr_stfs->f_files = stfs->f_files; 473 pr_stfs->f_ffree = stfs->f_ffree; 474 pr_stfs->f_fsid[0] = stfs->f_fsid.__val[0]; 475 pr_stfs->f_fsid[1] = stfs->f_fsid.__val[1]; 476 pr_stfs->f_namelen = stfs->f_namelen; 477 pr_stfs->f_frsize = stfs->f_frsize; 478 } 479 480 /* 481 * Gets stat/statfs information and packs in out_iovec structure 482 * on success returns number of bytes packed in out_iovec struture 483 * otherwise returns -errno 484 */ 485 static int do_stat(int type, struct iovec *iovec, struct iovec *out_iovec) 486 { 487 int retval; 488 V9fsString path; 489 ProxyStat pr_stat; 490 ProxyStatFS pr_stfs; 491 struct stat st_buf; 492 struct statfs stfs_buf; 493 494 v9fs_string_init(&path); 495 retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "s", &path); 496 if (retval < 0) { 497 return retval; 498 } 499 500 switch (type) { 501 case T_LSTAT: 502 retval = lstat(path.data, &st_buf); 503 if (retval < 0) { 504 retval = -errno; 505 } else { 506 stat_to_prstat(&pr_stat, &st_buf); 507 retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, 508 "qqqdddqqqqqqqqqq", pr_stat.st_dev, 509 pr_stat.st_ino, pr_stat.st_nlink, 510 pr_stat.st_mode, pr_stat.st_uid, 511 pr_stat.st_gid, pr_stat.st_rdev, 512 pr_stat.st_size, pr_stat.st_blksize, 513 pr_stat.st_blocks, 514 pr_stat.st_atim_sec, pr_stat.st_atim_nsec, 515 pr_stat.st_mtim_sec, pr_stat.st_mtim_nsec, 516 pr_stat.st_ctim_sec, pr_stat.st_ctim_nsec); 517 } 518 break; 519 case T_STATFS: 520 retval = statfs(path.data, &stfs_buf); 521 if (retval < 0) { 522 retval = -errno; 523 } else { 524 statfs_to_prstatfs(&pr_stfs, &stfs_buf); 525 retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, 526 "qqqqqqqqqqq", pr_stfs.f_type, 527 pr_stfs.f_bsize, pr_stfs.f_blocks, 528 pr_stfs.f_bfree, pr_stfs.f_bavail, 529 pr_stfs.f_files, pr_stfs.f_ffree, 530 pr_stfs.f_fsid[0], pr_stfs.f_fsid[1], 531 pr_stfs.f_namelen, pr_stfs.f_frsize); 532 } 533 break; 534 } 535 v9fs_string_free(&path); 536 return retval; 537 } 538 539 static int do_readlink(struct iovec *iovec, struct iovec *out_iovec) 540 { 541 char *buffer; 542 int size, retval; 543 V9fsString target, path; 544 545 v9fs_string_init(&path); 546 retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "sd", &path, &size); 547 if (retval < 0) { 548 v9fs_string_free(&path); 549 return retval; 550 } 551 buffer = g_malloc(size); 552 v9fs_string_init(&target); 553 retval = readlink(path.data, buffer, size); 554 if (retval > 0) { 555 buffer[retval] = '\0'; 556 v9fs_string_sprintf(&target, "%s", buffer); 557 retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "s", &target); 558 } else { 559 retval = -errno; 560 } 561 g_free(buffer); 562 v9fs_string_free(&target); 563 v9fs_string_free(&path); 564 return retval; 565 } 566 567 /* 568 * create other filesystem objects and send 0 on success 569 * return -errno on error 570 */ 571 static int do_create_others(int type, struct iovec *iovec) 572 { 573 dev_t rdev; 574 int retval = 0; 575 int offset = PROXY_HDR_SZ; 576 V9fsString oldpath, path; 577 int mode, uid, gid, cur_uid, cur_gid; 578 579 v9fs_string_init(&path); 580 v9fs_string_init(&oldpath); 581 cur_uid = geteuid(); 582 cur_gid = getegid(); 583 584 retval = proxy_unmarshal(iovec, offset, "dd", &uid, &gid); 585 if (retval < 0) { 586 return retval; 587 } 588 offset += retval; 589 retval = setfsugid(uid, gid); 590 if (retval < 0) { 591 retval = -errno; 592 goto err_out; 593 } 594 switch (type) { 595 case T_MKNOD: 596 retval = proxy_unmarshal(iovec, offset, "sdq", &path, &mode, &rdev); 597 if (retval < 0) { 598 goto err_out; 599 } 600 retval = mknod(path.data, mode, rdev); 601 break; 602 case T_MKDIR: 603 retval = proxy_unmarshal(iovec, offset, "sd", &path, &mode); 604 if (retval < 0) { 605 goto err_out; 606 } 607 retval = mkdir(path.data, mode); 608 break; 609 case T_SYMLINK: 610 retval = proxy_unmarshal(iovec, offset, "ss", &oldpath, &path); 611 if (retval < 0) { 612 goto err_out; 613 } 614 retval = symlink(oldpath.data, path.data); 615 break; 616 } 617 if (retval < 0) { 618 retval = -errno; 619 } 620 621 err_out: 622 v9fs_string_free(&path); 623 v9fs_string_free(&oldpath); 624 setfsugid(cur_uid, cur_gid); 625 return retval; 626 } 627 628 /* 629 * create a file and send fd on success 630 * return -errno on error 631 */ 632 static int do_create(struct iovec *iovec) 633 { 634 int ret; 635 V9fsString path; 636 int flags, mode, uid, gid, cur_uid, cur_gid; 637 638 v9fs_string_init(&path); 639 ret = proxy_unmarshal(iovec, PROXY_HDR_SZ, "sdddd", 640 &path, &flags, &mode, &uid, &gid); 641 if (ret < 0) { 642 goto unmarshal_err_out; 643 } 644 cur_uid = geteuid(); 645 cur_gid = getegid(); 646 ret = setfsugid(uid, gid); 647 if (ret < 0) { 648 /* 649 * On failure reset back to the 650 * old uid/gid 651 */ 652 ret = -errno; 653 goto err_out; 654 } 655 ret = open(path.data, flags, mode); 656 if (ret < 0) { 657 ret = -errno; 658 } 659 660 err_out: 661 setfsugid(cur_uid, cur_gid); 662 unmarshal_err_out: 663 v9fs_string_free(&path); 664 return ret; 665 } 666 667 /* 668 * open a file and send fd on success 669 * return -errno on error 670 */ 671 static int do_open(struct iovec *iovec) 672 { 673 int flags, ret; 674 V9fsString path; 675 676 v9fs_string_init(&path); 677 ret = proxy_unmarshal(iovec, PROXY_HDR_SZ, "sd", &path, &flags); 678 if (ret < 0) { 679 goto err_out; 680 } 681 ret = open(path.data, flags); 682 if (ret < 0) { 683 ret = -errno; 684 } 685 err_out: 686 v9fs_string_free(&path); 687 return ret; 688 } 689 690 /* create unix domain socket and return the descriptor */ 691 static int proxy_socket(const char *path, uid_t uid, gid_t gid) 692 { 693 int sock, client; 694 struct sockaddr_un proxy, qemu; 695 socklen_t size; 696 697 /* requested socket already exists, refuse to start */ 698 if (!access(path, F_OK)) { 699 do_log(LOG_CRIT, "socket already exists\n"); 700 return -1; 701 } 702 703 sock = socket(AF_UNIX, SOCK_STREAM, 0); 704 if (sock < 0) { 705 do_perror("socket"); 706 return -1; 707 } 708 709 /* mask other part of mode bits */ 710 umask(7); 711 712 proxy.sun_family = AF_UNIX; 713 strcpy(proxy.sun_path, path); 714 if (bind(sock, (struct sockaddr *)&proxy, 715 sizeof(struct sockaddr_un)) < 0) { 716 do_perror("bind"); 717 return -1; 718 } 719 if (chown(proxy.sun_path, uid, gid) < 0) { 720 do_perror("chown"); 721 return -1; 722 } 723 if (listen(sock, 1) < 0) { 724 do_perror("listen"); 725 return -1; 726 } 727 728 client = accept(sock, (struct sockaddr *)&qemu, &size); 729 if (client < 0) { 730 do_perror("accept"); 731 return -1; 732 } 733 return client; 734 } 735 736 static void usage(char *prog) 737 { 738 fprintf(stderr, "usage: %s\n" 739 " -p|--path <path> 9p path to export\n" 740 " {-f|--fd <socket-descriptor>} socket file descriptor to be used\n" 741 " {-s|--socket <socketname> socket file used for communication\n" 742 " \t-u|--uid <uid> -g|--gid <gid>} - uid:gid combination to give " 743 " access to this socket\n" 744 " \tNote: -s & -f can not be used together\n" 745 " [-n|--nodaemon] Run as a normal program\n", 746 basename(prog)); 747 } 748 749 static int process_reply(int sock, int type, 750 struct iovec *out_iovec, int retval) 751 { 752 switch (type) { 753 case T_OPEN: 754 case T_CREATE: 755 if (send_fd(sock, retval) < 0) { 756 return -1; 757 } 758 break; 759 case T_MKNOD: 760 case T_MKDIR: 761 case T_SYMLINK: 762 case T_LINK: 763 case T_CHMOD: 764 case T_CHOWN: 765 case T_TRUNCATE: 766 case T_UTIME: 767 case T_RENAME: 768 case T_REMOVE: 769 case T_LSETXATTR: 770 case T_LREMOVEXATTR: 771 if (send_status(sock, out_iovec, retval) < 0) { 772 return -1; 773 } 774 break; 775 case T_LSTAT: 776 case T_STATFS: 777 case T_READLINK: 778 case T_LGETXATTR: 779 case T_LLISTXATTR: 780 case T_GETVERSION: 781 if (send_response(sock, out_iovec, retval) < 0) { 782 return -1; 783 } 784 break; 785 default: 786 return -1; 787 break; 788 } 789 return 0; 790 } 791 792 static int process_requests(int sock) 793 { 794 int flags; 795 int size = 0; 796 int retval = 0; 797 uint64_t offset; 798 ProxyHeader header; 799 int mode, uid, gid; 800 V9fsString name, value; 801 struct timespec spec[2]; 802 V9fsString oldpath, path; 803 struct iovec in_iovec, out_iovec; 804 805 in_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ); 806 in_iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ; 807 out_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ); 808 out_iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ; 809 810 while (1) { 811 /* 812 * initialize the header type, so that we send 813 * response to proper request type. 814 */ 815 header.type = 0; 816 retval = read_request(sock, &in_iovec, &header); 817 if (retval < 0) { 818 goto err_out; 819 } 820 821 switch (header.type) { 822 case T_OPEN: 823 retval = do_open(&in_iovec); 824 break; 825 case T_CREATE: 826 retval = do_create(&in_iovec); 827 break; 828 case T_MKNOD: 829 case T_MKDIR: 830 case T_SYMLINK: 831 retval = do_create_others(header.type, &in_iovec); 832 break; 833 case T_LINK: 834 v9fs_string_init(&path); 835 v9fs_string_init(&oldpath); 836 retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, 837 "ss", &oldpath, &path); 838 if (retval > 0) { 839 retval = link(oldpath.data, path.data); 840 if (retval < 0) { 841 retval = -errno; 842 } 843 } 844 v9fs_string_free(&oldpath); 845 v9fs_string_free(&path); 846 break; 847 case T_LSTAT: 848 case T_STATFS: 849 retval = do_stat(header.type, &in_iovec, &out_iovec); 850 break; 851 case T_READLINK: 852 retval = do_readlink(&in_iovec, &out_iovec); 853 break; 854 case T_CHMOD: 855 v9fs_string_init(&path); 856 retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, 857 "sd", &path, &mode); 858 if (retval > 0) { 859 retval = chmod(path.data, mode); 860 if (retval < 0) { 861 retval = -errno; 862 } 863 } 864 v9fs_string_free(&path); 865 break; 866 case T_CHOWN: 867 v9fs_string_init(&path); 868 retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sdd", &path, 869 &uid, &gid); 870 if (retval > 0) { 871 retval = lchown(path.data, uid, gid); 872 if (retval < 0) { 873 retval = -errno; 874 } 875 } 876 v9fs_string_free(&path); 877 break; 878 case T_TRUNCATE: 879 v9fs_string_init(&path); 880 retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sq", 881 &path, &offset); 882 if (retval > 0) { 883 retval = truncate(path.data, offset); 884 if (retval < 0) { 885 retval = -errno; 886 } 887 } 888 v9fs_string_free(&path); 889 break; 890 case T_UTIME: 891 v9fs_string_init(&path); 892 retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sqqqq", &path, 893 &spec[0].tv_sec, &spec[0].tv_nsec, 894 &spec[1].tv_sec, &spec[1].tv_nsec); 895 if (retval > 0) { 896 retval = qemu_utimens(path.data, spec); 897 if (retval < 0) { 898 retval = -errno; 899 } 900 } 901 v9fs_string_free(&path); 902 break; 903 case T_RENAME: 904 v9fs_string_init(&path); 905 v9fs_string_init(&oldpath); 906 retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, 907 "ss", &oldpath, &path); 908 if (retval > 0) { 909 retval = rename(oldpath.data, path.data); 910 if (retval < 0) { 911 retval = -errno; 912 } 913 } 914 v9fs_string_free(&oldpath); 915 v9fs_string_free(&path); 916 break; 917 case T_REMOVE: 918 v9fs_string_init(&path); 919 retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "s", &path); 920 if (retval > 0) { 921 retval = remove(path.data); 922 if (retval < 0) { 923 retval = -errno; 924 } 925 } 926 v9fs_string_free(&path); 927 break; 928 case T_LGETXATTR: 929 case T_LLISTXATTR: 930 retval = do_getxattr(header.type, &in_iovec, &out_iovec); 931 break; 932 case T_LSETXATTR: 933 v9fs_string_init(&path); 934 v9fs_string_init(&name); 935 v9fs_string_init(&value); 936 retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sssdd", &path, 937 &name, &value, &size, &flags); 938 if (retval > 0) { 939 retval = lsetxattr(path.data, 940 name.data, value.data, size, flags); 941 if (retval < 0) { 942 retval = -errno; 943 } 944 } 945 v9fs_string_free(&path); 946 v9fs_string_free(&name); 947 v9fs_string_free(&value); 948 break; 949 case T_LREMOVEXATTR: 950 v9fs_string_init(&path); 951 v9fs_string_init(&name); 952 retval = proxy_unmarshal(&in_iovec, 953 PROXY_HDR_SZ, "ss", &path, &name); 954 if (retval > 0) { 955 retval = lremovexattr(path.data, name.data); 956 if (retval < 0) { 957 retval = -errno; 958 } 959 } 960 v9fs_string_free(&path); 961 v9fs_string_free(&name); 962 break; 963 case T_GETVERSION: 964 retval = do_getversion(&in_iovec, &out_iovec); 965 break; 966 default: 967 goto err_out; 968 break; 969 } 970 971 if (process_reply(sock, header.type, &out_iovec, retval) < 0) { 972 goto err_out; 973 } 974 } 975 err_out: 976 g_free(in_iovec.iov_base); 977 g_free(out_iovec.iov_base); 978 return -1; 979 } 980 981 int main(int argc, char **argv) 982 { 983 int sock; 984 uid_t own_u; 985 gid_t own_g; 986 char *rpath = NULL; 987 char *sock_name = NULL; 988 struct stat stbuf; 989 int c, option_index; 990 #ifdef FS_IOC_GETVERSION 991 int retval; 992 struct statfs st_fs; 993 #endif 994 995 is_daemon = true; 996 sock = -1; 997 own_u = own_g = -1; 998 while (1) { 999 option_index = 0; 1000 c = getopt_long(argc, argv, "p:nh?f:s:u:g:", helper_opts, 1001 &option_index); 1002 if (c == -1) { 1003 break; 1004 } 1005 switch (c) { 1006 case 'p': 1007 rpath = strdup(optarg); 1008 break; 1009 case 'n': 1010 is_daemon = false; 1011 break; 1012 case 'f': 1013 sock = atoi(optarg); 1014 break; 1015 case 's': 1016 sock_name = strdup(optarg); 1017 break; 1018 case 'u': 1019 own_u = atoi(optarg); 1020 break; 1021 case 'g': 1022 own_g = atoi(optarg); 1023 break; 1024 case '?': 1025 case 'h': 1026 default: 1027 usage(argv[0]); 1028 exit(EXIT_FAILURE); 1029 } 1030 } 1031 1032 /* Parameter validation */ 1033 if ((sock_name == NULL && sock == -1) || rpath == NULL) { 1034 fprintf(stderr, "socket, socket descriptor or path not specified\n"); 1035 usage(argv[0]); 1036 return -1; 1037 } 1038 1039 if (*sock_name && (own_u == -1 || own_g == -1)) { 1040 fprintf(stderr, "owner uid:gid not specified, "); 1041 fprintf(stderr, 1042 "owner uid:gid specifies who can access the socket file\n"); 1043 usage(argv[0]); 1044 exit(EXIT_FAILURE); 1045 } 1046 1047 if (lstat(rpath, &stbuf) < 0) { 1048 fprintf(stderr, "invalid path \"%s\" specified, %s\n", 1049 rpath, strerror(errno)); 1050 exit(EXIT_FAILURE); 1051 } 1052 1053 if (!S_ISDIR(stbuf.st_mode)) { 1054 fprintf(stderr, "specified path \"%s\" is not directory\n", rpath); 1055 exit(EXIT_FAILURE); 1056 } 1057 1058 if (is_daemon) { 1059 if (daemon(0, 0) < 0) { 1060 fprintf(stderr, "daemon call failed\n"); 1061 exit(EXIT_FAILURE); 1062 } 1063 openlog(PROGNAME, LOG_PID, LOG_DAEMON); 1064 } 1065 1066 do_log(LOG_INFO, "Started\n"); 1067 if (*sock_name) { 1068 sock = proxy_socket(sock_name, own_u, own_g); 1069 if (sock < 0) { 1070 goto error; 1071 } 1072 } 1073 1074 get_version = false; 1075 #ifdef FS_IOC_GETVERSION 1076 /* check whether underlying FS support IOC_GETVERSION */ 1077 retval = statfs(rpath, &st_fs); 1078 if (!retval) { 1079 switch (st_fs.f_type) { 1080 case EXT2_SUPER_MAGIC: 1081 case BTRFS_SUPER_MAGIC: 1082 case REISERFS_SUPER_MAGIC: 1083 case XFS_SUPER_MAGIC: 1084 get_version = true; 1085 break; 1086 } 1087 } 1088 #endif 1089 1090 if (chdir("/") < 0) { 1091 do_perror("chdir"); 1092 goto error; 1093 } 1094 if (chroot(rpath) < 0) { 1095 do_perror("chroot"); 1096 goto error; 1097 } 1098 umask(0); 1099 1100 if (init_capabilities() < 0) { 1101 goto error; 1102 } 1103 1104 process_requests(sock); 1105 error: 1106 do_log(LOG_INFO, "Done\n"); 1107 closelog(); 1108 return 0; 1109 } 1110