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