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