9p-local.c 34.3 KB
Newer Older
1
/*
2
 * 9p Posix callback
3 4 5 6 7 8 9 10 11 12
 *
 * Copyright IBM, Corp. 2010
 *
 * Authors:
 *  Anthony Liguori   <aliguori@us.ibm.com>
 *
 * This work is licensed under the terms of the GNU GPL, version 2.  See
 * the COPYING file in the top-level directory.
 *
 */
13

P
Peter Maydell 已提交
14
#include "qemu/osdep.h"
W
Wei Liu 已提交
15
#include "9p.h"
16
#include "9p-xattr.h"
17
#include "fsdev/qemu-fsdev.h"   /* local_ops */
18
#include <arpa/inet.h>
19 20
#include <pwd.h>
#include <grp.h>
21 22
#include <sys/socket.h>
#include <sys/un.h>
23
#include "qemu/xattr.h"
24
#include "qemu/cutils.h"
25
#include "qemu/error-report.h"
26
#include <libgen.h>
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
#include <linux/fs.h>
#ifdef CONFIG_LINUX_MAGIC_H
#include <linux/magic.h>
#endif
#include <sys/ioctl.h>

#ifndef XFS_SUPER_MAGIC
#define XFS_SUPER_MAGIC  0x58465342
#endif
#ifndef EXT2_SUPER_MAGIC
#define EXT2_SUPER_MAGIC 0xEF53
#endif
#ifndef REISERFS_SUPER_MAGIC
#define REISERFS_SUPER_MAGIC 0x52654973
#endif
#ifndef BTRFS_SUPER_MAGIC
#define BTRFS_SUPER_MAGIC 0x9123683E
#endif
45

46 47
#define VIRTFS_META_DIR ".virtfs_metadata"

48
static char *local_mapped_attr_path(FsContext *ctx, const char *path)
49
{
50 51 52 53 54 55 56 57 58 59 60
    int dirlen;
    const char *name = strrchr(path, '/');
    if (name) {
        dirlen = name - path;
        ++name;
    } else {
        name = path;
        dirlen = 0;
    }
    return g_strdup_printf("%s/%.*s/%s/%s", ctx->fs_root,
                           dirlen, path, VIRTFS_META_DIR, name);
61 62
}

63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
static FILE *local_fopen(const char *path, const char *mode)
{
    int fd, o_mode = 0;
    FILE *fp;
    int flags = O_NOFOLLOW;
    /*
     * only supports two modes
     */
    if (mode[0] == 'r') {
        flags |= O_RDONLY;
    } else if (mode[0] == 'w') {
        flags |= O_WRONLY | O_TRUNC | O_CREAT;
        o_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
    } else {
        return NULL;
    }
    fd = open(path, flags, o_mode);
    if (fd == -1) {
        return NULL;
    }
    fp = fdopen(fd, mode);
    if (!fp) {
        close(fd);
    }
    return fp;
}

90 91 92 93 94 95
#define ATTR_MAX 100
static void local_mapped_file_attr(FsContext *ctx, const char *path,
                                   struct stat *stbuf)
{
    FILE *fp;
    char buf[ATTR_MAX];
96
    char *attr_path;
97

98
    attr_path = local_mapped_attr_path(ctx, path);
99
    fp = local_fopen(attr_path, "r");
100
    g_free(attr_path);
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
    if (!fp) {
        return;
    }
    memset(buf, 0, ATTR_MAX);
    while (fgets(buf, ATTR_MAX, fp)) {
        if (!strncmp(buf, "virtfs.uid", 10)) {
            stbuf->st_uid = atoi(buf+11);
        } else if (!strncmp(buf, "virtfs.gid", 10)) {
            stbuf->st_gid = atoi(buf+11);
        } else if (!strncmp(buf, "virtfs.mode", 11)) {
            stbuf->st_mode = atoi(buf+12);
        } else if (!strncmp(buf, "virtfs.rdev", 11)) {
            stbuf->st_rdev = atoi(buf+12);
        }
        memset(buf, 0, ATTR_MAX);
    }
    fclose(fp);
}

