9p-local.c 37.4 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-local.h"
17
#include "9p-xattr.h"
18
#include "9p-util.h"
19
#include "fsdev/qemu-fsdev.h"   /* local_ops */
20
#include <arpa/inet.h>
21 22
#include <pwd.h>
#include <grp.h>
23 24
#include <sys/socket.h>
#include <sys/un.h>
25
#include "qemu/xattr.h"
26
#include "qemu/cutils.h"
27
#include "qemu/error-report.h"
28
#include <libgen.h>
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
#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
47

48 49 50 51
typedef struct {
    int mountfd;
} LocalData;

52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
                        mode_t mode)
{
    LocalData *data = fs_ctx->private;

    /* All paths are relative to the path data->mountfd points to */
    while (*path == '/') {
        path++;
    }

    return relative_openat_nofollow(data->mountfd, path, flags, mode);
}

int local_opendir_nofollow(FsContext *fs_ctx, const char *path)
{
    return local_open_nofollow(fs_ctx, path, O_DIRECTORY | O_RDONLY, 0);
}

70 71 72 73 74 75 76 77
static void renameat_preserve_errno(int odirfd, const char *opath, int ndirfd,
                                    const char *npath)
{
    int serrno = errno;
    renameat(odirfd, opath, ndirfd, npath);
    errno = serrno;
}

78 79
#define VIRTFS_META_DIR ".virtfs_metadata"

80
static char *local_mapped_attr_path(FsContext *ctx, const char *path)
81
{
82 83 84 85 86 87 88 89 90 91 92
    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);
93 94
}

95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
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;
}

122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
static FILE *local_fopenat(int dirfd, const char *name, const char *mode)
{
    int fd, o_mode = 0;
    FILE *fp;
    int flags;
    /*
     * 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 = openat_file(dirfd, name, flags, o_mode);
    if (fd == -1) {
        return NULL;
    }
    fp = fdopen(fd, mode);
    if (!fp) {
        close(fd);
    }
    return fp;
}

149
#define ATTR_MAX 100
150
static void local_mapped_file_attr(int dirfd, const char *name,
151 152 153 154
                                   struct stat *stbuf)
{
    FILE *fp;
    char buf[ATTR_MAX];
155 156
    int map_dirfd;

157
    map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
158 159 160
    if (map_dirfd == -1) {
        return;
    }
161

162 163
    fp = local_fopenat(map_dirfd, name, "r");
    close_preserve_errno(map_dirfd);
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
    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);
}

183
static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
184
{
185 186 187 188 189 190 191 192 193
    int err = -1;
    char *dirpath = g_path_get_dirname(fs_path->data);
    char *name = g_path_get_basename(fs_path->data);
    int dirfd;

    dirfd = local_opendir_nofollow(fs_ctx, dirpath);
    if (dirfd == -1) {
        goto out;
    }
194

195
    err = fstatat(dirfd, name, stbuf, AT_SYMLINK_NOFOLLOW);
196
    if (err) {
197
        goto err_out;
198
    }
199
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
200 201 202 203 204
        /* Actual credentials are part of extended attrs */
        uid_t tmp_uid;
        gid_t tmp_gid;
        mode_t tmp_mode;
        dev_t tmp_dev;
205 206 207

        if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.uid", &tmp_uid,
                                 sizeof(uid_t)) > 0) {
208
            stbuf->st_uid = le32_to_cpu(tmp_uid);
209
        }
210 211
        if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.gid", &tmp_gid,
                                 sizeof(gid_t)) > 0) {
212
            stbuf->st_gid = le32_to_cpu(tmp_gid);
213
        }
214 215
        if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.mode", &tmp_mode,
                                 sizeof(mode_t)) > 0) {
216
            stbuf->st_mode = le32_to_cpu(tmp_mode);
217
        }
218 219
        if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.rdev", &tmp_dev,
                                 sizeof(dev_t)) > 0) {
220
            stbuf->st_rdev = le64_to_cpu(tmp_dev);
221
        }
222
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
223
        local_mapped_file_attr(dirfd, name, stbuf);
224
    }
225 226

err_out:
227 228 229 230
    close_preserve_errno(dirfd);
out:
    g_free(name);
    g_free(dirpath);
231 232 233 234 235 236
    return err;
}

