Statistics
| Branch: | Revision:

root / hw / 9pfs / virtio-9p-proxy.c @ 84a87cc4

History | View | Annotate | Download (34 kB)

1
/*
2
 * Virtio 9p Proxy callback
3
 *
4
 * Copyright IBM, Corp. 2011
5
 *
6
 * Authors:
7
 * M. Mohan Kumar <mohan@in.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
#include <sys/socket.h>
13
#include <sys/un.h>
14
#include "hw/virtio.h"
15
#include "virtio-9p.h"
16
#include "fsdev/qemu-fsdev.h"
17
#include "virtio-9p-proxy.h"
18

    
19
typedef struct V9fsProxy {
20
    int sockfd;
21
    QemuMutex mutex;
22
    struct iovec in_iovec;
23
    struct iovec out_iovec;
24
} V9fsProxy;
25

    
26
/*
27
 * Return received file descriptor on success in *status.
28
 * errno is also returned on *status (which will be < 0)
29
 * return < 0 on transport error.
30
 */
31
static int v9fs_receivefd(int sockfd, int *status)
32
{
33
    struct iovec iov;
34
    struct msghdr msg;
35
    struct cmsghdr *cmsg;
36
    int retval, data, fd;
37
    union MsgControl msg_control;
38

    
39
    iov.iov_base = &data;
40
    iov.iov_len = sizeof(data);
41

    
42
    memset(&msg, 0, sizeof(msg));
43
    msg.msg_iov = &iov;
44
    msg.msg_iovlen = 1;
45
    msg.msg_control = &msg_control;
46
    msg.msg_controllen = sizeof(msg_control);
47

    
48
    do {
49
        retval = recvmsg(sockfd, &msg, 0);
50
    } while (retval < 0 && errno == EINTR);
51
    if (retval <= 0) {
52
        return retval;
53
    }
54
    /*
55
     * data is set to V9FS_FD_VALID, if ancillary data is sent.  If this
56
     * request doesn't need ancillary data (fd) or an error occurred,
57
     * data is set to negative errno value.
58
     */
59
    if (data != V9FS_FD_VALID) {
60
        *status = data;
61
        return 0;
62
    }
63
    /*
64
     * File descriptor (fd) is sent in the ancillary data. Check if we
65
     * indeed received it. One of the reasons to fail to receive it is if
66
     * we exceeded the maximum number of file descriptors!
67
     */
68
    for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
69
        if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)) ||
70
            cmsg->cmsg_level != SOL_SOCKET ||
71
            cmsg->cmsg_type != SCM_RIGHTS) {
72
            continue;
73
        }
74
        fd = *((int *)CMSG_DATA(cmsg));
75
        *status = fd;
76
        return 0;
77
    }
78
    *status = -ENFILE;  /* Ancillary data sent but not received */
79
    return 0;
80
}
81

    
82
static ssize_t socket_read(int sockfd, void *buff, size_t size)
83
{
84
    ssize_t retval, total = 0;
85

    
86
    while (size) {
87
        retval = read(sockfd, buff, size);
88
        if (retval == 0) {
89
            return -EIO;
90
        }
91
        if (retval < 0) {
92
            if (errno == EINTR) {
93
                continue;
94
            }
95
            return -errno;
96
        }
97
        size -= retval;
98
        buff += retval;
99
        total += retval;
100
    }
101
    return total;
102
}
103

    
104
/* Converts proxy_statfs to VFS statfs structure */
105
static void prstatfs_to_statfs(struct statfs *stfs, ProxyStatFS *prstfs)
106
{
107
    memset(stfs, 0, sizeof(*stfs));
108
    stfs->f_type = prstfs->f_type;
109
    stfs->f_bsize = prstfs->f_bsize;
110
    stfs->f_blocks = prstfs->f_blocks;
111
    stfs->f_bfree = prstfs->f_bfree;
112
    stfs->f_bavail = prstfs->f_bavail;
113
    stfs->f_files = prstfs->f_files;
114
    stfs->f_ffree = prstfs->f_ffree;
115
    stfs->f_fsid.__val[0] = prstfs->f_fsid[0] & 0xFFFFFFFFUL;
116
    stfs->f_fsid.__val[1] = prstfs->f_fsid[1] >> 32 & 0xFFFFFFFFFUL;
117
    stfs->f_namelen = prstfs->f_namelen;
118
    stfs->f_frsize = prstfs->f_frsize;
119
}
120

    
121
/* Converts proxy_stat structure to VFS stat structure */
122
static void prstat_to_stat(struct stat *stbuf, ProxyStat *prstat)
123
{
124
   memset(stbuf, 0, sizeof(*stbuf));
125
   stbuf->st_dev = prstat->st_dev;
126
   stbuf->st_ino = prstat->st_ino;
127
   stbuf->st_nlink = prstat->st_nlink;
128
   stbuf->st_mode = prstat->st_mode;
129
   stbuf->st_uid = prstat->st_uid;
130
   stbuf->st_gid = prstat->st_gid;
131
   stbuf->st_rdev = prstat->st_rdev;
132
   stbuf->st_size = prstat->st_size;
133
   stbuf->st_blksize = prstat->st_blksize;
134
   stbuf->st_blocks = prstat->st_blocks;
135
   stbuf->st_atim.tv_sec = prstat->st_atim_sec;
136
   stbuf->st_atim.tv_nsec = prstat->st_atim_nsec;
137
   stbuf->st_mtime = prstat->st_mtim_sec;
138
   stbuf->st_mtim.tv_nsec = prstat->st_mtim_nsec;
139
   stbuf->st_ctime = prstat->st_ctim_sec;
140
   stbuf->st_ctim.tv_nsec = prstat->st_ctim_nsec;
141
}
142

    
143
/*
144
 * Response contains two parts
145
 * {header, data}
146
 * header.type == T_ERROR, data -> -errno
147
 * header.type == T_SUCCESS, data -> response
148
 * size of errno/response is given by header.size
149
 * returns < 0, on transport error. response is
150
 * valid only if status >= 0.
151
 */
