9p-local.c 34.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-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/error-report.h"
25
#include <libgen.h>
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
#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
44

45 46
#define VIRTFS_META_DIR ".virtfs_metadata"

47
static char *local_mapped_attr_path(FsContext *ctx, const char *path)
48
{
49 50 51 52 53 54 55 56 57 58 59
    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);
60 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
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;
}

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

97
    attr_path = local_mapped_attr_path(ctx, path);
98
    fp = local_fopen(attr_path, "r");
99
    g_free(attr_path);
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
    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);
}

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

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

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

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

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

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

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

185 186
    attr_path = local_mapped_attr_path(ctx, path);
    fp = local_fopen(attr_path, "r");
187 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
    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:
213
    fp = local_fopen(attr_path, "w");
214 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
    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:
248
    g_free(attr_path);
249 250 251
    return ret;
}

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

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

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

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

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

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

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

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

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

348
static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs)
349
{
350
    return closedir(fs->dir);
351
}
352

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

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

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

371 372 373
    buffer = rpath(ctx, path);
    fs->dir = opendir(buffer);
    g_free(buffer);
374 375 376 377
    if (!fs->dir) {
        return -1;
    }
    return 0;
378 379
}

380
static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
381
{
S
Stefan Weil 已提交
382
    rewinddir(fs->dir);
383 384
}

385
static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs)
386
{
387
    return telldir(fs->dir);
388 389
}

390 391
static int local_readdir_r(FsContext *ctx, V9fsFidOpenState *fs,
                           struct dirent *entry,
392
                           struct dirent **result)
393
{
394 395 396 397
    int ret;

again:
    ret = readdir_r(fs->dir, entry, result);
398 399 400
    if (ctx->export_flags & V9FS_SM_MAPPED) {
        entry->d_type = DT_UNKNOWN;
    } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
401 402 403 404 405
        if (!ret && *result != NULL &&
            !strcmp(entry->d_name, VIRTFS_META_DIR)) {
            /* skp the meta data directory */
            goto again;
        }
406
        entry->d_type = DT_UNKNOWN;
407 408
    }
    return ret;
409 410
}

411
static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
412
{
S
Stefan Weil 已提交
413
    seekdir(fs->dir, off);
414 415
}

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

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

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

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

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

492 493 494 495
    v9fs_string_init(&fullname);
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
    path = fullname.data;

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

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

err_end:
536
    remove(buffer);
537
    errno = serrno;
538
out:
539
    g_free(buffer);
540
    v9fs_string_free(&fullname);
541
    return err;
542 543
}

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

553 554 555 556
    v9fs_string_init(&fullname);
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
    path = fullname.data;

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

err_end:
598
    remove(buffer);
599
    errno = serrno;
600
out:
601
    g_free(buffer);
602
    v9fs_string_free(&fullname);
603
    return err;
604 605
}

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

    if (fid_type == P9_FID_DIR) {
        fd = dirfd(fs->dir);
    } else {
        fd = fs->fd;
    }

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

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

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

657 658 659 660 661
    /*
     * Mark all the open to not follow symlinks
     */
    flags |= O_NOFOLLOW;

662 663 664 665
    v9fs_string_init(&fullname);
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
    path = fullname.data;

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

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

723

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

733 734 735 736
    v9fs_string_init(&fullname);
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
    newpath = fullname.data;

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

err_end:
819
    remove(buffer);
820
    errno = serrno;
821
out:
822
    g_free(buffer);
823
    v9fs_string_free(&fullname);
824
    return err;
825 826
}

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

834 835 836
    v9fs_string_init(&newpath);
    v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name);

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

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

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

870 871 872 873
    buffer = rpath(ctx, path);
    ret = truncate(buffer, size);
    g_free(buffer);
    return ret;
874 875 876 877 878
}

static int local_rename(FsContext *ctx, const char *oldpath,
                        const char *newpath)
{
879
    int err;
880
    char *buffer, *buffer1;
881

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

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

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

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

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

935 936 937 938
    buffer = rpath(s, path);
    ret = qemu_utimens(buffer, buf);
    g_free(buffer);
    return ret;
939 940
}

941 942
static int local_remove(FsContext *ctx, const char *path)
{
943 944
    int err;
    struct stat stbuf;
945
    char *buffer;
946 947

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

    buffer = rpath(ctx, path);
    err = remove(buffer);
    g_free(buffer);
990 991
err_out:
    return err;
992 993
}

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

    if (fid_type == P9_FID_DIR) {
        fd = dirfd(fs->dir);
    } else {
        fd = fs->fd;
    }

1005
    if (datasync) {
1006
        return qemu_fdatasync(fd);
1007
    } else {
1008
        return fsync(fd);
1009
    }
1010 1011
}

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

1018 1019 1020 1021
    buffer = rpath(s, path);
    ret = statfs(buffer, stbuf);
    g_free(buffer);
    return ret;
1022 1023
}

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

1029
    return v9fs_get_xattr(ctx, path, name, value, size);
1030 1031
}

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

1037
    return v9fs_list_xattr(ctx, path, value, size);
1038 1039
}

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

1045
    return v9fs_set_xattr(ctx, path, name, value, size, flags);
1046 1047
}

1048 1049
static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
                              const char *name)
1050
{
1051 1052
    char *path = fs_path->data;

1053
    return v9fs_remove_xattr(ctx, path, name);
1054 1055
}

1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 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
static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path,
                              const char *name, V9fsPath *target)
{
    if (dir_path) {
        v9fs_string_sprintf((V9fsString *)target, "%s/%s",
                            dir_path->data, name);
    } else {
        v9fs_string_sprintf((V9fsString *)target, "%s", name);
    }
    /* Bump the size for including terminating NULL */
    target->size++;
    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,
1256
    .readdir_r = local_readdir_r,
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
};