~pmdj/ubuntu/trusty/qemu/2.9+applesmc+fadtv3

« back to all changes in this revision

Viewing changes to .pc/9pfs-local-forbid-client-access-to-metadata-CVE-2017-7493.patch/hw/9pfs/9p-local.c

  • Committer: Phil Dennis-Jordan
  • Date: 2017-07-21 08:03:43 UTC
  • mfrom: (1.1.1)
  • Revision ID: phil@philjordan.eu-20170721080343-2yr2vdj7713czahv
New upstream release 2.9.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * 9p Posix callback
3
 
 *
4
 
 * Copyright IBM, Corp. 2010
5
 
 *
6
 
 * Authors:
7
 
 *  Anthony Liguori   <aliguori@us.ibm.com>
8
 
 *
9
 
 * This work is licensed under the terms of the GNU GPL, version 2.  See
10
 
 * the COPYING file in the top-level directory.
11
 
 *
12
 
 */
13
 
 
14
 
#include "qemu/osdep.h"
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
 
#include <pwd.h>
22
 
#include <grp.h>
23
 
#include <sys/socket.h>
24
 
#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
 
#include <linux/fs.h>
30
 
#ifdef CONFIG_LINUX_MAGIC_H
31
 
#include <linux/magic.h>
32
 
#endif
33
 
#include <sys/ioctl.h>
34
 
 
35
 
#ifndef XFS_SUPER_MAGIC
36
 
#define XFS_SUPER_MAGIC  0x58465342
37
 
#endif
38
 
#ifndef EXT2_SUPER_MAGIC
39
 
#define EXT2_SUPER_MAGIC 0xEF53
40
 
#endif
41
 
#ifndef REISERFS_SUPER_MAGIC
42
 
#define REISERFS_SUPER_MAGIC 0x52654973
43
 
#endif
44
 
#ifndef BTRFS_SUPER_MAGIC
45
 
#define BTRFS_SUPER_MAGIC 0x9123683E
46
 
#endif
47
 
 
48
 
typedef struct {
49
 
    int mountfd;
50
 
} LocalData;
51
 
 
52
 
int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
53
 
                        mode_t mode)
54
 
{
55
 
    LocalData *data = fs_ctx->private;
56
 
 
57
 
    /* All paths are relative to the path data->mountfd points to */
58
 
    while (*path == '/') {
59
 
        path++;
60
 
    }
61
 
 
62
 
    return relative_openat_nofollow(data->mountfd, path, flags, mode);
63
 
}
64
 
 
65
 
int local_opendir_nofollow(FsContext *fs_ctx, const char *path)
66
 
{
67
 
    return local_open_nofollow(fs_ctx, path, O_DIRECTORY | O_RDONLY, 0);
68
 
}
69
 
 
70
 
static void renameat_preserve_errno(int odirfd, const char *opath, int ndirfd,
71
 
                                    const char *npath)
72
 
{
73
 
    int serrno = errno;
74
 
    renameat(odirfd, opath, ndirfd, npath);
75
 
    errno = serrno;
76
 
}
77
 
 
78
 
static void unlinkat_preserve_errno(int dirfd, const char *path, int flags)
79
 
{
80
 
    int serrno = errno;
81
 
    unlinkat(dirfd, path, flags);
82
 
    errno = serrno;
83
 
}
84
 
 
85
 
#define VIRTFS_META_DIR ".virtfs_metadata"
86
 
 
87
 
static FILE *local_fopenat(int dirfd, const char *name, const char *mode)
88
 
{
89
 
    int fd, o_mode = 0;
90
 
    FILE *fp;
91
 
    int flags;
92
 
    /*
93
 
     * only supports two modes
94
 
     */
95
 
    if (mode[0] == 'r') {
96
 
        flags = O_RDONLY;
97
 
    } else if (mode[0] == 'w') {
98
 
        flags = O_WRONLY | O_TRUNC | O_CREAT;
99
 
        o_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
100
 
    } else {
101
 
        return NULL;
102
 
    }
103
 
    fd = openat_file(dirfd, name, flags, o_mode);
104
 
    if (fd == -1) {
105
 
        return NULL;
106
 
    }
107
 
    fp = fdopen(fd, mode);
108
 
    if (!fp) {
109
 
        close(fd);
110
 
    }
111
 
    return fp;
112
 
}
113
 
 
114
 
#define ATTR_MAX 100
115
 
static void local_mapped_file_attr(int dirfd, const char *name,
116
 
                                   struct stat *stbuf)
117
 
{
118
 
    FILE *fp;
119
 
    char buf[ATTR_MAX];
120
 
    int map_dirfd;
121
 
 
122
 
    map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
123
 
    if (map_dirfd == -1) {
124
 
        return;
125
 
    }
126
 
 
127
 
    fp = local_fopenat(map_dirfd, name, "r");
128
 
    close_preserve_errno(map_dirfd);
129
 
    if (!fp) {
130
 
        return;
131
 
    }
132
 
    memset(buf, 0, ATTR_MAX);
133
 
    while (fgets(buf, ATTR_MAX, fp)) {
134
 
        if (!strncmp(buf, "virtfs.uid", 10)) {
135
 
            stbuf->st_uid = atoi(buf+11);
136
 
        } else if (!strncmp(buf, "virtfs.gid", 10)) {
137
 
            stbuf->st_gid = atoi(buf+11);
138
 
        } else if (!strncmp(buf, "virtfs.mode", 11)) {
139
 
            stbuf->st_mode = atoi(buf+12);
140
 
        } else if (!strncmp(buf, "virtfs.rdev", 11)) {
141
 
            stbuf->st_rdev = atoi(buf+12);
142
 
        }
143
 
        memset(buf, 0, ATTR_MAX);
144
 
    }
145
 
    fclose(fp);
146
 
}
147
 
 
148
 
static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
149
 
{
150
 
    int err = -1;
151
 
    char *dirpath = g_path_get_dirname(fs_path->data);
152
 
    char *name = g_path_get_basename(fs_path->data);
153
 
    int dirfd;
154
 
 
155
 
    dirfd = local_opendir_nofollow(fs_ctx, dirpath);
156
 
    if (dirfd == -1) {
157
 
        goto out;
158
 
    }
159
 
 
160
 
    err = fstatat(dirfd, name, stbuf, AT_SYMLINK_NOFOLLOW);
161
 
    if (err) {
162
 
        goto err_out;
163
 
    }
164
 
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
165
 
        /* Actual credentials are part of extended attrs */
166
 
        uid_t tmp_uid;
167
 
        gid_t tmp_gid;
168
 
        mode_t tmp_mode;
169
 
        dev_t tmp_dev;
170
 
 
171
 
        if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.uid", &tmp_uid,
172
 
                                 sizeof(uid_t)) > 0) {
173
 
            stbuf->st_uid = le32_to_cpu(tmp_uid);
174
 
        }