152
static int v9fs_receive_response(V9fsProxy *proxy, int type,
153
                                 int *status, void *response)
154
{
155
    int retval;
156
    ProxyHeader header;
157
    struct iovec *reply = &proxy->in_iovec;
158

    
159
    *status = 0;
160
    reply->iov_len = 0;
161
    retval = socket_read(proxy->sockfd, reply->iov_base, PROXY_HDR_SZ);
162
    if (retval < 0) {
163
        return retval;
164
    }
165
    reply->iov_len = PROXY_HDR_SZ;
166
    proxy_unmarshal(reply, 0, "dd", &header.type, &header.size);
167
    /*
168
     * if response size > PROXY_MAX_IO_SZ, read the response but ignore it and
169
     * return -ENOBUFS
170
     */
171
    if (header.size > PROXY_MAX_IO_SZ) {
172
        int count;
173
        while (header.size > 0) {
174
            count = MIN(PROXY_MAX_IO_SZ, header.size);
175
            count = socket_read(proxy->sockfd, reply->iov_base, count);
176
            if (count < 0) {
177
                return count;
178
            }
179
            header.size -= count;
180
        }
181
        *status = -ENOBUFS;
182
        return 0;
183
    }
184

    
185
    retval = socket_read(proxy->sockfd,
186
                         reply->iov_base + PROXY_HDR_SZ, header.size);
187
    if (retval < 0) {
188
        return retval;
189
    }
190
    reply->iov_len += header.size;
191
    /* there was an error during processing request */
192
    if (header.type == T_ERROR) {
193
        int ret;
194
        ret = proxy_unmarshal(reply, PROXY_HDR_SZ, "d", status);
195
        if (ret < 0) {
196
            *status = ret;
197
        }
198
        return 0;
199
    }
200

    
201
    switch (type) {
202
    case T_LSTAT: {
203
        ProxyStat prstat;
204
        retval = proxy_unmarshal(reply, PROXY_HDR_SZ,
205
                                 "qqqdddqqqqqqqqqq", &prstat.st_dev,
206
                                 &prstat.st_ino, &prstat.st_nlink,
207
                                 &prstat.st_mode, &prstat.st_uid,
208
                                 &prstat.st_gid, &prstat.st_rdev,
209
                                 &prstat.st_size, &prstat.st_blksize,
210
                                 &prstat.st_blocks,
211
                                 &prstat.st_atim_sec, &prstat.st_atim_nsec,
212
                                 &prstat.st_mtim_sec, &prstat.st_mtim_nsec,
213
                                 &prstat.st_ctim_sec, &prstat.st_ctim_nsec);
214
        prstat_to_stat(response, &prstat);
215
        break;
216
    }
217
    case T_STATFS: {
218
        ProxyStatFS prstfs;
219
        retval = proxy_unmarshal(reply, PROXY_HDR_SZ,
220
                                 "qqqqqqqqqqq", &prstfs.f_type,
221
                                 &prstfs.f_bsize, &prstfs.f_blocks,
222
                                 &prstfs.f_bfree, &prstfs.f_bavail,
223
                                 &prstfs.f_files, &prstfs.f_ffree,
224
                                 &prstfs.f_fsid[0], &prstfs.f_fsid[1],
225
                                 &prstfs.f_namelen, &prstfs.f_frsize);
226
        prstatfs_to_statfs(response, &prstfs);
227
        break;
228
    }
229
    case T_READLINK: {
230
        V9fsString target;
231
        v9fs_string_init(&target);
232
        retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "s", &target);
233
        strcpy(response, target.data);
234
        v9fs_string_free(&target);
235
        break;
236
    }
237
    case T_LGETXATTR:
238
    case T_LLISTXATTR: {
239
        V9fsString xattr;
240
        v9fs_string_init(&xattr);
241
        retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "s", &xattr);
242
        memcpy(response, xattr.data, xattr.size);
243
        v9fs_string_free(&xattr);
244
        break;
245
    }
246
    case T_GETVERSION:
247
        proxy_unmarshal(reply, PROXY_HDR_SZ, "q", response);
248
        break;
249
    default:
250
        return -1;
251
    }
252
    if (retval < 0) {
253
        *status  = retval;
254
    }
255
    return 0;
256
}
257

    
258
/*
259
 * return < 0 on transport error.
260
 * *status is valid only if return >= 0
261
 */
262
static int v9fs_receive_status(V9fsProxy *proxy,
263
                               struct iovec *reply, int *status)
