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