Statistics
| Branch: | Revision:

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

History | View | Annotate | Download (17.5 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 <attr/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
#ifdef CONFIG_OPEN_BY_HANDLE
49
static inline int name_to_handle(int dirfd, const char *name,
50
                                 struct file_handle *fh, int *mnt_id, int flags)
51
{
52
    return name_to_handle_at(dirfd, name, fh, mnt_id, flags);
53
}
54

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

    
61
struct file_handle {
62
    unsigned int handle_bytes;
63
    int handle_type;
64
    unsigned char handle[0];
65
};
66

    
67
#ifndef AT_EMPTY_PATH
68
#define AT_EMPTY_PATH   0x1000  /* Allow empty relative pathname */
69
#endif
70
#ifndef O_PATH
71
#define O_PATH    010000000
72
#endif
73

    
74
static inline int name_to_handle(int dirfd, const char *name,
75
                                 struct file_handle *fh, int *mnt_id, int flags)
76
{
77
    errno = ENOSYS;
78
    return -1;
79
}
80

    
81
static inline int open_by_handle(int mountfd, const char *fh, int flags)
82
{
83
    errno = ENOSYS;
84
    return -1;
85
}
86
#endif
87

    
88
static int handle_update_file_cred(int dirfd, const char *name, FsCred *credp)
89
{
90
    int fd, ret;
91
    fd = openat(dirfd, name, O_NONBLOCK | O_NOFOLLOW);;
92
    if (fd < 0) {
93
        return fd;
94
    }
95
    ret = fchmod(fd, credp->fc_mode & 07777);
96
    if (ret < 0) {
97
        goto err_out;
98
    }
99
    ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH);
100
err_out:
101
    close(fd);
102
    return ret;
103
}
104

    
105

    
106
static int handle_lstat(FsContext *fs_ctx, V9fsPath *fs_path,
107
                        struct stat *stbuf)
108
{
109
    int fd, ret;
110
    struct handle_data *data = (struct handle_data *)fs_ctx->private;
111

    
112
    fd = open_by_handle(data->mountfd, fs_path->data, O_PATH);
113
    if (fd < 0) {
114
        return fd;
115
    }
116
    ret = fstatat(fd, "", stbuf, AT_EMPTY_PATH);
117
    close(fd);
118
    return ret;
119
}
120

    
121
static ssize_t handle_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
122
                               char *buf, size_t bufsz)
123
{
124
    int fd, ret;
125
    struct handle_data *data = (struct handle_data *)fs_ctx->private;
126

    
127
    fd = open_by_handle(data->mountfd, fs_path->data, O_PATH);
128
    if (fd < 0) {
129
        return fd;
130
    }
131
    ret = readlinkat(fd, "", buf, bufsz);
132
    close(fd);
133
    return ret;
134
}
135

    
136
static int handle_close(FsContext *ctx, V9fsFidOpenState *fs)
137
{
138
    return close(fs->fd);
139
}
140

    
141
static int handle_closedir(FsContext *ctx, V9fsFidOpenState *fs)
142
{
143
    return closedir(fs->dir);
144
}
145

    
146
static int handle_open(FsContext *ctx, V9fsPath *fs_path,
147
                       int flags, V9fsFidOpenState *fs)
148
{
149
    struct handle_data *data = (struct handle_data *)ctx->private;
150

    
151
    fs->fd = open_by_handle(data->mountfd, fs_path->data, flags);
152
    return fs->fd;
153
}
154

    
155
static int handle_opendir(FsContext *ctx,
156
                          V9fsPath *fs_path, V9fsFidOpenState *fs)
157
{
158
    int ret;
159
    ret = handle_open(ctx, fs_path, O_DIRECTORY, fs);
160
    if (ret < 0) {
161
        return -1;
162
    }
163
    fs->dir = fdopendir(ret);
164
    if (!fs->dir) {
165
        return -1;
166
    }
167
    return 0;
168
}
169

    
170
static void handle_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
171
{
172
    return rewinddir(fs->dir);
173
}
174

    
175
static off_t handle_telldir(FsContext *ctx, V9fsFidOpenState *fs)
176
{
177
    return telldir(fs->dir);
178
}
179

    
180
static int handle_readdir_r(FsContext *ctx, V9fsFidOpenState *fs,
181
                            struct dirent *entry,
182
                            struct dirent **result)
183
{
184
    return readdir_r(fs->dir, entry, result);
185
}
186

    
187
static void handle_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
188
{
189
    return seekdir(fs->dir, off);
190
}
191

    
192
static ssize_t handle_preadv(FsContext *ctx, V9fsFidOpenState *fs,
193
                             const struct iovec *iov,
194
                             int iovcnt, off_t offset)
