Statistics
| Branch: | Revision:

root / hw / 9pfs / virtio-9p-handle.c @ 1de7afc9

History | View | Annotate | Download (17.8 kB)

1
/*
2
 * Virtio 9p handle callback
3
 *
4
 * Copyright IBM, Corp. 2011
5
 *
6
 * Authors:
7
 *    Aneesh Kumar K.V <aneesh.kumar@linux.vnet.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 "hw/virtio.h"
15
#include "virtio-9p.h"
16
#include "virtio-9p-xattr.h"
17
#include <arpa/inet.h>
18
#include <pwd.h>
19
#include <grp.h>
20
#include <sys/socket.h>
21
#include <sys/un.h>
22
#include "qemu/xattr.h"
23
#include <unistd.h>
24
#include <linux/fs.h>
25
#ifdef CONFIG_LINUX_MAGIC_H
26
#include <linux/magic.h>
27
#endif
28
#include <sys/ioctl.h>
29

    
30
#ifndef XFS_SUPER_MAGIC
31
#define XFS_SUPER_MAGIC  0x58465342
32
#endif
33
#ifndef EXT2_SUPER_MAGIC
34
#define EXT2_SUPER_MAGIC 0xEF53
35
#endif
36
#ifndef REISERFS_SUPER_MAGIC
37
#define REISERFS_SUPER_MAGIC 0x52654973
38
#endif
39
#ifndef BTRFS_SUPER_MAGIC
40
#define BTRFS_SUPER_MAGIC 0x9123683E
41
#endif
42

    
43
struct handle_data {
44
    int mountfd;
45
    int handle_bytes;
46
};
47

    
48
static inline int name_to_handle(int dirfd, const char *name,
49
                                 struct file_handle *fh, int *mnt_id, int flags)
50
{
51
    return name_to_handle_at(dirfd, name, fh, mnt_id, flags);
52
}
53

    
54
static inline int open_by_handle(int mountfd, const char *fh, int flags)
55
{
56
    return open_by_handle_at(mountfd, (struct file_handle *)fh, flags);
57
}
58

    
59
static int handle_update_file_cred(int dirfd, const char *name, FsCred *credp)
60
{
61
    int fd, ret;
62
    fd = openat(dirfd, name, O_NONBLOCK | O_NOFOLLOW);
63
    if (fd < 0) {
64
        return fd;
65
    }
66
    ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH);
67
    if (ret < 0) {
68
        goto err_out;
69
    }
70
    ret = fchmod(fd, credp->fc_mode & 07777);
71
err_out:
72
    close(fd);
73
    return ret;
74
}
75

    
76

    
77
static int handle_lstat(FsContext *fs_ctx, V9fsPath *fs_path,
78
                        struct stat *stbuf)
79
{
80
    int fd, ret;
81
    struct handle_data *data = (struct handle_data *)fs_ctx->private;
82

    
83
    fd = open_by_handle(data->mountfd, fs_path->data, O_PATH);
84
    if (fd < 0) {
85
        return fd;
86
    }
87
    ret = fstatat(fd, "", stbuf, AT_EMPTY_PATH);
88
    close(fd);
89
    return ret;
90
}
91

    
92
static ssize_t handle_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
93
                               char *buf, size_t bufsz)
94
{
95
    int fd, ret;
96
    struct handle_data *data = (struct handle_data *)fs_ctx->private;
97

    
98
    fd = open_by_handle(data->mountfd, fs_path->data, O_PATH);
99
    if (fd < 0) {
100
        return fd;
101
    }
102
    ret = readlinkat(fd, "", buf, bufsz);
103
    close(fd);
104
    return ret;
105
}
106

    
107
static int handle_close(FsContext *ctx, V9fsFidOpenState *fs)
108
{
109
    return close(fs->fd);
110
}
111

    
112
static int handle_closedir(FsContext *ctx, V9fsFidOpenState *fs)
113
{
114
    return closedir(fs->dir);
115
}
116

    
117
static int handle_open(FsContext *ctx, V9fsPath *fs_path,
118
                       int flags, V9fsFidOpenState *fs)
119
{
120
    struct handle_data *data = (struct handle_data *)ctx->private;
121

    
122
    fs->fd = open_by_handle(data->mountfd, fs_path->data, flags);
123
    return fs->fd;
124
}
125

    
126
static int handle_opendir(FsContext *ctx,
127
                          V9fsPath *fs_path, V9fsFidOpenState *fs)
128
{
129
    int ret;
130
    ret = handle_open(ctx, fs_path, O_DIRECTORY, fs);
131
    if (ret < 0) {
132
        return -1;
133
    }
134
    fs->dir = fdopendir(ret);
135
    if (!fs->dir) {
136
        return -1;
137
    }
138
    return 0;
139
}
140

    
141
static void handle_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
142
{
143
    return rewinddir(fs->dir);
144
}
145

    
146
static off_t handle_telldir(FsContext *ctx, V9fsFidOpenState *fs)
147
{
148
    return telldir(fs->dir);
149
}
150

    
151
static int handle_readdir_r(FsContext *ctx, V9fsFidOpenState *fs,
152
                            struct dirent *entry,
153
                            struct dirent **result)
154
{
155
    return readdir_r(fs->dir, entry, result);
156
}
157

    
158
static void handle_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
159
{
160
    return seekdir(fs->dir, off);
161
}
162

    
163
static ssize_t handle_preadv(FsContext *ctx, V9fsFidOpenState *fs,
164
                             const struct iovec *iov,
165
                             int iovcnt, off_t offset)
166
{
167
#ifdef CONFIG_PREADV
168
    return preadv(fs->fd, iov, iovcnt, offset);
169
#else
170
    int err = lseek(fs->fd, offset, SEEK_SET);
171
    if (err == -1) {
172
        return err;
173
    } else {
174
        return readv(fs->fd, iov, iovcnt);
175
    }
176
#endif
177
}
178

    
179
static ssize_t handle_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
180
                              const struct iovec *iov,
181
                              int iovcnt, off_t offset)
182
{
183
    ssize_t ret;
184
#ifdef CONFIG_PREADV
185
    ret = pwritev(fs->fd, iov, iovcnt, offset);
186
#else
187
    int err = lseek(fs->fd, offset, SEEK_SET);
188
    if (err == -1) {
189
        return err;
190
    } else {
191
        ret = writev(fs->fd, iov, iovcnt);
192
    }
193
#endif
194
#ifdef CONFIG_SYNC_FILE_RANGE
195
    if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) {
196
        /*
197
         * Initiate a writeback. This is not a data integrity sync.
198
         * We want to ensure that we don't leave dirty pages in the cache
199
         * after write when writeout=immediate is sepcified.
200
         */
