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