120
static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
121
{
122
    int err;
123
    char *buffer;
124 125
    char *path = fs_path->data;

126 127
    buffer = rpath(fs_ctx, path);
    err =  lstat(buffer, stbuf);
128
    if (err) {
129
        goto err_out;
130
    }
131
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
132 133 134 135 136
        /* Actual credentials are part of extended attrs */
        uid_t tmp_uid;
        gid_t tmp_gid;
        mode_t tmp_mode;
        dev_t tmp_dev;
137
        if (getxattr(buffer, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) {
138
            stbuf->st_uid = le32_to_cpu(tmp_uid);
139
        }
140
        if (getxattr(buffer, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) {
141
            stbuf->st_gid = le32_to_cpu(tmp_gid);
142
        }
143
        if (getxattr(buffer, "user.virtfs.mode",
144
                    &tmp_mode, sizeof(mode_t)) > 0) {
145
            stbuf->st_mode = le32_to_cpu(tmp_mode);
146
        }
147
        if (getxattr(buffer, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) {
148
            stbuf->st_rdev = le64_to_cpu(tmp_dev);
149
        }
150 151 152
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
        local_mapped_file_attr(fs_ctx, path, stbuf);
    }
153 154 155

err_out:
    g_free(buffer);
156 157 158 159 160 161
    return err;
}

static int local_create_mapped_attr_dir(FsContext *ctx, const char *path)
{
    int err;
162
    char *attr_dir;
163
    char *tmp_path = g_strdup(path);
164

165
    attr_dir = g_strdup_printf("%s/%s/%s",
166 167 168 169 170
             ctx->fs_root, dirname(tmp_path), VIRTFS_META_DIR);

    err = mkdir(attr_dir, 0700);
    if (err < 0 && errno == EEXIST) {
        err = 0;
171
    }
172
    g_free(attr_dir);
173
    g_free(tmp_path);
174
    return err;
175 176
}

177 178 179 180 181 182
static int local_set_mapped_file_attr(FsContext *ctx,
                                      const char *path, FsCred *credp)
{
    FILE *fp;
    int ret = 0;
    char buf[ATTR_MAX];
183
    char *attr_path;
184 185
    int uid = -1, gid = -1, mode = -1, rdev = -1;

186 187
    attr_path = local_mapped_attr_path(ctx, path);
    fp = local_fopen(attr_path, "r");
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
    if (!fp) {
        goto create_map_file;
    }
    memset(buf, 0, ATTR_MAX);
    while (fgets(buf, ATTR_MAX, fp)) {
        if (!strncmp(buf, "virtfs.uid", 10)) {
            uid = atoi(buf+11);
        } else if (!strncmp(buf, "virtfs.gid", 10)) {
            gid = atoi(buf+11);
        } else if (!strncmp(buf, "virtfs.mode", 11)) {
            mode = atoi(buf+12);
        } else if (!strncmp(buf, "virtfs.rdev", 11)) {
            rdev = atoi(buf+12);
        }
        memset(buf, 0, ATTR_MAX);
    }
    fclose(fp);
    goto update_map_file;

create_map_file:
    ret = local_create_mapped_attr_dir(ctx, path);
    if (ret < 0) {
        goto err_out;
    }

update_map_file:
214
    fp = local_fopen(attr_path, "w");
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
    if (!fp) {
        ret = -1;
        goto err_out;
    }

    if (credp->fc_uid != -1) {
        uid = credp->fc_uid;
    }
    if (credp->fc_gid != -1) {
        gid = credp->fc_gid;
    }
    if (credp->fc_mode != -1) {
        mode = credp->fc_mode;
    }
    if (credp->fc_rdev != -1) {
        rdev = credp->fc_rdev;
    }


    if (uid != -1) {
        fprintf(fp, "virtfs.uid=%d\n", uid);
    }
    if (gid != -1) {
        fprintf(fp, "virtfs.gid=%d\n", gid);
    }
    if (mode != -1) {
        fprintf(fp, "virtfs.mode=%d\n", mode);
    }
    if (rdev != -1) {
        fprintf(fp, "virtfs.rdev=%d\n", rdev);
    }
    fclose(fp);

err_out:
249
    g_free(attr_path);
250 251 252
    return ret;
}

253
static int local_set_xattr(const char *path, FsCred *credp)
254
{
255
    int err;
256

257
    if (credp->fc_uid != -1) {
258 259
        uint32_t tmp_uid = cpu_to_le32(credp->fc_uid);
        err = setxattr(path, "user.virtfs.uid", &tmp_uid, sizeof(uid_t), 0);
260 261 262
        if (err) {
            return err;
        }
263
    }
264
    if (credp->fc_gid != -1) {
265 266
        uint32_t tmp_gid = cpu_to_le32(credp->fc_gid);
        err = setxattr(path, "user.virtfs.gid", &tmp_gid, sizeof(gid_t), 0);
267 268 269
        if (err) {
            return err;
        }
270
    }
271
    if (credp->fc_mode != -1) {
272 273
        uint32_t tmp_mode = cpu_to_le32(credp->fc_mode);
        err = setxattr(path, "user.virtfs.mode", &tmp_mode, sizeof(mode_t), 0);
274 275 276
        if (err) {
            return err;
        }
277
    }
278
    if (credp->fc_rdev != -1) {
279 280
        uint64_t tmp_rdev = cpu_to_le64(credp->fc_rdev);
        err = setxattr(path, "user.virtfs.rdev", &tmp_rdev, sizeof(dev_t), 0);
281 282 283
        if (err) {
            return err;
        }
284 285 286 287
    }
    return 0;
}

288
static int local_post_create_passthrough(FsContext *fs_ctx, const char *path,
289
                                         FsCred *credp)
290
{
291
    char *buffer;
292

293 294
    buffer = rpath(fs_ctx, path);
    if (lchown(buffer, credp->fc_uid, credp->fc_gid) < 0) {
295 296 297 298
        /*
         * If we fail to change ownership and if we are
         * using security model none. Ignore the error
         */
299
        if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
300
            goto err;
301
        }
302
    }
M
M. Mohan Kumar 已提交
303

304 305
    if (chmod(buffer, credp->fc_mode & 07777) < 0) {
        goto err;
M
M. Mohan Kumar 已提交
306
    }
307 308

    g_free(buffer);
309
    return 0;
310 311 312
err:
    g_free(buffer);
    return -1;
313 314
}

315 316
static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
                              char *buf, size_t bufsz)