static int local_create_mapped_attr_dir(FsContext *ctx, const char *path)
{
    int err;
237
    char *attr_dir;
238
    char *tmp_path = g_strdup(path);
239

240
    attr_dir = g_strdup_printf("%s/%s/%s",
241 242 243 244 245
             ctx->fs_root, dirname(tmp_path), VIRTFS_META_DIR);

    err = mkdir(attr_dir, 0700);
    if (err < 0 && errno == EEXIST) {
        err = 0;
246
    }
247
    g_free(attr_dir);
248
    g_free(tmp_path);
249
    return err;
250 251
}

252 253 254 255 256 257
static int local_set_mapped_file_attr(FsContext *ctx,
                                      const char *path, FsCred *credp)
{
    FILE *fp;
    int ret = 0;
    char buf[ATTR_MAX];
258
    char *attr_path;
259 260
    int uid = -1, gid = -1, mode = -1, rdev = -1;

261 262
    attr_path = local_mapped_attr_path(ctx, path);
    fp = local_fopen(attr_path, "r");
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
    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:
289
    fp = local_fopen(attr_path, "w");
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
    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:
324
    g_free(attr_path);
325 326 327
    return ret;
}

328
static int local_set_xattr(const char *path, FsCred *credp)
329
{
330
    int err;
331

332
    if (credp->fc_uid != -1) {
333 334
        uint32_t tmp_uid = cpu_to_le32(credp->fc_uid);
        err = setxattr(path, "user.virtfs.uid", &tmp_uid, sizeof(uid_t), 0);
335 336 337
        if (err) {
            return err;
        }
338
    }
339
    if (credp->fc_gid != -1) {
340 341
        uint32_t tmp_gid = cpu_to_le32(credp->fc_gid);
        err = setxattr(path, "user.virtfs.gid", &tmp_gid, sizeof(gid_t), 0);
342 343 344
        if (err) {
            return err;
        }
345
    }
346
    if (credp->fc_mode != -1) {
347 348
        uint32_t tmp_mode = cpu_to_le32(credp->fc_mode);
        err = setxattr(path, "user.virtfs.mode", &tmp_mode, sizeof(mode_t), 0);
349 350 351
        if (err) {
            return err;
        }
352
    }
353
    if (credp->fc_rdev != -1) {
354 355
        uint64_t tmp_rdev = cpu_to_le64(credp->fc_rdev);
        err = setxattr(path, "user.virtfs.rdev", &tmp_rdev, sizeof(dev_t), 0);
356 357 358
        if (err) {
            return err;
        }
359 360 361 362
    }
    return 0;
}

363
static int local_post_create_passthrough(FsContext *fs_ctx, const char *path,
364
                                         FsCred *credp)
365
{
366
    char *buffer;
367

368 369
    buffer = rpath(fs_ctx, path);
    if (lchown(buffer, credp->fc_uid, credp->fc_gid) < 0) {
370 371 372 373
        /*
         * If we fail to change ownership and if we are
         * using security model none. Ignore the error
         */
374
        if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
375
            goto err;
376
        }
377
    }
M
M. Mohan Kumar 已提交
378

379 380
    if (chmod(buffer, credp->fc_mode & 07777) < 0) {
        goto err;
M
M. Mohan Kumar 已提交
381
    }
382 383

    g_free(buffer);
384
    return 0;
385 386 387
err:
    g_free(buffer);
    return -1;
388 389
}

390 391
static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
                              char *buf, size_t bufsz)
392
{
393
    ssize_t tsize = -1;
394

395 396
    if ((fs_ctx->export_flags & V9FS_SM_MAPPED) ||
        (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE)) {
397
        int fd;
398 399

        fd = local_open_nofollow(fs_ctx, fs_path->data, O_RDONLY, 0);
400 401 402 403 404 405
        if (fd == -1) {
            return -1;
        }
        do {
            tsize = read(fd, (void *)buf, bufsz);
        } while (tsize == -1 && errno == EINTR);
406
        close_preserve_errno(fd);
407 408
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
409 410 411 412 413 414 415 416 417 418 419 420 421 422
        char *dirpath = g_path_get_dirname(fs_path->data);
        char *name = g_path_get_basename(fs_path->data);
        int dirfd;

        dirfd = local_opendir_nofollow(fs_ctx, dirpath);
        if (dirfd == -1) {
            goto out;
        }

        tsize = readlinkat(dirfd, name, buf, bufsz);
        close_preserve_errno(dirfd);
    out:
        g_free(name);
        g_free(dirpath);
423 424
    }
    return tsize;
425 426
}

427
static int local_close(FsContext *ctx, V9fsFidOpenState *fs)
428
{
429
    return close(fs->fd);
430 431
}

