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