175
 
        if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.gid", &tmp_gid,
176
 
                                 sizeof(gid_t)) > 0) {
177
 
            stbuf->st_gid = le32_to_cpu(tmp_gid);
178
 
        }
179
 
        if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.mode", &tmp_mode,
180
 
                                 sizeof(mode_t)) > 0) {
181
 
            stbuf->st_mode = le32_to_cpu(tmp_mode);
182
 
        }
183
 
        if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.rdev", &tmp_dev,
184
 
                                 sizeof(dev_t)) > 0) {
185
 
            stbuf->st_rdev = le64_to_cpu(tmp_dev);
186
 
        }
187
 
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
188
 
        local_mapped_file_attr(dirfd, name, stbuf);
189
 
    }
190
 
 
191
 
err_out:
192
 
    close_preserve_errno(dirfd);
193
 
out:
194
 
    g_free(name);
195
 
    g_free(dirpath);
196
 
    return err;
197
 
}
198
 
 
199
 
static int local_set_mapped_file_attrat(int dirfd, const char *name,
200
 
                                        FsCred *credp)
201
 
{
202
 
    FILE *fp;
203
 
    int ret;
204
 
    char buf[ATTR_MAX];
205
 
    int uid = -1, gid = -1, mode = -1, rdev = -1;
206
 
    int map_dirfd;
207
 
 
208
 
    ret = mkdirat(dirfd, VIRTFS_META_DIR, 0700);
209
 
    if (ret < 0 && errno != EEXIST) {
210
 
        return -1;
211
 
    }
212
 
 
213
 
    map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
214
 
    if (map_dirfd == -1) {
215
 
        return -1;
216
 
    }
217
 
 
218
 
    fp = local_fopenat(map_dirfd, name, "r");
219
 
    if (!fp) {
220
 
        if (errno == ENOENT) {
221
 
            goto update_map_file;
222
 
        } else {
223
 
            close_preserve_errno(map_dirfd);
224
 
            return -1;
225
 
        }
226
 
    }
227
 
    memset(buf, 0, ATTR_MAX);
228
 
    while (fgets(buf, ATTR_MAX, fp)) {
229
 
        if (!strncmp(buf, "virtfs.uid", 10)) {
230
 
            uid = atoi(buf + 11);
231
 
        } else if (!strncmp(buf, "virtfs.gid", 10)) {
232
 
            gid = atoi(buf + 11);
233
 
        } else if (!strncmp(buf, "virtfs.mode", 11)) {
234
 
            mode = atoi(buf + 12);
235
 
        } else if (!strncmp(buf, "virtfs.rdev", 11)) {
236
 
            rdev = atoi(buf + 12);
237
 
        }
238
 
        memset(buf, 0, ATTR_MAX);
239
 
    }
240
 
    fclose(fp);
241
 
 
242
 
update_map_file:
243
 
    fp = local_fopenat(map_dirfd, name, "w");
244
 
    close_preserve_errno(map_dirfd);
245
 
    if (!fp) {
246
 
        return -1;
247
 
    }
248
 
 
249
 
    if (credp->fc_uid != -1) {
250
 
        uid = credp->fc_uid;
251
 
    }
252
 
    if (credp->fc_gid != -1) {
253
 
        gid = credp->fc_gid;
254
 
    }
255
 
    if (credp->fc_mode != -1) {
256
 
        mode = credp->fc_mode;
257
 
    }
258
 
    if (credp->fc_rdev != -1) {
259
 
        rdev = credp->fc_rdev;
260
 
    }
261
 
 
262
 
    if (uid != -1) {
263
 
        fprintf(fp, "virtfs.uid=%d\n", uid);
264
 
    }
265
 
    if (gid != -1) {
266
 
        fprintf(fp, "virtfs.gid=%d\n", gid);
267
 
    }
268
 
    if (mode != -1) {
269
 
        fprintf(fp, "virtfs.mode=%d\n", mode);
270
 
    }
271
 
    if (rdev != -1) {
272
 
        fprintf(fp, "virtfs.rdev=%d\n", rdev);
273
 
    }
274
 
    fclose(fp);
275
 
 
276
 
    return 0;
277
 
}
278
 
 
279
 
static int fchmodat_nofollow(int dirfd, const char *name, mode_t mode)
280
 
{
281
 
    int fd, ret;
282
 
 
283
 
    /* FIXME: this should be handled with fchmodat(AT_SYMLINK_NOFOLLOW).
284
 
     * Unfortunately, the linux kernel doesn't implement it yet. As an
285
 
     * alternative, let's open the file and use fchmod() instead. This
286
 
     * may fail depending on the permissions of the file, but it is the
287
 
     * best we can do to avoid TOCTTOU. We first try to open read-only
288
 
     * in case name points to a directory. If that fails, we try write-only
289
 
     * in case name doesn't point to a directory.
290
 
     */
291
 
    fd = openat_file(dirfd, name, O_RDONLY, 0);
292
 
    if (fd == -1) {
293
 
        /* In case the file is writable-only and isn't a directory. */
294
 
        if (errno == EACCES) {
295
 
            fd = openat_file(dirfd, name, O_WRONLY, 0);
296
 
        }
297
 
        if (fd == -1 && errno == EISDIR) {
298
 
            errno = EACCES;
299
 
        }
300
 
    }
301
 
    if (fd == -1) {
302
 
        return -1;
303
 
    }
304
 
    ret = fchmod(fd, mode);
305
 
    close_preserve_errno(fd);
306
 
    return ret;
307
 
}
308
 
 
309
 
static int local_set_xattrat(int dirfd, const char *path, FsCred *credp)
310
 
{
311
 
    int err;
312
 
 
313
 
    if (credp->fc_uid != -1) {
314
 
        uint32_t tmp_uid = cpu_to_le32(credp->fc_uid);
315
 
        err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.uid", &tmp_uid,
316
 
                                   sizeof(uid_t), 0);
317
 
        if (err) {
318
 
            return err;
319
 
        }
320
 
    }
321
 
    if (credp->fc_gid != -1) {
322
 
        uint32_t tmp_gid = cpu_to_le32(credp->fc_gid);
323
 
        err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.gid", &tmp_gid,
324
 
                                   sizeof(gid_t), 0);