432
static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs)
433
{
G
Greg Kurz 已提交
434
    return closedir(fs->dir.stream);
435
}
436

437 438
static int local_open(FsContext *ctx, V9fsPath *fs_path,
                      int flags, V9fsFidOpenState *fs)
439
{
440
    int fd;
441

442
    fd = local_open_nofollow(ctx, fs_path->data, flags, 0);
443 444 445 446
    if (fd == -1) {
        return -1;
    }
    fs->fd = fd;
447
    return fs->fd;
448 449
}

450 451
static int local_opendir(FsContext *ctx,
                         V9fsPath *fs_path, V9fsFidOpenState *fs)
452
{
453
    int dirfd;
454
    DIR *stream;
455

456 457 458 459 460 461
    dirfd = local_opendir_nofollow(ctx, fs_path->data);
    if (dirfd == -1) {
        return -1;
    }

    stream = fdopendir(dirfd);
462
    if (!stream) {
463 464
        return -1;
    }
465
    fs->dir.stream = stream;
466
    return 0;
467 468
}

469
static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
470
{
G
Greg Kurz 已提交
471
    rewinddir(fs->dir.stream);
472 473
}

474
static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs)
475
{
G
Greg Kurz 已提交
476
    return telldir(fs->dir.stream);
477 478
}

G
Greg Kurz 已提交
479
static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs)
480
{
G
Greg Kurz 已提交
481
    struct dirent *entry;
482 483

again:
G
Greg Kurz 已提交
484 485 486 487 488
    entry = readdir(fs->dir.stream);
    if (!entry) {
        return NULL;
    }

489 490 491
    if (ctx->export_flags & V9FS_SM_MAPPED) {
        entry->d_type = DT_UNKNOWN;
    } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
G
Greg Kurz 已提交
492
        if (!strcmp(entry->d_name, VIRTFS_META_DIR)) {
493 494 495
            /* skp the meta data directory */
            goto again;
        }
496
        entry->d_type = DT_UNKNOWN;
497
    }
G
Greg Kurz 已提交
498 499

    return entry;
500 501
}

502
static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
503
{
G
Greg Kurz 已提交
504
    seekdir(fs->dir.stream, off);
505 506
}

507 508
static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs,
                            const struct iovec *iov,
509
                            int iovcnt, off_t offset)
510
{
511
#ifdef CONFIG_PREADV
512
    return preadv(fs->fd, iov, iovcnt, offset);
513
#else
514
    int err = lseek(fs->fd, offset, SEEK_SET);
515 516 517
    if (err == -1) {
        return err;
    } else {
518
        return readv(fs->fd, iov, iovcnt);
519 520
    }
#endif
521 522
}

523 524
static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
                             const struct iovec *iov,
525
                             int iovcnt, off_t offset)
526
{
527 528
    ssize_t ret
;
529
#ifdef CONFIG_PREADV
530
    ret = pwritev(fs->fd, iov, iovcnt, offset);
531
#else
532
    int err = lseek(fs->fd, offset, SEEK_SET);
533 534 535
    if (err == -1) {
        return err;
    } else {
536
        ret = writev(fs->fd, iov, iovcnt);
537 538
    }
#endif
539 540 541 542 543 544 545
#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.
         */
546
        sync_file_range(fs->fd, offset, ret,
547 548 549 550
                        SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
    }
#endif
    return ret;
551 552
}

553
static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
554
{
555 556
    char *buffer;
    int ret = -1;
557 558
    char *path = fs_path->data;

559
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
560 561 562
        buffer = rpath(fs_ctx, path);
        ret = local_set_xattr(buffer, credp);
        g_free(buffer);
563 564
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
        return local_set_mapped_file_attr(fs_ctx, path, credp);
565 566
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
567 568 569
        buffer = rpath(fs_ctx, path);
        ret = chmod(buffer, credp->fc_mode);
        g_free(buffer);
570
    }
571
    return ret;
572 573
}

574 575
static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
                       const char *name, FsCred *credp)
576
{
577
    char *path;
578 579
    int err = -1;
    int serrno = 0;
580
    V9fsString fullname;
581
    char *buffer = NULL;
582

583 584 585 586
    v9fs_string_init(&fullname);
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
    path = fullname.data;

587
    /* Determine the security model */
588
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
589 590
        buffer = rpath(fs_ctx, path);
        err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0);
591
        if (err == -1) {
592
            goto out;
593
        }
594
        err = local_set_xattr(buffer, credp);
