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