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 359
    char *path = fs_path->data;

360 361 362
    buffer = rpath(ctx, path);
    fs->fd = open(buffer, flags | O_NOFOLLOW);
    g_free(buffer);
363
    return fs->fd;
364 365
}

366 367
static int local_opendir(FsContext *ctx,
                         V9fsPath *fs_path, V9fsFidOpenState *fs)
368
{
369
    char *buffer;
370 371
    char *path = fs_path->data;

372
    buffer = rpath(ctx, path);
G
Greg Kurz 已提交
373
    fs->dir.stream = opendir(buffer);
374
    g_free(buffer);
G
Greg Kurz 已提交
375
    if (!fs->dir.stream) {
376 377 378
        return -1;
    }
    return 0;
379 380
}

381
static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
382
{
G
Greg Kurz 已提交
383
    rewinddir(fs->dir.stream);
384 385
}

386
static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs)
387
{
G
Greg Kurz 已提交
388
    return telldir(fs->dir.stream);
389 390
}

G
Greg Kurz 已提交
391
static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs)
392
{
G
Greg Kurz 已提交
393
    struct dirent *entry;
394 395

again:
G
Greg Kurz 已提交
396 397 398 399 400
    entry = readdir(fs->dir.stream);
    if (!entry) {
        return NULL;
    }

401 402 403
    if (ctx->export_flags & V9FS_SM_MAPPED) {
        entry->d_type = DT_UNKNOWN;
    } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
G
Greg Kurz 已提交
404
        if (!strcmp(entry->d_name, VIRTFS_META_DIR)) {
405 406 407
            /* skp the meta data directory */
            goto again;
        }
408
        entry->d_type = DT_UNKNOWN;
409
    }
G
Greg Kurz 已提交
410 411

    return entry;
412 413
}

414
static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
415
{
G
Greg Kurz 已提交
416
    seekdir(fs->dir.stream, off);
417 418
}

419 420
static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs,
                            const struct iovec *iov,
421
                            int iovcnt, off_t offset)
422
{
423
#ifdef CONFIG_PREADV
424
    return preadv(fs->fd, iov, iovcnt, offset);
425
#else
426
    int err = lseek(fs->fd, offset, SEEK_SET);
427 428 429
    if (err == -1) {
        return err;
    } else {
430
        return readv(fs->fd, iov, iovcnt);
431 432
    }
#endif
433 434
}

435 436
static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
                             const struct iovec *iov,
437
                             int iovcnt, off_t offset)
438
{
439 440
    ssize_t ret
;
441
#ifdef CONFIG_PREADV
442
    ret = pwritev(fs->fd, iov, iovcnt, offset);
443
#else
444
    int err = lseek(fs->fd, offset, SEEK_SET);
445 446 447
    if (err == -1) {
        return err;
    } else {
448
        ret = writev(fs->fd, iov, iovcnt);
449 450
    }
#endif
451 452 453 454 455 456 457
#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.
         */
458
        sync_file_range(fs->fd, offset, ret,
459 460 461 462
                        SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
    }
#endif
    return ret;
463 464
}

465
static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
466
{
467 468
    char *buffer;
    int ret = -1;
469 470
    char *path = fs_path->data;

471
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
472 473 474
        buffer = rpath(fs_ctx, path);
        ret = local_set_xattr(buffer, credp);
        g_free(buffer);
475 476
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
        return local_set_mapped_file_attr(fs_ctx, path, credp);
477 478
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
479 480 481
        buffer = rpath(fs_ctx, path);
        ret = chmod(buffer, credp->fc_mode);
        g_free(buffer);
482
    }
483
    return ret;
484 485
}

486 487
static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
                       const char *name, FsCred *credp)
488
{
489
    char *path;
490 491
    int err = -1;
    int serrno = 0;
492
    V9fsString fullname;
493
    char *buffer = NULL;
494

495 496 497 498
    v9fs_string_init(&fullname);
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
    path = fullname.data;

499
    /* Determine the security model */
500
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
501 502
        buffer = rpath(fs_ctx, path);
        err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0);