325
 
        if (err) {
326
 
            return err;
327
 
        }
328
 
    }
329
 
    if (credp->fc_mode != -1) {
330
 
        uint32_t tmp_mode = cpu_to_le32(credp->fc_mode);
331
 
        err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.mode", &tmp_mode,
332
 
                                   sizeof(mode_t), 0);
333
 
        if (err) {
334
 
            return err;
335
 
        }
336
 
    }
337
 
    if (credp->fc_rdev != -1) {
338
 
        uint64_t tmp_rdev = cpu_to_le64(credp->fc_rdev);
339
 
        err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.rdev", &tmp_rdev,
340
 
                                   sizeof(dev_t), 0);
341
 
        if (err) {
342
 
            return err;
343
 
        }
344
 
    }
345
 
    return 0;
346
 
}
347
 
 
348
 
static int local_set_cred_passthrough(FsContext *fs_ctx, int dirfd,
349
 
                                      const char *name, FsCred *credp)
350
 
{
351
 
    if (fchownat(dirfd, name, credp->fc_uid, credp->fc_gid,
352
 
                 AT_SYMLINK_NOFOLLOW) < 0) {
353
 
        /*
354
 
         * If we fail to change ownership and if we are
355
 
         * using security model none. Ignore the error
356
 
         */
357
 
        if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
358
 
            return -1;
359
 
        }
360
 
    }
361
 
 
362
 
    return fchmodat_nofollow(dirfd, name, credp->fc_mode & 07777);
363
 
}
364
 
 
365
 
static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
366
 
                              char *buf, size_t bufsz)
367
 
{
368
 
    ssize_t tsize = -1;
369
 
 
370
 
    if ((fs_ctx->export_flags & V9FS_SM_MAPPED) ||
371
 
        (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE)) {
372
 
        int fd;
373
 
 
374
 
        fd = local_open_nofollow(fs_ctx, fs_path->data, O_RDONLY, 0);
375
 
        if (fd == -1) {
376
 
            return -1;
377
 
        }
378
 
        do {
379
 
            tsize = read(fd, (void *)buf, bufsz);
380
 
        } while (tsize == -1 && errno == EINTR);
381
 
        close_preserve_errno(fd);
382
 
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
383
 
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
384
 
        char *dirpath = g_path_get_dirname(fs_path->data);
385
 
        char *name = g_path_get_basename(fs_path->data);
386
 
        int dirfd;
387
 
 
388
 
        dirfd = local_opendir_nofollow(fs_ctx, dirpath);
389
 
        if (dirfd == -1) {
390
 
            goto out;
391
 
        }
392
 
 
393
 
        tsize = readlinkat(dirfd, name, buf, bufsz);
394
 
        close_preserve_errno(dirfd);
395
 
    out:
396
 
        g_free(name);
397
 
        g_free(dirpath);
398
 
    }
399
 
    return tsize;
400
 
}
401
 
 
402
 
static int local_close(FsContext *ctx, V9fsFidOpenState *fs)
403
 
{
404
 
    return close(fs->fd);
405
 
}
406
 
 
407
 
static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs)
408
 
{
409
 
    return closedir(fs->dir.stream);
410
 
}
411
 
 
412
 
static int local_open(FsContext *ctx, V9fsPath *fs_path,
413
 
                      int flags, V9fsFidOpenState *fs)
414
 
{
415
 
    int fd;
416
 
 
417
 
    fd = local_open_nofollow(ctx, fs_path->data, flags, 0);
418
 
    if (fd == -1) {
419
 
        return -1;
420
 
    }
421
 
    fs->fd = fd;
422
 
    return fs->fd;
423
 
}
424
 
 
425
 
static int local_opendir(FsContext *ctx,
426
 
                         V9fsPath *fs_path, V9fsFidOpenState *fs)
427
 
{
428
 
    int dirfd;
429
 
    DIR *stream;
430
 
 
431
 
    dirfd = local_opendir_nofollow(ctx, fs_path->data);
432
 
    if (dirfd == -1) {
433
 
        return -1;
434
 
    }
435
 
 
436
 
    stream = fdopendir(dirfd);
437
 
    if (!stream) {
438
 
        close(dirfd);
439
 
        return -1;
440
 
    }
441
 
    fs->dir.stream = stream;
442
 
    return 0;
443
 
}
444
 
 
445
 
static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
446
 
{
447
 
    rewinddir(fs->dir.stream);
448
 
}
449
 
 
450
 
static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs)
451
 
{
452
 
    return telldir(fs->dir.stream);
453
 
}
454
 
 
455
 
static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs)
456
 
{
457
 
    struct dirent *entry;
458
 
 
459
 
again:
460
 
    entry = readdir(fs->dir.stream);
461
 
    if (!entry) {
462
 
        return NULL;
463
 
    }
464
 
 
465
 
    if (ctx->export_flags & V9FS_SM_MAPPED) {
466
 
        entry->d_type = DT_UNKNOWN;
467
 
    } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
468
 
        if (!strcmp(entry->d_name, VIRTFS_META_DIR)) {
469
 
            /* skp the meta data directory */
470
 
            goto again;
471
 
        }
472
 
        entry->d_type = DT_UNKNOWN;
473
 
    }
474
 
 
475
 
    return entry;
476
 
}
477
 
 
478
 
static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
479
 
{
480
 
    seekdir(fs->dir.stream, off);
481
 
}
482
 
 
483
 
static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs,
484
 
                            const struct iovec *iov,
485
 
                            int iovcnt, off_t offset)
486
 
{
487
 
#ifdef CONFIG_PREADV
488
 
    return preadv(fs->fd, iov, iovcnt, offset);
489
 
#else
490
 
    int err = lseek(fs->fd, offset, SEEK_SET);
491
 
    if (err == -1) {
492
 
        return err;
493
 
    } else {
494
 
        return readv(fs->fd, iov, iovcnt);
495
 
    }
496
 
#endif
497
 
}
498
 
 
499
 
static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
500
 
                             const struct iovec *iov,
501
 
                             int iovcnt, off_t offset)
502
 