195
{
196
#ifdef CONFIG_PREADV
197
    return preadv(fs->fd, iov, iovcnt, offset);
198
#else
199
    int err = lseek(fs->fd, offset, SEEK_SET);
200
    if (err == -1) {
201
        return err;
202
    } else {
203
        return readv(fs->fd, iov, iovcnt);
204
    }
205
#endif
206
}
207

    
208
static ssize_t handle_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
209
                              const struct iovec *iov,
210
                              int iovcnt, off_t offset)
211
{
212
    ssize_t ret;
213
#ifdef CONFIG_PREADV
214
    ret = pwritev(fs->fd, iov, iovcnt, offset);
215
#else
216
    int err = lseek(fs->fd, offset, SEEK_SET);
217
    if (err == -1) {
218
        return err;
219
    } else {
220
        ret = writev(fs->fd, iov, iovcnt);
221
    }
222
#endif
223
#ifdef CONFIG_SYNC_FILE_RANGE
224
    if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) {
225
        /*
226
         * Initiate a writeback. This is not a data integrity sync.
227
         * We want to ensure that we don't leave dirty pages in the cache
228
         * after write when writeout=immediate is sepcified.
229
         */
230
        sync_file_range(fs->fd, offset, ret,
231
                        SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
232
    }
233
#endif
234
    return ret;
235
}
236

    
237
static int handle_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
238
{
239
    int fd, ret;
240
    struct handle_data *data = (struct handle_data *)fs_ctx->private;
241

    
242
    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
243
    if (fd < 0) {
244
        return fd;
245
    }
246
    ret = fchmod(fd, credp->fc_mode);
247
    close(fd);
248
    return ret;
249
}
250

    
251
static int handle_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
252
                       const char *name, FsCred *credp)
253
{
254
    int dirfd, ret;
255
    struct handle_data *data = (struct handle_data *)fs_ctx->private;
256

    
257
    dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
258
    if (dirfd < 0) {
259
        return dirfd;
260
    }
261
    ret = mknodat(dirfd, name, credp->fc_mode, credp->fc_rdev);
262
    if (!ret) {
263
        ret = handle_update_file_cred(dirfd, name, credp);
264
    }
265
    close(dirfd);
266
    return ret;
267
}
268

    
269
static int handle_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
270
                       const char *name, FsCred *credp)
271
{
272
    int dirfd, ret;
273
    struct handle_data *data = (struct handle_data *)fs_ctx->private;
274

    
275
    dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
276
    if (dirfd < 0) {
277
        return dirfd;
278
    }
279
    ret = mkdirat(dirfd, name, credp->fc_mode);
280
    if (!ret) {
281
        ret = handle_update_file_cred(dirfd, name, credp);
282
    }
283
    close(dirfd);
284
    return ret;
285
}
286

    
287
static int handle_fstat(FsContext *fs_ctx, V9fsFidOpenState *fs,
288
                        struct stat *stbuf)
289
{
290
    return fstat(fs->fd, stbuf);
291
}
292

    
293
static int handle_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
294
                        int flags, FsCred *credp, V9fsFidOpenState *fs)
295
{
296
    int ret;
297
    int dirfd, fd;
298
    struct handle_data *data = (struct handle_data *)fs_ctx->private;
299

    
300
    dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
301
    if (dirfd < 0) {
302
        return dirfd;
303
    }
304
    fd = openat(dirfd, name, flags | O_NOFOLLOW, credp->fc_mode);
305
    if (fd >= 0) {
306
        ret = handle_update_file_cred(dirfd, name, credp);
307
        if (ret < 0) {
308
            close(fd);
309
            fd = ret;
310
        } else {
311
            fs->fd = fd;
312
        }
313
    }
314
    close(dirfd);
315
    return fd;
316
}
317

    
318

    
319
static int handle_symlink(FsContext *fs_ctx, const char *oldpath,
320
                          V9fsPath *dir_path, const char *name, FsCred *credp)
321
{
322
    int fd, dirfd, ret;
323
    struct handle_data *data = (struct handle_data *)fs_ctx->private;
324

    
325
    dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
326
    if (dirfd < 0) {
327
        return dirfd;
328
    }
329
    ret = symlinkat(oldpath, dirfd, name);
330
    if (!ret) {
331
        fd = openat(dirfd, name, O_PATH | O_NOFOLLOW);
332
        if (fd < 0) {
333
            ret = fd;
334
            goto err_out;
335
        }
336
        ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH);
337
        close(fd);
338
    }
339
err_out:
340
    close(dirfd);
341
    return ret;
342
}
343

    
344
static int handle_link(FsContext *ctx, V9fsPath *oldpath,
345
                       V9fsPath *dirpath, const char *name)