264
{
265
    int retval;
266
    ProxyHeader header;
267

    
268
    *status = 0;
269
    reply->iov_len = 0;
270
    retval = socket_read(proxy->sockfd, reply->iov_base, PROXY_HDR_SZ);
271
    if (retval < 0) {
272
        return retval;
273
    }
274
    reply->iov_len = PROXY_HDR_SZ;
275
    proxy_unmarshal(reply, 0, "dd", &header.type, &header.size);
276
    if (header.size != sizeof(int)) {
277
        *status = -ENOBUFS;
278
        return 0;
279
    }
280
    retval = socket_read(proxy->sockfd,
281
                         reply->iov_base + PROXY_HDR_SZ, header.size);
282
    if (retval < 0) {
283
        return retval;
284
    }
285
    reply->iov_len += header.size;
286
    proxy_unmarshal(reply, PROXY_HDR_SZ, "d", status);
287
    return 0;
288
}
289

    
290
/*
291
 * Proxy->header and proxy->request written to socket by QEMU process.
292
 * This request read by proxy helper process
293
 * returns 0 on success and -errno on error
294
 */
295
static int v9fs_request(V9fsProxy *proxy, int type,
296
                        void *response, const char *fmt, ...)
297
{
298
    dev_t rdev;
299
    va_list ap;
300
    int size = 0;
301
    int retval = 0;
302
    uint64_t offset;
303
    ProxyHeader header = { 0, 0};
304
    struct timespec spec[2];
305
    int flags, mode, uid, gid;
306
    V9fsString *name, *value;
307
    V9fsString *path, *oldpath;
308
    struct iovec *iovec = NULL, *reply = NULL;
309

    
310
    qemu_mutex_lock(&proxy->mutex);
311

    
312
    if (proxy->sockfd == -1) {
313
        retval = -EIO;
314
        goto err_out;
315
    }
316
    iovec = &proxy->out_iovec;
317
    reply = &proxy->in_iovec;
318
    va_start(ap, fmt);
319
    switch (type) {
320
    case T_OPEN:
321
        path = va_arg(ap, V9fsString *);
322
        flags = va_arg(ap, int);
323
        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, flags);
324
        if (retval > 0) {
325
            header.size = retval;
326
            header.type = T_OPEN;
327
        }
328
        break;
329
    case T_CREATE:
330
        path = va_arg(ap, V9fsString *);
331
        flags = va_arg(ap, int);
332
        mode = va_arg(ap, int);
333
        uid = va_arg(ap, int);
334
        gid = va_arg(ap, int);
335
        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sdddd", path,
336
                                    flags, mode, uid, gid);
337
        if (retval > 0) {
338
            header.size = retval;
339
            header.type = T_CREATE;
340
        }
341
        break;
342
    case T_MKNOD:
343
        path = va_arg(ap, V9fsString *);
344
        mode = va_arg(ap, int);
345
        rdev = va_arg(ap, long int);
346
        uid = va_arg(ap, int);
347
        gid = va_arg(ap, int);
348
        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddsdq",
349
                                    uid, gid, path, mode, rdev);
350
        if (retval > 0) {
351
            header.size = retval;
352
            header.type = T_MKNOD;
353
        }
354
        break;
355
    case T_MKDIR:
356
        path = va_arg(ap, V9fsString *);
357
        mode = va_arg(ap, int);
358
        uid = va_arg(ap, int);
359
        gid = va_arg(ap, int);
360
        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddsd",
361
                                    uid, gid, path, mode);
362
        if (retval > 0) {
363
            header.size = retval;
364
            header.type = T_MKDIR;
365
        }
366
        break;
367
    case T_SYMLINK:
368
        oldpath = va_arg(ap, V9fsString *);
369
        path = va_arg(ap, V9fsString *);
370
        uid = va_arg(ap, int);
371
        gid = va_arg(ap, int);
372
        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddss",
373
                                    uid, gid, oldpath, path);
374
        if (retval > 0) {
375
            header.size = retval;
376
            header.type = T_SYMLINK;
377
        }
378
        break;
379
    case T_LINK:
380
        oldpath = va_arg(ap, V9fsString *);
381
        path = va_arg(ap, V9fsString *);
382
        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss",
383
                                    oldpath, path);
384
        if (retval > 0) {
385
            header.size = retval;
386
            header.type = T_LINK;
387
        }
388
        break;
389
    case T_LSTAT:
390
        path = va_arg(ap, V9fsString *);
391
        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path);
392
        if (retval > 0) {
393
            header.size = retval;
394
            header.type = T_LSTAT;
395
        }
396
        break;
397
    case T_READLINK:
398
        path = va_arg(ap, V9fsString *);
399
        size = va_arg(ap, int);
400
        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, size);
401
        if (retval > 0) {
402
            header.size = retval;
403
            header.type = T_READLINK;
404
        }
405
        break;
406
    case T_STATFS:
407
        path = va_arg(ap, V9fsString *);
408
        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path);
409
        if (retval > 0) {
410
            header.size = retval;
411
            header.type = T_STATFS;
412
        }
413
        break;
414
    case T_CHMOD:
415
        path = va_arg(ap, V9fsString *);
416
        mode = va_arg(ap, int);
417
        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, mode);
418
        if (retval > 0) {
419
            header.size = retval;
420
            header.type = T_CHMOD;
421
        }
422
        break;
423
    case T_CHOWN:
424
        path = va_arg(ap, V9fsString *);
425
        uid = va_arg(ap, int);
426
        gid = va_arg(ap, int);
427
        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sdd", path, uid, gid);
