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