317
{
318
    ssize_t tsize = -1;
319
    char *buffer;
320 321
    char *path = fs_path->data;

322 323
    if ((fs_ctx->export_flags & V9FS_SM_MAPPED) ||
        (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE)) {
324
        int fd;
325 326 327
        buffer = rpath(fs_ctx, path);
        fd = open(buffer, O_RDONLY | O_NOFOLLOW);
        g_free(buffer);
328 329 330 331 332 333 334
        if (fd == -1) {
            return -1;
        }
        do {
            tsize = read(fd, (void *)buf, bufsz);
        } while (tsize == -1 && errno == EINTR);
        close(fd);
335 336
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
337 338 339
        buffer = rpath(fs_ctx, path);
        tsize = readlink(buffer, buf, bufsz);
        g_free(buffer);
340 341
    }
    return tsize;
342 343
}

344
static int local_close(FsContext *ctx, V9fsFidOpenState *fs)
345
{
346
    return close(fs->fd);
347 348
}

349
static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs)
350
{
G
Greg Kurz 已提交
351
    return closedir(fs->dir.stream);
352
}
353

354 355
static int local_open(FsContext *ctx, V9fsPath *fs_path,
                      int flags, V9fsFidOpenState *fs)
356
{
357
    char *buffer;
358
    char *path = fs_path->data;
359
    int fd;
360

361
    buffer = rpath(ctx, path);
362
    fd = open(buffer, flags | O_NOFOLLOW);
363
    g_free(buffer);
364 365 366 367
    if (fd == -1) {
        return -1;
    }
    fs->fd = fd;
368
    return fs->fd;
369 370
}

371 372
static int local_opendir(FsContext *ctx,
                         V9fsPath *fs_path, V9fsFidOpenState *fs)
373
{
374
    char *buffer;
375
    char *path = fs_path->data;
376
    DIR *stream;
377

378
    buffer = rpath(ctx, path);
379
    stream = opendir(buffer);
380
    g_free(buffer);
381
    if (!stream) {
382 383
        return -1;
    }
384
    fs->dir.stream = stream;
385
    return 0;
386 387
}

388
static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
389
{
G
Greg Kurz 已提交
390
    rewinddir(fs->dir.stream);
391 392
}

393
static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs)
394
{
G
Greg Kurz 已提交
395
    return telldir(fs->dir.stream);
396 397
}

G
Greg Kurz 已提交
398
static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs)
399
{
G
Greg Kurz 已提交
400
    struct dirent *entry;
401 402

again:
G
Greg Kurz 已提交
403 404 405 406 407
    entry = readdir(fs->dir.stream);
    if (!entry) {
        return NULL;
    }

408 409 410
    if (ctx->export_flags & V9FS_SM_MAPPED) {
        entry->d_type = DT_UNKNOWN;
    } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
G
Greg Kurz 已提交
411
        if (!strcmp(entry->d_name, VIRTFS_META_DIR)) {
412 413 414
            /* skp the meta data directory */
            goto again;
        }
415
        entry->d_type = DT_UNKNOWN;
416
    }
G
Greg Kurz 已提交
417 418

    return entry;
419 420
}

421
static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
422
{
G
Greg Kurz 已提交
423
    seekdir(fs->dir.stream, off);
424 425
}

426 427
static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs,
                            const struct iovec *iov,
428
                            int iovcnt, off_t offset)
429
{
430
#ifdef CONFIG_PREADV
431
    return preadv(fs->fd, iov, iovcnt, offset);
432
#else
433
    int err = lseek(fs->fd, offset, SEEK_SET);
434 435 436
    if (err == -1) {
        return err;
    } else {
437
        return readv(fs->fd, iov, iovcnt);
438 439
    }
#endif
440 441
}

442 443
static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
                             const struct iovec *iov,
444
                             int iovcnt, off_t offset)
445
{
446 447
    ssize_t ret
;
448
#ifdef CONFIG_PREADV
449
    ret = pwritev(fs->fd, iov, iovcnt, offset);
450
#else
451
    int err = lseek(fs->fd, offset, SEEK_SET);
452 453 454
    if (err == -1) {
        return err;
    } else {
455
        ret = writev(fs->fd, iov, iovcnt);
456 457
    }
#endif
458 459 460 461 462 463 464
#ifdef CONFIG_SYNC_FILE_RANGE
    if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) {
        /*
         * Initiate a writeback. This is not a data integrity sync.
         * We want to ensure that we don't leave dirty pages in the cache
         * after write when writeout=immediate is sepcified.
         */
465
        sync_file_range(fs->fd, offset, ret,
466 467 468 469
                        SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
    }
#endif
    return ret;
470 471
}