503
        if (err == -1) {
504
            goto out;
505
        }
506
        err = local_set_xattr(buffer, credp);
507 508 509 510
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
511 512
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {

513 514
        buffer = rpath(fs_ctx, path);
        err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0);
515 516 517 518 519 520 521 522
        if (err == -1) {
            goto out;
        }
        err = local_set_mapped_file_attr(fs_ctx, path, credp);
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
523 524
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
525 526
        buffer = rpath(fs_ctx, path);
        err = mknod(buffer, credp->fc_mode, credp->fc_rdev);
527
        if (err == -1) {
528
            goto out;
529 530 531 532 533 534 535
        }
        err = local_post_create_passthrough(fs_ctx, path, credp);
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
    }
536
    goto out;
537 538

err_end:
539
    remove(buffer);
540
    errno = serrno;
541
out:
542
    g_free(buffer);
543
    v9fs_string_free(&fullname);
544
    return err;
545 546
}

547 548
static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
                       const char *name, FsCred *credp)
549
{
550
    char *path;
551 552
    int err = -1;
    int serrno = 0;
553
    V9fsString fullname;
554
    char *buffer = NULL;
555

556 557 558 559
    v9fs_string_init(&fullname);
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
    path = fullname.data;

560
    /* Determine the security model */
561
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
562 563
        buffer = rpath(fs_ctx, path);
        err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS);
564
        if (err == -1) {
565
            goto out;
566 567
        }
        credp->fc_mode = credp->fc_mode|S_IFDIR;
568
        err = local_set_xattr(buffer, credp);
569 570 571 572
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
573
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
574 575
        buffer = rpath(fs_ctx, path);
        err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS);
576 577 578 579 580 581 582 583 584
        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;
        }
585 586
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
587 588
        buffer = rpath(fs_ctx, path);
        err = mkdir(buffer, credp->fc_mode);
589
        if (err == -1) {
590
            goto out;
591 592 593 594 595 596 597
        }
        err = local_post_create_passthrough(fs_ctx, path, credp);
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
    }
598
    goto out;
599 600

err_end:
601
    remove(buffer);
602
    errno = serrno;
603
out:
604
    g_free(buffer);
605
    v9fs_string_free(&fullname);
606
    return err;
607 608
}

609
static int local_fstat(FsContext *fs_ctx, int fid_type,
610
                       V9fsFidOpenState *fs, struct stat *stbuf)
611
{
612 613 614
    int err, fd;

    if (fid_type == P9_FID_DIR) {
G
Greg Kurz 已提交
615
        fd = dirfd(fs->dir.stream);
616 617 618 619 620
    } else {
        fd = fs->fd;
    }

    err = fstat(fd, stbuf);
621 622 623
    if (err) {
        return err;
    }
624
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
625 626 627 628 629 630
        /* Actual credentials are part of extended attrs */
        uid_t tmp_uid;
        gid_t tmp_gid;
        mode_t tmp_mode;
        dev_t tmp_dev;

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

650
static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
651
                       int flags, FsCred *credp, V9fsFidOpenState *fs)
652
{
653
    char *path;
654 655 656
    int fd = -1;
    int err = -1;
    int serrno = 0;
657
    V9fsString fullname;
658
    char *buffer = NULL;
659

660 661 662 663 664
    /*
     * Mark all the open to not follow symlinks
     */
    flags |= O_NOFOLLOW;

665 666 667 668
    v9fs_string_init(&fullname);
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
    path = fullname.data;

669
    /* Determine the security model */
670
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
671 672
        buffer = rpath(fs_ctx, path);
        fd = open(buffer, flags, SM_LOCAL_MODE_BITS);
673
        if (fd == -1) {
674 675
            err = fd;
            goto out;
676 677 678
        }
        credp->fc_mode = credp->fc_mode|S_IFREG;
        /* Set cleint credentials in xattr */
679
        err = local_set_xattr(buffer, credp);
680 681 682 683
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
684
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
685 686
        buffer = rpath(fs_ctx, path);
        fd = open(buffer, flags, SM_LOCAL_MODE_BITS);
687 688 689 690 691 692 693 694 695 696 697
        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;
        }