{
503
 
    ssize_t ret
504
 
;
505
 
#ifdef CONFIG_PREADV
506
 
    ret = pwritev(fs->fd, iov, iovcnt, offset);
507
 
#else
508
 
    int err = lseek(fs->fd, offset, SEEK_SET);
509
 
    if (err == -1) {
510
 
        return err;
511
 
    } else {
512
 
        ret = writev(fs->fd, iov, iovcnt);
513
 
    }
514
 
#endif
515
 
#ifdef CONFIG_SYNC_FILE_RANGE
516
 
    if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) {
517
 
        /*
518
 
         * Initiate a writeback. This is not a data integrity sync.
519
 
         * We want to ensure that we don't leave dirty pages in the cache
520
 
         * after write when writeout=immediate is sepcified.
521
 
         */
522
 
        sync_file_range(fs->fd, offset, ret,
523
 
                        SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
524
 
    }
525
 
#endif
526
 
    return ret;
527
 
}
528
 
 
529
 
static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
530
 
{
531
 
    char *dirpath = g_path_get_dirname(fs_path->data);
532
 
    char *name = g_path_get_basename(fs_path->data);
533
 
    int ret = -1;
534
 
    int dirfd;
535
 
 
536
 
    dirfd = local_opendir_nofollow(fs_ctx, dirpath);
537
 
    if (dirfd == -1) {
538
 
        goto out;
539
 
    }
540
 
 
541
 
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
542
 
        ret = local_set_xattrat(dirfd, name, credp);
543
 
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
544
 
        ret = local_set_mapped_file_attrat(dirfd, name, credp);
545
 
    } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH ||
546
 
               fs_ctx->export_flags & V9FS_SM_NONE) {
547
 
        ret = fchmodat_nofollow(dirfd, name, credp->fc_mode);
548
 
    }
549
 
    close_preserve_errno(dirfd);
550
 
 
551
 
out:
552
 
    g_free(dirpath);
553
 
    g_free(name);
554
 
    return ret;
555
 
}
556
 
 
557
 
static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
558
 
                       const char *name, FsCred *credp)
559
 
{
560
 
    int err = -1;
561
 
    int dirfd;
562
 
 
563
 
    dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
564
 
    if (dirfd == -1) {
565
 
        return -1;
566
 
    }
567
 
 
568
 
    if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
569
 
        fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
570
 
        err = mknodat(dirfd, name, SM_LOCAL_MODE_BITS | S_IFREG, 0);
571
 
        if (err == -1) {
572
 
            goto out;
573
 
        }
574
 
 
575
 
        if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
576
 
            err = local_set_xattrat(dirfd, name, credp);
577
 
        } else {
578
 
            err = local_set_mapped_file_attrat(dirfd, name, credp);
579
 
        }
580
 
        if (err == -1) {
581
 
            goto err_end;
582
 
        }
583
 
    } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH ||
584
 
               fs_ctx->export_flags & V9FS_SM_NONE) {
585
 
        err = mknodat(dirfd, name, credp->fc_mode, credp->fc_rdev);
586
 
        if (err == -1) {
587
 
            goto out;
588
 
        }
589
 
        err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp);
590
 
        if (err == -1) {
591
 
            goto err_end;
592
 
        }
593
 
    }
594
 
    goto out;
595
 
 
596
 
err_end:
597
 
    unlinkat_preserve_errno(dirfd, name, 0);
598
 
out:
599
 
    close_preserve_errno(dirfd);
600
 
    return err;
601
 
}
602
 
 
603
 
static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
604
 
                       const char *name, FsCred *credp)
605
 
{
606
 
    int err = -1;
607
 
    int dirfd;
608
 
 
609
 
    dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
610
 
    if (dirfd == -1) {
611
 
        return -1;
612
 
    }
613
 
 
614
 
    if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
615
 
        fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
616
 
        err = mkdirat(dirfd, name, SM_LOCAL_DIR_MODE_BITS);
617
 
        if (err == -1) {
618
 
            goto out;
619
 
        }
620
 
        credp->fc_mode = credp->fc_mode | S_IFDIR;
621
 
 
622
 
        if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
623
 
            err = local_set_xattrat(dirfd, name, credp);
624
 
        } else {
625
 
            err = local_set_mapped_file_attrat(dirfd, name, credp);
626
 
        }
627
 
        if (err == -1) {
628
 
            goto err_end;
629
 
        }
630
 
    } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH ||
631
 
               fs_ctx->export_flags & V9FS_SM_NONE) {
632
 
        err = mkdirat(dirfd, name, credp->fc_mode);
633
 
        if (err == -1) {
634
 
            goto out;
635
 
        }
636
 
        err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp);
637
 
        if (err == -1) {
638
 
            goto err_end;
639
 
        }
640
 
    }
641
 
    goto out;
642
 
 
643
 
err_end:
644
 
    unlinkat_preserve_errno(dirfd, name, AT_REMOVEDIR);
645
 
out:
646
 
    close_preserve_errno(dirfd);
647
 
    return err;
648
 
}
649
 
 
650
 
static int local_fstat(FsContext *fs_ctx, int fid_type,
651
 
                       V9fsFidOpenState *fs, struct stat *stbuf)
652
 
{
653
 
    int err, fd;
654
 
 
655
 
    if (fid_type == P9_FID_DIR) {
656
 
        fd = dirfd(fs->dir.stream);
657
 
    } else {
658
 
        fd = fs->fd;
659
 
    }
660
 
 
661
 
    err = fstat(fd, stbuf);
662
 
    if (err) {
663
 
        return err;
664
 
    }
665
 
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
666
 
        /* Actual credentials are part of extended attrs */
667
 
        uid_t tmp_uid;
668
 
        gid_t tmp_gid;
669
 
        mode_t tmp_mode;
670
 
        dev_t tmp_dev;
671
 
 
672
 
        if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) {
673
 
            stbuf->st_uid = le32_to_cpu(tmp_uid);
674
 
        }
675
 
        if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) {
676
 
            stbuf->st_gid = le32_to_cpu(tmp_gid);
677
 
        }
678
 
        if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) {
679
 
            stbuf->st_mode = le32_to_cpu(tmp_mode);
680
 
        }
681
 
        if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) {
682
 
            stbuf->st_rdev = le64_to_cpu(tmp_dev);
683
 
        }
684
 
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
685
 
        errno = EOPNOTSUPP;
686
 
        return -1;
687
 
    }
688
 
    return err;
689
 
}
690
 
 
691
 
static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
692
 
                       int flags, FsCred *credp, V9fsFidOpenState *fs)
693
 