428
        if (retval > 0) {
429
            header.size = retval;
430
            header.type = T_CHOWN;
431
        }
432
        break;
433
    case T_TRUNCATE:
434
        path = va_arg(ap, V9fsString *);
435
        offset = va_arg(ap, uint64_t);
436
        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sq", path, offset);
437
        if (retval > 0) {
438
            header.size = retval;
439
            header.type = T_TRUNCATE;
440
        }
441
        break;
442
    case T_UTIME:
443
        path = va_arg(ap, V9fsString *);
444
        spec[0].tv_sec = va_arg(ap, long);
445
        spec[0].tv_nsec = va_arg(ap, long);
446
        spec[1].tv_sec = va_arg(ap, long);
447
        spec[1].tv_nsec = va_arg(ap, long);
448
        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sqqqq", path,
449
                                    spec[0].tv_sec, spec[1].tv_nsec,
450
                                    spec[1].tv_sec, spec[1].tv_nsec);
451
        if (retval > 0) {
452
            header.size = retval;
453
            header.type = T_UTIME;
454
        }
455
        break;
456
    case T_RENAME:
457
        oldpath = va_arg(ap, V9fsString *);
458
        path = va_arg(ap, V9fsString *);
459
        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss", oldpath, path);
460
        if (retval > 0) {
461
            header.size = retval;
462
            header.type = T_RENAME;
463
        }
464
        break;
465
    case T_REMOVE:
466
        path = va_arg(ap, V9fsString *);
467
        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path);
468
        if (retval > 0) {
469
            header.size = retval;
470
            header.type = T_REMOVE;
471
        }
472
        break;
473
    case T_LGETXATTR:
474
        size = va_arg(ap, int);
475
        path = va_arg(ap, V9fsString *);
476
        name = va_arg(ap, V9fsString *);
477
        retval = proxy_marshal(iovec, PROXY_HDR_SZ,
478
                                    "dss", size, path, name);
479
        if (retval > 0) {
480
            header.size = retval;
481
            header.type = T_LGETXATTR;
482
        }
483
        break;
484
    case T_LLISTXATTR:
485
        size = va_arg(ap, int);
486
        path = va_arg(ap, V9fsString *);
487
        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ds", size, path);
488
        if (retval > 0) {
489
            header.size = retval;
490
            header.type = T_LLISTXATTR;
491
        }
492
        break;
493
    case T_LSETXATTR:
494
        path = va_arg(ap, V9fsString *);
495
        name = va_arg(ap, V9fsString *);
496
        value = va_arg(ap, V9fsString *);
497
        size = va_arg(ap, int);
498
        flags = va_arg(ap, int);
499
        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sssdd",
500
                                    path, name, value, size, flags);
501
        if (retval > 0) {
502
            header.size = retval;
503
            header.type = T_LSETXATTR;
504
        }
505
        break;
506
    case T_LREMOVEXATTR:
507
        path = va_arg(ap, V9fsString *);
508
        name = va_arg(ap, V9fsString *);
509
        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss", path, name);
510
        if (retval > 0) {
511
            header.size = retval;
512
            header.type = T_LREMOVEXATTR;
513
        }
514
        break;
515
    case T_GETVERSION:
516
        path = va_arg(ap, V9fsString *);
517
        retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path);
518
        if (retval > 0) {
519
            header.size = retval;
520
            header.type = T_GETVERSION;
521
        }
522
        break;
523
    default:
524
        error_report("Invalid type %d\n", type);
525
        retval = -EINVAL;
526
        break;
527
    }
528
    va_end(ap);
529

    
530
    if (retval < 0) {
531
        goto err_out;
532
    }
533

    
534
    /* marshal the header details */
535
    proxy_marshal(iovec, 0, "dd", header.type, header.size);
536
    header.size += PROXY_HDR_SZ;
537

    
538
    retval = qemu_write_full(proxy->sockfd, iovec->iov_base, header.size);
539
    if (retval != header.size) {
540
        goto close_error;
541
    }
542

    
543
    switch (type) {
544
    case T_OPEN:
545
    case T_CREATE:
546
        /*
547
         * A file descriptor is returned as response for
548
         * T_OPEN,T_CREATE on success
549
         */
550
        if (v9fs_receivefd(proxy->sockfd, &retval) < 0) {
551
            goto close_error;
552
        }
553
        break;
554
    case T_MKNOD:
555
    case T_MKDIR:
556
    case T_SYMLINK:
557
    case T_LINK:
558
    case T_CHMOD:
559
    case T_CHOWN:
560
    case T_RENAME:
561
    case T_TRUNCATE:
562
    case T_UTIME:
563
    case T_REMOVE:
564
    case T_LSETXATTR:
565
    case T_LREMOVEXATTR:
566
        if (v9fs_receive_status(proxy, reply, &retval) < 0) {
567
            goto close_error;
568
        }
569
        break;
570
    case T_LSTAT:
571
    case T_READLINK:
572
    case T_STATFS:
573
    case T_GETVERSION:
574
        if (v9fs_receive_response(proxy, type, &retval, response) < 0) {
575
            goto close_error;
576
        }
577
        break;
578
    case T_LGETXATTR:
579
    case T_LLISTXATTR:
580
        if (!size) {
581
            if (v9fs_receive_status(proxy, reply, &retval) < 0) {
582
                goto close_error;
583
            }
584
        } else {
585
            if (v9fs_receive_response(proxy, type, &retval, response) < 0) {
586
                goto close_error;
587
            }
588
        }
589
        break;
590
    }