698 699
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
700 701
        buffer = rpath(fs_ctx, path);
        fd = open(buffer, flags, credp->fc_mode);
702
        if (fd == -1) {
703 704
            err = fd;
            goto out;
705 706 707 708 709 710 711
        }
        err = local_post_create_passthrough(fs_ctx, path, credp);
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
    }
712
    err = fd;
713
    fs->fd = fd;
714
    goto out;
715 716 717

err_end:
    close(fd);
718
    remove(buffer);
719
    errno = serrno;
720
out:
721
    g_free(buffer);
722
    v9fs_string_free(&fullname);
723
    return err;
724 725
}

726

727
static int local_symlink(FsContext *fs_ctx, const char *oldpath,
728
                         V9fsPath *dir_path, const char *name, FsCred *credp)
729
{
730 731
    int err = -1;
    int serrno = 0;
732 733
    char *newpath;
    V9fsString fullname;
734
    char *buffer = NULL;
735

736 737 738 739
    v9fs_string_init(&fullname);
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
    newpath = fullname.data;

740
    /* Determine the security model */
741
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
742 743
        int fd;
        ssize_t oldpath_size, write_size;
744 745
        buffer = rpath(fs_ctx, newpath);
        fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS);
746
        if (fd == -1) {
747 748
            err = fd;
            goto out;
749 750
        }
        /* Write the oldpath (target) to the file. */
751
        oldpath_size = strlen(oldpath);
752 753 754 755 756 757 758 759 760 761 762 763 764
        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;
765
        err = local_set_xattr(buffer, credp);
766 767 768 769
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
770 771 772
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
        int fd;
        ssize_t oldpath_size, write_size;
773 774
        buffer = rpath(fs_ctx, newpath);
        fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS);
775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798
        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;
        }
799 800
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
801 802
        buffer = rpath(fs_ctx, newpath);
        err = symlink(oldpath, buffer);
803
        if (err) {
804
            goto out;
805
        }
806
        err = lchown(buffer, credp->fc_uid, credp->fc_gid);
807
        if (err == -1) {
808 809 810 811
            /*
             * If we fail to change ownership and if we are
             * using security model none. Ignore the error
             */
812
            if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
813 814 815 816
                serrno = errno;
                goto err_end;
            } else
                err = 0;
817 818
        }
    }
819
    goto out;
820 821

err_end:
822
    remove(buffer);
823
    errno = serrno;
824
out:
825
    g_free(buffer);
826
    v9fs_string_free(&fullname);
827
    return err;
828 829
}

830 831
static int local_link(FsContext *ctx, V9fsPath *oldpath,
                      V9fsPath *dirpath, const char *name)
832
{
833 834
    int ret;
    V9fsString newpath;
835
    char *buffer, *buffer1;
836

837 838 839
    v9fs_string_init(&newpath);
    v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name);

840 841 842 843 844
    buffer = rpath(ctx, oldpath->data);
    buffer1 = rpath(ctx, newpath.data);
    ret = link(buffer, buffer1);
    g_free(buffer);
    g_free(buffer1);
845 846 847 848 849 850 851 852

    /* 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;
        }
853 854 855 856 857
        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);
858 859 860 861 862
        if (ret < 0 && errno != ENOENT) {
            goto err_out;
        }
    }
err_out:
863 864
    v9fs_string_free(&newpath);
    return ret;
865 866
}

867
static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
868
{
869 870
    char *buffer;
    int ret;
871 872
    char *path = fs_path->data;

873 874 875 876
    buffer = rpath(ctx, path);
    ret = truncate(buffer, size);
    g_free(buffer);
    return ret;
877 878 879 880 881
}

static int local_rename(FsContext *ctx, const char *oldpath,
                        const char *newpath)
{
882
    int err;
883
    char *buffer, *buffer1;
884

885 886 887 888 889 890
    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 */
