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