{
694
 
    int fd = -1;
695
 
    int err = -1;
696
 
    int dirfd;
697
 
 
698
 
    /*
699
 
     * Mark all the open to not follow symlinks
700
 
     */
701
 
    flags |= O_NOFOLLOW;
702
 
 
703
 
    dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
704
 
    if (dirfd == -1) {
705
 
        return -1;
706
 
    }
707
 
 
708
 
    /* Determine the security model */
709
 
    if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
710
 
        fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
711
 
        fd = openat_file(dirfd, name, flags, SM_LOCAL_MODE_BITS);
712
 
        if (fd == -1) {
713
 
            goto out;
714
 
        }
715
 
        credp->fc_mode = credp->fc_mode|S_IFREG;
716
 
        if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
717
 
            /* Set cleint credentials in xattr */
718
 
            err = local_set_xattrat(dirfd, name, credp);
719
 
        } else {
720
 
            err = local_set_mapped_file_attrat(dirfd, name, credp);
721
 
        }
722
 
        if (err == -1) {
723
 
            goto err_end;
724
 
        }
725
 
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
726
 
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
727
 
        fd = openat_file(dirfd, name, flags, credp->fc_mode);
728
 
        if (fd == -1) {
729
 
            goto out;
730
 
        }
731
 
        err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp);
732
 
        if (err == -1) {
733
 
            goto err_end;
734
 
        }
735
 
    }
736
 
    err = fd;
737
 
    fs->fd = fd;
738
 
    goto out;
739
 
 
740
 
err_end:
741
 
    unlinkat_preserve_errno(dirfd, name,
742
 
                            flags & O_DIRECTORY ? AT_REMOVEDIR : 0);
743
 
    close_preserve_errno(fd);
744
 
out:
745
 
    close_preserve_errno(dirfd);
746
 
    return err;
747
 
}
748
 
 
749
 
 
750
 
static int local_symlink(FsContext *fs_ctx, const char *oldpath,
751
 
                         V9fsPath *dir_path, const char *name, FsCred *credp)
752
 
{
753
 
    int err = -1;
754
 
    int dirfd;
755
 
 
756
 
    dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
757
 
    if (dirfd == -1) {
758
 
        return -1;
759
 
    }
760
 
 
761
 
    /* Determine the security model */
762
 
    if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
763
 
        fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
764
 
        int fd;
765
 
        ssize_t oldpath_size, write_size;
766
 
 
767
 
        fd = openat_file(dirfd, name, O_CREAT | O_EXCL | O_RDWR,
768
 
                         SM_LOCAL_MODE_BITS);
769
 
        if (fd == -1) {
770
 
            goto out;
771
 
        }
772
 
        /* Write the oldpath (target) to the file. */
773
 
        oldpath_size = strlen(oldpath);
774
 
        do {
775
 
            write_size = write(fd, (void *)oldpath, oldpath_size);
776
 
        } while (write_size == -1 && errno == EINTR);
777
 
        close_preserve_errno(fd);
778
 
 
779
 
        if (write_size != oldpath_size) {
780
 
            goto err_end;
781
 
        }
782
 
        /* Set cleint credentials in symlink's xattr */
783
 
        credp->fc_mode = credp->fc_mode | S_IFLNK;
784
 
 
785
 
        if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
786
 
            err = local_set_xattrat(dirfd, name, credp);
787
 
        } else {
788
 
            err = local_set_mapped_file_attrat(dirfd, name, credp);
789
 
        }
790
 
        if (err == -1) {
791
 
            goto err_end;
792
 
        }
793
 
    } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH ||
794
 
               fs_ctx->export_flags & V9FS_SM_NONE) {
795
 
        err = symlinkat(oldpath, dirfd, name);
796
 
        if (err) {
797
 
            goto out;
798
 
        }
799
 
        err = fchownat(dirfd, name, credp->fc_uid, credp->fc_gid,
800
 
                       AT_SYMLINK_NOFOLLOW);
801
 
        if (err == -1) {
802
 
            /*
803
 
             * If we fail to change ownership and if we are
804
 
             * using security model none. Ignore the error
805
 
             */
806
 
            if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
807
 
                goto err_end;
808
 
            } else {
809
 
                err = 0;
810
 
            }
811
 
        }
812
 
    }
813
 
    goto out;
814
 
 
815
 
err_end:
816
 
    unlinkat_preserve_errno(dirfd, name, 0);
817
 
out:
818
 
    close_preserve_errno(dirfd);
819
 
    return err;
820
 
}
821
 
 
822
 
static int local_link(FsContext *ctx, V9fsPath *oldpath,
823
 
                      V9fsPath *dirpath, const char *name)
824
 
{
825
 
    char *odirpath = g_path_get_dirname(oldpath->data);
826
 
    char *oname = g_path_get_basename(oldpath->data);
827
 
    int ret = -1;
828
 
    int odirfd, ndirfd;
829
 
 
830
 
    odirfd = local_opendir_nofollow(ctx, odirpath);
831
 
    if (odirfd == -1) {
832
 
        goto out;
833
 
    }
834
 
 
835
 
    ndirfd = local_opendir_nofollow(ctx, dirpath->data);
836
 
    if (ndirfd == -1) {
837
 
        close_preserve_errno(odirfd);
838
 
        goto out;
839
 
    }
840
 
 
841
 
    ret = linkat(odirfd, oname, ndirfd, name, 0);
842
 
    if (ret < 0) {
843
 
        goto out_close;
844
 
    }
845
 
 
846
 
    /* now link the virtfs_metadata files */
847
 
    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
848
 
        int omap_dirfd, nmap_dirfd;
849
 
 
850
 
        ret = mkdirat(ndirfd, VIRTFS_META_DIR, 0700);
851
 
        if (ret < 0 && errno != EEXIST) {
852
 
            goto err_undo_link;
853
 
        }
854
 
 
855
 
        omap_dirfd = openat_dir(odirfd, VIRTFS_META_DIR);
856
 
        if (omap_dirfd == -1) {
857
 
            goto err;
858
 
        }
859
 
 
860
 
        nmap_dirfd = openat_dir(ndirfd, VIRTFS_META_DIR);
861
 
        if (nmap_dirfd == -1) {
862
 
            close_preserve_errno(omap_dirfd);
863
 
            goto err;
864
 
        }
865
 
 
866
 
        ret = linkat(omap_dirfd, oname, nmap_dirfd, name, 0);
867
 
        close_preserve_errno(nmap_dirfd);
868
 
        close_preserve_errno(omap_dirfd);
869
 
        if (ret < 0 && errno != ENOENT) {
870
 
            goto err_undo_link;
871
 
        }
872
 
 
873
 
        ret = 0;
874
 
    }
875
 
    goto out_close;
876
 
 
877
 
err:
878
 
    ret = -1;
879
 
err_undo_link:
880
 
    unlinkat_preserve_errno(ndirfd, name, 0);
881
 
out_close:
882
 
    close_preserve_errno(ndirfd);
