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