591

    
592
err_out:
593
    qemu_mutex_unlock(&proxy->mutex);
594
    return retval;
595

    
596
close_error:
597
    close(proxy->sockfd);
598
    proxy->sockfd = -1;
599
    qemu_mutex_unlock(&proxy->mutex);
600
    return -EIO;
601
}
602

    
603
static int proxy_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
604
{
605
    int retval;
606
    retval = v9fs_request(fs_ctx->private, T_LSTAT, stbuf, "s", fs_path);
607
    if (retval < 0) {
608
        errno = -retval;
609
        return -1;
610
    }
611
    return retval;
612
}
613

    
614
static ssize_t proxy_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
615
                              char *buf, size_t bufsz)
616
{
617
    int retval;
618
    retval = v9fs_request(fs_ctx->private, T_READLINK, buf, "sd",
619
                          fs_path, bufsz);
620
    if (retval < 0) {
621
        errno = -retval;
622
        return -1;
623
    }
624
    return strlen(buf);
625
}
626

    
627
static int proxy_close(FsContext *ctx, V9fsFidOpenState *fs)
628
{
629
    return close(fs->fd);
630
}
631

    
632
static int proxy_closedir(FsContext *ctx, V9fsFidOpenState *fs)
633
{
634
    return closedir(fs->dir);
635
}
636

    
637
static int proxy_open(FsContext *ctx, V9fsPath *fs_path,
638
                      int flags, V9fsFidOpenState *fs)
639
{
640
    fs->fd = v9fs_request(ctx->private, T_OPEN, NULL, "sd", fs_path, flags);
641
    if (fs->fd < 0) {
642
        errno = -fs->fd;
643
        fs->fd = -1;
644
    }
645
    return fs->fd;
646
}
647

    
648
static int proxy_opendir(FsContext *ctx,
649
                         V9fsPath *fs_path, V9fsFidOpenState *fs)
650
{
651
    int serrno, fd;
652

    
653
    fs->dir = NULL;
654
    fd = v9fs_request(ctx->private, T_OPEN, NULL, "sd", fs_path, O_DIRECTORY);
655
    if (fd < 0) {
656
        errno = -fd;
657
        return -1;
658
    }
659
    fs->dir = fdopendir(fd);
660
    if (!fs->dir) {
661
        serrno = errno;
662
        close(fd);
663
        errno = serrno;
664
        return -1;
665
    }
666
    return 0;
667
}
668

    
669
static void proxy_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
670
{
671
    return rewinddir(fs->dir);
672
}
673

    
674
static off_t proxy_telldir(FsContext *ctx, V9fsFidOpenState *fs)
675
{
676
    return telldir(fs->dir);
677
}
678

    
679
static int proxy_readdir_r(FsContext *ctx, V9fsFidOpenState *fs,
680
                           struct dirent *entry,
681
                           struct dirent **result)
682
{
683
    return readdir_r(fs->dir, entry, result);
684
}
685

    
686
static void proxy_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
687
{
688
    return seekdir(fs->dir, off);
689
}
690

    
691
static ssize_t proxy_preadv(FsContext *ctx, V9fsFidOpenState *fs,
692
                            const struct iovec *iov,
693
                            int iovcnt, off_t offset)
694
{
695
#ifdef CONFIG_PREADV
696
    return preadv(fs->fd, iov, iovcnt, offset);
697
#else
698
    int err = lseek(fs->fd, offset, SEEK_SET);
699
    if (err == -1) {
700
        return err;
701
    } else {
702
        return readv(fs->fd, iov, iovcnt);
703
    }
704
#endif
705
}
706

    
707
static ssize_t proxy_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
708
                             const struct iovec *iov,
709
                             int iovcnt, off_t offset)
710
{
711
    ssize_t ret;
712

    
713
#ifdef CONFIG_PREADV
714
    ret = pwritev(fs->fd, iov, iovcnt, offset);
715
#else
716
    int err = lseek(fs->fd, offset, SEEK_SET);
717
    if (err == -1) {
718
        return err;
719
    } else {
720
        ret = writev(fs->fd, iov, iovcnt);
721
    }
722
#endif
723
#ifdef CONFIG_SYNC_FILE_RANGE
724
    if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) {
725
        /*
726
         * Initiate a writeback. This is not a data integrity sync.
727
         * We want to ensure that we don't leave dirty pages in the cache
728
         * after write when writeout=immediate is sepcified.
729
         */
730
        sync_file_range(fs->fd, offset, ret,
731
                        SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
732
    }
733
#endif
734
    return ret;
735
}
736

    
737
static int proxy_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
738
{
739
    int retval;
740
    retval = v9fs_request(fs_ctx->private, T_CHMOD, NULL, "sd",
741
                          fs_path, credp->fc_mode);
742
    if (retval < 0) {
743
        errno = -retval;
744
    }
745
    return retval;
746
}
747

    
748
static int proxy_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
749
                       const char *name, FsCred *credp)
