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