346
{
347
    int oldfd, newdirfd, ret;
348
    struct handle_data *data = (struct handle_data *)ctx->private;
349

    
350
    oldfd = open_by_handle(data->mountfd, oldpath->data, O_PATH);
351
    if (oldfd < 0) {
352
        return oldfd;
353
    }
354
    newdirfd = open_by_handle(data->mountfd, dirpath->data, O_PATH);
355
    if (newdirfd < 0) {
356
        close(oldfd);
357
        return newdirfd;
358
    }
359
    ret = linkat(oldfd, "", newdirfd, name, AT_EMPTY_PATH);
360
    close(newdirfd);
361
    close(oldfd);
362
    return ret;
363
}
364

    
365
static int handle_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
366
{
367
    int fd, ret;
368
    struct handle_data *data = (struct handle_data *)ctx->private;
369

    
370
    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK | O_WRONLY);
371
    if (fd < 0) {
372
        return fd;
373
    }
374
    ret = ftruncate(fd, size);
375
    close(fd);
376
    return ret;
377
}
378

    
379
static int handle_rename(FsContext *ctx, const char *oldpath,
380
                         const char *newpath)
381
{
382
    errno = EOPNOTSUPP;
383
    return -1;
384
}
385

    
386
static int handle_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
387
{
388
    int fd, ret;
389
    struct handle_data *data = (struct handle_data *)fs_ctx->private;
390

    
391
    fd = open_by_handle(data->mountfd, fs_path->data, O_PATH);
392
    if (fd < 0) {
393
        return fd;
394
    }
395
    ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH);
396
    close(fd);
397
    return ret;
398
}
399

    
400
static int handle_utimensat(FsContext *ctx, V9fsPath *fs_path,
401
                            const struct timespec *buf)
402
{
403
    int ret;
404
#ifdef CONFIG_UTIMENSAT
405
    int fd;
406
    struct handle_data *data = (struct handle_data *)ctx->private;
407

    
408
    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
409
    if (fd < 0) {
410
        return fd;
411
    }
412
    ret = futimens(fd, buf);
413
    close(fd);
414
#else
415
    ret = -1;
416
    errno = ENOSYS;
417
#endif
418
    return ret;
419
}
420

    
421
static int handle_remove(FsContext *ctx, const char *path)
422
{
423
    errno = EOPNOTSUPP;
424
    return -1;
425
}
426

    
427
static int handle_fsync(FsContext *ctx, V9fsFidOpenState *fs, int datasync)
428
{
429
    if (datasync) {
430
        return qemu_fdatasync(fs->fd);
431
    } else {
432
        return fsync(fs->fd);
433
    }
434
}
435

    
436
static int handle_statfs(FsContext *ctx, V9fsPath *fs_path,
437
                         struct statfs *stbuf)
438
{
439
    int fd, ret;
440
    struct handle_data *data = (struct handle_data *)ctx->private;
441

    
442
    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
443
    if (fd < 0) {
444
        return fd;
445
    }
446
    ret = fstatfs(fd, stbuf);
447
    close(fd);
448
    return ret;
449
}
450

    
451
static ssize_t handle_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
452
                                const char *name, void *value, size_t size)
453
{
454
    int fd, ret;
455
    struct handle_data *data = (struct handle_data *)ctx->private;
456

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

    
466
static ssize_t handle_llistxattr(FsContext *ctx, V9fsPath *fs_path,
467
                                 void *value, size_t size)
468
{
469
    int fd, ret;
470
    struct handle_data *data = (struct handle_data *)ctx->private;
471

    
472
    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
473
    if (fd < 0) {
474
        return fd;
475
    }
476
    ret = flistxattr(fd, value, size);
477
    close(fd);
478
    return ret;
479
}
480

    
481
static int handle_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
482
                            void *value, size_t size, int flags)
483
{
484
    int fd, ret;
485
    struct handle_data *data = (struct handle_data *)ctx->private;
486

    
487
    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
488
    if (fd < 0) {
489
        return fd;
490
    }
491
    ret = fsetxattr(fd, name, value, size, flags);
492
    close(fd);
493
    return ret;
494
}
495

    
496
static int handle_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
497
                               const char *name)
498
{
499
    int fd, ret;
500
    struct handle_data *data = (struct handle_data *)ctx->private;
501

    
502
    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
503
    if (fd < 0) {
504
        return fd;
505
    }
506
    ret = fremovexattr(fd, name);
507
    close(fd);
508
    return ret;
509
}
510

    
511
static int handle_name_to_path(FsContext *ctx, V9fsPath *dir_path,
512
                              const char *name, V9fsPath *target)