750
{
751
    int retval;
752
    V9fsString fullname;
753

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

    
757
    retval = v9fs_request(fs_ctx->private, T_MKNOD, NULL, "sdqdd",
758
                          &fullname, credp->fc_mode, credp->fc_rdev,
759
                          credp->fc_uid, credp->fc_gid);
760
    v9fs_string_free(&fullname);
761
    if (retval < 0) {
762
        errno = -retval;
763
        retval = -1;
764
    }
765
    return retval;
766
}
767

    
768
static int proxy_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
769
                       const char *name, FsCred *credp)
770
{
771
    int retval;
772
    V9fsString fullname;
773

    
774
    v9fs_string_init(&fullname);
775
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
776

    
777
    retval = v9fs_request(fs_ctx->private, T_MKDIR, NULL, "sddd", &fullname,
778
                          credp->fc_mode, credp->fc_uid, credp->fc_gid);
779
    v9fs_string_free(&fullname);
780
    if (retval < 0) {
781
        errno = -retval;
782
        retval = -1;
783
    }
784
    v9fs_string_free(&fullname);
785
    return retval;
786
}
787

    
788
static int proxy_fstat(FsContext *fs_ctx, int fid_type,
789
                       V9fsFidOpenState *fs, struct stat *stbuf)
790
{
791
    int fd;
792

    
793
    if (fid_type == P9_FID_DIR) {
794
        fd = dirfd(fs->dir);
795
    } else {
796
        fd = fs->fd;
797
    }
798
    return fstat(fd, stbuf);
799
}
800

    
801
static int proxy_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
802
                       int flags, FsCred *credp, V9fsFidOpenState *fs)
803
{
804
    V9fsString fullname;
805

    
806
    v9fs_string_init(&fullname);
807
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
808

    
809
    fs->fd = v9fs_request(fs_ctx->private, T_CREATE, NULL, "sdddd",
810
                          &fullname, flags, credp->fc_mode,
811
                          credp->fc_uid, credp->fc_gid);
812
    v9fs_string_free(&fullname);
813
    if (fs->fd < 0) {
814
        errno = -fs->fd;
815
        fs->fd = -1;
816
    }
817
    return fs->fd;
818
}
819

    
820
static int proxy_symlink(FsContext *fs_ctx, const char *oldpath,
821
                         V9fsPath *dir_path, const char *name, FsCred *credp)
822
{
823
    int retval;
824
    V9fsString fullname, target;
825

    
826
    v9fs_string_init(&fullname);
827
    v9fs_string_init(&target);
828

    
829
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
830
    v9fs_string_sprintf(&target, "%s", oldpath);
831

    
832
    retval = v9fs_request(fs_ctx->private, T_SYMLINK, NULL, "ssdd",
833
                          &target, &fullname, credp->fc_uid, credp->fc_gid);
834
    v9fs_string_free(&fullname);
835
    v9fs_string_free(&target);
836
    if (retval < 0) {
837
        errno = -retval;
838
        retval = -1;
839
    }
840
    return retval;
841
}
842

    
843
static int proxy_link(FsContext *ctx, V9fsPath *oldpath,
844
                      V9fsPath *dirpath, const char *name)
845
{
846
    int retval;
847
    V9fsString newpath;
848

    
849
    v9fs_string_init(&newpath);
850
    v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name);
851

    
852
    retval = v9fs_request(ctx->private, T_LINK, NULL, "ss", oldpath, &newpath);
853
    v9fs_string_free(&newpath);
854
    if (retval < 0) {
855
        errno = -retval;
856
        retval = -1;
857
    }
858
    return retval;
859
}
860

    
861
static int proxy_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
862
{
863
    int retval;
864

    
865
    retval = v9fs_request(ctx->private, T_TRUNCATE, NULL, "sq", fs_path, size);
866
    if (retval < 0) {
867
        errno = -retval;
868
        return -1;
869
    }
870
    return 0;
871
}
872

    
873
static int proxy_rename(FsContext *ctx, const char *oldpath,
874
                        const char *newpath)
875
{
876
    int retval;
877
    V9fsString oldname, newname;
878

    
879
    v9fs_string_init(&oldname);
880
    v9fs_string_init(&newname);
881

    
882
    v9fs_string_sprintf(&oldname, "%s", oldpath);
883
    v9fs_string_sprintf(&newname, "%s", newpath);
884
    retval = v9fs_request(ctx->private, T_RENAME, NULL, "ss",
885
                          &oldname, &newname);
886
    v9fs_string_free(&oldname);
887
    v9fs_string_free(&newname);
888
    if (retval < 0) {
889
        errno = -retval;
890
    }
891
    return retval;
892
}
893

    
894
static int proxy_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
895
{
896
    int retval;
897
    retval = v9fs_request(fs_ctx->private, T_CHOWN, NULL, "sdd",
898
                          fs_path, credp->fc_uid, credp->fc_gid);
899
    if (retval < 0) {
900
        errno = -retval;
901
    }
902
    return retval;
903
}
904

    
905
static int proxy_utimensat(FsContext *s, V9fsPath *fs_path,
906
                           const struct timespec *buf)
907
{
908
    int retval;
909
    retval = v9fs_request(s->private, T_UTIME, NULL, "sqqqq",
910
                          fs_path,
911
                          buf[0].tv_sec, buf[0].tv_nsec,
912
                          buf[1].tv_sec, buf[1].tv_nsec);
913
    if (retval < 0) {
914
        errno = -retval;
915
    }
916
    return retval;
917
}
918

    
919
static int proxy_remove(FsContext *ctx, const char *path)
920
{
921
    int retval;
922
    V9fsString name;
923
    v9fs_string_init(&name);
924
    v9fs_string_sprintf(&name, "%s", path);
925
    retval = v9fs_request(ctx->private, T_REMOVE, NULL, "s", &name);
926
    v9fs_string_free(&name);
927
    if (retval < 0) {
928
        errno = -retval;
929
    }
930
    return retval;
931
}
932

    
933
static int proxy_fsync(FsContext *ctx, int fid_type,
934
                       V9fsFidOpenState *fs, int datasync)