201
        sync_file_range(fs->fd, offset, ret,
202
                        SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
203
    }
204
#endif
205
    return ret;
206
}
207

    
208
static int handle_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
209
{
210
    int fd, ret;
211
    struct handle_data *data = (struct handle_data *)fs_ctx->private;
212

    
213
    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
214
    if (fd < 0) {
215
        return fd;
216
    }
217
    ret = fchmod(fd, credp->fc_mode);
218
    close(fd);
219
    return ret;
220
}
221

    
222
static int handle_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
223
                       const char *name, FsCred *credp)
224
{
225
    int dirfd, ret;
226
    struct handle_data *data = (struct handle_data *)fs_ctx->private;
227

    
228
    dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
229
    if (dirfd < 0) {
230
        return dirfd;
231
    }
232
    ret = mknodat(dirfd, name, credp->fc_mode, credp->fc_rdev);
233
    if (!ret) {
234
        ret = handle_update_file_cred(dirfd, name, credp);
235
    }
236
    close(dirfd);
237
    return ret;
238
}
239

    
240
static int handle_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
241
                       const char *name, FsCred *credp)
242
{
243
    int dirfd, ret;
244
    struct handle_data *data = (struct handle_data *)fs_ctx->private;
245

    
246
    dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
247
    if (dirfd < 0) {
248
        return dirfd;
249
    }
250
    ret = mkdirat(dirfd, name, credp->fc_mode);
251
    if (!ret) {
252
        ret = handle_update_file_cred(dirfd, name, credp);
253
    }
254
    close(dirfd);
255
    return ret;
256
}
257

    
258
static int handle_fstat(FsContext *fs_ctx, int fid_type,
259
                        V9fsFidOpenState *fs, struct stat *stbuf)
260
{
261
    int fd;
262

    
263
    if (fid_type == P9_FID_DIR) {
264
        fd = dirfd(fs->dir);
265
    } else {
266
        fd = fs->fd;
267
    }
268
    return fstat(fd, stbuf);
269
}
270

    
271
static int handle_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
272
                        int flags, FsCred *credp, V9fsFidOpenState *fs)