883
 
    close_preserve_errno(odirfd);
884
 
out:
885
 
    g_free(oname);
886
 
    g_free(odirpath);
887
 
    return ret;
888
 
}
889
 
 
890
 
static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
891
 
{
892
 
    int fd, ret;
893
 
 
894
 
    fd = local_open_nofollow(ctx, fs_path->data, O_WRONLY, 0);
895
 
    if (fd == -1) {
896
 
        return -1;
897
 
    }
898
 
    ret = ftruncate(fd, size);
899
 
    close_preserve_errno(fd);
900
 
    return ret;
901
 
}
902
 
 
903
 
static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
904
 
{
905
 
    char *dirpath = g_path_get_dirname(fs_path->data);
906
 
    char *name = g_path_get_basename(fs_path->data);
907
 
    int ret = -1;
908
 
    int dirfd;
909
 
 
910
 
    dirfd = local_opendir_nofollow(fs_ctx, dirpath);
911
 
    if (dirfd == -1) {
912
 
        goto out;
913
 
    }
914
 
 
915
 
    if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
916
 
        (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
917
 
        (fs_ctx->export_flags & V9FS_SM_NONE)) {
918
 
        ret = fchownat(dirfd, name, credp->fc_uid, credp->fc_gid,
919
 
                       AT_SYMLINK_NOFOLLOW);
920
 
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
921
 
        ret = local_set_xattrat(dirfd, name, credp);
922
 
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
923
 
        ret = local_set_mapped_file_attrat(dirfd, name, credp);
924
 
    }
925
 
 
926
 
    close_preserve_errno(dirfd);
927
 
out:
928
 
    g_free(name);
929
 
    g_free(dirpath);
930
 
    return ret;
931
 
}
932
 
 
933
 
static int local_utimensat(FsContext *s, V9fsPath *fs_path,
934
 
                           const struct timespec *buf)
935
 
{
936
 
    char *dirpath = g_path_get_dirname(fs_path->data);
937
 
    char *name = g_path_get_basename(fs_path->data);
938
 
    int dirfd, ret = -1;
939
 
 
940
 
    dirfd = local_opendir_nofollow(s, dirpath);
941
 
    if (dirfd == -1) {
942
 
        goto out;
943
 
    }
944
 
 
945
 
    ret = utimensat(dirfd, name, buf, AT_SYMLINK_NOFOLLOW);
946
 
    close_preserve_errno(dirfd);
947
 
out:
948
 
    g_free(dirpath);
949
 
    g_free(name);
950
 
    return ret;
951
 
}
952
 
 
953
 
static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name,
954
 
                                 int flags)
955
 
{
956
 
    int ret = -1;
957
 
 
958
 
    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
959
 
        int map_dirfd;
960
 
 
961
 
        if (flags == AT_REMOVEDIR) {
962
 
            int fd;
963
 
 
964
 
            fd = openat_dir(dirfd, name);
965
 
            if (fd == -1) {
966
 
                goto err_out;
967
 
            }
968
 
            /*
969
 
             * If directory remove .virtfs_metadata contained in the
970
 
             * directory
971
 
             */
972
 
            ret = unlinkat(fd, VIRTFS_META_DIR, AT_REMOVEDIR);
973
 
            close_preserve_errno(fd);
974
 
            if (ret < 0 && errno != ENOENT) {
975
 
                /*
976
 
                 * We didn't had the .virtfs_metadata file. May be file created
977
 
                 * in non-mapped mode ?. Ignore ENOENT.
978
 
                 */
979
 
                goto err_out;
980
 
            }
981
 
        }
982
 
        /*
983
 
         * Now remove the name from parent directory
984
 
         * .virtfs_metadata directory.
985
 
         */
986
 
        map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
987
 
        ret = unlinkat(map_dirfd, name, 0);
988
 
        close_preserve_errno(map_dirfd);
989
 
        if (ret < 0 && errno != ENOENT) {
990
 
            /*
991
 
             * We didn't had the .virtfs_metadata file. May be file created
992
 
             * in non-mapped mode ?. Ignore ENOENT.
993
 
             */
994
 
            goto err_out;
995
 
        }
996
 
    }
997
 
 
998
 
    ret = unlinkat(dirfd, name, flags);
999
 
err_out:
1000
 
    return ret;
1001
 
}
1002
 
 
1003
 
static int local_remove(FsContext *ctx, const char *path)
1004
 
{
1005
 
    struct stat stbuf;
1006
 
    char *dirpath = g_path_get_dirname(path);
1007
 
    char *name = g_path_get_basename(path);
1008
 
    int flags = 0;
1009
 
    int dirfd;
1010
 
    int err = -1;
1011
 
 
1012
 
    dirfd = local_opendir_nofollow(ctx, dirpath);
1013
 
    if (dirfd == -1) {
1014
 
        goto out;
1015
 
    }
1016
 
 
1017
 
    if (fstatat(dirfd, path, &stbuf, AT_SYMLINK_NOFOLLOW) < 0) {
1018
 
        goto err_out;
1019
 
    }
1020
 
 
1021
 
    if (S_ISDIR(stbuf.st_mode)) {
1022
 
        flags |= AT_REMOVEDIR;
1023
 
    }
1024
 
 
1025
 
    err = local_unlinkat_common(ctx, dirfd, name, flags);
1026
 
err_out:
1027
 
    close_preserve_errno(dirfd);
1028
 
out:
1029
 
    g_free(name);
1030
 
    g_free(dirpath);
1031
 
    return err;
1032
 
}
1033
 
 
1034
 
static int local_fsync(FsContext *ctx, int fid_type,
1035
 
                       V9fsFidOpenState *fs, int datasync)
1036
 
{
1037
 
    int fd;
1038
 
 
1039
 
    if (fid_type == P9_FID_DIR) {
1040
 
        fd = dirfd(fs->dir.stream);
1041
 
    } else {
1042
 
        fd = fs->fd;
1043
 
    }
1044
 
 
1045
 
    if (datasync) {
1046
 
        return qemu_fdatasync(fd);
1047
 
    } else {
1048
 
        return fsync(fd);
1049
 
    }
1050
 
}
1051
 
 
1052
 
static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
1053
 
{
1054
 
    int fd, ret;
1055
 
 
1056
 
    fd = local_open_nofollow(s, fs_path->data, O_RDONLY, 0);
1057
 
    if (fd == -1) {
1058
 
        return -1;
1059
 
    }
1060
 
    ret = fstatfs(fd, stbuf);
1061
 
    close_preserve_errno(fd);
1062
 
    return ret;
1063
 
}
1064
 
 
1065
 
