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