935
{
936
    int fd;
937

    
938
    if (fid_type == P9_FID_DIR) {
939
        fd = dirfd(fs->dir);
940
    } else {
941
        fd = fs->fd;
942
    }
943

    
944
    if (datasync) {
945
        return qemu_fdatasync(fd);
946
    } else {
947
        return fsync(fd);
948
    }
949
}
950

    
951
static int proxy_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
952
{
953
    int retval;
954
    retval = v9fs_request(s->private, T_STATFS, stbuf, "s", fs_path);
955
    if (retval < 0) {
956
        errno = -retval;
957
        return -1;
958
    }
959
    return retval;
960
}
961

    
962
static ssize_t proxy_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
963
                               const char *name, void *value, size_t size)
964
{
965
    int retval;
966
    V9fsString xname;
967

    
968
    v9fs_string_init(&xname);
969
    v9fs_string_sprintf(&xname, "%s", name);
970
    retval = v9fs_request(ctx->private, T_LGETXATTR, value, "dss", size,
971
                          fs_path, &xname);
972
    v9fs_string_free(&xname);
973
    if (retval < 0) {
974
        errno = -retval;
975
    }
976
    return retval;
977
}
978

    
979
static ssize_t proxy_llistxattr(FsContext *ctx, V9fsPath *fs_path,
980
                                void *value, size_t size)
981
{
982
    int retval;
983
    retval = v9fs_request(ctx->private, T_LLISTXATTR, value, "ds", size,
984
                        fs_path);
985
    if (retval < 0) {
986
        errno = -retval;
987
    }
988
    return retval;
989
}
990

    
991
static int proxy_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
992
                           void *value, size_t size, int flags)
993
{
994
    int retval;
995
    V9fsString xname, xvalue;
996

    
997
    v9fs_string_init(&xname);
998
    v9fs_string_sprintf(&xname, "%s", name);
999

    
1000
    v9fs_string_init(&xvalue);
1001
    xvalue.size = size;
1002
    xvalue.data = g_malloc(size);
1003
    memcpy(xvalue.data, value, size);
1004

    
1005
    retval = v9fs_request(ctx->private, T_LSETXATTR, value, "sssdd",
1006
                          fs_path, &xname, &xvalue, size, flags);
1007
    v9fs_string_free(&xname);
1008
    v9fs_string_free(&xvalue);
1009
    if (retval < 0) {
1010
        errno = -retval;
1011
    }
1012
    return retval;
1013
}
1014

    
1015
static int proxy_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
1016
                              const char *name)
1017
{
1018
    int retval;
1019
    V9fsString xname;
1020

    
1021
    v9fs_string_init(&xname);
1022
    v9fs_string_sprintf(&xname, "%s", name);
1023
    retval = v9fs_request(ctx->private, T_LREMOVEXATTR, NULL, "ss",
1024
                          fs_path, &xname);
1025
    v9fs_string_free(&xname);
1026
    if (retval < 0) {
1027
        errno = -retval;
1028
    }
1029
    return retval;
1030
}
1031

    
1032
static int proxy_name_to_path(FsContext *ctx, V9fsPath *dir_path,
1033
                              const char *name, V9fsPath *target)
1034
{
1035
    if (dir_path) {
1036
        v9fs_string_sprintf((V9fsString *)target, "%s/%s",
1037
                            dir_path->data, name);
1038
    } else {
1039
        v9fs_string_sprintf((V9fsString *)target, "%s", name);
1040
    }
1041
    /* Bump the size for including terminating NULL */
1042
    target->size++;
1043
    return 0;
1044
}
1045

    
1046
static int proxy_renameat(FsContext *ctx, V9fsPath *olddir,
1047
                          const char *old_name, V9fsPath *newdir,
1048
                          const char *new_name)
1049
{
1050
    int ret;
1051
    V9fsString old_full_name, new_full_name;
1052

    
1053
    v9fs_string_init(&old_full_name);
1054
    v9fs_string_init(&new_full_name);
1055

    
1056
    v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name);
1057
    v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name);
1058

    
1059
    ret = proxy_rename(ctx, old_full_name.data, new_full_name.data);
1060
    v9fs_string_free(&old_full_name);
1061
    v9fs_string_free(&new_full_name);
1062
    return ret;
1063
}
1064

    
1065
static int proxy_unlinkat(FsContext *ctx, V9fsPath *dir,
1066
                          const char *name, int flags)
1067
{
1068
    int ret;
1069
    V9fsString fullname;
1070
    v9fs_string_init(&fullname);
1071

    
1072
    v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name);
1073
    ret = proxy_remove(ctx, fullname.data);
1074
    v9fs_string_free(&fullname);
1075

    
1076
    return ret;
1077
}
1078

    
1079
static int proxy_ioc_getversion(FsContext *fs_ctx, V9fsPath *path,
1080
                                mode_t st_mode, uint64_t *st_gen)