472
static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
473
{
474 475
    char *buffer;
    int ret = -1;
476 477
    char *path = fs_path->data;

478
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
479 480 481
        buffer = rpath(fs_ctx, path);
        ret = local_set_xattr(buffer, credp);
        g_free(buffer);
482 483
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
        return local_set_mapped_file_attr(fs_ctx, path, credp);
484 485
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
486 487 488
        buffer = rpath(fs_ctx, path);
        ret = chmod(buffer, credp->fc_mode);
        g_free(buffer);
489
    }
490
    return ret;
491 492
}

493 494
static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
                       const char *name, FsCred *credp)
495
{
496
    char *path;
497 498
    int err = -1;
    int serrno = 0;
499
    V9fsString fullname;
500
    char *buffer = NULL;
501

502 503 504 505
    v9fs_string_init(&fullname);
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
    path = fullname.data;

506
    /* Determine the security model */
507
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
508 509
        buffer = rpath(fs_ctx, path);
        err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0);
510
        if (err == -1) {
511
            goto out;
512
        }
513
        err = local_set_xattr(buffer, credp);
514 515 516 517
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
518 519
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {

520 521
        buffer = rpath(fs_ctx, path);
        err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0);
522 523 524 525 526 527 528 529
        if (err == -1) {
            goto out;
        }
        err = local_set_mapped_file_attr(fs_ctx, path, credp);
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
530 531
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
532 533
        buffer = rpath(fs_ctx, path);
        err = mknod(buffer, credp->fc_mode, credp->fc_rdev);
534
        if (err == -1) {
535
            goto out;
536 537 538 539 540 541 542
        }
        err = local_post_create_passthrough(fs_ctx, path, credp);
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
    }
543
    goto out;
544 545

err_end:
546
    remove(buffer);
547
    errno = serrno;
548
out:
549
    g_free(buffer);
550
    v9fs_string_free(&fullname);
551
    return err;
552 553
}

554 555
static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
                       const char *name, FsCred *credp)
556
{
557
    char *path;
558 559
    int err = -1;
    int serrno = 0;
560
    V9fsString fullname;
561
    char *buffer = NULL;
562

563 564 565 566
    v9fs_string_init(&fullname);
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
    path = fullname.data;

567
    /* Determine the security model */
568
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
569 570
        buffer = rpath(fs_ctx, path);
        err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS);
571
        if (err == -1) {
572
            goto out;
573 574
        }
        credp->fc_mode = credp->fc_mode|S_IFDIR;
575
        err = local_set_xattr(buffer, credp);
576 577 578 579
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
580
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
581 582
        buffer = rpath(fs_ctx, path);
        err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS);
583 584 585 586 587 588 589 590 591
        if (err == -1) {
            goto out;
        }
        credp->fc_mode = credp->fc_mode|S_IFDIR;
        err = local_set_mapped_file_attr(fs_ctx, path, credp);
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
592 593
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
594 595
        buffer = rpath(fs_ctx, path);
        err = mkdir(buffer, credp->fc_mode);
596
        if (err == -1) {
597
            goto out;
598 599 600 601 602 603 604
        }
        err = local_post_create_passthrough(fs_ctx, path, credp);
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
    }
605
    goto out;
606 607

err_end:
608
    remove(buffer);
609
    errno = serrno;
610
out:
611
    g_free(buffer);
612
    v9fs_string_free(&fullname);
613
    return err;
614 615
}

616
static int local_fstat(FsContext *fs_ctx, int fid_type,
617
                       V9fsFidOpenState *fs, struct stat *stbuf)
618
{
619 620 621
    int err, fd;

    if (fid_type == P9_FID_DIR) {
G
Greg Kurz 已提交
622
        fd = dirfd(fs->dir.stream);
623 624 625 626 627
    } else {
        fd = fs->fd;
    }

    err = fstat(fd, stbuf);
628 629 630
    if (err) {
        return err;
    }
631
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
632 633 634 635 636 637
        /* Actual credentials are part of extended attrs */
        uid_t tmp_uid;
        gid_t tmp_gid;
        mode_t tmp_mode;
        dev_t tmp_dev;

638 639
        if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) {
            stbuf->st_uid = le32_to_cpu(tmp_uid);
640
        }
641 642
        if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) {
            stbuf->st_gid = le32_to_cpu(tmp_gid);
643
        }
644 645
        if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) {
            stbuf->st_mode = le32_to_cpu(tmp_mode);
646
        }
647 648
        if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) {
            stbuf->st_rdev = le64_to_cpu(tmp_dev);
649
        }
650 651 652
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
        errno = EOPNOTSUPP;
        return -1;