595 596 597 598
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
599 600
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {

601 602
        buffer = rpath(fs_ctx, path);
        err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0);
603 604 605 606 607 608 609 610
        if (err == -1) {
            goto out;
        }
        err = local_set_mapped_file_attr(fs_ctx, path, credp);
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
611 612
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
613 614
        buffer = rpath(fs_ctx, path);
        err = mknod(buffer, credp->fc_mode, credp->fc_rdev);
615
        if (err == -1) {
616
            goto out;
617 618 619 620 621 622 623
        }
        err = local_post_create_passthrough(fs_ctx, path, credp);
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
    }
624
    goto out;
625 626

err_end:
627
    remove(buffer);
628
    errno = serrno;
629
out:
630
    g_free(buffer);
631
    v9fs_string_free(&fullname);
632
    return err;
633 634
}

635 636
static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
                       const char *name, FsCred *credp)
637
{
638
    char *path;
639 640
    int err = -1;
    int serrno = 0;
641
    V9fsString fullname;
642
    char *buffer = NULL;
643

644 645 646 647
    v9fs_string_init(&fullname);
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
    path = fullname.data;

648
    /* Determine the security model */
649
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
650 651
        buffer = rpath(fs_ctx, path);
        err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS);
652
        if (err == -1) {
653
            goto out;
654 655
        }
        credp->fc_mode = credp->fc_mode|S_IFDIR;
656
        err = local_set_xattr(buffer, credp);
657 658 659 660
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
661
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
662 663
        buffer = rpath(fs_ctx, path);
        err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS);
664 665 666 667 668 669 670 671 672
        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;
        }
673 674
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
675 676
        buffer = rpath(fs_ctx, path);
        err = mkdir(buffer, credp->fc_mode);
677
        if (err == -1) {
678
            goto out;
679 680 681 682 683 684 685
        }
        err = local_post_create_passthrough(fs_ctx, path, credp);
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
    }
686
    goto out;
687 688

err_end:
689
    remove(buffer);
690
    errno = serrno;
691
out:
692
    g_free(buffer);
693
    v9fs_string_free(&fullname);
694
    return err;
695 696
}

697
static int local_fstat(FsContext *fs_ctx, int fid_type,
698
                       V9fsFidOpenState *fs, struct stat *stbuf)
699
{
700 701 702
    int err, fd;

    if (fid_type == P9_FID_DIR) {
G
Greg Kurz 已提交
703
        fd = dirfd(fs->dir.stream);
704 705 706 707 708
    } else {
        fd = fs->fd;
    }

    err = fstat(fd, stbuf);
709 710 711
    if (err) {
        return err;
    }
712
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
713 714 715 716 717 718
        /* Actual credentials are part of extended attrs */
        uid_t tmp_uid;
        gid_t tmp_gid;
        mode_t tmp_mode;
        dev_t tmp_dev;

719 720
        if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) {
            stbuf->st_uid = le32_to_cpu(tmp_uid);
721
        }
722 723
        if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) {
            stbuf->st_gid = le32_to_cpu(tmp_gid);
724
        }
725 726
        if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) {
            stbuf->st_mode = le32_to_cpu(tmp_mode);
727
        }
728 729
        if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) {
            stbuf->st_rdev = le64_to_cpu(tmp_dev);
730
        }
731 732 733
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
        errno = EOPNOTSUPP;
        return -1;
734 735
    }
    return err;
736 737
}

738
static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
739
                       int flags, FsCred *credp, V9fsFidOpenState *fs)
740
{
741
    char *path;
742 743 744
    int fd = -1;
    int err = -1;
    int serrno = 0;
745
    V9fsString fullname;
746
    char *buffer = NULL;
747

748 749 750 751 752
    /*
     * Mark all the open to not follow symlinks
     */
    flags |= O_NOFOLLOW;

753 754 755 756
    v9fs_string_init(&fullname);
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
    path = fullname.data;

757
    /* Determine the security model */
758
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
759 760
        buffer = rpath(fs_ctx, path);
        fd = open(buffer, flags, SM_LOCAL_MODE_BITS);
761
        if (fd == -1) {
762 763
            err = fd;
            goto out;
764 765 766
        }
        credp->fc_mode = credp->fc_mode|S_IFREG;
        /* Set cleint credentials in xattr */
767
        err = local_set_xattr(buffer, credp);
768 769 770 771
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
772
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
773 774
        buffer = rpath(fs_ctx, path);
        fd = open(buffer, flags, SM_LOCAL_MODE_BITS);
775 776 777 778 779 780 781 782 783 784 785
        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;
        }