273
{
274
    int ret;
275
    int dirfd, fd;
276
    struct handle_data *data = (struct handle_data *)fs_ctx->private;
277

    
278
    dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
279
    if (dirfd < 0) {
280
        return dirfd;
281
    }
282
    fd = openat(dirfd, name, flags | O_NOFOLLOW, credp->fc_mode);
283
    if (fd >= 0) {
284
        ret = handle_update_file_cred(dirfd, name, credp);
285
        if (ret < 0) {
286
            close(fd);
287
            fd = ret;
288
        } else {
289
            fs->fd = fd;
290
        }
291
    }
292
    close(dirfd);
293
    return fd;
294
}
295

    
296

    
297
static int handle_symlink(FsContext *fs_ctx, const char *oldpath,
298
                          V9fsPath *dir_path, const char *name, FsCred *credp)
299
{
300
    int fd, dirfd, ret;
301
    struct handle_data *data = (struct handle_data *)fs_ctx->private;
302

    
303
    dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
304
    if (dirfd < 0) {
305
        return dirfd;
306
    }
307
    ret = symlinkat(oldpath, dirfd, name);
308
    if (!ret) {
309
        fd = openat(dirfd, name, O_PATH | O_NOFOLLOW);
310
        if (fd < 0) {
311
            ret = fd;
312
            goto err_out;
313
        }
314
        ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH);
315
        close(fd);
316
    }
317
err_out:
318
    close(dirfd);
319
    return ret;
320
}
321

    
322
static int handle_link(FsContext *ctx, V9fsPath *oldpath,
323
                       V9fsPath *dirpath, const char *name)
324
{
325
    int oldfd, newdirfd, ret;
326
    struct handle_data *data = (struct handle_data *)ctx->private;
327

    
328
    oldfd = open_by_handle(data->mountfd, oldpath->data, O_PATH);
329
    if (oldfd < 0) {
330
        return oldfd;
331
    }
332
    newdirfd = open_by_handle(data->mountfd, dirpath->data, O_PATH);
333
    if (newdirfd < 0) {
334
        close(oldfd);
335
        return newdirfd;
336
    }
337
    ret = linkat(oldfd, "", newdirfd, name, AT_EMPTY_PATH);
338
    close(newdirfd);
339
    close(oldfd);
340
    return ret;
341
}
342

    
343
static int handle_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
344
{
345
    int fd, ret;
346
    struct handle_data *data = (struct handle_data *)ctx->private;
347

    
348
    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK | O_WRONLY);
349
    if (fd < 0) {
350
        return fd;
351
    }
352
    ret = ftruncate(fd, size);
353
    close(fd);
354
    return ret;
355
}
356

    
357
static int handle_rename(FsContext *ctx, const char *oldpath,
358
                         const char *newpath)
359
{
360
    errno = EOPNOTSUPP;
361
    return -1;
362
}
363

    
364
static int handle_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
365
{
366
    int fd, ret;
367
    struct handle_data *data = (struct handle_data *)fs_ctx->private;
368

    
369
    fd = open_by_handle(data->mountfd, fs_path->data, O_PATH);
370
    if (fd < 0) {
371
        return fd;
372
    }
373
    ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH);
374
    close(fd);
375
    return ret;
376
}
377

    
378
static int handle_utimensat(FsContext *ctx, V9fsPath *fs_path,
379
                            const struct timespec *buf)
380
{
381
    int ret;
382
#ifdef CONFIG_UTIMENSAT
383
    int fd;
384
    struct handle_data *data = (struct handle_data *)ctx->private;
385

    
386
    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
387
    if (fd < 0) {
388
        return fd;
389
    }
390
    ret = futimens(fd, buf);
391
    close(fd);
392
#else
393
    ret = -1;
394
    errno = ENOSYS;
395
#endif
396
    return ret;
397
}
398

    
399
static int handle_remove(FsContext *ctx, const char *path)
400
{
401
    errno = EOPNOTSUPP;
402
    return -1;
403
}
404

    
405
static int handle_fsync(FsContext *ctx, int fid_type,
406
                        V9fsFidOpenState *fs, int datasync)
407
{
408
    int fd;
409

    
410
    if (fid_type == P9_FID_DIR) {
411
        fd = dirfd(fs->dir);
412
    } else {
413
        fd = fs->fd;
414
    }
415

    
416
    if (datasync) {
417
        return qemu_fdatasync(fd);
418
    } else {
419
        return fsync(fd);
420
    }
421
}
422

    
423
static int handle_statfs(FsContext *ctx, V9fsPath *fs_path,
424
                         struct statfs *stbuf)
425
{
426
    int fd, ret;
427
    struct handle_data *data = (struct handle_data *)ctx->private;
428

    
429
    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
430
    if (fd < 0) {
431
        return fd;
432
    }
433
    ret = fstatfs(fd, stbuf);
434
    close(fd);
435
    return ret;
436
}
437

    
438
static ssize_t handle_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
439
                                const char *name, void *value, size_t size)
