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