653 654
    }
    return err;
655 656
}

657
static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
658
                       int flags, FsCred *credp, V9fsFidOpenState *fs)
659
{
660
    char *path;
661 662 663
    int fd = -1;
    int err = -1;
    int serrno = 0;
664
    V9fsString fullname;
665
    char *buffer = NULL;
666

667 668 669 670 671
    /*
     * Mark all the open to not follow symlinks
     */
    flags |= O_NOFOLLOW;

672 673 674 675
    v9fs_string_init(&fullname);
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
    path = fullname.data;

676
    /* Determine the security model */
677
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
678 679
        buffer = rpath(fs_ctx, path);
        fd = open(buffer, flags, SM_LOCAL_MODE_BITS);
680
        if (fd == -1) {
681 682
            err = fd;
            goto out;
683 684 685
        }
        credp->fc_mode = credp->fc_mode|S_IFREG;
        /* Set cleint credentials in xattr */
686
        err = local_set_xattr(buffer, credp);
687 688 689 690
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
691
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
692 693
        buffer = rpath(fs_ctx, path);
        fd = open(buffer, flags, SM_LOCAL_MODE_BITS);
694 695 696 697 698 699 700 701 702 703 704
        if (fd == -1) {
            err = fd;
            goto out;
        }
        credp->fc_mode = credp->fc_mode|S_IFREG;
        /* Set client credentials in .virtfs_metadata directory files */
        err = local_set_mapped_file_attr(fs_ctx, path, credp);
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
705 706
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
707 708
        buffer = rpath(fs_ctx, path);
        fd = open(buffer, flags, credp->fc_mode);
709
        if (fd == -1) {
710 711
            err = fd;
            goto out;
712 713 714 715 716 717 718
        }
        err = local_post_create_passthrough(fs_ctx, path, credp);
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
    }
719
    err = fd;
720
    fs->fd = fd;
721
    goto out;
722 723 724

err_end:
    close(fd);
725
    remove(buffer);
726
    errno = serrno;
727
out:
728
    g_free(buffer);
729
    v9fs_string_free(&fullname);
730
    return err;
731 732
}

733

734
static int local_symlink(FsContext *fs_ctx, const char *oldpath,
735
                         V9fsPath *dir_path, const char *name, FsCred *credp)
736
{
737 738
    int err = -1;
    int serrno = 0;
739 740
    char *newpath;
    V9fsString fullname;
741
    char *buffer = NULL;
742

743 744 745 746
    v9fs_string_init(&fullname);
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
    newpath = fullname.data;

747
    /* Determine the security model */
748
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
749 750
        int fd;
        ssize_t oldpath_size, write_size;
751 752
        buffer = rpath(fs_ctx, newpath);
        fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS);
753
        if (fd == -1) {
754 755
            err = fd;
            goto out;
756 757
        }
        /* Write the oldpath (target) to the file. */
758
        oldpath_size = strlen(oldpath);
759 760 761 762 763 764 765 766 767 768 769 770 771
        do {
            write_size = write(fd, (void *)oldpath, oldpath_size);
        } while (write_size == -1 && errno == EINTR);

        if (write_size != oldpath_size) {
            serrno = errno;
            close(fd);
            err = -1;
            goto err_end;
        }
        close(fd);
        /* Set cleint credentials in symlink's xattr */
        credp->fc_mode = credp->fc_mode|S_IFLNK;
772
        err = local_set_xattr(buffer, credp);
773 774 775 776
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
777 778 779
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
        int fd;
        ssize_t oldpath_size, write_size;
780 781
        buffer = rpath(fs_ctx, newpath);
        fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS);
782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805
        if (fd == -1) {
            err = fd;
            goto out;
        }
        /* Write the oldpath (target) to the file. */
        oldpath_size = strlen(oldpath);
        do {
            write_size = write(fd, (void *)oldpath, oldpath_size);
        } while (write_size == -1 && errno == EINTR);

        if (write_size != oldpath_size) {
            serrno = errno;
            close(fd);
            err = -1;
            goto err_end;
        }
        close(fd);
        /* Set cleint credentials in symlink's xattr */
        credp->fc_mode = credp->fc_mode|S_IFLNK;
        err = local_set_mapped_file_attr(fs_ctx, newpath, credp);
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
806 807
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
808 809
        buffer = rpath(fs_ctx, newpath);
        err = symlink(oldpath, buffer);
810
        if (err) {
811
            goto out;
812
        }
813
        err = lchown(buffer, credp->fc_uid, credp->fc_gid);
814
        if (err == -1) {
815 816 817 818
            /*
             * If we fail to change ownership and if we are
             * using security model none. Ignore the error
             */
819
            if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
820 821 822 823
                serrno = errno;
                goto err_end;
            } else
                err = 0;
824 825
        }
    }
826
    goto out;
827 828

err_end:
829
    remove(buffer);
830
    errno = serrno;
831
out:
832
    g_free(buffer);
833
    v9fs_string_free(&fullname);
