cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

virtfs-proxy-helper.c (31415B)


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