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