891 892 893 894 895
        buffer = local_mapped_attr_path(ctx, oldpath);
        buffer1 = local_mapped_attr_path(ctx, newpath);
        err = rename(buffer, buffer1);
        g_free(buffer);
        g_free(buffer1);
896 897 898 899
        if (err < 0 && errno != ENOENT) {
            return err;
        }
    }
900 901 902 903 904 905 906

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

909
static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
910
{
911 912
    char *buffer;
    int ret = -1;
913 914
    char *path = fs_path->data;

915
    if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
916 917
        (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
        (fs_ctx->export_flags & V9FS_SM_NONE)) {
918 919 920
        buffer = rpath(fs_ctx, path);
        ret = lchown(buffer, credp->fc_uid, credp->fc_gid);
        g_free(buffer);
921
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
922 923 924
        buffer = rpath(fs_ctx, path);
        ret = local_set_xattr(buffer, credp);
        g_free(buffer);
925 926
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
        return local_set_mapped_file_attr(fs_ctx, path, credp);
927
    }
928
    return ret;
929 930
}

931
static int local_utimensat(FsContext *s, V9fsPath *fs_path,
932
                           const struct timespec *buf)
933
{
934 935
    char *buffer;
    int ret;
936 937
    char *path = fs_path->data;

938 939 940 941
    buffer = rpath(s, path);
    ret = qemu_utimens(buffer, buf);
    g_free(buffer);
    return ret;
942 943
}

944 945
static int local_remove(FsContext *ctx, const char *path)
{
946 947
    int err;
    struct stat stbuf;
948
    char *buffer;
949 950

    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
951 952 953
        buffer = rpath(ctx, path);
        err =  lstat(buffer, &stbuf);
        g_free(buffer);
954 955 956 957 958 959 960 961
        if (err) {
            goto err_out;
        }
        /*
         * If directory remove .virtfs_metadata contained in the
         * directory
         */
        if (S_ISDIR(stbuf.st_mode)) {
962 963
            buffer = g_strdup_printf("%s/%s/%s", ctx->fs_root,
                                     path, VIRTFS_META_DIR);
964
            err = remove(buffer);
965
            g_free(buffer);
966 967 968 969 970 971 972 973 974 975 976 977
            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
         */
978 979 980
        buffer = local_mapped_attr_path(ctx, path);
        err = remove(buffer);
        g_free(buffer);
981 982 983 984 985 986 987 988
        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;
        }
    }
989 990 991 992

    buffer = rpath(ctx, path);
    err = remove(buffer);
    g_free(buffer);
993 994
err_out:
    return err;
995 996
}

997 998
static int local_fsync(FsContext *ctx, int fid_type,
                       V9fsFidOpenState *fs, int datasync)
999
{
1000 1001 1002
    int fd;

    if (fid_type == P9_FID_DIR) {
G
Greg Kurz 已提交
1003
        fd = dirfd(fs->dir.stream);
1004 1005 1006 1007
    } else {
        fd = fs->fd;
    }

1008
    if (datasync) {
1009
        return qemu_fdatasync(fd);
1010
    } else {
1011
        return fsync(fd);
1012
    }
1013 1014
}

1015
static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
1016
{
1017 1018
    char *buffer;
    int ret;
1019 1020
    char *path = fs_path->data;

1021 1022 1023 1024
    buffer = rpath(s, path);
    ret = statfs(buffer, stbuf);
    g_free(buffer);
    return ret;
1025 1026
}

1027
static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
1028 1029
                               const char *name, void *value, size_t size)
{
1030 1031
    char *path = fs_path->data;

1032
    return v9fs_get_xattr(ctx, path, name, value, size);
1033 1034
}

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

1040
    return v9fs_list_xattr(ctx, path, value, size);
1041 1042
}

1043
static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
1044 1045
                           void *value, size_t size, int flags)
{
1046 1047
    char *path = fs_path->data;

1048
    return v9fs_set_xattr(ctx, path, name, value, size, flags);
1049 1050
}