static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
1066
 
                               const char *name, void *value, size_t size)
1067
 
{
1068
 
    char *path = fs_path->data;
1069
 
 
1070
 
    return v9fs_get_xattr(ctx, path, name, value, size);
1071
 
}
1072
 
 
1073
 
static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path,
1074
 
                                void *value, size_t size)
1075
 
{
1076
 
    char *path = fs_path->data;
1077
 
 
1078
 
    return v9fs_list_xattr(ctx, path, value, size);
1079
 
}
1080
 
 
1081
 
static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
1082
 
                           void *value, size_t size, int flags)
1083
 
{
1084
 
    char *path = fs_path->data;
1085
 
 
1086
 
    return v9fs_set_xattr(ctx, path, name, value, size, flags);
1087
 
}
1088
 
 
1089
 
static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
1090
 
                              const char *name)
1091
 
{
1092
 
    char *path = fs_path->data;
1093
 
 
1094
 
    return v9fs_remove_xattr(ctx, path, name);
1095
 
}
1096
 
 
1097
 
static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path,
1098
 
                              const char *name, V9fsPath *target)
1099
 
{
1100
 
    if (dir_path) {
1101
 
        v9fs_path_sprintf(target, "%s/%s", dir_path->data, name);
1102
 
    } else if (strcmp(name, "/")) {
1103
 
        v9fs_path_sprintf(target, "%s", name);
1104
 
    } else {
1105
 
        /* We want the path of the export root to be relative, otherwise
1106
 
         * "*at()" syscalls would treat it as "/" in the host.
1107
 
         */
1108
 
        v9fs_path_sprintf(target, "%s", ".");
1109
 
    }
1110
 
    return 0;
1111
 
}
1112
 
 
1113
 
static int local_renameat(FsContext *ctx, V9fsPath *olddir,
1114
 
                          const char *old_name, V9fsPath *newdir,
1115
 
                          const char *new_name)
1116
 
{
1117
 
    int ret;
1118
 
    int odirfd, ndirfd;
1119
 
 
1120
 
    odirfd = local_opendir_nofollow(ctx, olddir->data);
1121
 
    if (odirfd == -1) {
1122
 
        return -1;
1123
 
    }
1124
 
 
1125
 
    ndirfd = local_opendir_nofollow(ctx, newdir->data);
1126
 
    if (ndirfd == -1) {
1127
 
        close_preserve_errno(odirfd);
1128
 
        return -1;
1129
 
    }
1130
 
 
1131
 
    ret = renameat(odirfd, old_name, ndirfd, new_name);
1132
 
    if (ret < 0) {
1133
 
        goto out;
1134
 
    }
1135
 
 
1136
 
    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
1137
 
        int omap_dirfd, nmap_dirfd;
1138
 
 
1139
 
        ret = mkdirat(ndirfd, VIRTFS_META_DIR, 0700);
1140
 
        if (ret < 0 && errno != EEXIST) {
1141
 
            goto err_undo_rename;
1142
 
        }
1143
 
 
1144
 
        omap_dirfd = openat_dir(odirfd, VIRTFS_META_DIR);
1145
 
        if (omap_dirfd == -1) {
1146
 
            goto err;
1147
 
        }
1148
 
 
1149
 
        nmap_dirfd = openat_dir(ndirfd, VIRTFS_META_DIR);
1150
 
        if (nmap_dirfd == -1) {
1151
 
            close_preserve_errno(omap_dirfd);
1152
 
            goto err;
1153
 
        }
1154
 
 
1155
 
        /* rename the .virtfs_metadata files */
1156
 
        ret = renameat(omap_dirfd, old_name, nmap_dirfd, new_name);
1157
 
        close_preserve_errno(nmap_dirfd);
1158
 
        close_preserve_errno(omap_dirfd);
1159
 
        if (ret < 0 && errno != ENOENT) {
1160
 
            goto err_undo_rename;
1161
 
        }
1162
 
 
1163
 
        ret = 0;
1164
 
    }
1165
 
    goto out;
1166
 
 
1167
 
err:
1168
 
    ret = -1;
1169
 
err_undo_rename:
1170
 
    renameat_preserve_errno(ndirfd, new_name, odirfd, old_name);
1171
 
out:
1172
 
    close_preserve_errno(ndirfd);
1173
 
    close_preserve_errno(odirfd);
1174
 
    return ret;
1175
 
}
1176
 
 
1177
 
static void v9fs_path_init_dirname(V9fsPath *path, const char *str)
1178
 
{
1179
 
    path->data = g_path_get_dirname(str);
1180
 
    path->size = strlen(path->data) + 1;
1181
 
}
1182
 
 
1183
 
static int local_rename(FsContext *ctx, const char *oldpath,
1184
 
                        const char *newpath)
1185
 
{
1186
 
    int err;
1187
 
    char *oname = g_path_get_basename(oldpath);
1188
 
    char *nname = g_path_get_basename(newpath);
1189
 
    V9fsPath olddir, newdir;
1190
 
 
1191
 
    v9fs_path_init_dirname(&olddir, oldpath);
1192
 
    v9fs_path_init_dirname(&newdir, newpath);
1193
 
 
1194
 
    err = local_renameat(ctx, &olddir, oname, &newdir, nname);
1195
 
 
1196
 
    v9fs_path_free(&newdir);
1197
 
    v9fs_path_free(&olddir);
1198
 
    g_free(nname);
1199
 
    g_free(oname);
1200
 
 
1201
 
    return err;
1202
 
}
1203
 
 
1204
 
static int local_unlinkat(FsContext *ctx, V9fsPath *dir,
1205
 
                          const char *name, int flags)
1206
 
{
1207
 
    int ret;
1208
 
    int dirfd;
1209
 
 
1210
 
    dirfd = local_opendir_nofollow(ctx, dir->data);
1211
 
    if (dirfd == -1) {
1212
 
        return -1;
1213
 
    }
1214
 
 
1215
 
    ret = local_unlinkat_common(ctx, dirfd, name, flags);
1216
 
    close_preserve_errno(dirfd);
1217
 
    return ret;
1218
 
}
1219
 
 
1220
 
static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
1221
 
                                mode_t st_mode, uint64_t *st_gen)
1222
 
{
1223
 
#ifdef FS_IOC_GETVERSION
1224
 
    int err;
1225
 
    V9fsFidOpenState fid_open;
1226
 
 
1227
 
    /*
1228
 
     * Do not try to open special files like device nodes, fifos etc
1229
 
     * We can get fd for regular files and directories only
1230
 
     */
1231
 
    if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
1232
 
        errno = ENOTTY;
1233
 
        return -1;
1234
 
    }
