2 * Virtio 9p Posix callback
4 * Copyright IBM, Corp. 2010
7 * Anthony Liguori <aliguori@us.ibm.com>
9 * This work is licensed under the terms of the GNU GPL, version 2. See
10 * the COPYING file in the top-level directory.
14 #include "hw/virtio.h"
15 #include "virtio-9p.h"
16 #include "virtio-9p-xattr.h"
17 #include <arpa/inet.h>
20 #include <sys/socket.h>
22 #include <attr/xattr.h>
24 static int local_lstat(FsContext
*fs_ctx
, V9fsPath
*fs_path
, struct stat
*stbuf
)
27 char buffer
[PATH_MAX
];
28 char *path
= fs_path
->data
;
30 err
= lstat(rpath(fs_ctx
, path
, buffer
), stbuf
);
34 if (fs_ctx
->fs_sm
== SM_MAPPED
) {
35 /* Actual credentials are part of extended attrs */
40 if (getxattr(rpath(fs_ctx
, path
, buffer
), "user.virtfs.uid", &tmp_uid
,
42 stbuf
->st_uid
= tmp_uid
;
44 if (getxattr(rpath(fs_ctx
, path
, buffer
), "user.virtfs.gid", &tmp_gid
,
46 stbuf
->st_gid
= tmp_gid
;
48 if (getxattr(rpath(fs_ctx
, path
, buffer
), "user.virtfs.mode",
49 &tmp_mode
, sizeof(mode_t
)) > 0) {
50 stbuf
->st_mode
= tmp_mode
;
52 if (getxattr(rpath(fs_ctx
, path
, buffer
), "user.virtfs.rdev", &tmp_dev
,
54 stbuf
->st_rdev
= tmp_dev
;
60 static int local_set_xattr(const char *path
, FsCred
*credp
)
64 if (credp
->fc_uid
!= -1) {
65 err
= setxattr(path
, "user.virtfs.uid", &credp
->fc_uid
, sizeof(uid_t
),
71 if (credp
->fc_gid
!= -1) {
72 err
= setxattr(path
, "user.virtfs.gid", &credp
->fc_gid
, sizeof(gid_t
),
78 if (credp
->fc_mode
!= -1) {
79 err
= setxattr(path
, "user.virtfs.mode", &credp
->fc_mode
,
85 if (credp
->fc_rdev
!= -1) {
86 err
= setxattr(path
, "user.virtfs.rdev", &credp
->fc_rdev
,
95 static int local_post_create_passthrough(FsContext
*fs_ctx
, const char *path
,
98 char buffer
[PATH_MAX
];
100 if (chmod(rpath(fs_ctx
, path
, buffer
), credp
->fc_mode
& 07777) < 0) {
103 if (lchown(rpath(fs_ctx
, path
, buffer
), credp
->fc_uid
,
104 credp
->fc_gid
) < 0) {
106 * If we fail to change ownership and if we are
107 * using security model none. Ignore the error
109 if (fs_ctx
->fs_sm
!= SM_NONE
) {
116 static ssize_t
local_readlink(FsContext
*fs_ctx
, V9fsPath
*fs_path
,
117 char *buf
, size_t bufsz
)
120 char buffer
[PATH_MAX
];
121 char *path
= fs_path
->data
;
123 if (fs_ctx
->fs_sm
== SM_MAPPED
) {
125 fd
= open(rpath(fs_ctx
, path
, buffer
), O_RDONLY
);
130 tsize
= read(fd
, (void *)buf
, bufsz
);
131 } while (tsize
== -1 && errno
== EINTR
);
134 } else if ((fs_ctx
->fs_sm
== SM_PASSTHROUGH
) ||
135 (fs_ctx
->fs_sm
== SM_NONE
)) {
136 tsize
= readlink(rpath(fs_ctx
, path
, buffer
), buf
, bufsz
);
141 static int local_close(FsContext
*ctx
, int fd
)
146 static int local_closedir(FsContext
*ctx
, DIR *dir
)
148 return closedir(dir
);
151 static int local_open(FsContext
*ctx
, V9fsPath
*fs_path
, int flags
)
153 char buffer
[PATH_MAX
];
154 char *path
= fs_path
->data
;
156 return open(rpath(ctx
, path
, buffer
), flags
);
159 static DIR *local_opendir(FsContext
*ctx
, V9fsPath
*fs_path
)
161 char buffer
[PATH_MAX
];
162 char *path
= fs_path
->data
;
164 return opendir(rpath(ctx
, path
, buffer
));
167 static void local_rewinddir(FsContext
*ctx
, DIR *dir
)
169 return rewinddir(dir
);
172 static off_t
local_telldir(FsContext
*ctx
, DIR *dir
)
177 static int local_readdir_r(FsContext
*ctx
, DIR *dir
, struct dirent
*entry
,
178 struct dirent
**result
)
180 return readdir_r(dir
, entry
, result
);
183 static void local_seekdir(FsContext
*ctx
, DIR *dir
, off_t off
)
185 return seekdir(dir
, off
);
188 static ssize_t
local_preadv(FsContext
*ctx
, int fd
, const struct iovec
*iov
,
189 int iovcnt
, off_t offset
)
192 return preadv(fd
, iov
, iovcnt
, offset
);
194 int err
= lseek(fd
, offset
, SEEK_SET
);
198 return readv(fd
, iov
, iovcnt
);
203 static ssize_t
local_pwritev(FsContext
*ctx
, int fd
, const struct iovec
*iov
,
204 int iovcnt
, off_t offset
)
209 ret
= pwritev(fd
, iov
, iovcnt
, offset
);
211 int err
= lseek(fd
, offset
, SEEK_SET
);
215 ret
= writev(fd
, iov
, iovcnt
);
218 #ifdef CONFIG_SYNC_FILE_RANGE
219 if (ret
> 0 && ctx
->export_flags
& V9FS_IMMEDIATE_WRITEOUT
) {
221 * Initiate a writeback. This is not a data integrity sync.
222 * We want to ensure that we don't leave dirty pages in the cache
223 * after write when writeout=immediate is sepcified.
225 sync_file_range(fd
, offset
, ret
,
226 SYNC_FILE_RANGE_WAIT_BEFORE
| SYNC_FILE_RANGE_WRITE
);
232 static int local_chmod(FsContext
*fs_ctx
, V9fsPath
*fs_path
, FsCred
*credp
)
234 char buffer
[PATH_MAX
];
235 char *path
= fs_path
->data
;
237 if (fs_ctx
->fs_sm
== SM_MAPPED
) {
238 return local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
239 } else if ((fs_ctx
->fs_sm
== SM_PASSTHROUGH
) ||
240 (fs_ctx
->fs_sm
== SM_NONE
)) {
241 return chmod(rpath(fs_ctx
, path
, buffer
), credp
->fc_mode
);
246 static int local_mknod(FsContext
*fs_ctx
, V9fsPath
*dir_path
,
247 const char *name
, FsCred
*credp
)
253 char buffer
[PATH_MAX
];
255 v9fs_string_init(&fullname
);
256 v9fs_string_sprintf(&fullname
, "%s/%s", dir_path
->data
, name
);
257 path
= fullname
.data
;
259 /* Determine the security model */
260 if (fs_ctx
->fs_sm
== SM_MAPPED
) {
261 err
= mknod(rpath(fs_ctx
, path
, buffer
),
262 SM_LOCAL_MODE_BITS
|S_IFREG
, 0);
266 local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
271 } else if ((fs_ctx
->fs_sm
== SM_PASSTHROUGH
) ||
272 (fs_ctx
->fs_sm
== SM_NONE
)) {
273 err
= mknod(rpath(fs_ctx
, path
, buffer
), credp
->fc_mode
,
278 err
= local_post_create_passthrough(fs_ctx
, path
, credp
);
287 remove(rpath(fs_ctx
, path
, buffer
));
290 v9fs_string_free(&fullname
);
294 static int local_mkdir(FsContext
*fs_ctx
, V9fsPath
*dir_path
,
295 const char *name
, FsCred
*credp
)
301 char buffer
[PATH_MAX
];
303 v9fs_string_init(&fullname
);
304 v9fs_string_sprintf(&fullname
, "%s/%s", dir_path
->data
, name
);
305 path
= fullname
.data
;
307 /* Determine the security model */
308 if (fs_ctx
->fs_sm
== SM_MAPPED
) {
309 err
= mkdir(rpath(fs_ctx
, path
, buffer
), SM_LOCAL_DIR_MODE_BITS
);
313 credp
->fc_mode
= credp
->fc_mode
|S_IFDIR
;
314 err
= local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
319 } else if ((fs_ctx
->fs_sm
== SM_PASSTHROUGH
) ||
320 (fs_ctx
->fs_sm
== SM_NONE
)) {
321 err
= mkdir(rpath(fs_ctx
, path
, buffer
), credp
->fc_mode
);
325 err
= local_post_create_passthrough(fs_ctx
, path
, credp
);
334 remove(rpath(fs_ctx
, path
, buffer
));
337 v9fs_string_free(&fullname
);
341 static int local_fstat(FsContext
*fs_ctx
, int fd
, struct stat
*stbuf
)
344 err
= fstat(fd
, stbuf
);
348 if (fs_ctx
->fs_sm
== SM_MAPPED
) {
349 /* Actual credentials are part of extended attrs */
355 if (fgetxattr(fd
, "user.virtfs.uid", &tmp_uid
, sizeof(uid_t
)) > 0) {
356 stbuf
->st_uid
= tmp_uid
;
358 if (fgetxattr(fd
, "user.virtfs.gid", &tmp_gid
, sizeof(gid_t
)) > 0) {
359 stbuf
->st_gid
= tmp_gid
;
361 if (fgetxattr(fd
, "user.virtfs.mode", &tmp_mode
, sizeof(mode_t
)) > 0) {
362 stbuf
->st_mode
= tmp_mode
;
364 if (fgetxattr(fd
, "user.virtfs.rdev", &tmp_dev
, sizeof(dev_t
)) > 0) {
365 stbuf
->st_rdev
= tmp_dev
;
371 static int local_open2(FsContext
*fs_ctx
, V9fsPath
*dir_path
, const char *name
,
372 int flags
, FsCred
*credp
)
379 char buffer
[PATH_MAX
];
381 v9fs_string_init(&fullname
);
382 v9fs_string_sprintf(&fullname
, "%s/%s", dir_path
->data
, name
);
383 path
= fullname
.data
;
385 /* Determine the security model */
386 if (fs_ctx
->fs_sm
== SM_MAPPED
) {
387 fd
= open(rpath(fs_ctx
, path
, buffer
), flags
, SM_LOCAL_MODE_BITS
);
392 credp
->fc_mode
= credp
->fc_mode
|S_IFREG
;
393 /* Set cleint credentials in xattr */
394 err
= local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
399 } else if ((fs_ctx
->fs_sm
== SM_PASSTHROUGH
) ||
400 (fs_ctx
->fs_sm
== SM_NONE
)) {
401 fd
= open(rpath(fs_ctx
, path
, buffer
), flags
, credp
->fc_mode
);
406 err
= local_post_create_passthrough(fs_ctx
, path
, credp
);
417 remove(rpath(fs_ctx
, path
, buffer
));
420 v9fs_string_free(&fullname
);
425 static int local_symlink(FsContext
*fs_ctx
, const char *oldpath
,
426 V9fsPath
*dir_path
, const char *name
, FsCred
*credp
)
432 char buffer
[PATH_MAX
];
434 v9fs_string_init(&fullname
);
435 v9fs_string_sprintf(&fullname
, "%s/%s", dir_path
->data
, name
);
436 newpath
= fullname
.data
;
438 /* Determine the security model */
439 if (fs_ctx
->fs_sm
== SM_MAPPED
) {
441 ssize_t oldpath_size
, write_size
;
442 fd
= open(rpath(fs_ctx
, newpath
, buffer
), O_CREAT
|O_EXCL
|O_RDWR
,
448 /* Write the oldpath (target) to the file. */
449 oldpath_size
= strlen(oldpath
);
451 write_size
= write(fd
, (void *)oldpath
, oldpath_size
);
452 } while (write_size
== -1 && errno
== EINTR
);
454 if (write_size
!= oldpath_size
) {
461 /* Set cleint credentials in symlink's xattr */
462 credp
->fc_mode
= credp
->fc_mode
|S_IFLNK
;
463 err
= local_set_xattr(rpath(fs_ctx
, newpath
, buffer
), credp
);
468 } else if ((fs_ctx
->fs_sm
== SM_PASSTHROUGH
) ||
469 (fs_ctx
->fs_sm
== SM_NONE
)) {
470 err
= symlink(oldpath
, rpath(fs_ctx
, newpath
, buffer
));
474 err
= lchown(rpath(fs_ctx
, newpath
, buffer
), credp
->fc_uid
,
478 * If we fail to change ownership and if we are
479 * using security model none. Ignore the error
481 if (fs_ctx
->fs_sm
!= SM_NONE
) {
491 remove(rpath(fs_ctx
, newpath
, buffer
));
494 v9fs_string_free(&fullname
);
498 static int local_link(FsContext
*ctx
, V9fsPath
*oldpath
,
499 V9fsPath
*dirpath
, const char *name
)
503 char buffer
[PATH_MAX
], buffer1
[PATH_MAX
];
505 v9fs_string_init(&newpath
);
506 v9fs_string_sprintf(&newpath
, "%s/%s", dirpath
->data
, name
);
508 ret
= link(rpath(ctx
, oldpath
->data
, buffer
),
509 rpath(ctx
, newpath
.data
, buffer1
));
510 v9fs_string_free(&newpath
);
514 static int local_truncate(FsContext
*ctx
, V9fsPath
*fs_path
, off_t size
)
516 char buffer
[PATH_MAX
];
517 char *path
= fs_path
->data
;
519 return truncate(rpath(ctx
, path
, buffer
), size
);
522 static int local_rename(FsContext
*ctx
, const char *oldpath
,
525 char buffer
[PATH_MAX
], buffer1
[PATH_MAX
];
527 return rename(rpath(ctx
, oldpath
, buffer
), rpath(ctx
, newpath
, buffer1
));
530 static int local_chown(FsContext
*fs_ctx
, V9fsPath
*fs_path
, FsCred
*credp
)
532 char buffer
[PATH_MAX
];
533 char *path
= fs_path
->data
;
535 if ((credp
->fc_uid
== -1 && credp
->fc_gid
== -1) ||
536 (fs_ctx
->fs_sm
== SM_PASSTHROUGH
)) {
537 return lchown(rpath(fs_ctx
, path
, buffer
), credp
->fc_uid
,
539 } else if (fs_ctx
->fs_sm
== SM_MAPPED
) {
540 return local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
541 } else if ((fs_ctx
->fs_sm
== SM_PASSTHROUGH
) ||
542 (fs_ctx
->fs_sm
== SM_NONE
)) {
543 return lchown(rpath(fs_ctx
, path
, buffer
), credp
->fc_uid
,
549 static int local_utimensat(FsContext
*s
, V9fsPath
*fs_path
,
550 const struct timespec
*buf
)
552 char buffer
[PATH_MAX
];
553 char *path
= fs_path
->data
;
555 return qemu_utimensat(AT_FDCWD
, rpath(s
, path
, buffer
), buf
,
556 AT_SYMLINK_NOFOLLOW
);
559 static int local_remove(FsContext
*ctx
, const char *path
)
561 char buffer
[PATH_MAX
];
562 return remove(rpath(ctx
, path
, buffer
));
565 static int local_fsync(FsContext
*ctx
, int fd
, int datasync
)
568 return qemu_fdatasync(fd
);
574 static int local_statfs(FsContext
*s
, V9fsPath
*fs_path
, struct statfs
*stbuf
)
576 char buffer
[PATH_MAX
];
577 char *path
= fs_path
->data
;
579 return statfs(rpath(s
, path
, buffer
), stbuf
);
582 static ssize_t
local_lgetxattr(FsContext
*ctx
, V9fsPath
*fs_path
,
583 const char *name
, void *value
, size_t size
)
585 char *path
= fs_path
->data
;
587 return v9fs_get_xattr(ctx
, path
, name
, value
, size
);
590 static ssize_t
local_llistxattr(FsContext
*ctx
, V9fsPath
*fs_path
,
591 void *value
, size_t size
)
593 char *path
= fs_path
->data
;
595 return v9fs_list_xattr(ctx
, path
, value
, size
);
598 static int local_lsetxattr(FsContext
*ctx
, V9fsPath
*fs_path
, const char *name
,
599 void *value
, size_t size
, int flags
)
601 char *path
= fs_path
->data
;
603 return v9fs_set_xattr(ctx
, path
, name
, value
, size
, flags
);
606 static int local_lremovexattr(FsContext
*ctx
, V9fsPath
*fs_path
,
609 char *path
= fs_path
->data
;
611 return v9fs_remove_xattr(ctx
, path
, name
);
614 static int local_name_to_path(FsContext
*ctx
, V9fsPath
*dir_path
,
615 const char *name
, V9fsPath
*target
)
618 v9fs_string_sprintf((V9fsString
*)target
, "%s/%s",
619 dir_path
->data
, name
);
621 v9fs_string_sprintf((V9fsString
*)target
, "%s", name
);
623 /* Bump the size for including terminating NULL */
628 static int local_renameat(FsContext
*ctx
, V9fsPath
*olddir
,
629 const char *old_name
, V9fsPath
*newdir
,
630 const char *new_name
)
633 V9fsString old_full_name
, new_full_name
;
635 v9fs_string_init(&old_full_name
);
636 v9fs_string_init(&new_full_name
);
638 v9fs_string_sprintf(&old_full_name
, "%s/%s", olddir
->data
, old_name
);
639 v9fs_string_sprintf(&new_full_name
, "%s/%s", newdir
->data
, new_name
);
641 ret
= local_rename(ctx
, old_full_name
.data
, new_full_name
.data
);
642 v9fs_string_free(&old_full_name
);
643 v9fs_string_free(&new_full_name
);
647 static int local_unlinkat(FsContext
*ctx
, V9fsPath
*dir
,
648 const char *name
, int flags
)
652 char buffer
[PATH_MAX
];
653 v9fs_string_init(&fullname
);
655 v9fs_string_sprintf(&fullname
, "%s/%s", dir
->data
, name
);
656 ret
= remove(rpath(ctx
, fullname
.data
, buffer
));
657 v9fs_string_free(&fullname
);
662 static int local_init(FsContext
*ctx
)
664 ctx
->flags
|= PATHNAME_FSCONTEXT
;
668 FileOperations local_ops
= {
670 .lstat
= local_lstat
,
671 .readlink
= local_readlink
,
672 .close
= local_close
,
673 .closedir
= local_closedir
,
675 .opendir
= local_opendir
,
676 .rewinddir
= local_rewinddir
,
677 .telldir
= local_telldir
,
678 .readdir_r
= local_readdir_r
,
679 .seekdir
= local_seekdir
,
680 .preadv
= local_preadv
,
681 .pwritev
= local_pwritev
,
682 .chmod
= local_chmod
,
683 .mknod
= local_mknod
,
684 .mkdir
= local_mkdir
,
685 .fstat
= local_fstat
,
686 .open2
= local_open2
,
687 .symlink
= local_symlink
,
689 .truncate
= local_truncate
,
690 .rename
= local_rename
,
691 .chown
= local_chown
,
692 .utimensat
= local_utimensat
,
693 .remove
= local_remove
,
694 .fsync
= local_fsync
,
695 .statfs
= local_statfs
,
696 .lgetxattr
= local_lgetxattr
,
697 .llistxattr
= local_llistxattr
,
698 .lsetxattr
= local_lsetxattr
,
699 .lremovexattr
= local_lremovexattr
,
700 .name_to_path
= local_name_to_path
,
701 .renameat
= local_renameat
,
702 .unlinkat
= local_unlinkat
,