Statistics
| Branch: | Revision:

root / hw / 9pfs / virtio-9p-proxy.c @ 1a9978a5

History | View | Annotate | Download (34.1 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/virtio.h"
15
#include "virtio-9p.h"
16
#include "qemu/error-report.h"
17
#include "fsdev/qemu-fsdev.h"
18
#include "virtio-9p-proxy.h"
19

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
311
    qemu_mutex_lock(&proxy->mutex);
312

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1077
    return ret;
1078
}
1079

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

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

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

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

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

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

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

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

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

    
1164
    proxy->in_iovec.iov_base  = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ);
1165
    proxy->in_iovec.iov_len   = PROXY_MAX_IO_SZ + PROXY_HDR_SZ;
1166
    proxy->out_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ);
1167
    proxy->out_iovec.iov_len  = PROXY_MAX_IO_SZ + PROXY_HDR_SZ;
1168

    
1169
    ctx->private = proxy;
1170
    proxy->sockfd = sock_id;
1171
    qemu_mutex_init(&proxy->mutex);
1172

    
1173
    ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT;
1174
    ctx->exops.get_st_gen = proxy_ioc_getversion;
1175
    return 0;
1176
}
1177

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