]> git.proxmox.com Git - mirror_qemu.git/blame - hw/9pfs/virtio-9p-handle.c
hw/9pfs: Add new virtfs option writeout=immediate skip host page cache
[mirror_qemu.git] / hw / 9pfs / virtio-9p-handle.c
CommitLineData
5f542225
AK
1/*
2 * Virtio 9p handle callback
3 *
4 * Copyright IBM, Corp. 2011
5 *
6 * Authors:
7 * Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
8 *
9 * This work is licensed under the terms of the GNU GPL, version 2. See
10 * the COPYING file in the top-level directory.
11 *
12 */
13
14#include "hw/virtio.h"
15#include "virtio-9p.h"
16#include "virtio-9p-xattr.h"
17#include <arpa/inet.h>
18#include <pwd.h>
19#include <grp.h>
20#include <sys/socket.h>
21#include <sys/un.h>
22#include <attr/xattr.h>
23#include <unistd.h>
24
25struct handle_data {
26 int mountfd;
27 int handle_bytes;
28};
29
30#if __GLIBC__ <= 2 && __GLIBC_MINOR__ < 14
31struct file_handle {
32 unsigned int handle_bytes;
33 int handle_type;
34 unsigned char handle[0];
35};
36#endif
37
38#ifndef AT_EMPTY_PATH
39#define AT_EMPTY_PATH 0x1000 /* Allow empty relative pathname */
40#endif
41#ifndef O_PATH
42#define O_PATH 010000000
43#endif
44
45#ifndef __NR_name_to_handle_at
46#if defined(__i386__)
47#define __NR_name_to_handle_at 341
48#define __NR_open_by_handle_at 342
49#elif defined(__x86_64__)
50#define __NR_name_to_handle_at 303
51#define __NR_open_by_handle_at 304
52#endif
53#endif
54
55#ifdef __NR_name_to_handle_at
56static inline int name_to_handle(int dirfd, const char *name,
57 struct file_handle *fh, int *mnt_id, int flags)
58{
59 return syscall(__NR_name_to_handle_at, dirfd, name, fh, mnt_id, flags);
60}
61
62static inline int open_by_handle(int mountfd, const char *fh, int flags)
63{
64 return syscall(__NR_open_by_handle_at, mountfd, fh, flags);
65}
66#else
67static inline int name_to_handle(int dirfd, const char *name,
68 struct file_handle *fh, int *mnt_id, int flags)
69{
70 errno = ENOSYS;
71 return -1;
72}
73
74static inline int open_by_handle(int mountfd, const char *fh, int flags)
75{
76 errno = ENOSYS;
77 return -1;
78}
79#endif
80
81static int handle_update_file_cred(int dirfd, const char *name, FsCred *credp)
82{
83 int fd, ret;
84 fd = openat(dirfd, name, O_NONBLOCK | O_NOFOLLOW);;
85 if (fd < 0) {
86 return fd;
87 }
88 ret = fchmod(fd, credp->fc_mode & 07777);
89 if (ret < 0) {
90 goto err_out;
91 }
92 ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH);
93err_out:
94 close(fd);
95 return ret;
96}
97
98
99static int handle_lstat(FsContext *fs_ctx, V9fsPath *fs_path,
100 struct stat *stbuf)
101{
102 int fd, ret;
103 struct handle_data *data = (struct handle_data *)fs_ctx->private;
104
105 fd = open_by_handle(data->mountfd, fs_path->data, O_PATH);
106 if (fd < 0) {
107 return fd;
108 }
109 ret = fstatat(fd, "", stbuf, AT_EMPTY_PATH);
110 close(fd);
111 return ret;
112}
113
114static ssize_t handle_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
115 char *buf, size_t bufsz)
116{
117 int fd, ret;
118 struct handle_data *data = (struct handle_data *)fs_ctx->private;
119
120 fd = open_by_handle(data->mountfd, fs_path->data, O_PATH);
121 if (fd < 0) {
122 return fd;
123 }
124 ret = readlinkat(fd, "", buf, bufsz);
125 close(fd);
126 return ret;
127}
128
129static int handle_close(FsContext *ctx, int fd)
130{
131 return close(fd);
132}
133
134static int handle_closedir(FsContext *ctx, DIR *dir)
135{
136 return closedir(dir);
137}
138
139static int handle_open(FsContext *ctx, V9fsPath *fs_path, int flags)
140{
141 struct handle_data *data = (struct handle_data *)ctx->private;
142
143 return open_by_handle(data->mountfd, fs_path->data, flags);
144}
145
146static DIR *handle_opendir(FsContext *ctx, V9fsPath *fs_path)
147{
148 int fd;
149 fd = handle_open(ctx, fs_path, O_DIRECTORY);
150 if (fd < 0) {
151 return NULL;
152 }
153 return fdopendir(fd);
154}
155
156static void handle_rewinddir(FsContext *ctx, DIR *dir)
157{
158 return rewinddir(dir);
159}
160
161static off_t handle_telldir(FsContext *ctx, DIR *dir)
162{
163 return telldir(dir);
164}
165
166static int handle_readdir_r(FsContext *ctx, DIR *dir, struct dirent *entry,
167 struct dirent **result)
168{
169 return readdir_r(dir, entry, result);
170}
171
172static void handle_seekdir(FsContext *ctx, DIR *dir, off_t off)
173{
174 return seekdir(dir, off);
175}
176
177static ssize_t handle_preadv(FsContext *ctx, int fd, const struct iovec *iov,
178 int iovcnt, off_t offset)
179{
180#ifdef CONFIG_PREADV
181 return preadv(fd, iov, iovcnt, offset);
182#else
183 int err = lseek(fd, offset, SEEK_SET);
184 if (err == -1) {
185 return err;
186 } else {
187 return readv(fd, iov, iovcnt);
188 }
189#endif
190}
191
192static ssize_t handle_pwritev(FsContext *ctx, int fd, const struct iovec *iov,
193 int iovcnt, off_t offset)
194{
d3ab98e6 195 ssize_t ret;
5f542225 196#ifdef CONFIG_PREADV
d3ab98e6 197 ret = pwritev(fd, iov, iovcnt, offset);
5f542225
AK
198#else
199 int err = lseek(fd, offset, SEEK_SET);
200 if (err == -1) {
201 return err;
202 } else {
d3ab98e6 203 ret = writev(fd, iov, iovcnt);
5f542225
AK
204 }
205#endif
d3ab98e6
AK
206#ifdef CONFIG_SYNC_FILE_RANGE
207 if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) {
208 /*
209 * Initiate a writeback. This is not a data integrity sync.
210 * We want to ensure that we don't leave dirty pages in the cache
211 * after write when writeout=immediate is sepcified.
212 */
213 sync_file_range(fd, offset, ret,
214 SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
215 }
216#endif
217 return ret;
5f542225
AK
218}
219
220static int handle_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
221{
222 int fd, ret;
223 struct handle_data *data = (struct handle_data *)fs_ctx->private;
224
225 fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
226 if (fd < 0) {
227 return fd;
228 }
229 ret = fchmod(fd, credp->fc_mode);
230 close(fd);
231 return ret;
232}
233
234static int handle_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
235 const char *name, FsCred *credp)
236{
237 int dirfd, ret;
238 struct handle_data *data = (struct handle_data *)fs_ctx->private;
239
240 dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
241 if (dirfd < 0) {
242 return dirfd;
243 }
244 ret = mknodat(dirfd, name, credp->fc_mode, credp->fc_rdev);
245 if (!ret) {
246 ret = handle_update_file_cred(dirfd, name, credp);
247 }
248 close(dirfd);
249 return ret;
250}
251
252static int handle_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
253 const char *name, FsCred *credp)
254{
255 int dirfd, ret;
256 struct handle_data *data = (struct handle_data *)fs_ctx->private;
257
258 dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
259 if (dirfd < 0) {
260 return dirfd;
261 }
262 ret = mkdirat(dirfd, name, credp->fc_mode);
263 if (!ret) {
264 ret = handle_update_file_cred(dirfd, name, credp);
265 }
266 close(dirfd);
267 return ret;
268}
269
270static int handle_fstat(FsContext *fs_ctx, int fd, struct stat *stbuf)
271{
272 return fstat(fd, stbuf);
273}
274
275static int handle_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
276 int flags, FsCred *credp)
277{
278 int ret;
279 int dirfd, fd;
280 struct handle_data *data = (struct handle_data *)fs_ctx->private;
281
282 dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
283 if (dirfd < 0) {
284 return dirfd;
285 }
286 fd = openat(dirfd, name, flags | O_NOFOLLOW, credp->fc_mode);
287 if (fd >= 0) {
288 ret = handle_update_file_cred(dirfd, name, credp);
289 if (ret < 0) {
290 close(fd);
291 fd = ret;
292 }
293 }
294 close(dirfd);
295 return fd;
296}
297
298
299static int handle_symlink(FsContext *fs_ctx, const char *oldpath,
300 V9fsPath *dir_path, const char *name, FsCred *credp)
301{
302 int fd, dirfd, ret;
303 struct handle_data *data = (struct handle_data *)fs_ctx->private;
304
305 dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
306 if (dirfd < 0) {
307 return dirfd;
308 }
309 ret = symlinkat(oldpath, dirfd, name);
310 if (!ret) {
311 fd = openat(dirfd, name, O_PATH | O_NOFOLLOW);
312 if (fd < 0) {
313 ret = fd;
314 goto err_out;
315 }
316 ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH);
317 close(fd);
318 }
319err_out:
320 close(dirfd);
321 return ret;
322}
323
324static int handle_link(FsContext *ctx, V9fsPath *oldpath,
325 V9fsPath *dirpath, const char *name)
326{
327 int oldfd, newdirfd, ret;
328 struct handle_data *data = (struct handle_data *)ctx->private;
329
330 oldfd = open_by_handle(data->mountfd, oldpath->data, O_PATH);
331 if (oldfd < 0) {
332 return oldfd;
333 }
334 newdirfd = open_by_handle(data->mountfd, dirpath->data, O_PATH);
335 if (newdirfd < 0) {
336 close(oldfd);
337 return newdirfd;
338 }
339 ret = linkat(oldfd, "", newdirfd, name, AT_EMPTY_PATH);
340 close(newdirfd);
341 close(oldfd);
342 return ret;
343}
344
345static int handle_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
346{
347 int fd, ret;
348 struct handle_data *data = (struct handle_data *)ctx->private;
349
350 fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK | O_WRONLY);
351 if (fd < 0) {
352 return fd;
353 }
354 ret = ftruncate(fd, size);
355 close(fd);
356 return ret;
357}
358
359static int handle_rename(FsContext *ctx, const char *oldpath,
360 const char *newpath)
361{
362 errno = EOPNOTSUPP;
363 return -1;
364}
365
366static int handle_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
367{
368 int fd, ret;
369 struct handle_data *data = (struct handle_data *)fs_ctx->private;
370
371 fd = open_by_handle(data->mountfd, fs_path->data, O_PATH);
372 if (fd < 0) {
373 return fd;
374 }
375 ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH);
376 close(fd);
377 return ret;
378}
379
380static int handle_utimensat(FsContext *ctx, V9fsPath *fs_path,
381 const struct timespec *buf)
382{
383 int fd, ret;
384 struct handle_data *data = (struct handle_data *)ctx->private;
385
386 fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
387 if (fd < 0) {
388 return fd;
389 }
390 ret = futimens(fd, buf);
391 close(fd);
392 return ret;
393}
394
395static int handle_remove(FsContext *ctx, const char *path)
396{
397 errno = EOPNOTSUPP;
398 return -1;
399}
400
401static int handle_fsync(FsContext *ctx, int fd, int datasync)
402{
403 if (datasync) {
404 return qemu_fdatasync(fd);
405 } else {
406 return fsync(fd);
407 }
408}
409
410static int handle_statfs(FsContext *ctx, V9fsPath *fs_path,
411 struct statfs *stbuf)
412{
413 int fd, ret;
414 struct handle_data *data = (struct handle_data *)ctx->private;
415
416 fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
417 if (fd < 0) {
418 return fd;
419 }
420 ret = fstatfs(fd, stbuf);
421 close(fd);
422 return ret;
423}
424
425static ssize_t handle_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
426 const char *name, void *value, size_t size)
427{
428 int fd, ret;
429 struct handle_data *data = (struct handle_data *)ctx->private;
430
431 fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
432 if (fd < 0) {
433 return fd;
434 }
435 ret = fgetxattr(fd, name, value, size);
436 close(fd);
437 return ret;
438}
439
440static ssize_t handle_llistxattr(FsContext *ctx, V9fsPath *fs_path,
441 void *value, size_t size)
442{
443 int fd, ret;
444 struct handle_data *data = (struct handle_data *)ctx->private;
445
446 fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
447 if (fd < 0) {
448 return fd;
449 }
450 ret = flistxattr(fd, value, size);
451 close(fd);
452 return ret;
453}
454
455static int handle_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
456 void *value, size_t size, int flags)
457{
458 int fd, ret;
459 struct handle_data *data = (struct handle_data *)ctx->private;
460
461 fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
462 if (fd < 0) {
463 return fd;
464 }
465 ret = fsetxattr(fd, name, value, size, flags);
466 close(fd);
467 return ret;
468}
469
470static int handle_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
471 const char *name)
472{
473 int fd, ret;
474 struct handle_data *data = (struct handle_data *)ctx->private;
475
476 fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
477 if (fd < 0) {
478 return fd;
479 }
480 ret = fremovexattr(fd, name);
481 close(fd);
482 return ret;
483}
484
485static int handle_name_to_path(FsContext *ctx, V9fsPath *dir_path,
486 const char *name, V9fsPath *target)
487{
488 char buffer[PATH_MAX];
489 struct file_handle *fh;
490 int dirfd, ret, mnt_id;
491 struct handle_data *data = (struct handle_data *)ctx->private;
492
493 /* "." and ".." are not allowed */
494 if (!strcmp(name, ".") || !strcmp(name, "..")) {
495 errno = EINVAL;
496 return -1;
497
498 }
499 if (dir_path) {
500 dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
501 } else {
502 /* relative to export root */
503 dirfd = open(rpath(ctx, ".", buffer), O_DIRECTORY);
504 }
505 if (dirfd < 0) {
506 return dirfd;
507 }
508 fh = g_malloc(sizeof(struct file_handle) + data->handle_bytes);
509 fh->handle_bytes = data->handle_bytes;
510 /* add a "./" at the begining of the path */
511 snprintf(buffer, PATH_MAX, "./%s", name);
512 /* flag = 0 imply don't follow symlink */
513 ret = name_to_handle(dirfd, buffer, fh, &mnt_id, 0);
514 if (!ret) {
515 target->data = (char *)fh;
516 target->size = sizeof(struct file_handle) + data->handle_bytes;
517 } else {
518 g_free(fh);
519 }
520 close(dirfd);
521 return ret;
522}
523
524static int handle_renameat(FsContext *ctx, V9fsPath *olddir,
525 const char *old_name, V9fsPath *newdir,
526 const char *new_name)
527{
528 int olddirfd, newdirfd, ret;
529 struct handle_data *data = (struct handle_data *)ctx->private;
530
531 olddirfd = open_by_handle(data->mountfd, olddir->data, O_PATH);
532 if (olddirfd < 0) {
533 return olddirfd;
534 }
535 newdirfd = open_by_handle(data->mountfd, newdir->data, O_PATH);
536 if (newdirfd < 0) {
537 close(olddirfd);
538 return newdirfd;
539 }
540 ret = renameat(olddirfd, old_name, newdirfd, new_name);
541 close(newdirfd);
542 close(olddirfd);
543 return ret;
544}
545
546static int handle_unlinkat(FsContext *ctx, V9fsPath *dir,
547 const char *name, int flags)
548{
549 int dirfd, ret;
550 struct handle_data *data = (struct handle_data *)ctx->private;
551
552 dirfd = open_by_handle(data->mountfd, dir->data, O_PATH);
553 if (dirfd < 0) {
554 return dirfd;
555 }
556
557 ret = unlinkat(dirfd, name, flags);
558 close(dirfd);
559 return ret;
560}
561
562static int handle_init(FsContext *ctx)
563{
564 int ret, mnt_id;
565 struct file_handle fh;
566 struct handle_data *data = g_malloc(sizeof(struct handle_data));
567 data->mountfd = open(ctx->fs_root, O_DIRECTORY);
568 if (data->mountfd < 0) {
569 ret = data->mountfd;
570 goto err_out;
571 }
572 memset(&fh, 0, sizeof(struct file_handle));
573 ret = name_to_handle(data->mountfd, ".", &fh, &mnt_id, 0);
574 if (ret && errno == EOVERFLOW) {
575 data->handle_bytes = fh.handle_bytes;
576 ctx->private = data;
577 ret = 0;
578 goto out;
579 }
580 /* we got 0 byte handle ? */
581 ret = -1;
582 close(data->mountfd);
583err_out:
584 g_free(data);
585out:
586 return ret;
587}
588
589FileOperations handle_ops = {
590 .init = handle_init,
591 .lstat = handle_lstat,
592 .readlink = handle_readlink,
593 .close = handle_close,
594 .closedir = handle_closedir,
595 .open = handle_open,
596 .opendir = handle_opendir,
597 .rewinddir = handle_rewinddir,
598 .telldir = handle_telldir,
599 .readdir_r = handle_readdir_r,
600 .seekdir = handle_seekdir,
601 .preadv = handle_preadv,
602 .pwritev = handle_pwritev,
603 .chmod = handle_chmod,
604 .mknod = handle_mknod,
605 .mkdir = handle_mkdir,
606 .fstat = handle_fstat,
607 .open2 = handle_open2,
608 .symlink = handle_symlink,
609 .link = handle_link,
610 .truncate = handle_truncate,
611 .rename = handle_rename,
612 .chown = handle_chown,
613 .utimensat = handle_utimensat,
614 .remove = handle_remove,
615 .fsync = handle_fsync,
616 .statfs = handle_statfs,
617 .lgetxattr = handle_lgetxattr,
618 .llistxattr = handle_llistxattr,
619 .lsetxattr = handle_lsetxattr,
620 .lremovexattr = handle_lremovexattr,
621 .name_to_path = handle_name_to_path,
622 .renameat = handle_renameat,
623 .unlinkat = handle_unlinkat,
624};