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); 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 client = accept(sock, (struct sockaddr *)&qemu, &size); 764 if (client < 0) { 765 do_perror("accept"); 766 return -1; 767 } 768 return client; 769 } 770 771 static void usage(char *prog) 772 { 773 fprintf(stderr, "usage: %s\n" 774 " -p|--path <path> 9p path to export\n" 775 " {-f|--fd <socket-descriptor>} socket file descriptor to be used\n" 776 " {-s|--socket <socketname> socket file used for communication\n" 777 " \t-u|--uid <uid> -g|--gid <gid>} - uid:gid combination to give " 778 " access to this socket\n" 779 " \tNote: -s & -f can not be used together\n" 780 " [-n|--nodaemon] Run as a normal program\n", 781 basename(prog)); 782 } 783 784 static int process_reply(int sock, int type, 785 struct iovec *out_iovec, int retval) 786 { 787 switch (type) { 788 case T_OPEN: 789 case T_CREATE: 790 if (send_fd(sock, retval) < 0) { 791 return -1; 792 } 793 break; 794 case T_MKNOD: 795 case T_MKDIR: 796 case T_SYMLINK: 797 case T_LINK: 798 case T_CHMOD: 799 case T_CHOWN: 800 case T_TRUNCATE: 801 case T_UTIME: 802 case T_RENAME: 803 case T_REMOVE: 804 case T_LSETXATTR: 805 case T_LREMOVEXATTR: 806 if (send_status(sock, out_iovec, retval) < 0) { 807 return -1; 808 } 809 break; 810 case T_LSTAT: 811 case T_STATFS: 812 case T_READLINK: 813 case T_LGETXATTR: 814 case T_LLISTXATTR: 815 case T_GETVERSION: 816 if (send_response(sock, out_iovec, retval) < 0) { 817 return -1; 818 } 819 break; 820 default: 821 return -1; 822 break; 823 } 824 return 0; 825 } 826 827 static int process_requests(int sock) 828 { 829 int flags; 830 int size = 0; 831 int retval = 0; 832 uint64_t offset; 833 ProxyHeader header; 834 int mode, uid, gid; 835 V9fsString name, value; 836 struct timespec spec[2]; 837 V9fsString oldpath, path; 838 struct iovec in_iovec, out_iovec; 839 840 in_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ); 841 in_iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ; 842 out_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ); 843 out_iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ; 844 845 while (1) { 846 /* 847 * initialize the header type, so that we send 848 * response to proper request type. 849 */ 850 header.type = 0; 851 retval = read_request(sock, &in_iovec, &header); 852 if (retval < 0) { 853 goto err_out; 854 } 855 856 switch (header.type) { 857 case T_OPEN: 858 retval = do_open(&in_iovec); 859 break; 860 case T_CREATE: 861 retval = do_create(&in_iovec); 862 break; 863 case T_MKNOD: 864 case T_MKDIR: 865 case T_SYMLINK: 866 retval = do_create_others(header.type, &in_iovec); 867 break; 868 case T_LINK: 869 v9fs_string_init(&path); 870 v9fs_string_init(&oldpath); 871 retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, 872 "ss", &oldpath, &path); 873 if (retval > 0) { 874 retval = link(oldpath.data, path.data); 875 if (retval < 0) { 876 retval = -errno; 877 } 878 } 879 v9fs_string_free(&oldpath); 880 v9fs_string_free(&path); 881 break; 882 case T_LSTAT: 883 case T_STATFS: 884 retval = do_stat(header.type, &in_iovec, &out_iovec); 885 break; 886 case T_READLINK: 887 retval = do_readlink(&in_iovec, &out_iovec); 888 break; 889 case T_CHMOD: 890 v9fs_string_init(&path); 891 retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, 892 "sd", &path, &mode); 893 if (retval > 0) { 894 retval = chmod(path.data, mode); 895 if (retval < 0) { 896 retval = -errno; 897 } 898 } 899 v9fs_string_free(&path); 900 break; 901 case T_CHOWN: 902 v9fs_string_init(&path); 903 retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sdd", &path, 904 &uid, &gid); 905 if (retval > 0) { 906 retval = lchown(path.data, uid, gid); 907 if (retval < 0) { 908 retval = -errno; 909 } 910 } 911 v9fs_string_free(&path); 912 break; 913 case T_TRUNCATE: 914 v9fs_string_init(&path); 915 retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sq", 916 &path, &offset); 917 if (retval > 0) { 918 retval = truncate(path.data, offset); 919 if (retval < 0) { 920 retval = -errno; 921 } 922 } 923 v9fs_string_free(&path); 924 break; 925 case T_UTIME: 926 v9fs_string_init(&path); 927 retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sqqqq", &path, 928 &spec[0].tv_sec, &spec[0].tv_nsec, 929 &spec[1].tv_sec, &spec[1].tv_nsec); 930 if (retval > 0) { 931 retval = qemu_utimens(path.data, spec); 932 if (retval < 0) { 933 retval = -errno; 934 } 935 } 936 v9fs_string_free(&path); 937 break; 938 case T_RENAME: 939 v9fs_string_init(&path); 940 v9fs_string_init(&oldpath); 941 retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, 942 "ss", &oldpath, &path); 943 if (retval > 0) { 944 retval = rename(oldpath.data, path.data); 945 if (retval < 0) { 946 retval = -errno; 947 } 948 } 949 v9fs_string_free(&oldpath); 950 v9fs_string_free(&path); 951 break; 952 case T_REMOVE: 953 v9fs_string_init(&path); 954 retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "s", &path); 955 if (retval > 0) { 956 retval = remove(path.data); 957 if (retval < 0) { 958 retval = -errno; 959 } 960 } 961 v9fs_string_free(&path); 962 break; 963 case T_LGETXATTR: 964 case T_LLISTXATTR: 965 retval = do_getxattr(header.type, &in_iovec, &out_iovec); 966 break; 967 case T_LSETXATTR: 968 v9fs_string_init(&path); 969 v9fs_string_init(&name); 970 v9fs_string_init(&value); 971 retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sssdd", &path, 972 &name, &value, &size, &flags); 973 if (retval > 0) { 974 retval = lsetxattr(path.data, 975 name.data, value.data, size, flags); 976 if (retval < 0) { 977 retval = -errno; 978 } 979 } 980 v9fs_string_free(&path); 981 v9fs_string_free(&name); 982 v9fs_string_free(&value); 983 break; 984 case T_LREMOVEXATTR: 985 v9fs_string_init(&path); 986 v9fs_string_init(&name); 987 retval = proxy_unmarshal(&in_iovec, 988 PROXY_HDR_SZ, "ss", &path, &name); 989 if (retval > 0) { 990 retval = lremovexattr(path.data, name.data); 991 if (retval < 0) { 992 retval = -errno; 993 } 994 } 995 v9fs_string_free(&path); 996 v9fs_string_free(&name); 997 break; 998 case T_GETVERSION: 999 retval = do_getversion(&in_iovec, &out_iovec); 1000 break; 1001 default: 1002 goto err_out; 1003 break; 1004 } 1005 1006 if (process_reply(sock, header.type, &out_iovec, retval) < 0) { 1007 goto err_out; 1008 } 1009 } 1010 err_out: 1011 g_free(in_iovec.iov_base); 1012 g_free(out_iovec.iov_base); 1013 return -1; 1014 } 1015 1016 int main(int argc, char **argv) 1017 { 1018 int sock; 1019 uid_t own_u; 1020 gid_t own_g; 1021 char *rpath = NULL; 1022 char *sock_name = NULL; 1023 struct stat stbuf; 1024 int c, option_index; 1025 #ifdef FS_IOC_GETVERSION 1026 int retval; 1027 struct statfs st_fs; 1028 #endif 1029 1030 is_daemon = true; 1031 sock = -1; 1032 own_u = own_g = -1; 1033 while (1) { 1034 option_index = 0; 1035 c = getopt_long(argc, argv, "p:nh?f:s:u:g:", helper_opts, 1036 &option_index); 1037 if (c == -1) { 1038 break; 1039 } 1040 switch (c) { 1041 case 'p': 1042 rpath = g_strdup(optarg); 1043 break; 1044 case 'n': 1045 is_daemon = false; 1046 break; 1047 case 'f': 1048 sock = atoi(optarg); 1049 break; 1050 case 's': 1051 sock_name = g_strdup(optarg); 1052 break; 1053 case 'u': 1054 own_u = atoi(optarg); 1055 break; 1056 case 'g': 1057 own_g = atoi(optarg); 1058 break; 1059 case '?': 1060 case 'h': 1061 default: 1062 usage(argv[0]); 1063 exit(EXIT_FAILURE); 1064 } 1065 } 1066 1067 /* Parameter validation */ 1068 if ((sock_name == NULL && sock == -1) || rpath == NULL) { 1069 fprintf(stderr, "socket, socket descriptor or path not specified\n"); 1070 usage(argv[0]); 1071 return -1; 1072 } 1073 1074 if (sock_name && sock != -1) { 1075 fprintf(stderr, "both named socket and socket descriptor specified\n"); 1076 usage(argv[0]); 1077 exit(EXIT_FAILURE); 1078 } 1079 1080 if (sock_name && (own_u == -1 || own_g == -1)) { 1081 fprintf(stderr, "owner uid:gid not specified, "); 1082 fprintf(stderr, 1083 "owner uid:gid specifies who can access the socket file\n"); 1084 usage(argv[0]); 1085 exit(EXIT_FAILURE); 1086 } 1087 1088 if (lstat(rpath, &stbuf) < 0) { 1089 fprintf(stderr, "invalid path \"%s\" specified, %s\n", 1090 rpath, strerror(errno)); 1091 exit(EXIT_FAILURE); 1092 } 1093 1094 if (!S_ISDIR(stbuf.st_mode)) { 1095 fprintf(stderr, "specified path \"%s\" is not directory\n", rpath); 1096 exit(EXIT_FAILURE); 1097 } 1098 1099 if (is_daemon) { 1100 if (daemon(0, 0) < 0) { 1101 fprintf(stderr, "daemon call failed\n"); 1102 exit(EXIT_FAILURE); 1103 } 1104 openlog(PROGNAME, LOG_PID, LOG_DAEMON); 1105 } 1106 1107 do_log(LOG_INFO, "Started\n"); 1108 if (sock_name) { 1109 sock = proxy_socket(sock_name, own_u, own_g); 1110 if (sock < 0) { 1111 goto error; 1112 } 1113 } 1114 1115 get_version = false; 1116 #ifdef FS_IOC_GETVERSION 1117 /* check whether underlying FS support IOC_GETVERSION */ 1118 retval = statfs(rpath, &st_fs); 1119 if (!retval) { 1120 switch (st_fs.f_type) { 1121 case EXT2_SUPER_MAGIC: 1122 case BTRFS_SUPER_MAGIC: 1123 case REISERFS_SUPER_MAGIC: 1124 case XFS_SUPER_MAGIC: 1125 get_version = true; 1126 break; 1127 } 1128 } 1129 #endif 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 umask(0); 1140 1141 if (init_capabilities() < 0) { 1142 goto error; 1143 } 1144 1145 process_requests(sock); 1146 error: 1147 do_log(LOG_INFO, "Done\n"); 1148 closelog(); 1149 return 0; 1150 } 1151