1081
{
1082
    int err;
1083

    
1084
    /* Do not try to open special files like device nodes, fifos etc
1085
     * we can get fd for regular files and directories only
1086
     */
1087
    if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
1088
        return 0;
1089
    }
1090
    err = v9fs_request(fs_ctx->private, T_GETVERSION, st_gen, "s", path);
1091
    if (err < 0) {
1092
        errno = -err;
1093
        err = -1;
1094
    }
1095
    return err;
1096
}
1097

    
1098
static int connect_namedsocket(const char *path)
1099
{
1100
    int sockfd, size;
1101
    struct sockaddr_un helper;
1102

    
1103
    sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
1104
    if (sockfd < 0) {
1105
        fprintf(stderr, "socket %s\n", strerror(errno));
1106
        return -1;
1107
    }
1108
    strcpy(helper.sun_path, path);
1109
    helper.sun_family = AF_UNIX;
1110
    size = strlen(helper.sun_path) + sizeof(helper.sun_family);
1111
    if (connect(sockfd, (struct sockaddr *)&helper, size) < 0) {
1112
        fprintf(stderr, "socket error\n");
1113
        return -1;
1114
    }
1115

    
1116
    /* remove the socket for security reasons */
1117
    unlink(path);
1118
    return sockfd;
1119
}
1120

    
1121
static int proxy_parse_opts(QemuOpts *opts, struct FsDriverEntry *fs)
1122
{
1123
    const char *socket = qemu_opt_get(opts, "socket");
1124
    const char *sock_fd = qemu_opt_get(opts, "sock_fd");
1125

    
1126
    if (!socket && !sock_fd) {
1127
        fprintf(stderr, "socket and sock_fd none of the option specified\n");
1128
        return -1;
1129
    }
1130
    if (socket && sock_fd) {
1131
        fprintf(stderr, "Both socket and sock_fd options specified\n");
1132
        return -1;
1133
    }
1134
    if (socket) {
1135
        fs->path = g_strdup(socket);
1136
        fs->export_flags = V9FS_PROXY_SOCK_NAME;
1137
    } else {
1138
        fs->path = g_strdup(sock_fd);
1139
        fs->export_flags = V9FS_PROXY_SOCK_FD;
1140
    }
1141
    return 0;
1142
}
1143

    
1144
static int proxy_init(FsContext *ctx)
1145
{
1146
    V9fsProxy *proxy = g_malloc(sizeof(V9fsProxy));
1147
    int sock_id;
1148

    
1149
    if (ctx->export_flags & V9FS_PROXY_SOCK_NAME) {
1150
        sock_id = connect_namedsocket(ctx->fs_root);
1151
    } else {
1152
        sock_id = atoi(ctx->fs_root);
1153
        if (sock_id < 0) {
1154
            fprintf(stderr, "socket descriptor not initialized\n");
1155
            return -1;
1156
        }
1157
    }
1158
    g_free(ctx->fs_root);
1159

    
1160
    proxy->in_iovec.iov_base  = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ);
1161
    proxy->in_iovec.iov_len   = PROXY_MAX_IO_SZ + PROXY_HDR_SZ;
1162
    proxy->out_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ);
1163
    proxy->out_iovec.iov_len  = PROXY_MAX_IO_SZ + PROXY_HDR_SZ;
1164

    
1165
    ctx->private = proxy;
1166
    proxy->sockfd = sock_id;
1167
    qemu_mutex_init(&proxy->mutex);
1168

    
1169
    ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT;
1170
    ctx->exops.get_st_gen = proxy_ioc_getversion;
1171
    return 0;
1172
}
1173

    
1174
FileOperations proxy_ops = {
1175
    .parse_opts   = proxy_parse_opts,
1176
    .init         = proxy_init,
1177
    .lstat        = proxy_lstat,
1178
    .readlink     = proxy_readlink,
1179
    .close        = proxy_close,
1180
    .closedir     = proxy_closedir,
1181
    .open         = proxy_open,
1182
    .opendir      = proxy_opendir,
1183
    .rewinddir    = proxy_rewinddir,
1184
    .telldir      = proxy_telldir,
1185
    .readdir_r    = proxy_readdir_r,
1186
    .seekdir      = proxy_seekdir,
1187
    .preadv       = proxy_preadv,
1188
    .pwritev      = proxy_pwritev,
1189
    .chmod        = proxy_chmod,
1190
    .mknod        = proxy_mknod,
1191
    .mkdir        = proxy_mkdir,
1192
    .fstat        = proxy_fstat,
1193
    .open2        = proxy_open2,
1194
    .symlink      = proxy_symlink,
1195
    .link         = proxy_link,
1196
    .truncate     = proxy_truncate,
1197
    .rename       = proxy_rename,
1198
    .chown        = proxy_chown,
1199
    .utimensat    = proxy_utimensat,
1200
    .remove       = proxy_remove,
1201
    .fsync        = proxy_fsync,
1202
    .statfs       = proxy_statfs,
1203
    .lgetxattr    = proxy_lgetxattr,
1204
    .llistxattr   = proxy_llistxattr,
1205
    .lsetxattr    = proxy_lsetxattr,
1206
    .lremovexattr = proxy_lremovexattr,
1207
    .name_to_path = proxy_name_to_path,
1208
    .renameat     = proxy_renameat,
1209
    .unlinkat     = proxy_unlinkat,
1210
};