1051 1052
static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
                              const char *name)
1053
{
1054 1055
    char *path = fs_path->data;

1056
    return v9fs_remove_xattr(ctx, path, name);
1057 1058
}

1059 1060 1061 1062
static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path,
                              const char *name, V9fsPath *target)
{
    if (dir_path) {
1063
        v9fs_path_sprintf(target, "%s/%s", dir_path->data, name);
1064
    } else {
1065
        v9fs_path_sprintf(target, "%s", name);
1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093
    }
    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;
1094
    char *buffer;
1095

1096 1097 1098
    v9fs_string_init(&fullname);

    v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name);
1099 1100 1101 1102 1103 1104
    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
        if (flags == AT_REMOVEDIR) {
            /*
             * If directory remove .virtfs_metadata contained in the
             * directory
             */
1105 1106
            buffer = g_strdup_printf("%s/%s/%s", ctx->fs_root,
                                     fullname.data, VIRTFS_META_DIR);
1107
            ret = remove(buffer);
1108
            g_free(buffer);
1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120
            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.
         */
1121 1122 1123
        buffer = local_mapped_attr_path(ctx, fullname.data);
        ret = remove(buffer);
        g_free(buffer);
1124 1125 1126 1127 1128 1129 1130 1131 1132
        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 */
1133 1134 1135
    buffer = rpath(ctx, fullname.data);
    ret = remove(buffer);
    g_free(buffer);
1136

1137
err_out:
1138
    v9fs_string_free(&fullname);
1139 1140
    return ret;
}
1141

1142 1143 1144
static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
                                mode_t st_mode, uint64_t *st_gen)
{
1145
#ifdef FS_IOC_GETVERSION
1146
    int err;
1147 1148
    V9fsFidOpenState fid_open;

1149 1150 1151 1152 1153
    /*
     * 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)) {
1154 1155
        errno = ENOTTY;
        return -1;
1156
    }
1157 1158 1159
    err = local_open(ctx, path, O_RDONLY, &fid_open);
    if (err < 0) {
        return err;
1160
    }
1161 1162
    err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen);
    local_close(ctx, &fid_open);
1163
    return err;
1164
#else
1165 1166
    errno = ENOTTY;
    return -1;
1167
#endif
1168 1169
}

1170 1171
static int local_init(FsContext *ctx)
{
1172
    int err = 0;
1173 1174
    struct statfs stbuf;

1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187
    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;
    }
1188
    ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT;
1189 1190 1191 1192
#ifdef FS_IOC_GETVERSION
    /*
     * use ioc_getversion only if the iocl is definied
     */
1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203
    err = statfs(ctx->fs_root, &stbuf);
    if (!err) {
        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;
        }
    }
1204
#endif
1205
    return err;
1206 1207
}

1208 1209 1210 1211 1212 1213
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) {
1214 1215 1216
        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");
1217 1218 1219 1220 1221
        return -1;
    }

    if (!strcmp(sec_model, "passthrough")) {
        fse->export_flags |= V9FS_SM_PASSTHROUGH;
1222 1223
    } else if (!strcmp(sec_model, "mapped") ||
               !strcmp(sec_model, "mapped-xattr")) {
1224 1225 1226
        fse->export_flags |= V9FS_SM_MAPPED;
    } else if (!strcmp(sec_model, "none")) {
        fse->export_flags |= V9FS_SM_NONE;
1227 1228
    } else if (!strcmp(sec_model, "mapped-file")) {
        fse->export_flags |= V9FS_SM_MAPPED_FILE;
1229
    } else {
1230 1231 1232
        error_report("Invalid security model %s specified", sec_model);
        error_printf("valid options are:"
                     "\t[passthrough|mapped-xattr|mapped-file|none]\n");
1233 1234 1235 1236
        return -1;
    }

    if (!path) {
1237
        error_report("fsdev: No path specified");
1238 1239 1240 1241 1242 1243 1244
        return -1;
    }
    fse->path = g_strdup(path);

    return 0;
}

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