786 787
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
788 789
        buffer = rpath(fs_ctx, path);
        fd = open(buffer, flags, credp->fc_mode);
790
        if (fd == -1) {
791 792
            err = fd;
            goto out;
793 794 795 796 797 798 799
        }
        err = local_post_create_passthrough(fs_ctx, path, credp);
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
    }
800
    err = fd;
801
    fs->fd = fd;
802
    goto out;
803 804 805

err_end:
    close(fd);
806
    remove(buffer);
807
    errno = serrno;
808
out:
809
    g_free(buffer);
810
    v9fs_string_free(&fullname);
811
    return err;
812 813
}

814

815
static int local_symlink(FsContext *fs_ctx, const char *oldpath,
816
                         V9fsPath *dir_path, const char *name, FsCred *credp)
817
{
818 819
    int err = -1;
    int serrno = 0;
820 821
    char *newpath;
    V9fsString fullname;
822
    char *buffer = NULL;
823

824 825 826 827
    v9fs_string_init(&fullname);
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
    newpath = fullname.data;

828
    /* Determine the security model */
829
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
830 831
        int fd;
        ssize_t oldpath_size, write_size;
832 833
        buffer = rpath(fs_ctx, newpath);
        fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS);
834
        if (fd == -1) {
835 836
            err = fd;
            goto out;
837 838
        }
        /* Write the oldpath (target) to the file. */
839
        oldpath_size = strlen(oldpath);
840 841 842 843 844 845 846 847 848 849 850 851 852
        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;
853
        err = local_set_xattr(buffer, credp);
854 855 856 857
        if (err == -1) {
            serrno = errno;
            goto err_end;
        }
858 859 860
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
        int fd;
        ssize_t oldpath_size, write_size;
861 862
        buffer = rpath(fs_ctx, newpath);
        fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS);
863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886
        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;
        }
887 888
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
889 890
        buffer = rpath(fs_ctx, newpath);
        err = symlink(oldpath, buffer);
891
        if (err) {
892
            goto out;
893
        }
894
        err = lchown(buffer, credp->fc_uid, credp->fc_gid);
895
        if (err == -1) {
896 897 898 899
            /*
             * If we fail to change ownership and if we are
             * using security model none. Ignore the error
             */
900
            if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
901 902 903 904
                serrno = errno;
                goto err_end;
            } else
                err = 0;
905 906
        }
    }
907
    goto out;
908 909

err_end:
910
    remove(buffer);
911
    errno = serrno;
912
out:
913
    g_free(buffer);
914
    v9fs_string_free(&fullname);
915
    return err;
916 917
}

918 919
static int local_link(FsContext *ctx, V9fsPath *oldpath,
                      V9fsPath *dirpath, const char *name)
920
{
921 922
    int ret;
    V9fsString newpath;
923
    char *buffer, *buffer1;
924
    int serrno;
925

926 927 928
    v9fs_string_init(&newpath);
    v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name);

929 930 931 932
    buffer = rpath(ctx, oldpath->data);
    buffer1 = rpath(ctx, newpath.data);
    ret = link(buffer, buffer1);
    g_free(buffer);
933 934 935
    if (ret < 0) {
        goto out;
    }
936 937

    /* now link the virtfs_metadata files */
938 939 940
    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
        char *vbuffer, *vbuffer1;

941 942 943 944 945
        /* Link the .virtfs_metadata files. Create the metada directory */
        ret = local_create_mapped_attr_dir(ctx, newpath.data);
        if (ret < 0) {
            goto err_out;
        }
946 947 948 949 950
        vbuffer = local_mapped_attr_path(ctx, oldpath->data);
        vbuffer1 = local_mapped_attr_path(ctx, newpath.data);
        ret = link(vbuffer, vbuffer1);
        g_free(vbuffer);
        g_free(vbuffer1);
951 952 953 954
        if (ret < 0 && errno != ENOENT) {
            goto err_out;
        }
    }
955 956
    goto out;

957
err_out:
958 959 960 961 962
    serrno = errno;
    remove(buffer1);
    errno = serrno;
out:
    g_free(buffer1);
963 964
    v9fs_string_free(&newpath);
    return ret;
965 966
}

967
static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
968
{
969
    int fd, ret;
970

971 972 973 974 975 976
    fd = local_open_nofollow(ctx, fs_path->data, O_WRONLY, 0);
    if (fd == -1) {
        return -1;
    }
    ret = ftruncate(fd, size);
    close_preserve_errno(fd);
977
    return ret;
978 979
}