1235
 
    err = local_open(ctx, path, O_RDONLY, &fid_open);
1236
 
    if (err < 0) {
1237
 
        return err;
1238
 
    }
1239
 
    err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen);
1240
 
    local_close(ctx, &fid_open);
1241
 
    return err;
1242
 
#else
1243
 
    errno = ENOTTY;
1244
 
    return -1;
1245
 
#endif
1246
 
}
1247
 
 
1248
 
static int local_init(FsContext *ctx)
1249
 
{
1250
 
    struct statfs stbuf;
1251
 
    LocalData *data = g_malloc(sizeof(*data));
1252
 
 
1253
 
    data->mountfd = open(ctx->fs_root, O_DIRECTORY | O_RDONLY);
1254
 
    if (data->mountfd == -1) {
1255
 
        goto err;
1256
 
    }
1257
 
 
1258
 
#ifdef FS_IOC_GETVERSION
1259
 
    /*
1260
 
     * use ioc_getversion only if the ioctl is definied
1261
 
     */
1262
 
    if (fstatfs(data->mountfd, &stbuf) < 0) {
1263
 
        close_preserve_errno(data->mountfd);
1264
 
        goto err;
1265
 
    }
1266
 
    switch (stbuf.f_type) {
1267
 
    case EXT2_SUPER_MAGIC:
1268
 
    case BTRFS_SUPER_MAGIC:
1269
 
    case REISERFS_SUPER_MAGIC:
1270
 
    case XFS_SUPER_MAGIC:
1271
 
        ctx->exops.get_st_gen = local_ioc_getversion;
1272
 
        break;
1273
 
    }
1274
 
#endif
1275
 
 
1276
 
    if (ctx->export_flags & V9FS_SM_PASSTHROUGH) {
1277
 
        ctx->xops = passthrough_xattr_ops;
1278
 
    } else if (ctx->export_flags & V9FS_SM_MAPPED) {
1279
 
        ctx->xops = mapped_xattr_ops;
1280
 
    } else if (ctx->export_flags & V9FS_SM_NONE) {
1281
 
        ctx->xops = none_xattr_ops;
1282
 
    } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
1283
 
        /*
1284
 
         * xattr operation for mapped-file and passthrough
1285
 
         * remain same.
1286
 
         */
1287
 
        ctx->xops = passthrough_xattr_ops;
1288
 
    }
1289
 
    ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT;
1290
 
 
1291
 
    ctx->private = data;
1292
 
    return 0;
1293
 
 
1294
 
err:
1295
 
    g_free(data);
1296
 
    return -1;
1297
 
}
1298
 
 
1299
 
static void local_cleanup(FsContext *ctx)
1300
 
{
1301
 
    LocalData *data = ctx->private;
1302
 
 
1303
 
    close(data->mountfd);
1304
 
    g_free(data);
1305
 
}
1306
 
 
1307
 
static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse)
1308
 
{
1309
 
    const char *sec_model = qemu_opt_get(opts, "security_model");
1310
 
    const char *path = qemu_opt_get(opts, "path");
1311
 
 
1312
 
    if (!sec_model) {
1313
 
        error_report("Security model not specified, local fs needs security model");
1314
 
        error_printf("valid options are:"
1315
 
                     "\tsecurity_model=[passthrough|mapped-xattr|mapped-file|none]\n");
1316
 
        return -1;
1317
 
    }
1318
 
 
1319
 
    if (!strcmp(sec_model, "passthrough")) {
1320
 
        fse->export_flags |= V9FS_SM_PASSTHROUGH;
1321
 
    } else if (!strcmp(sec_model, "mapped") ||
1322
 
               !strcmp(sec_model, "mapped-xattr")) {
1323
 
        fse->export_flags |= V9FS_SM_MAPPED;
1324
 
    } else if (!strcmp(sec_model, "none")) {
1325
 
        fse->export_flags |= V9FS_SM_NONE;
1326
 
    } else if (!strcmp(sec_model, "mapped-file")) {
1327
 
        fse->export_flags |= V9FS_SM_MAPPED_FILE;
1328
 
    } else {
1329
 
        error_report("Invalid security model %s specified", sec_model);
1330
 
        error_printf("valid options are:"
1331
 
                     "\t[passthrough|mapped-xattr|mapped-file|none]\n");
1332
 
        return -1;
1333
 
    }
1334
 
 
1335
 
    if (!path) {
1336
 
        error_report("fsdev: No path specified");
1337
 
        return -1;
1338
 
    }
1339
 
    fse->path = g_strdup(path);
1340
 
 
1341
 
    return 0;
1342
 
}
1343
 
 
1344
 
FileOperations local_ops = {
1345
 
    .parse_opts = local_parse_opts,
1346
 
    .init  = local_init,
1347
 
    .cleanup = local_cleanup,
1348
 
    .lstat = local_lstat,
1349
 
    .readlink = local_readlink,
1350
 
    .close = local_close,
1351
 
    .closedir = local_closedir,
1352
 
    .open = local_open,
1353
 
    .opendir = local_opendir,
1354
 
    .rewinddir = local_rewinddir,
1355
 
    .telldir = local_telldir,
1356
 
    .readdir = local_readdir,
1357
 
    .seekdir = local_seekdir,
1358
 
    .preadv = local_preadv,
1359
 
    .pwritev = local_pwritev,
1360
 
    .chmod = local_chmod,
1361
 
    .mknod = local_mknod,
1362
 
    .mkdir = local_mkdir,
1363
 
    .fstat = local_fstat,
1364
 
    .open2 = local_open2,
1365
 
    .symlink = local_symlink,
1366
 
    .link = local_link,
1367
 
    .truncate = local_truncate,
1368
 
    .rename = local_rename,
1369
 
    .chown = local_chown,
1370
 
    .utimensat = local_utimensat,
1371
 
    .remove = local_remove,
1372
 
    .fsync = local_fsync,
1373
 
    .statfs = local_statfs,
1374
 
    .lgetxattr = local_lgetxattr,
1375
 
    .llistxattr = local_llistxattr,
1376
 
    .lsetxattr = local_lsetxattr,
1377
 
    .lremovexattr = local_lremovexattr,
1378
 
    .name_to_path = local_name_to_path,
1379
 
    .renameat  = local_renameat,
1380
 
    .unlinkat = local_unlinkat,
1381
 
};