qemu/hw/virtio-9p-local.c
<<
>>
Prefs
   1/*
   2 * Virtio 9p Posix callback
   3 *
   4 * Copyright IBM, Corp. 2010
   5 *
   6 * Authors:
   7 *  Anthony Liguori   <aliguori@us.ibm.com>
   8 *
   9 * This work is licensed under the terms of the GNU GPL, version 2.  See
  10 * the COPYING file in the top-level directory.
  11 *
  12 */
  13#include "virtio.h"
  14#include "virtio-9p.h"
  15#include <arpa/inet.h>
  16#include <pwd.h>
  17#include <grp.h>
  18#include <sys/socket.h>
  19#include <sys/un.h>
  20#include <attr/xattr.h>
  21
  22static const char *rpath(FsContext *ctx, const char *path)
  23{
  24    /* FIXME: so wrong... */
  25    static char buffer[4096];
  26    snprintf(buffer, sizeof(buffer), "%s/%s", ctx->fs_root, path);
  27    return buffer;
  28}
  29
  30
  31static int local_lstat(FsContext *fs_ctx, const char *path, struct stat *stbuf)
  32{
  33    int err;
  34    err =  lstat(rpath(fs_ctx, path), stbuf);
  35    if (err) {
  36        return err;
  37    }
  38    if (fs_ctx->fs_sm == SM_MAPPED) {
  39        /* Actual credentials are part of extended attrs */
  40        uid_t tmp_uid;
  41        gid_t tmp_gid;
  42        mode_t tmp_mode;
  43        dev_t tmp_dev;
  44        if (getxattr(rpath(fs_ctx, path), "user.virtfs.uid", &tmp_uid,
  45                    sizeof(uid_t)) > 0) {
  46            stbuf->st_uid = tmp_uid;
  47        }
  48        if (getxattr(rpath(fs_ctx, path), "user.virtfs.gid", &tmp_gid,
  49                    sizeof(gid_t)) > 0) {
  50            stbuf->st_gid = tmp_gid;
  51        }
  52        if (getxattr(rpath(fs_ctx, path), "user.virtfs.mode", &tmp_mode,
  53                    sizeof(mode_t)) > 0) {
  54            stbuf->st_mode = tmp_mode;
  55        }
  56        if (getxattr(rpath(fs_ctx, path), "user.virtfs.rdev", &tmp_dev,
  57                        sizeof(dev_t)) > 0) {
  58                stbuf->st_rdev = tmp_dev;
  59        }
  60    }
  61    return err;
  62}
  63
  64static int local_set_xattr(const char *path, FsCred *credp)
  65{
  66    int err;
  67    if (credp->fc_uid != -1) {
  68        err = setxattr(path, "user.virtfs.uid", &credp->fc_uid, sizeof(uid_t),
  69                0);
  70        if (err) {
  71            return err;
  72        }
  73    }
  74    if (credp->fc_gid != -1) {
  75        err = setxattr(path, "user.virtfs.gid", &credp->fc_gid, sizeof(gid_t),
  76                0);
  77        if (err) {
  78            return err;
  79        }
  80    }
  81    if (credp->fc_mode != -1) {
  82        err = setxattr(path, "user.virtfs.mode", &credp->fc_mode,
  83                sizeof(mode_t), 0);
  84        if (err) {
  85            return err;
  86        }
  87    }
  88    if (credp->fc_rdev != -1) {
  89        err = setxattr(path, "user.virtfs.rdev", &credp->fc_rdev,
  90                sizeof(dev_t), 0);
  91        if (err) {
  92            return err;
  93        }
  94    }
  95    return 0;
  96}
  97
  98static int local_post_create_passthrough(FsContext *fs_ctx, const char *path,
  99        FsCred *credp)
 100{
 101    if (chmod(rpath(fs_ctx, path), credp->fc_mode & 07777) < 0) {
 102        return -1;
 103    }
 104    if (chown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid) < 0) {
 105        return -1;
 106    }
 107    return 0;
 108}
 109
 110static ssize_t local_readlink(FsContext *fs_ctx, const char *path,
 111        char *buf, size_t bufsz)
 112{
 113    ssize_t tsize = -1;
 114    if (fs_ctx->fs_sm == SM_MAPPED) {
 115        int fd;
 116        fd = open(rpath(fs_ctx, path), O_RDONLY);
 117        if (fd == -1) {
 118            return -1;
 119        }
 120        do {
 121            tsize = read(fd, (void *)buf, bufsz);
 122        } while (tsize == -1 && errno == EINTR);
 123        close(fd);
 124        return tsize;
 125    } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
 126        tsize = readlink(rpath(fs_ctx, path), buf, bufsz);
 127    }
 128    return tsize;
 129}
 130
 131static int local_close(FsContext *ctx, int fd)
 132{
 133    return close(fd);
 134}
 135
 136static int local_closedir(FsContext *ctx, DIR *dir)
 137{
 138    return closedir(dir);
 139}
 140
 141static int local_open(FsContext *ctx, const char *path, int flags)
 142{
 143    return open(rpath(ctx, path), flags);
 144}
 145
 146static DIR *local_opendir(FsContext *ctx, const char *path)
 147{
 148    return opendir(rpath(ctx, path));
 149}
 150
 151static void local_rewinddir(FsContext *ctx, DIR *dir)
 152{
 153    return rewinddir(dir);
 154}
 155
 156static off_t local_telldir(FsContext *ctx, DIR *dir)
 157{
 158    return telldir(dir);
 159}
 160
 161static struct dirent *local_readdir(FsContext *ctx, DIR *dir)
 162{
 163    return readdir(dir);
 164}
 165
 166static void local_seekdir(FsContext *ctx, DIR *dir, off_t off)
 167{
 168    return seekdir(dir, off);
 169}
 170
 171static ssize_t local_readv(FsContext *ctx, int fd, const struct iovec *iov,
 172                            int iovcnt)
 173{
 174    return readv(fd, iov, iovcnt);
 175}
 176
 177static off_t local_lseek(FsContext *ctx, int fd, off_t offset, int whence)
 178{
 179    return lseek(fd, offset, whence);
 180}
 181
 182static ssize_t local_writev(FsContext *ctx, int fd, const struct iovec *iov,
 183                            int iovcnt)
 184{
 185    return writev(fd, iov, iovcnt);
 186}
 187
 188static int local_chmod(FsContext *fs_ctx, const char *path, FsCred *credp)
 189{
 190    if (fs_ctx->fs_sm == SM_MAPPED) {
 191        return local_set_xattr(rpath(fs_ctx, path), credp);
 192    } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
 193        return chmod(rpath(fs_ctx, path), credp->fc_mode);
 194    }
 195    return -1;
 196}
 197
 198static int local_mknod(FsContext *fs_ctx, const char *path, FsCred *credp)
 199{
 200    int err = -1;
 201    int serrno = 0;
 202
 203    /* Determine the security model */
 204    if (fs_ctx->fs_sm == SM_MAPPED) {
 205        err = mknod(rpath(fs_ctx, path), SM_LOCAL_MODE_BITS|S_IFREG, 0);
 206        if (err == -1) {
 207            return err;
 208        }
 209        local_set_xattr(rpath(fs_ctx, path), credp);
 210        if (err == -1) {
 211            serrno = errno;
 212            goto err_end;
 213        }
 214    } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
 215        err = mknod(rpath(fs_ctx, path), credp->fc_mode, credp->fc_rdev);
 216        if (err == -1) {
 217            return err;
 218        }
 219        err = local_post_create_passthrough(fs_ctx, path, credp);
 220        if (err == -1) {
 221            serrno = errno;
 222            goto err_end;
 223        }
 224    }
 225    return err;
 226
 227err_end:
 228    remove(rpath(fs_ctx, path));
 229    errno = serrno;
 230    return err;
 231}
 232
 233static int local_mkdir(FsContext *fs_ctx, const char *path, FsCred *credp)
 234{
 235    int err = -1;
 236    int serrno = 0;
 237
 238    /* Determine the security model */
 239    if (fs_ctx->fs_sm == SM_MAPPED) {
 240        err = mkdir(rpath(fs_ctx, path), SM_LOCAL_DIR_MODE_BITS);
 241        if (err == -1) {
 242            return err;
 243        }
 244        credp->fc_mode = credp->fc_mode|S_IFDIR;
 245        err = local_set_xattr(rpath(fs_ctx, path), credp);
 246        if (err == -1) {
 247            serrno = errno;
 248            goto err_end;
 249        }
 250    } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
 251        err = mkdir(rpath(fs_ctx, path), credp->fc_mode);
 252        if (err == -1) {
 253            return err;
 254        }
 255        err = local_post_create_passthrough(fs_ctx, path, credp);
 256        if (err == -1) {
 257            serrno = errno;
 258            goto err_end;
 259        }
 260    }
 261    return err;
 262
 263err_end:
 264    remove(rpath(fs_ctx, path));
 265    errno = serrno;
 266    return err;
 267}
 268
 269static int local_fstat(FsContext *fs_ctx, int fd, struct stat *stbuf)
 270{
 271    int err;
 272    err = fstat(fd, stbuf);
 273    if (err) {
 274        return err;
 275    }
 276    if (fs_ctx->fs_sm == SM_MAPPED) {
 277        /* Actual credentials are part of extended attrs */
 278        uid_t tmp_uid;
 279        gid_t tmp_gid;
 280        mode_t tmp_mode;
 281        dev_t tmp_dev;
 282
 283        if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) {
 284            stbuf->st_uid = tmp_uid;
 285        }
 286        if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) {
 287            stbuf->st_gid = tmp_gid;
 288        }
 289        if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) {
 290            stbuf->st_mode = tmp_mode;
 291        }
 292        if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) {
 293                stbuf->st_rdev = tmp_dev;
 294        }
 295    }
 296    return err;
 297}
 298
 299static int local_open2(FsContext *fs_ctx, const char *path, int flags,
 300        FsCred *credp)
 301{
 302    int fd = -1;
 303    int err = -1;
 304    int serrno = 0;
 305
 306    /* Determine the security model */
 307    if (fs_ctx->fs_sm == SM_MAPPED) {
 308        fd = open(rpath(fs_ctx, path), flags, SM_LOCAL_MODE_BITS);
 309        if (fd == -1) {
 310            return fd;
 311        }
 312        credp->fc_mode = credp->fc_mode|S_IFREG;
 313        /* Set cleint credentials in xattr */
 314        err = local_set_xattr(rpath(fs_ctx, path), credp);
 315        if (err == -1) {
 316            serrno = errno;
 317            goto err_end;
 318        }
 319    } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
 320        fd = open(rpath(fs_ctx, path), flags, credp->fc_mode);
 321        if (fd == -1) {
 322            return fd;
 323        }
 324        err = local_post_create_passthrough(fs_ctx, path, credp);
 325        if (err == -1) {
 326            serrno = errno;
 327            goto err_end;
 328        }
 329    }
 330    return fd;
 331
 332err_end:
 333    close(fd);
 334    remove(rpath(fs_ctx, path));
 335    errno = serrno;
 336    return err;
 337}
 338
 339
 340static int local_symlink(FsContext *fs_ctx, const char *oldpath,
 341        const char *newpath, FsCred *credp)
 342{
 343    int err = -1;
 344    int serrno = 0;
 345
 346    /* Determine the security model */
 347    if (fs_ctx->fs_sm == SM_MAPPED) {
 348        int fd;
 349        ssize_t oldpath_size, write_size;
 350        fd = open(rpath(fs_ctx, newpath), O_CREAT|O_EXCL|O_RDWR,
 351                SM_LOCAL_MODE_BITS);
 352        if (fd == -1) {
 353            return fd;
 354        }
 355        /* Write the oldpath (target) to the file. */
 356        oldpath_size = strlen(oldpath) + 1;
 357        do {
 358            write_size = write(fd, (void *)oldpath, oldpath_size);
 359        } while (write_size == -1 && errno == EINTR);
 360
 361        if (write_size != oldpath_size) {
 362            serrno = errno;
 363            close(fd);
 364            err = -1;
 365            goto err_end;
 366        }
 367        close(fd);
 368        /* Set cleint credentials in symlink's xattr */
 369        credp->fc_mode = credp->fc_mode|S_IFLNK;
 370        err = local_set_xattr(rpath(fs_ctx, newpath), credp);
 371        if (err == -1) {
 372            serrno = errno;
 373            goto err_end;
 374        }
 375    } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
 376        err = symlink(oldpath, rpath(fs_ctx, newpath));
 377        if (err) {
 378            return err;
 379        }
 380        err = lchown(rpath(fs_ctx, newpath), credp->fc_uid, credp->fc_gid);
 381        if (err == -1) {
 382            serrno = errno;
 383            goto err_end;
 384        }
 385    }
 386    return err;
 387
 388err_end:
 389    remove(rpath(fs_ctx, newpath));
 390    errno = serrno;
 391    return err;
 392}
 393
 394static int local_link(FsContext *ctx, const char *oldpath, const char *newpath)
 395{
 396    char *tmp = qemu_strdup(rpath(ctx, oldpath));
 397    int err, serrno = 0;
 398
 399    if (tmp == NULL) {
 400        return -ENOMEM;
 401    }
 402
 403    err = link(tmp, rpath(ctx, newpath));
 404    if (err == -1) {
 405        serrno = errno;
 406    }
 407
 408    qemu_free(tmp);
 409
 410    if (err == -1) {
 411        errno = serrno;
 412    }
 413
 414    return err;
 415}
 416
 417static int local_truncate(FsContext *ctx, const char *path, off_t size)
 418{
 419    return truncate(rpath(ctx, path), size);
 420}
 421
 422static int local_rename(FsContext *ctx, const char *oldpath,
 423                        const char *newpath)
 424{
 425    char *tmp;
 426    int err;
 427
 428    tmp = qemu_strdup(rpath(ctx, oldpath));
 429    if (tmp == NULL) {
 430        return -1;
 431    }
 432
 433    err = rename(tmp, rpath(ctx, newpath));
 434    if (err == -1) {
 435        int serrno = errno;
 436        qemu_free(tmp);
 437        errno = serrno;
 438    } else {
 439        qemu_free(tmp);
 440    }
 441
 442    return err;
 443
 444}
 445
 446static int local_chown(FsContext *fs_ctx, const char *path, FsCred *credp)
 447{
 448    if (fs_ctx->fs_sm == SM_MAPPED) {
 449        return local_set_xattr(rpath(fs_ctx, path), credp);
 450    } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
 451        return lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid);
 452    }
 453    return -1;
 454}
 455
 456static int local_utime(FsContext *ctx, const char *path,
 457                        const struct utimbuf *buf)
 458{
 459    return utime(rpath(ctx, path), buf);
 460}
 461
 462static int local_remove(FsContext *ctx, const char *path)
 463{
 464    return remove(rpath(ctx, path));
 465}
 466
 467static int local_fsync(FsContext *ctx, int fd)
 468{
 469    return fsync(fd);
 470}
 471
 472FileOperations local_ops = {
 473    .lstat = local_lstat,
 474    .readlink = local_readlink,
 475    .close = local_close,
 476    .closedir = local_closedir,
 477    .open = local_open,
 478    .opendir = local_opendir,
 479    .rewinddir = local_rewinddir,
 480    .telldir = local_telldir,
 481    .readdir = local_readdir,
 482    .seekdir = local_seekdir,
 483    .readv = local_readv,
 484    .lseek = local_lseek,
 485    .writev = local_writev,
 486    .chmod = local_chmod,
 487    .mknod = local_mknod,
 488    .mkdir = local_mkdir,
 489    .fstat = local_fstat,
 490    .open2 = local_open2,
 491    .symlink = local_symlink,
 492    .link = local_link,
 493    .truncate = local_truncate,
 494    .rename = local_rename,
 495    .chown = local_chown,
 496    .utime = local_utime,
 497    .remove = local_remove,
 498    .fsync = local_fsync,
 499};
 500