980
static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
981
{
982 983
    char *buffer;
    int ret = -1;
984 985
    char *path = fs_path->data;

986
    if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
987 988
        (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
        (fs_ctx->export_flags & V9FS_SM_NONE)) {
989 990 991
        buffer = rpath(fs_ctx, path);
        ret = lchown(buffer, credp->fc_uid, credp->fc_gid);
        g_free(buffer);
992
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
993 994 995
        buffer = rpath(fs_ctx, path);
        ret = local_set_xattr(buffer, credp);
        g_free(buffer);
996 997
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
        return local_set_mapped_file_attr(fs_ctx, path, credp);
998
    }
999
    return ret;
1000 1001
}

1002
static int local_utimensat(FsContext *s, V9fsPath *fs_path,
1003
                           const struct timespec *buf)
1004
{
1005 1006 1007
    char *dirpath = g_path_get_dirname(fs_path->data);
    char *name = g_path_get_basename(fs_path->data);
    int dirfd, ret = -1;
1008

1009 1010 1011 1012 1013 1014 1015 1016 1017 1018
    dirfd = local_opendir_nofollow(s, dirpath);
    if (dirfd == -1) {
        goto out;
    }

    ret = utimensat(dirfd, name, buf, AT_SYMLINK_NOFOLLOW);
    close_preserve_errno(dirfd);
out:
    g_free(dirpath);
    g_free(name);
1019
    return ret;
1020 1021
}

1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071
static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name,
                                 int flags)
{
    int ret = -1;

    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
        int map_dirfd;

        if (flags == AT_REMOVEDIR) {
            int fd;

            fd = openat(dirfd, name, O_RDONLY | O_DIRECTORY | O_PATH);
            if (fd == -1) {
                goto err_out;
            }
            /*
             * If directory remove .virtfs_metadata contained in the
             * directory
             */
            ret = unlinkat(fd, VIRTFS_META_DIR, AT_REMOVEDIR);
            close_preserve_errno(fd);
            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.
         */
        map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
        ret = unlinkat(map_dirfd, name, 0);
        close_preserve_errno(map_dirfd);
        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;
        }
    }

    ret = unlinkat(dirfd, name, flags);
err_out:
    return ret;
}

1072 1073
static int local_remove(FsContext *ctx, const char *path)
{
1074
    struct stat stbuf;
1075 1076 1077 1078 1079
    char *dirpath = g_path_get_dirname(path);
    char *name = g_path_get_basename(path);
    int flags = 0;
    int dirfd;
    int err = -1;
1080

1081 1082 1083
    dirfd = local_opendir_nofollow(ctx, dirpath);
    if (dirfd) {
        goto out;
1084
    }
1085

1086 1087 1088 1089 1090 1091 1092 1093 1094
    if (fstatat(dirfd, path, &stbuf, AT_SYMLINK_NOFOLLOW) < 0) {
        goto err_out;
    }

    if (S_ISDIR(stbuf.st_mode)) {
        flags |= AT_REMOVEDIR;
    }

    err = local_unlinkat_common(ctx, dirfd, name, flags);
1095
err_out:
1096 1097 1098 1099
    close_preserve_errno(dirfd);
out:
    g_free(name);
    g_free(dirpath);
1100
    return err;
1101 1102
}

1103 1104
static int local_fsync(FsContext *ctx, int fid_type,
                       V9fsFidOpenState *fs, int datasync)
1105
{
1106 1107 1108
    int fd;

    if (fid_type == P9_FID_DIR) {
G
Greg Kurz 已提交
1109
        fd = dirfd(fs->dir.stream);
1110 1111 1112 1113
    } else {
        fd = fs->fd;
    }

1114
    if (datasync) {
1115
        return qemu_fdatasync(fd);
1116
    } else {
1117
        return fsync(fd);
1118
    }
1119 1120
}

1121
static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
1122
{
1123
    int fd, ret;
1124

1125 1126 1127
    fd = local_open_nofollow(s, fs_path->data, O_RDONLY, 0);
    ret = fstatfs(fd, stbuf);
    close_preserve_errno(fd);
1128
    return ret;
1129 1130
}

1131
static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
1132 1133
                               const char *name, void *value, size_t size)
{
1134 1135
    char *path = fs_path->data;

1136
    return v9fs_get_xattr(ctx, path, name, value, size);
1137 1138
}

