]> git.proxmox.com Git - qemu.git/blame - hw/9pfs/virtio-9p-proxy.c
hw/9pfs: Open and create files
[qemu.git] / hw / 9pfs / virtio-9p-proxy.c
CommitLineData
4c793dda
MK
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
19typedef struct V9fsProxy {
20 int sockfd;
21 QemuMutex mutex;
22 struct iovec iovec;
23} V9fsProxy;
24
daf0b9ac
MK
25/*
26 * Return received file descriptor on success in *status.
27 * errno is also returned on *status (which will be < 0)
28 * return < 0 on transport error.
29 */
30static int v9fs_receivefd(int sockfd, int *status)
31{
32 struct iovec iov;
33 struct msghdr msg;
34 struct cmsghdr *cmsg;
35 int retval, data, fd;
36 union MsgControl msg_control;
37
38 iov.iov_base = &data;
39 iov.iov_len = sizeof(data);
40
41 memset(&msg, 0, sizeof(msg));
42 msg.msg_iov = &iov;
43 msg.msg_iovlen = 1;
44 msg.msg_control = &msg_control;
45 msg.msg_controllen = sizeof(msg_control);
46
47 do {
48 retval = recvmsg(sockfd, &msg, 0);
49 } while (retval < 0 && errno == EINTR);
50 if (retval <= 0) {
51 return retval;
52 }
53 /*
54 * data is set to V9FS_FD_VALID, if ancillary data is sent. If this
55 * request doesn't need ancillary data (fd) or an error occurred,
56 * data is set to negative errno value.
57 */
58 if (data != V9FS_FD_VALID) {
59 *status = data;
60 return 0;
61 }
62 /*
63 * File descriptor (fd) is sent in the ancillary data. Check if we
64 * indeed received it. One of the reasons to fail to receive it is if
65 * we exceeded the maximum number of file descriptors!
66 */
67 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
68 if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)) ||
69 cmsg->cmsg_level != SOL_SOCKET ||
70 cmsg->cmsg_type != SCM_RIGHTS) {
71 continue;
72 }
73 fd = *((int *)CMSG_DATA(cmsg));
74 *status = fd;
75 return 0;
76 }
77 *status = -ENFILE; /* Ancillary data sent but not received */
78 return 0;
79}
80
81/*
82 * Proxy->header and proxy->request written to socket by QEMU process.
83 * This request read by proxy helper process
84 * returns 0 on success and -errno on error
85 */
86static int v9fs_request(V9fsProxy *proxy, int type,
87 void *response, const char *fmt, ...)
88{
89 va_list ap;
90 int retval = 0;
91 V9fsString *path;
92 ProxyHeader header = { 0, 0};
93 struct iovec *iovec = NULL;
94 int flags, mode, uid, gid;
95
96 qemu_mutex_lock(&proxy->mutex);
97
98 if (proxy->sockfd == -1) {
99 retval = -EIO;
100 goto err_out;
101 }
102 iovec = &proxy->iovec;
103
104 va_start(ap, fmt);
105 switch (type) {
106 case T_OPEN:
107 path = va_arg(ap, V9fsString *);
108 flags = va_arg(ap, int);
109 retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, flags);
110 if (retval > 0) {
111 header.size = retval;
112 header.type = T_OPEN;
113 }
114 break;
115 case T_CREATE:
116 path = va_arg(ap, V9fsString *);
117 flags = va_arg(ap, int);
118 mode = va_arg(ap, int);
119 uid = va_arg(ap, int);
120 gid = va_arg(ap, int);
121 retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sdddd", path,
122 flags, mode, uid, gid);
123 if (retval > 0) {
124 header.size = retval;
125 header.type = T_CREATE;
126 }
127 break;
128 default:
129 error_report("Invalid type %d\n", type);
130 retval = -EINVAL;
131 break;
132 }
133 va_end(ap);
134
135 if (retval < 0) {
136 goto err_out;
137 }
138
139 /* marshal the header details */
140 proxy_marshal(iovec, 0, "dd", header.type, header.size);
141 header.size += PROXY_HDR_SZ;
142
143 retval = qemu_write_full(proxy->sockfd, iovec->iov_base, header.size);
144 if (retval != header.size) {
145 goto close_error;
146 }
147
148 switch (type) {
149 case T_OPEN:
150 case T_CREATE:
151 /*
152 * A file descriptor is returned as response for
153 * T_OPEN,T_CREATE on success
154 */
155 if (v9fs_receivefd(proxy->sockfd, &retval) < 0) {
156 goto close_error;
157 }
158 break;
159 }
160
161err_out:
162 qemu_mutex_unlock(&proxy->mutex);
163 return retval;
164
165close_error:
166 close(proxy->sockfd);
167 proxy->sockfd = -1;
168 qemu_mutex_unlock(&proxy->mutex);
169 return -EIO;
170}
171
4c793dda
MK
172static int proxy_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
173{
174 errno = EOPNOTSUPP;
175 return -1;
176}
177
178static ssize_t proxy_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
179 char *buf, size_t bufsz)
180{
181 errno = EOPNOTSUPP;
182 return -1;
183}
184
185static int proxy_close(FsContext *ctx, V9fsFidOpenState *fs)
186{
187 return close(fs->fd);
188}
189
190static int proxy_closedir(FsContext *ctx, V9fsFidOpenState *fs)
191{
192 return closedir(fs->dir);
193}
194
195static int proxy_open(FsContext *ctx, V9fsPath *fs_path,
196 int flags, V9fsFidOpenState *fs)
197{
daf0b9ac
MK
198 fs->fd = v9fs_request(ctx->private, T_OPEN, NULL, "sd", fs_path, flags);
199 if (fs->fd < 0) {
200 errno = -fs->fd;
201 fs->fd = -1;
202 }
4c793dda
MK
203 return fs->fd;
204}
205
206static int proxy_opendir(FsContext *ctx,
207 V9fsPath *fs_path, V9fsFidOpenState *fs)
208{
daf0b9ac
MK
209 int serrno, fd;
210
4c793dda 211 fs->dir = NULL;
daf0b9ac
MK
212 fd = v9fs_request(ctx->private, T_OPEN, NULL, "sd", fs_path, O_DIRECTORY);
213 if (fd < 0) {
214 errno = -fd;
215 return -1;
216 }
217 fs->dir = fdopendir(fd);
218 if (!fs->dir) {
219 serrno = errno;
220 close(fd);
221 errno = serrno;
222 return -1;
223 }
224 return 0;
4c793dda
MK
225}
226
227static void proxy_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
228{
229 return rewinddir(fs->dir);
230}
231
232static off_t proxy_telldir(FsContext *ctx, V9fsFidOpenState *fs)
233{
234 return telldir(fs->dir);
235}
236
237static int proxy_readdir_r(FsContext *ctx, V9fsFidOpenState *fs,
238 struct dirent *entry,
239 struct dirent **result)
240{
241 return readdir_r(fs->dir, entry, result);
242}
243
244static void proxy_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
245{
246 return seekdir(fs->dir, off);
247}
248
249static ssize_t proxy_preadv(FsContext *ctx, V9fsFidOpenState *fs,
250 const struct iovec *iov,
251 int iovcnt, off_t offset)
252{
253#ifdef CONFIG_PREADV
254 return preadv(fs->fd, iov, iovcnt, offset);
255#else
256 int err = lseek(fs->fd, offset, SEEK_SET);
257 if (err == -1) {
258 return err;
259 } else {
260 return readv(fs->fd, iov, iovcnt);
261 }
262#endif
263}
264
265static ssize_t proxy_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
266 const struct iovec *iov,
267 int iovcnt, off_t offset)
268{
269 ssize_t ret;
270
271#ifdef CONFIG_PREADV
272 ret = pwritev(fs->fd, iov, iovcnt, offset);
273#else
274 int err = lseek(fs->fd, offset, SEEK_SET);
275 if (err == -1) {
276 return err;
277 } else {
278 ret = writev(fs->fd, iov, iovcnt);
279 }
280#endif
281#ifdef CONFIG_SYNC_FILE_RANGE
282 if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) {
283 /*
284 * Initiate a writeback. This is not a data integrity sync.
285 * We want to ensure that we don't leave dirty pages in the cache
286 * after write when writeout=immediate is sepcified.
287 */
288 sync_file_range(fs->fd, offset, ret,
289 SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
290 }
291#endif
292 return ret;
293}
294
295static int proxy_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
296{
297 errno = EOPNOTSUPP;
298 return -1;
299}
300
301static int proxy_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
302 const char *name, FsCred *credp)
303{
304 errno = EOPNOTSUPP;
305 return -1;
306}
307
308static int proxy_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
309 const char *name, FsCred *credp)
310{
311 errno = EOPNOTSUPP;
312 return -1;
313}
314
315static int proxy_fstat(FsContext *fs_ctx, int fid_type,
316 V9fsFidOpenState *fs, struct stat *stbuf)
317{
318 int fd;
319
320 if (fid_type == P9_FID_DIR) {
321 fd = dirfd(fs->dir);
322 } else {
323 fd = fs->fd;
324 }
325 return fstat(fd, stbuf);
326}
327
328static int proxy_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
329 int flags, FsCred *credp, V9fsFidOpenState *fs)
330{
daf0b9ac
MK
331 V9fsString fullname;
332
333 v9fs_string_init(&fullname);
334 v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
335
336 fs->fd = v9fs_request(fs_ctx->private, T_CREATE, NULL, "sdddd",
337 &fullname, flags, credp->fc_mode,
338 credp->fc_uid, credp->fc_gid);
339 v9fs_string_free(&fullname);
340 if (fs->fd < 0) {
341 errno = -fs->fd;
342 fs->fd = -1;
343 }
344 return fs->fd;
4c793dda
MK
345}
346
347
348static int proxy_symlink(FsContext *fs_ctx, const char *oldpath,
349 V9fsPath *dir_path, const char *name, FsCred *credp)
350{
351 errno = EOPNOTSUPP;
352 return -1;
353}
354
355static int proxy_link(FsContext *ctx, V9fsPath *oldpath,
356 V9fsPath *dirpath, const char *name)
357{
358 errno = EOPNOTSUPP;
359 return -1;
360}
361
362static int proxy_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
363{
364 errno = EOPNOTSUPP;
365 return -1;
366}
367
368static int proxy_rename(FsContext *ctx, const char *oldpath,
369 const char *newpath)
370{
371 errno = EOPNOTSUPP;
372 return -1;
373}
374
375static int proxy_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
376{
377 errno = EOPNOTSUPP;
378 return -1;
379}
380
381static int proxy_utimensat(FsContext *s, V9fsPath *fs_path,
382 const struct timespec *buf)
383{
384 errno = EOPNOTSUPP;
385 return -1;
386}
387
388static int proxy_remove(FsContext *ctx, const char *path)
389{
390 errno = EOPNOTSUPP;
391 return -1;
392}
393
394static int proxy_fsync(FsContext *ctx, int fid_type,
395 V9fsFidOpenState *fs, int datasync)
396{
397 int fd;
398
399 if (fid_type == P9_FID_DIR) {
400 fd = dirfd(fs->dir);
401 } else {
402 fd = fs->fd;
403 }
404
405 if (datasync) {
406 return qemu_fdatasync(fd);
407 } else {
408 return fsync(fd);
409 }
410}
411
412static int proxy_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
413{
414 errno = EOPNOTSUPP;
415 return -1;
416}
417
418static ssize_t proxy_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
419 const char *name, void *value, size_t size)
420{
421 errno = EOPNOTSUPP;
422 return -1;
423}
424
425static ssize_t proxy_llistxattr(FsContext *ctx, V9fsPath *fs_path,
426 void *value, size_t size)
427{
428 errno = EOPNOTSUPP;
429 return -1;
430}
431
432static int proxy_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
433 void *value, size_t size, int flags)
434{
435 errno = EOPNOTSUPP;
436 return -1;
437}
438
439static int proxy_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
440 const char *name)
441{
442 errno = EOPNOTSUPP;
443 return -1;
444}
445
446static int proxy_name_to_path(FsContext *ctx, V9fsPath *dir_path,
447 const char *name, V9fsPath *target)
448{
449 if (dir_path) {
450 v9fs_string_sprintf((V9fsString *)target, "%s/%s",
451 dir_path->data, name);
452 } else {
453 v9fs_string_sprintf((V9fsString *)target, "%s", name);
454 }
455 /* Bump the size for including terminating NULL */
456 target->size++;
457 return 0;
458}
459
460static int proxy_renameat(FsContext *ctx, V9fsPath *olddir,
461 const char *old_name, V9fsPath *newdir,
462 const char *new_name)
463{
464 int ret;
465 V9fsString old_full_name, new_full_name;
466
467 v9fs_string_init(&old_full_name);
468 v9fs_string_init(&new_full_name);
469
470 v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name);
471 v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name);
472
473 ret = proxy_rename(ctx, old_full_name.data, new_full_name.data);
474 v9fs_string_free(&old_full_name);
475 v9fs_string_free(&new_full_name);
476 return ret;
477}
478
479static int proxy_unlinkat(FsContext *ctx, V9fsPath *dir,
480 const char *name, int flags)
481{
482 int ret;
483 V9fsString fullname;
484 v9fs_string_init(&fullname);
485
486 v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name);
487 ret = proxy_remove(ctx, fullname.data);
488 v9fs_string_free(&fullname);
489
490 return ret;
491}
492
493static int proxy_parse_opts(QemuOpts *opts, struct FsDriverEntry *fs)
494{
495 const char *sock_fd = qemu_opt_get(opts, "sock_fd");
496
497 if (sock_fd) {
498 fprintf(stderr, "sock_fd option not specified\n");
499 return -1;
500 }
501 fs->path = g_strdup(sock_fd);
502 return 0;
503}
504
505static int proxy_init(FsContext *ctx)
506{
507 V9fsProxy *proxy = g_malloc(sizeof(V9fsProxy));
508 int sock_id;
509
510 sock_id = atoi(ctx->fs_root);
511 if (sock_id < 0) {
512 fprintf(stderr, "socket descriptor not initialized\n");
513 return -1;
514 }
515 g_free(ctx->fs_root);
516
517 proxy->iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ);
518 proxy->iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ;
519 ctx->private = proxy;
520 proxy->sockfd = sock_id;
521 qemu_mutex_init(&proxy->mutex);
522
523 ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT;
524 return 0;
525}
526
527FileOperations proxy_ops = {
528 .parse_opts = proxy_parse_opts,
529 .init = proxy_init,
530 .lstat = proxy_lstat,
531 .readlink = proxy_readlink,
532 .close = proxy_close,
533 .closedir = proxy_closedir,
534 .open = proxy_open,
535 .opendir = proxy_opendir,
536 .rewinddir = proxy_rewinddir,
537 .telldir = proxy_telldir,
538 .readdir_r = proxy_readdir_r,
539 .seekdir = proxy_seekdir,
540 .preadv = proxy_preadv,
541 .pwritev = proxy_pwritev,
542 .chmod = proxy_chmod,
543 .mknod = proxy_mknod,
544 .mkdir = proxy_mkdir,
545 .fstat = proxy_fstat,
546 .open2 = proxy_open2,
547 .symlink = proxy_symlink,
548 .link = proxy_link,
549 .truncate = proxy_truncate,
550 .rename = proxy_rename,
551 .chown = proxy_chown,
552 .utimensat = proxy_utimensat,
553 .remove = proxy_remove,
554 .fsync = proxy_fsync,
555 .statfs = proxy_statfs,
556 .lgetxattr = proxy_lgetxattr,
557 .llistxattr = proxy_llistxattr,
558 .lsetxattr = proxy_lsetxattr,
559 .lremovexattr = proxy_lremovexattr,
560 .name_to_path = proxy_name_to_path,
561 .renameat = proxy_renameat,
562 .unlinkat = proxy_unlinkat,
563};