440
{
441
    int fd, ret;
442
    struct handle_data *data = (struct handle_data *)ctx->private;
443

    
444
    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
445
    if (fd < 0) {
446
        return fd;
447
    }
448
    ret = fgetxattr(fd, name, value, size);
449
    close(fd);
450
    return ret;
451
}
452

    
453
static ssize_t handle_llistxattr(FsContext *ctx, V9fsPath *fs_path,
454
                                 void *value, size_t size)
455
{
456
    int fd, ret;
457
    struct handle_data *data = (struct handle_data *)ctx->private;
458

    
459
    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
460
    if (fd < 0) {
461
        return fd;
462
    }
463
    ret = flistxattr(fd, value, size);
464
    close(fd);
465
    return ret;
466
}
467

    
468
static int handle_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
469
                            void *value, size_t size, int flags)
470
{
471
    int fd, ret;
472
    struct handle_data *data = (struct handle_data *)ctx->private;
473

    
474
    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
475
    if (fd < 0) {
476
        return fd;
477
    }
478
    ret = fsetxattr(fd, name, value, size, flags);
479
    close(fd);
480
    return ret;
481
}
482

    
483
static int handle_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
484
                               const char *name)
485
{
486
    int fd, ret;
487
    struct handle_data *data = (struct handle_data *)ctx->private;
488

    
489
    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
490
    if (fd < 0) {
491
        return fd;
492
    }
493
    ret = fremovexattr(fd, name);
494
    close(fd);
495
    return ret;
496
}
497

    
498
static int handle_name_to_path(FsContext *ctx, V9fsPath *dir_path,
499
                              const char *name, V9fsPath *target)
500
{
501
    char buffer[PATH_MAX];
502
    struct file_handle *fh;
503
    int dirfd, ret, mnt_id;
504
    struct handle_data *data = (struct handle_data *)ctx->private;
505

    
506
    /* "." and ".." are not allowed */
507
    if (!strcmp(name, ".") || !strcmp(name, "..")) {
508
        errno = EINVAL;
509
        return -1;
510

    
511
    }
512
    if (dir_path) {
513
        dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
514
    } else {
515
        /* relative to export root */
516
        dirfd = open(rpath(ctx, ".", buffer), O_DIRECTORY);
517
    }
518
    if (dirfd < 0) {
519
        return dirfd;
520
    }
521
    fh = g_malloc(sizeof(struct file_handle) + data->handle_bytes);
522
    fh->handle_bytes = data->handle_bytes;
523
    /* add a "./" at the beginning of the path */
524
    snprintf(buffer, PATH_MAX, "./%s", name);
525
    /* flag = 0 imply don't follow symlink */
526
    ret = name_to_handle(dirfd, buffer, fh, &mnt_id, 0);
527
    if (!ret) {
528
        target->data = (char *)fh;
529
        target->size = sizeof(struct file_handle) + data->handle_bytes;
530
    } else {
531
        g_free(fh);
532
    }
533
    close(dirfd);
534
    return ret;
535
}
536

    
537
static int handle_renameat(FsContext *ctx, V9fsPath *olddir,
538
                           const char *old_name, V9fsPath *newdir,
539
                           const char *new_name)
540
{
541
    int olddirfd, newdirfd, ret;
542
    struct handle_data *data = (struct handle_data *)ctx->private;
543

    
544
    olddirfd = open_by_handle(data->mountfd, olddir->data, O_PATH);
545
    if (olddirfd < 0) {
546
        return olddirfd;
547
    }
548
    newdirfd = open_by_handle(data->mountfd, newdir->data, O_PATH);
549
    if (newdirfd < 0) {
550
        close(olddirfd);
551
        return newdirfd;
552
    }
553
    ret = renameat(olddirfd, old_name, newdirfd, new_name);
554
    close(newdirfd);
555
    close(olddirfd);
556
    return ret;
557
}
558

    
559
static int handle_unlinkat(FsContext *ctx, V9fsPath *dir,
560
                           const char *name, int flags)