1139
static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path,
1140 1141
                                void *value, size_t size)
{
1142 1143
    char *path = fs_path->data;

1144
    return v9fs_list_xattr(ctx, path, value, size);
1145 1146
}

1147
static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
1148 1149
                           void *value, size_t size, int flags)
{
1150 1151
    char *path = fs_path->data;

1152
    return v9fs_set_xattr(ctx, path, name, value, size, flags);
1153 1154
}

1155 1156
static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
                              const char *name)
1157
{
1158 1159
    char *path = fs_path->data;

1160
    return v9fs_remove_xattr(ctx, path, name);
1161 1162
}

1163 1164 1165 1166
static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path,
                              const char *name, V9fsPath *target)
{
    if (dir_path) {
1167
        v9fs_path_sprintf(target, "%s/%s", dir_path->data, name);
1168
    } else {
1169
        v9fs_path_sprintf(target, "%s", name);
1170 1171 1172 1173 1174 1175 1176 1177 1178
    }
    return 0;
}

static int local_renameat(FsContext *ctx, V9fsPath *olddir,
                          const char *old_name, V9fsPath *newdir,
                          const char *new_name)
{
    int ret;
1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195
    int odirfd, ndirfd;

    odirfd = local_opendir_nofollow(ctx, olddir->data);
    if (odirfd == -1) {
        return -1;
    }

    ndirfd = local_opendir_nofollow(ctx, newdir->data);
    if (ndirfd == -1) {
        close_preserve_errno(odirfd);
        return -1;
    }

    ret = renameat(odirfd, old_name, ndirfd, new_name);
    if (ret < 0) {
        goto out;
    }
1196

1197 1198
    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
        int omap_dirfd, nmap_dirfd;
1199

1200 1201 1202 1203
        ret = mkdirat(ndirfd, VIRTFS_META_DIR, 0700);
        if (ret < 0 && errno != EEXIST) {
            goto err_undo_rename;
        }
1204

1205
        omap_dirfd = openat_dir(odirfd, VIRTFS_META_DIR);
1206 1207 1208 1209
        if (omap_dirfd == -1) {
            goto err;
        }

1210
        nmap_dirfd = openat_dir(ndirfd, VIRTFS_META_DIR);
1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234
        if (nmap_dirfd == -1) {
            close_preserve_errno(omap_dirfd);
            goto err;
        }

        /* rename the .virtfs_metadata files */
        ret = renameat(omap_dirfd, old_name, nmap_dirfd, new_name);
        close_preserve_errno(nmap_dirfd);
        close_preserve_errno(omap_dirfd);
        if (ret < 0 && errno != ENOENT) {
            goto err_undo_rename;
        }

        ret = 0;
    }
    goto out;

err:
    ret = -1;
err_undo_rename:
    renameat_preserve_errno(ndirfd, new_name, odirfd, old_name);
out:
    close_preserve_errno(ndirfd);
    close_preserve_errno(odirfd);
1235 1236 1237
    return ret;
}

G
Greg Kurz 已提交
1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264
static void v9fs_path_init_dirname(V9fsPath *path, const char *str)
{
    path->data = g_path_get_dirname(str);
    path->size = strlen(path->data) + 1;
}

static int local_rename(FsContext *ctx, const char *oldpath,
                        const char *newpath)
{
    int err;
    char *oname = g_path_get_basename(oldpath);
    char *nname = g_path_get_basename(newpath);
    V9fsPath olddir, newdir;

    v9fs_path_init_dirname(&olddir, oldpath);
    v9fs_path_init_dirname(&newdir, newpath);

    err = local_renameat(ctx, &olddir, oname, &newdir, nname);

    v9fs_path_free(&newdir);
    v9fs_path_free(&olddir);
    g_free(nname);
    g_free(oname);

    return err;
}

1265 1266 1267 1268
static int local_unlinkat(FsContext *ctx, V9fsPath *dir,
                          const char *name, int flags)
{
    int ret;
1269
    int dirfd;
1270

1271 1272 1273
    dirfd = local_opendir_nofollow(ctx, dir->data);
    if (dirfd == -1) {
        return -1;
1274
    }
1275

1276 1277
    ret = local_unlinkat_common(ctx, dirfd, name, flags);
    close_preserve_errno(dirfd);
1278 1279
    return ret;
}
1280