513
{
514
    char buffer[PATH_MAX];
515
    struct file_handle *fh;
516
    int dirfd, ret, mnt_id;
517
    struct handle_data *data = (struct handle_data *)ctx->private;
518

    
519
    /* "." and ".." are not allowed */
520
    if (!strcmp(name, ".") || !strcmp(name, "..")) {
521
        errno = EINVAL;
522
        return -1;
523

    
524
    }
525
    if (dir_path) {
526
        dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
527
    } else {
528
        /* relative to export root */
529
        dirfd = open(rpath(ctx, ".", buffer), O_DIRECTORY);
530
    }
531
    if (dirfd < 0) {
532
        return dirfd;
533
    }
534
    fh = g_malloc(sizeof(struct file_handle) + data->handle_bytes);
535
    fh->handle_bytes = data->handle_bytes;
536
    /* add a "./" at the begining of the path */
537
    snprintf(buffer, PATH_MAX, "./%s", name);
538
    /* flag = 0 imply don't follow symlink */
539
    ret = name_to_handle(dirfd, buffer, fh, &mnt_id, 0);
540
    if (!ret) {
541
        target->data = (char *)fh;
542
        target->size = sizeof(struct file_handle) + data->handle_bytes;
543
    } else {
544
        g_free(fh);
545
    }
546
    close(dirfd);
547
    return ret;
548
}
549

    
550
static int handle_renameat(FsContext *ctx, V9fsPath *olddir,
551
                           const char *old_name, V9fsPath *newdir,
552
                           const char *new_name)
553
{
554
    int olddirfd, newdirfd, ret;
555
    struct handle_data *data = (struct handle_data *)ctx->private;
556

    
557
    olddirfd = open_by_handle(data->mountfd, olddir->data, O_PATH);
558
    if (olddirfd < 0) {
559
        return olddirfd;
560
    }
561
    newdirfd = open_by_handle(data->mountfd, newdir->data, O_PATH);
562
    if (newdirfd < 0) {
563
        close(olddirfd);
564
        return newdirfd;
565
    }
566
    ret = renameat(olddirfd, old_name, newdirfd, new_name);
567
    close(newdirfd);
568
    close(olddirfd);
569
    return ret;
570
}
571

    
572
static int handle_unlinkat(FsContext *ctx, V9fsPath *dir,
573
                           const char *name, int flags)
574
{
575
    int dirfd, ret;
576
    struct handle_data *data = (struct handle_data *)ctx->private;
577

    
578
    dirfd = open_by_handle(data->mountfd, dir->data, O_PATH);
579
    if (dirfd < 0) {
580
        return dirfd;
581
    }
582

    
583
    ret = unlinkat(dirfd, name, flags);
584
    close(dirfd);
585
    return ret;
586
}
587

    
588
static int handle_ioc_getversion(FsContext *ctx, V9fsPath *path,
589
                                 mode_t st_mode, uint64_t *st_gen)
590
{
591
    int err;
592
    V9fsFidOpenState fid_open;
593

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

    
610
static int handle_init(FsContext *ctx)
611
{
612
    int ret, mnt_id;
613
    struct statfs stbuf;
614
    struct file_handle fh;
615
    struct handle_data *data = g_malloc(sizeof(struct handle_data));
616

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

    
650
FileOperations handle_ops = {
651
    .init         = handle_init,
652
    .lstat        = handle_lstat,
653
    .readlink     = handle_readlink,
654
    .close        = handle_close,
655
    .closedir     = handle_closedir,
656
    .open         = handle_open,
657
    .opendir      = handle_opendir,
658
    .rewinddir    = handle_rewinddir,
659
    .telldir      = handle_telldir,
660
    .readdir_r    = handle_readdir_r,
661
    .seekdir      = handle_seekdir,
662
    .preadv       = handle_preadv,
663
    .pwritev      = handle_pwritev,
664
    .chmod        = handle_chmod,
665
    .mknod        = handle_mknod,
666
    .mkdir        = handle_mkdir,
667
    .fstat        = handle_fstat,
668
    .open2        = handle_open2,
669
    .symlink      = handle_symlink,
670
    .link         = handle_link,
671
    .truncate     = handle_truncate,
672
    .rename       = handle_rename,
673
    .chown        = handle_chown,
674
    .utimensat    = handle_utimensat,
675
    .remove       = handle_remove,
676
    .fsync        = handle_fsync,
677
    .statfs       = handle_statfs,
678
    .lgetxattr    = handle_lgetxattr,
679
    .llistxattr   = handle_llistxattr,
680
    .lsetxattr    = handle_lsetxattr,
681
    .lremovexattr = handle_lremovexattr,
682
    .name_to_path = handle_name_to_path,
683
    .renameat     = handle_renameat,
684
    .unlinkat     = handle_unlinkat,
685
};