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>
25 static int local_lstat(FsContext
*fs_ctx
, const char *path
, struct stat
*stbuf
)
28 char buffer
[PATH_MAX
];
29 err
= lstat(rpath(fs_ctx
, path
, buffer
), stbuf
);
33 if (fs_ctx
->fs_sm
== SM_MAPPED
) {
34 /* Actual credentials are part of extended attrs */
39 if (getxattr(rpath(fs_ctx
, path
, buffer
), "user.virtfs.uid", &tmp_uid
,
41 stbuf
->st_uid
= tmp_uid
;
43 if (getxattr(rpath(fs_ctx
, path
, buffer
), "user.virtfs.gid", &tmp_gid
,
45 stbuf
->st_gid
= tmp_gid
;
47 if (getxattr(rpath(fs_ctx
, path
, buffer
), "user.virtfs.mode",
48 &tmp_mode
, sizeof(mode_t
)) > 0) {
49 stbuf
->st_mode
= tmp_mode
;
51 if (getxattr(rpath(fs_ctx
, path
, buffer
), "user.virtfs.rdev", &tmp_dev
,
53 stbuf
->st_rdev
= tmp_dev
;
59 static int local_set_xattr(const char *path
, FsCred
*credp
)
62 if (credp
->fc_uid
!= -1) {
63 err
= setxattr(path
, "user.virtfs.uid", &credp
->fc_uid
, sizeof(uid_t
),
69 if (credp
->fc_gid
!= -1) {
70 err
= setxattr(path
, "user.virtfs.gid", &credp
->fc_gid
, sizeof(gid_t
),
76 if (credp
->fc_mode
!= -1) {
77 err
= setxattr(path
, "user.virtfs.mode", &credp
->fc_mode
,
83 if (credp
->fc_rdev
!= -1) {
84 err
= setxattr(path
, "user.virtfs.rdev", &credp
->fc_rdev
,
93 static int local_post_create_passthrough(FsContext
*fs_ctx
, const char *path
,
96 char buffer
[PATH_MAX
];
97 if (chmod(rpath(fs_ctx
, path
, buffer
), credp
->fc_mode
& 07777) < 0) {
100 if (lchown(rpath(fs_ctx
, path
, buffer
), credp
->fc_uid
,
101 credp
->fc_gid
) < 0) {
103 * If we fail to change ownership and if we are
104 * using security model none. Ignore the error
106 if (fs_ctx
->fs_sm
!= SM_NONE
) {
113 static ssize_t
local_readlink(FsContext
*fs_ctx
, const char *path
,
114 char *buf
, size_t bufsz
)
117 char buffer
[PATH_MAX
];
118 if (fs_ctx
->fs_sm
== SM_MAPPED
) {
120 fd
= open(rpath(fs_ctx
, path
, buffer
), O_RDONLY
);
125 tsize
= read(fd
, (void *)buf
, bufsz
);
126 } while (tsize
== -1 && errno
== EINTR
);
129 } else if ((fs_ctx
->fs_sm
== SM_PASSTHROUGH
) ||
130 (fs_ctx
->fs_sm
== SM_NONE
)) {
131 tsize
= readlink(rpath(fs_ctx
, path
, buffer
), buf
, bufsz
);
136 static int local_close(FsContext
*ctx
, int fd
)
141 static int local_closedir(FsContext
*ctx
, DIR *dir
)
143 return closedir(dir
);
146 static int local_open(FsContext
*ctx
, const char *path
, int flags
)
148 char buffer
[PATH_MAX
];
149 return open(rpath(ctx
, path
, buffer
), flags
);
152 static DIR *local_opendir(FsContext
*ctx
, const char *path
)
154 char buffer
[PATH_MAX
];
155 return opendir(rpath(ctx
, path
, buffer
));
158 static void local_rewinddir(FsContext
*ctx
, DIR *dir
)
160 return rewinddir(dir
);
163 static off_t
local_telldir(FsContext
*ctx
, DIR *dir
)
168 static struct dirent
*local_readdir(FsContext
*ctx
, DIR *dir
)
173 static void local_seekdir(FsContext
*ctx
, DIR *dir
, off_t off
)
175 return seekdir(dir
, off
);
178 static ssize_t
local_preadv(FsContext
*ctx
, int fd
, const struct iovec
*iov
,
179 int iovcnt
, off_t offset
)
182 return preadv(fd
, iov
, iovcnt
, offset
);
184 int err
= lseek(fd
, offset
, SEEK_SET
);
188 return readv(fd
, iov
, iovcnt
);
193 static ssize_t
local_pwritev(FsContext
*ctx
, int fd
, const struct iovec
*iov
,
194 int iovcnt
, off_t offset
)
197 return pwritev(fd
, iov
, iovcnt
, offset
);
199 int err
= lseek(fd
, offset
, SEEK_SET
);
203 return writev(fd
, iov
, iovcnt
);
208 static int local_chmod(FsContext
*fs_ctx
, const char *path
, FsCred
*credp
)
210 char buffer
[PATH_MAX
];
211 if (fs_ctx
->fs_sm
== SM_MAPPED
) {
212 return local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
213 } else if ((fs_ctx
->fs_sm
== SM_PASSTHROUGH
) ||
214 (fs_ctx
->fs_sm
== SM_NONE
)) {
215 return chmod(rpath(fs_ctx
, path
, buffer
), credp
->fc_mode
);
220 static int local_mknod(FsContext
*fs_ctx
, const char *path
, FsCred
*credp
)
224 char buffer
[PATH_MAX
];
226 /* Determine the security model */
227 if (fs_ctx
->fs_sm
== SM_MAPPED
) {
228 err
= mknod(rpath(fs_ctx
, path
, buffer
),
229 SM_LOCAL_MODE_BITS
|S_IFREG
, 0);
233 local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
238 } else if ((fs_ctx
->fs_sm
== SM_PASSTHROUGH
) ||
239 (fs_ctx
->fs_sm
== SM_NONE
)) {
240 err
= mknod(rpath(fs_ctx
, path
, buffer
), credp
->fc_mode
,
245 err
= local_post_create_passthrough(fs_ctx
, path
, credp
);
254 remove(rpath(fs_ctx
, path
, buffer
));
259 static int local_mkdir(FsContext
*fs_ctx
, const char *path
, FsCred
*credp
)
263 char buffer
[PATH_MAX
];
265 /* Determine the security model */
266 if (fs_ctx
->fs_sm
== SM_MAPPED
) {
267 err
= mkdir(rpath(fs_ctx
, path
, buffer
), SM_LOCAL_DIR_MODE_BITS
);
271 credp
->fc_mode
= credp
->fc_mode
|S_IFDIR
;
272 err
= local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
277 } else if ((fs_ctx
->fs_sm
== SM_PASSTHROUGH
) ||
278 (fs_ctx
->fs_sm
== SM_NONE
)) {
279 err
= mkdir(rpath(fs_ctx
, path
, buffer
), credp
->fc_mode
);
283 err
= local_post_create_passthrough(fs_ctx
, path
, credp
);
292 remove(rpath(fs_ctx
, path
, buffer
));
297 static int local_fstat(FsContext
*fs_ctx
, int fd
, struct stat
*stbuf
)
300 err
= fstat(fd
, stbuf
);
304 if (fs_ctx
->fs_sm
== SM_MAPPED
) {
305 /* Actual credentials are part of extended attrs */
311 if (fgetxattr(fd
, "user.virtfs.uid", &tmp_uid
, sizeof(uid_t
)) > 0) {
312 stbuf
->st_uid
= tmp_uid
;
314 if (fgetxattr(fd
, "user.virtfs.gid", &tmp_gid
, sizeof(gid_t
)) > 0) {
315 stbuf
->st_gid
= tmp_gid
;
317 if (fgetxattr(fd
, "user.virtfs.mode", &tmp_mode
, sizeof(mode_t
)) > 0) {
318 stbuf
->st_mode
= tmp_mode
;
320 if (fgetxattr(fd
, "user.virtfs.rdev", &tmp_dev
, sizeof(dev_t
)) > 0) {
321 stbuf
->st_rdev
= tmp_dev
;
327 static int local_open2(FsContext
*fs_ctx
, const char *path
, int flags
,
333 char buffer
[PATH_MAX
];
335 /* Determine the security model */
336 if (fs_ctx
->fs_sm
== SM_MAPPED
) {
337 fd
= open(rpath(fs_ctx
, path
, buffer
), flags
, SM_LOCAL_MODE_BITS
);
341 credp
->fc_mode
= credp
->fc_mode
|S_IFREG
;
342 /* Set cleint credentials in xattr */
343 err
= local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
348 } else if ((fs_ctx
->fs_sm
== SM_PASSTHROUGH
) ||
349 (fs_ctx
->fs_sm
== SM_NONE
)) {
350 fd
= open(rpath(fs_ctx
, path
, buffer
), flags
, credp
->fc_mode
);
354 err
= local_post_create_passthrough(fs_ctx
, path
, credp
);
364 remove(rpath(fs_ctx
, path
, buffer
));
370 static int local_symlink(FsContext
*fs_ctx
, const char *oldpath
,
371 const char *newpath
, FsCred
*credp
)
375 char buffer
[PATH_MAX
];
377 /* Determine the security model */
378 if (fs_ctx
->fs_sm
== SM_MAPPED
) {
380 ssize_t oldpath_size
, write_size
;
381 fd
= open(rpath(fs_ctx
, newpath
, buffer
), O_CREAT
|O_EXCL
|O_RDWR
,
386 /* Write the oldpath (target) to the file. */
387 oldpath_size
= strlen(oldpath
);
389 write_size
= write(fd
, (void *)oldpath
, oldpath_size
);
390 } while (write_size
== -1 && errno
== EINTR
);
392 if (write_size
!= oldpath_size
) {
399 /* Set cleint credentials in symlink's xattr */
400 credp
->fc_mode
= credp
->fc_mode
|S_IFLNK
;
401 err
= local_set_xattr(rpath(fs_ctx
, newpath
, buffer
), credp
);
406 } else if ((fs_ctx
->fs_sm
== SM_PASSTHROUGH
) ||
407 (fs_ctx
->fs_sm
== SM_NONE
)) {
408 err
= symlink(oldpath
, rpath(fs_ctx
, newpath
, buffer
));
412 err
= lchown(rpath(fs_ctx
, newpath
, buffer
), credp
->fc_uid
,
416 * If we fail to change ownership and if we are
417 * using security model none. Ignore the error
419 if (fs_ctx
->fs_sm
!= SM_NONE
) {
429 remove(rpath(fs_ctx
, newpath
, buffer
));
434 static int local_link(FsContext
*ctx
, const char *oldpath
, const char *newpath
)
436 char buffer
[PATH_MAX
], buffer1
[PATH_MAX
];
438 return link(rpath(ctx
, oldpath
, buffer
), rpath(ctx
, newpath
, buffer1
));
441 static int local_truncate(FsContext
*ctx
, const char *path
, off_t size
)
443 char buffer
[PATH_MAX
];
444 return truncate(rpath(ctx
, path
, buffer
), size
);
447 static int local_rename(FsContext
*ctx
, const char *oldpath
,
450 char buffer
[PATH_MAX
], buffer1
[PATH_MAX
];
452 return rename(rpath(ctx
, oldpath
, buffer
), rpath(ctx
, newpath
, buffer1
));
455 static int local_chown(FsContext
*fs_ctx
, const char *path
, FsCred
*credp
)
457 char buffer
[PATH_MAX
];
458 if ((credp
->fc_uid
== -1 && credp
->fc_gid
== -1) ||
459 (fs_ctx
->fs_sm
== SM_PASSTHROUGH
)) {
460 return lchown(rpath(fs_ctx
, path
, buffer
), credp
->fc_uid
,
462 } else if (fs_ctx
->fs_sm
== SM_MAPPED
) {
463 return local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
464 } else if ((fs_ctx
->fs_sm
== SM_PASSTHROUGH
) ||
465 (fs_ctx
->fs_sm
== SM_NONE
)) {
466 return lchown(rpath(fs_ctx
, path
, buffer
), credp
->fc_uid
,
472 static int local_utimensat(FsContext
*s
, const char *path
,
473 const struct timespec
*buf
)
475 char buffer
[PATH_MAX
];
476 return qemu_utimensat(AT_FDCWD
, rpath(s
, path
, buffer
), buf
,
477 AT_SYMLINK_NOFOLLOW
);
480 static int local_remove(FsContext
*ctx
, const char *path
)
482 char buffer
[PATH_MAX
];
483 return remove(rpath(ctx
, path
, buffer
));
486 static int local_fsync(FsContext
*ctx
, int fd
, int datasync
)
489 return qemu_fdatasync(fd
);
495 static int local_statfs(FsContext
*s
, const char *path
, struct statfs
*stbuf
)
497 char buffer
[PATH_MAX
];
498 return statfs(rpath(s
, path
, buffer
), stbuf
);
501 static ssize_t
local_lgetxattr(FsContext
*ctx
, const char *path
,
502 const char *name
, void *value
, size_t size
)
504 return v9fs_get_xattr(ctx
, path
, name
, value
, size
);
507 static ssize_t
local_llistxattr(FsContext
*ctx
, const char *path
,
508 void *value
, size_t size
)
510 return v9fs_list_xattr(ctx
, path
, value
, size
);
513 static int local_lsetxattr(FsContext
*ctx
, const char *path
, const char *name
,
514 void *value
, size_t size
, int flags
)
516 return v9fs_set_xattr(ctx
, path
, name
, value
, size
, flags
);
519 static int local_lremovexattr(FsContext
*ctx
,
520 const char *path
, const char *name
)
522 return v9fs_remove_xattr(ctx
, path
, name
);
526 FileOperations local_ops
= {
527 .lstat
= local_lstat
,
528 .readlink
= local_readlink
,
529 .close
= local_close
,
530 .closedir
= local_closedir
,
532 .opendir
= local_opendir
,
533 .rewinddir
= local_rewinddir
,
534 .telldir
= local_telldir
,
535 .readdir
= local_readdir
,
536 .seekdir
= local_seekdir
,
537 .preadv
= local_preadv
,
538 .pwritev
= local_pwritev
,
539 .chmod
= local_chmod
,
540 .mknod
= local_mknod
,
541 .mkdir
= local_mkdir
,
542 .fstat
= local_fstat
,
543 .open2
= local_open2
,
544 .symlink
= local_symlink
,
546 .truncate
= local_truncate
,
547 .rename
= local_rename
,
548 .chown
= local_chown
,
549 .utimensat
= local_utimensat
,
550 .remove
= local_remove
,
551 .fsync
= local_fsync
,
552 .statfs
= local_statfs
,
553 .lgetxattr
= local_lgetxattr
,
554 .llistxattr
= local_llistxattr
,
555 .lsetxattr
= local_lsetxattr
,
556 .lremovexattr
= local_lremovexattr
,