561
{
562
    int dirfd, ret;
563
    struct handle_data *data = (struct handle_data *)ctx->private;
564
    int rflags;
565

    
566
    dirfd = open_by_handle(data->mountfd, dir->data, O_PATH);
567
    if (dirfd < 0) {
568
        return dirfd;
569
    }
570

    
571
    rflags = 0;
572
    if (flags & P9_DOTL_AT_REMOVEDIR) {
573
        rflags |= AT_REMOVEDIR;
574
    }
575

    
576
    ret = unlinkat(dirfd, name, rflags);
577

    
578
    close(dirfd);
579
    return ret;
580
}
581

    
582
static int handle_ioc_getversion(FsContext *ctx, V9fsPath *path,
583
                                 mode_t st_mode, uint64_t *st_gen)
584
{
585
    int err;
586
    V9fsFidOpenState fid_open;
587

    
588
    /*
589
     * Do not try to open special files like device nodes, fifos etc
590
     * We can get fd for regular files and directories only
591
     */
592
    if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
593
            return 0;
594
    }
595
    err = handle_open(ctx, path, O_RDONLY, &fid_open);
596
    if (err < 0) {
597
        return err;
598
    }
599
    err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen);
600
    handle_close(ctx, &fid_open);
601
    return err;
602
}
603

    
604
static int handle_init(FsContext *ctx)
605
{
606
    int ret, mnt_id;
607
    struct statfs stbuf;
608
    struct file_handle fh;
609
    struct handle_data *data = g_malloc(sizeof(struct handle_data));
610

    
611
    data->mountfd = open(ctx->fs_root, O_DIRECTORY);
612
    if (data->mountfd < 0) {
613
        ret = data->mountfd;
614
        goto err_out;
615
    }
616
    ret = statfs(ctx->fs_root, &stbuf);
617
    if (!ret) {
618
        switch (stbuf.f_type) {
619
        case EXT2_SUPER_MAGIC:
620
        case BTRFS_SUPER_MAGIC:
621
        case REISERFS_SUPER_MAGIC:
622
        case XFS_SUPER_MAGIC:
623
            ctx->exops.get_st_gen = handle_ioc_getversion;
624
            break;
625
        }
626
    }
627
    memset(&fh, 0, sizeof(struct file_handle));
628
    ret = name_to_handle(data->mountfd, ".", &fh, &mnt_id, 0);
629
    if (ret && errno == EOVERFLOW) {
630
        data->handle_bytes = fh.handle_bytes;
631
        ctx->private = data;
632
        ret = 0;
633
        goto out;
634
    }
635
    /* we got 0 byte handle ? */
636
    ret = -1;
637
    close(data->mountfd);
638
err_out:
639
    g_free(data);
640
out:
641
    return ret;
642
}
643

    
644
static int handle_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse)
645
{
646
    const char *sec_model = qemu_opt_get(opts, "security_model");
647
    const char *path = qemu_opt_get(opts, "path");
648

    
649
    if (sec_model) {
650
        fprintf(stderr, "Invalid argument security_model specified with handle fsdriver\n");
651
        return -1;
652
    }
653

    
654
    if (!path) {
655
        fprintf(stderr, "fsdev: No path specified.\n");
656
        return -1;
657
    }
658
    fse->path = g_strdup(path);
659
    return 0;
660

    
661
}
662

    
663
FileOperations handle_ops = {
664
    .parse_opts   = handle_parse_opts,
665
    .init         = handle_init,
666
    .lstat        = handle_lstat,
667
    .readlink     = handle_readlink,
668
    .close        = handle_close,
669
    .closedir     = handle_closedir,
670
    .open         = handle_open,
671
    .opendir      = handle_opendir,
672
    .rewinddir    = handle_rewinddir,
673
    .telldir      = handle_telldir,
674
    .readdir_r    = handle_readdir_r,
675
    .seekdir      = handle_seekdir,
676
    .preadv       = handle_preadv,
677
    .pwritev      = handle_pwritev,
678
    .chmod        = handle_chmod,
679
    .mknod        = handle_mknod,
680
    .mkdir        = handle_mkdir,
681
    .fstat        = handle_fstat,
682
    .open2        = handle_open2,
683
    .symlink      = handle_symlink,
684
    .link         = handle_link,
685
    .truncate     = handle_truncate,
686
    .rename       = handle_rename,
687
    .chown        = handle_chown,
688
    .utimensat    = handle_utimensat,
689
    .remove       = handle_remove,
690
    .fsync        = handle_fsync,
691
    .statfs       = handle_statfs,
692
    .lgetxattr    = handle_lgetxattr,
693
    .llistxattr   = handle_llistxattr,
694
    .lsetxattr    = handle_lsetxattr,
695
    .lremovexattr = handle_lremovexattr,
696
    .name_to_path = handle_name_to_path,
697
    .renameat     = handle_renameat,
698
    .unlinkat     = handle_unlinkat,
699
};