834
    return err;
835 836
}

837 838
static int local_link(FsContext *ctx, V9fsPath *oldpath,
                      V9fsPath *dirpath, const char *name)
839
{
840 841
    int ret;
    V9fsString newpath;
842
    char *buffer, *buffer1;
843

844 845 846
    v9fs_string_init(&newpath);
    v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name);

847 848 849 850 851
    buffer = rpath(ctx, oldpath->data);
    buffer1 = rpath(ctx, newpath.data);
    ret = link(buffer, buffer1);
    g_free(buffer);
    g_free(buffer1);
852 853 854 855 856 857 858 859

    /* now link the virtfs_metadata files */
    if (!ret && (ctx->export_flags & V9FS_SM_MAPPED_FILE)) {
        /* Link the .virtfs_metadata files. Create the metada directory */
        ret = local_create_mapped_attr_dir(ctx, newpath.data);
        if (ret < 0) {
            goto err_out;
        }
860 861 862 863 864
        buffer = local_mapped_attr_path(ctx, oldpath->data);
        buffer1 = local_mapped_attr_path(ctx, newpath.data);
        ret = link(buffer, buffer1);
        g_free(buffer);
        g_free(buffer1);
865 866 867 868 869
        if (ret < 0 && errno != ENOENT) {
            goto err_out;
        }
    }
err_out:
870 871
    v9fs_string_free(&newpath);
    return ret;
872 873
}

874
static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
875
{
876 877
    char *buffer;
    int ret;
878 879
    char *path = fs_path->data;

880 881 882 883
    buffer = rpath(ctx, path);
    ret = truncate(buffer, size);
    g_free(buffer);
    return ret;
884 885 886 887 888
}

static int local_rename(FsContext *ctx, const char *oldpath,
                        const char *newpath)
{
889
    int err;
890
    char *buffer, *buffer1;
891

892 893 894 895 896 897
    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
        err = local_create_mapped_attr_dir(ctx, newpath);
        if (err < 0) {
            return err;
        }
        /* rename the .virtfs_metadata files */
898 899 900 901 902
        buffer = local_mapped_attr_path(ctx, oldpath);
        buffer1 = local_mapped_attr_path(ctx, newpath);
        err = rename(buffer, buffer1);
        g_free(buffer);
        g_free(buffer1);
903 904 905 906
        if (err < 0 && errno != ENOENT) {
            return err;
        }
    }
907 908 909 910 911 912 913

    buffer = rpath(ctx, oldpath);
    buffer1 = rpath(ctx, newpath);
    err = rename(buffer, buffer1);
    g_free(buffer);
    g_free(buffer1);
    return err;
914 915
}

916
static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
917
{
918 919
    char *buffer;
    int ret = -1;
920 921
    char *path = fs_path->data;

922
    if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
923 924
        (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
        (fs_ctx->export_flags & V9FS_SM_NONE)) {
925 926 927
        buffer = rpath(fs_ctx, path);
        ret = lchown(buffer, credp->fc_uid, credp->fc_gid);
        g_free(buffer);
928
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
929 930 931
        buffer = rpath(fs_ctx, path);
        ret = local_set_xattr(buffer, credp);
        g_free(buffer);
932 933
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
        return local_set_mapped_file_attr(fs_ctx, path, credp);
934
    }
935
    return ret;
936 937
}

938
static int local_utimensat(FsContext *s, V9fsPath *fs_path,
939
                           const struct timespec *buf)
940
{
941 942
    char *buffer;
    int ret;
943 944
    char *path = fs_path->data;

945 946 947 948
    buffer = rpath(s, path);
    ret = qemu_utimens(buffer, buf);
    g_free(buffer);
    return ret;
949 950
}

951 952
static int local_remove(FsContext *ctx, const char *path)
{
953 954
    int err;
    struct stat stbuf;
955
    char *buffer;
956 957

    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
958 959 960
        buffer = rpath(ctx, path);
        err =  lstat(buffer, &stbuf);
        g_free(buffer);
961 962 963 964 965 966 967 968
        if (err) {
            goto err_out;
        }
        /*
         * If directory remove .virtfs_metadata contained in the
         * directory
         */
        if (S_ISDIR(stbuf.st_mode)) {
969 970
            buffer = g_strdup_printf("%s/%s/%s", ctx->fs_root,
                                     path, VIRTFS_META_DIR);
971
            err = remove(buffer);
972
            g_free(buffer);
973 974 975 976 977 978 979 980 981 982 983 984
            if (err < 0 && errno != ENOENT) {
                /*
                 * We didn't had the .virtfs_metadata file. May be file created
                 * in non-mapped mode ?. Ignore ENOENT.
                 */
                goto err_out;
            }
        }
        /*
         * Now remove the name from parent directory
         * .virtfs_metadata directory
         */
985 986 987
        buffer = local_mapped_attr_path(ctx, path);
        err = remove(buffer);
        g_free(buffer);
988 989 990 991 992 993 994 995
        if (err < 0 && errno != ENOENT) {
            /*
             * We didn't had the .virtfs_metadata file. May be file created
             * in non-mapped mode ?. Ignore ENOENT.
             */
            goto err_out;
        }
    }