1281 1282 1283
static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
                                mode_t st_mode, uint64_t *st_gen)
{
1284
#ifdef FS_IOC_GETVERSION
1285
    int err;
1286 1287
    V9fsFidOpenState fid_open;

1288 1289 1290 1291 1292
    /*
     * 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)) {
1293 1294
        errno = ENOTTY;
        return -1;
1295
    }
1296 1297 1298
    err = local_open(ctx, path, O_RDONLY, &fid_open);
    if (err < 0) {
        return err;
1299
    }
1300 1301
    err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen);
    local_close(ctx, &fid_open);
1302
    return err;
1303
#else
1304 1305
    errno = ENOTTY;
    return -1;
1306
#endif
1307 1308
}

1309 1310
static int local_init(FsContext *ctx)
{
1311
    struct statfs stbuf;
1312 1313 1314 1315 1316 1317
    LocalData *data = g_malloc(sizeof(*data));

    data->mountfd = open(ctx->fs_root, O_DIRECTORY | O_RDONLY);
    if (data->mountfd == -1) {
        goto err;
    }
1318

1319 1320 1321 1322
#ifdef FS_IOC_GETVERSION
    /*
     * use ioc_getversion only if the ioctl is definied
     */
1323 1324 1325
    if (fstatfs(data->mountfd, &stbuf) < 0) {
        close_preserve_errno(data->mountfd);
        goto err;
1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336
    }
    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

1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349
    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;
    }
1350
    ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT;
1351

1352
    ctx->private = data;
1353
    return 0;
1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365

err:
    g_free(data);
    return -1;
}

static void local_cleanup(FsContext *ctx)
{
    LocalData *data = ctx->private;

    close(data->mountfd);
    g_free(data);
1366 1367
}

1368 1369 1370 1371 1372 1373
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) {
1374 1375 1376
        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");
1377 1378 1379 1380 1381
        return -1;
    }

    if (!strcmp(sec_model, "passthrough")) {
        fse->export_flags |= V9FS_SM_PASSTHROUGH;
1382 1383
    } else if (!strcmp(sec_model, "mapped") ||
               !strcmp(sec_model, "mapped-xattr")) {
1384 1385 1386
        fse->export_flags |= V9FS_SM_MAPPED;
    } else if (!strcmp(sec_model, "none")) {
        fse->export_flags |= V9FS_SM_NONE;
1387 1388
    } else if (!strcmp(sec_model, "mapped-file")) {
        fse->export_flags |= V9FS_SM_MAPPED_FILE;
1389
    } else {
1390 1391 1392
        error_report("Invalid security model %s specified", sec_model);
        error_printf("valid options are:"
                     "\t[passthrough|mapped-xattr|mapped-file|none]\n");
1393 1394 1395 1396
        return -1;
    }

    if (!path) {
1397
        error_report("fsdev: No path specified");
1398 1399 1400 1401 1402 1403 1404
        return -1;
    }
    fse->path = g_strdup(path);

    return 0;
}

1405
FileOperations local_ops = {
1406
    .parse_opts = local_parse_opts,
1407
    .init  = local_init,
1408
    .cleanup = local_cleanup,
1409 1410 1411 1412
    .lstat = local_lstat,
    .readlink = local_readlink,
    .close = local_close,
    .closedir = local_closedir,
1413 1414
    .open = local_open,
    .opendir = local_opendir,
1415 1416
    .rewinddir = local_rewinddir,
    .telldir = local_telldir,
G
Greg Kurz 已提交
1417
    .readdir = local_readdir,
1418
    .seekdir = local_seekdir,
1419 1420
    .preadv = local_preadv,
    .pwritev = local_pwritev,
1421 1422 1423 1424 1425 1426 1427
    .chmod = local_chmod,
    .mknod = local_mknod,
    .mkdir = local_mkdir,
    .fstat = local_fstat,
    .open2 = local_open2,
    .symlink = local_symlink,
    .link = local_link,
1428 1429 1430
    .truncate = local_truncate,
    .rename = local_rename,
    .chown = local_chown,
M
M. Mohan Kumar 已提交
1431
    .utimensat = local_utimensat,
1432
    .remove = local_remove,
1433
    .fsync = local_fsync,
1434
    .statfs = local_statfs,
1435 1436
    .lgetxattr = local_lgetxattr,
    .llistxattr = local_llistxattr,
1437
    .lsetxattr = local_lsetxattr,
1438
    .lremovexattr = local_lremovexattr,
1439 1440 1441
    .name_to_path = local_name_to_path,
    .renameat  = local_renameat,
    .unlinkat = local_unlinkat,
1442
};