996 997 998 999

    buffer = rpath(ctx, path);
    err = remove(buffer);
    g_free(buffer);
1000 1001
err_out:
    return err;
1002 1003
}

1004 1005
static int local_fsync(FsContext *ctx, int fid_type,
                       V9fsFidOpenState *fs, int datasync)
1006
{
1007 1008 1009
    int fd;

    if (fid_type == P9_FID_DIR) {
G
Greg Kurz 已提交
1010
        fd = dirfd(fs->dir.stream);
1011 1012 1013 1014
    } else {
        fd = fs->fd;
    }

1015
    if (datasync) {
1016
        return qemu_fdatasync(fd);
1017
    } else {
1018
        return fsync(fd);
1019
    }
1020 1021
}

1022
static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
1023
{
1024 1025
    char *buffer;
    int ret;
1026 1027
    char *path = fs_path->data;

1028 1029 1030 1031
    buffer = rpath(s, path);
    ret = statfs(buffer, stbuf);
    g_free(buffer);
    return ret;
1032 1033
}

1034
static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
1035 1036
                               const char *name, void *value, size_t size)
{
1037 1038
    char *path = fs_path->data;

1039
    return v9fs_get_xattr(ctx, path, name, value, size);
1040 1041
}

1042
static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path,
1043 1044
                                void *value, size_t size)
{
1045 1046
    char *path = fs_path->data;

1047
    return v9fs_list_xattr(ctx, path, value, size);
1048 1049
}

1050
static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
1051 1052
                           void *value, size_t size, int flags)
{
1053 1054
    char *path = fs_path->data;

1055
    return v9fs_set_xattr(ctx, path, name, value, size, flags);
1056 1057
}

1058 1059
static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
                              const char *name)
1060
{
1061 1062
    char *path = fs_path->data;

1063
    return v9fs_remove_xattr(ctx, path, name);
1064 1065
}

1066 1067 1068 1069
static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path,
                              const char *name, V9fsPath *target)
{
    if (dir_path) {
1070
        v9fs_path_sprintf(target, "%s/%s", dir_path->data, name);
1071
    } else {
1072
        v9fs_path_sprintf(target, "%s", name);
1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100
    }
    return 0;
}

static int local_renameat(FsContext *ctx, V9fsPath *olddir,
                          const char *old_name, V9fsPath *newdir,
                          const char *new_name)
{
    int ret;
    V9fsString old_full_name, new_full_name;

    v9fs_string_init(&old_full_name);
    v9fs_string_init(&new_full_name);

    v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name);
    v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name);

    ret = local_rename(ctx, old_full_name.data, new_full_name.data);
    v9fs_string_free(&old_full_name);
    v9fs_string_free(&new_full_name);
    return ret;
}

static int local_unlinkat(FsContext *ctx, V9fsPath *dir,
                          const char *name, int flags)
{
    int ret;
    V9fsString fullname;
1101
    char *buffer;
1102

1103 1104 1105
    v9fs_string_init(&fullname);

    v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name);
1106 1107 1108 1109 1110 1111
    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
        if (flags == AT_REMOVEDIR) {
            /*
             * If directory remove .virtfs_metadata contained in the
             * directory
             */
1112 1113
            buffer = g_strdup_printf("%s/%s/%s", ctx->fs_root,
                                     fullname.data, VIRTFS_META_DIR);
1114
            ret = remove(buffer);
1115
            g_free(buffer);
1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127
            if (ret < 0 && errno != ENOENT) {
                /*
                 * We didn't had the .virtfs_metadata file. May be file created
                 * in non-mapped mode ?. Ignore ENOENT.
                 */
                goto err_out;
            }
        }
        /*
         * Now remove the name from parent directory
         * .virtfs_metadata directory.
         */
1128 1129 1130
        buffer = local_mapped_attr_path(ctx, fullname.data);
        ret = remove(buffer);
        g_free(buffer);
1131 1132 1133 1134 1135 1136 1137 1138 1139
        if (ret < 0 && errno != ENOENT) {
            /*
             * We didn't had the .virtfs_metadata file. May be file created
             * in non-mapped mode ?. Ignore ENOENT.
             */
            goto err_out;
        }
    }
    /* Remove the name finally */
1140 1141 1142
    buffer = rpath(ctx, fullname.data);
    ret = remove(buffer);
    g_free(buffer);
1143

1144
err_out:
1145
    v9fs_string_free(&fullname);
1146 1147
    return ret;
}
1148

1149 1150 1151
static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
                                mode_t st_mode, uint64_t *st_gen)
{
1152
#ifdef FS_IOC_GETVERSION
1153
    int err;
1154 1155
    V9fsFidOpenState fid_open;

1156 1157 1158 1159 1160
    /*
     * Do not try to open special files like device nodes, fifos etc
     * We can get fd for regular files and directories only
     */
    if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
1161 1162
        errno = ENOTTY;
        return -1;
1163
    }
1164 1165 1166
    err = local_open(ctx, path, O_RDONLY, &fid_open);
    if (err < 0) {
        return err;
1167
    }
1168 1169
    err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen);
    local_close(ctx, &fid_open);
1170
    return err;
1171
#else
1172 1173
    errno = ENOTTY;
    return -1;
1174
#endif
1175 1176
}

1177 1178
static int local_init(FsContext *ctx)
{
1179 1180
    struct statfs stbuf;

1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197
#ifdef FS_IOC_GETVERSION
    /*
     * use ioc_getversion only if the ioctl is definied
     */
    if (statfs(ctx->fs_root, &stbuf) < 0) {
        return -1;
    }
    switch (stbuf.f_type) {
    case EXT2_SUPER_MAGIC:
    case BTRFS_SUPER_MAGIC:
    case REISERFS_SUPER_MAGIC:
    case XFS_SUPER_MAGIC:
        ctx->exops.get_st_gen = local_ioc_getversion;
        break;
    }
#endif

1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210
    if (ctx->export_flags & V9FS_SM_PASSTHROUGH) {
        ctx->xops = passthrough_xattr_ops;
    } else if (ctx->export_flags & V9FS_SM_MAPPED) {
        ctx->xops = mapped_xattr_ops;
    } else if (ctx->export_flags & V9FS_SM_NONE) {
        ctx->xops = none_xattr_ops;
    } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
        /*
         * xattr operation for mapped-file and passthrough
         * remain same.
         */
        ctx->xops = passthrough_xattr_ops;
    }
1211
    ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT;
1212 1213

    return 0;
1214 1215
}

1216 1217 1218 1219 1220 1221
static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse)
{
    const char *sec_model = qemu_opt_get(opts, "security_model");
    const char *path = qemu_opt_get(opts, "path");

    if (!sec_model) {
1222 1223 1224
        error_report("Security model not specified, local fs needs security model");
        error_printf("valid options are:"
                     "\tsecurity_model=[passthrough|mapped-xattr|mapped-file|none]\n");
1225 1226 1227 1228 1229
        return -1;
    }

    if (!strcmp(sec_model, "passthrough")) {
        fse->export_flags |= V9FS_SM_PASSTHROUGH;
1230 1231
    } else if (!strcmp(sec_model, "mapped") ||
               !strcmp(sec_model, "mapped-xattr")) {
1232 1233 1234
        fse->export_flags |= V9FS_SM_MAPPED;
    } else if (!strcmp(sec_model, "none")) {
        fse->export_flags |= V9FS_SM_NONE;
1235 1236
    } else if (!strcmp(sec_model, "mapped-file")) {
        fse->export_flags |= V9FS_SM_MAPPED_FILE;
1237
    } else {
1238 1239 1240
        error_report("Invalid security model %s specified", sec_model);
        error_printf("valid options are:"
                     "\t[passthrough|mapped-xattr|mapped-file|none]\n");
1241 1242 1243 1244
        return -1;
    }

    if (!path) {
1245
        error_report("fsdev: No path specified");
1246 1247 1248 1249 1250 1251 1252
        return -1;
    }
    fse->path = g_strdup(path);

    return 0;
}

1253
FileOperations local_ops = {
1254
    .parse_opts = local_parse_opts,
1255
    .init  = local_init,
1256 1257 1258 1259
    .lstat = local_lstat,
    .readlink = local_readlink,
    .close = local_close,
    .closedir = local_closedir,
1260 1261
    .open = local_open,
    .opendir = local_opendir,
1262 1263
    .rewinddir = local_rewinddir,
    .telldir = local_telldir,
G
Greg Kurz 已提交
1264
    .readdir = local_readdir,
1265
    .seekdir = local_seekdir,
1266 1267
    .preadv = local_preadv,
    .pwritev = local_pwritev,
1268 1269 1270 1271 1272 1273 1274
    .chmod = local_chmod,
    .mknod = local_mknod,
    .mkdir = local_mkdir,
    .fstat = local_fstat,
    .open2 = local_open2,
    .symlink = local_symlink,
    .link = local_link,
1275 1276 1277
    .truncate = local_truncate,
    .rename = local_rename,
    .chown = local_chown,
M
M. Mohan Kumar 已提交
1278
    .utimensat = local_utimensat,
1279
    .remove = local_remove,
1280
    .fsync = local_fsync,
1281
    .statfs = local_statfs,
1282 1283
    .lgetxattr = local_lgetxattr,
    .llistxattr = local_llistxattr,
1284
    .lsetxattr = local_lsetxattr,
1285
    .lremovexattr = local_lremovexattr,
1286 1287 1288
    .name_to_path = local_name_to_path,
    .renameat  = local_renameat,
    .unlinkat = local_unlinkat,
1289
};