#ifdef CONFIG_LINUX
# include <sys/vfs.h>
-#endif
-#ifdef CONFIG_DARWIN
+#elif defined(CONFIG_DARWIN) || defined(CONFIG_FREEBSD)
# include <sys/param.h>
+# ifdef CONFIG_FREEBSD
+# undef MACHINE /* work around some unfortunate namespace pollution */
+# endif
# include <sys/mount.h>
#endif
'9p-marshal.c',
'qemu-fsdev.c',
), if_false: files('qemu-fsdev-dummy.c'))
-if host_os in ['linux', 'darwin']
+if host_os in ['linux', 'darwin', 'freebsd']
system_ss.add_all(fsdev_ss)
endif
stbuf->f_bsize = 512;
stbuf->f_blocks = 0;
stbuf->f_files = synth_node_count;
-#ifndef CONFIG_DARWIN
+#if !defined(CONFIG_DARWIN) && !defined(CONFIG_FREEBSD)
stbuf->f_namelen = NAME_MAX;
#endif
return 0;
--- /dev/null
+/*
+ * 9p utilities (FreeBSD Implementation)
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+/*
+ * Not so fast! You might want to read the 9p developer docs first:
+ * https://wiki.qemu.org/Documentation/9p
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/xattr.h"
+#include "9p-util.h"
+
+static int mangle_xattr_name(const char **namep)
+{
+ const char *name = *namep;
+
+ /*
+ * ZFS forbids attributes in starting with "user." or "system.".
+ */
+ if (strncmp(name, "system.", 7) == 0) {
+ *namep = name + 7;
+ return EXTATTR_NAMESPACE_SYSTEM;
+ }
+ if (strncmp(name, "user.", 5) == 0) {
+ *namep = name + 5;
+ }
+ return EXTATTR_NAMESPACE_USER;
+}
+
+ssize_t fgetxattr(int fd, const char *name, void *value, size_t size)
+{
+ int namespace;
+
+ namespace = mangle_xattr_name(&name);
+ return extattr_get_fd(fd, namespace, name, value, size);
+}
+
+ssize_t fgetxattrat_nofollow(int dirfd, const char *filename, const char *name,
+ void *value, size_t size)
+{
+ ssize_t ret;
+ int fd, namespace;
+
+ fd = openat_file(dirfd, filename,
+ O_RDONLY | O_PATH_9P_UTIL | O_NOFOLLOW, 0);
+ if (fd == -1) {
+ return -1;
+ }
+ namespace = mangle_xattr_name(&name);
+ ret = extattr_get_fd(fd, namespace, name, value, size);
+ close_preserve_errno(fd);
+ return ret;
+}
+
+ssize_t flistxattrat_nofollow(int dirfd, const char *filename,
+ char *list, size_t size)
+{
+ ssize_t ret;
+ int fd;
+
+ fd = openat_file(dirfd, filename,
+ O_RDONLY | O_PATH_9P_UTIL | O_NOFOLLOW, 0);
+ if (fd == -1) {
+ return -1;
+ }
+ ret = extattr_list_fd(fd, EXTATTR_NAMESPACE_USER, list, size);
+ close_preserve_errno(fd);
+ return ret;
+}
+
+ssize_t fremovexattrat_nofollow(int dirfd, const char *filename,
+ const char *name)
+{
+ int fd, namespace, ret;
+
+ fd = openat_file(dirfd, filename,
+ O_RDONLY | O_PATH_9P_UTIL | O_NOFOLLOW, 0);
+ if (fd == -1) {
+ return -1;
+ }
+ namespace = mangle_xattr_name(&name);
+ ret = extattr_delete_fd(fd, namespace, name);
+ close_preserve_errno(fd);
+ return ret;
+}
+
+int fsetxattrat_nofollow(int dirfd, const char *filename, const char *name,
+ void *value, size_t size, int flags)
+{
+ ssize_t ret;
+ int fd, namespace;
+
+ namespace = mangle_xattr_name(&name);
+ if (flags == (XATTR_CREATE | XATTR_REPLACE)) {
+ errno = EINVAL;
+ return -1;
+ }
+ fd = openat_file(dirfd, filename,
+ O_RDONLY | O_PATH_9P_UTIL | O_NOFOLLOW, 0);
+ if (fd == -1) {
+ return -1;
+ }
+ if (flags & (XATTR_CREATE | XATTR_REPLACE)) {
+ ret = extattr_get_fd(fd, namespace, name, NULL, 0);
+ if (ret == -1 && errno != ENOATTR) {
+ close_preserve_errno(fd);
+ return -1;
+ }
+ if (ret >= 0 && (flags & XATTR_CREATE)) {
+ errno = EEXIST;
+ close_preserve_errno(fd);
+ return -1;
+ }
+ if (ret == -1 && (flags & XATTR_REPLACE)) {
+ errno = ENOATTR;
+ close_preserve_errno(fd);
+ return -1;
+ }
+ }
+ ret = extattr_set_fd(fd, namespace, name, value, size);
+ close_preserve_errno(fd);
+ return ret;
+}
+
+int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev)
+{
+ return mknodat(dirfd, filename, mode, dev);
+}
#define O_PATH_9P_UTIL 0
#endif
+#ifdef CONFIG_FREEBSD
+/*
+ * FreeBSD does not have these flags, so we can only emulate their intended
+ * behaviour (racily).
+ */
+#define XATTR_CREATE 0x1
+#define XATTR_REPLACE 0x2
+#endif
+
#if !defined(CONFIG_LINUX)
/*
static inline int errno_to_dotl(int err) {
#if defined(CONFIG_LINUX)
/* nothing to translate (Linux -> Linux) */
-#elif defined(CONFIG_DARWIN)
+#elif defined(CONFIG_DARWIN) || defined(CONFIG_FREEBSD)
/*
- * translation mandatory for macOS hosts
+ * translation mandatory for non-Linux hosts
*
* FIXME: Only most important errnos translated here yet, this should be
* extended to as many errnos being translated as possible in future.
{
int fd, serrno, ret;
-#ifndef CONFIG_DARWIN
+#if !defined(CONFIG_DARWIN) && !defined(CONFIG_FREEBSD)
again:
#endif
fd = qemu_openat(dirfd, name, flags | O_NOFOLLOW | O_NOCTTY | O_NONBLOCK,
mode);
if (fd == -1) {
-#ifndef CONFIG_DARWIN
+#if !defined(CONFIG_DARWIN) && !defined(CONFIG_FREEBSD)
if (errno == EPERM && (flags & O_NOATIME)) {
/*
* The client passed O_NOATIME but we lack permissions to honor it.
return fd;
}
+#ifdef CONFIG_FREEBSD
+ssize_t fgetxattr(int dirfd, const char *name, void *value, size_t size);
+#endif
ssize_t fgetxattrat_nofollow(int dirfd, const char *path, const char *name,
void *value, size_t size);
int fsetxattrat_nofollow(int dirfd, const char *path, const char *name,
{ P9_DOTL_NONBLOCK, O_NONBLOCK } ,
{ P9_DOTL_DSYNC, O_DSYNC },
{ P9_DOTL_FASYNC, FASYNC },
-#ifndef CONFIG_DARWIN
+#if !defined(CONFIG_DARWIN) && !defined(CONFIG_FREEBSD)
{ P9_DOTL_NOATIME, O_NOATIME },
+#endif
+#ifndef CONFIG_DARWIN
/*
* On Darwin, we could map to F_NOCACHE, which is
* similar, but doesn't quite have the same
f_bavail = stbuf->f_bavail / bsize_factor;
f_files = stbuf->f_files;
f_ffree = stbuf->f_ffree;
-#ifdef CONFIG_DARWIN
+#if defined(CONFIG_DARWIN) || defined(CONFIG_FREEBSD)
fsid_val = (unsigned int)stbuf->f_fsid.val[0] |
(unsigned long long)stbuf->f_fsid.val[1] << 32;
f_namelen = NAME_MAX;
* Linux guests.
*/
#define P9_XATTR_SIZE_MAX 65536
+#elif defined(CONFIG_FREEBSD)
+/*
+ * FreeBSD similarly doesn't define a maximum xattr size, the limit is
+ * filesystem dependent. On UFS filesystems it's 2 times the filesystem block
+ * size, typically 32KB. On ZFS it depends on the value of the xattr property;
+ * with the default value there is no limit, and with xattr=sa it is 64KB.
+ *
+ * So, a limit of 64k seems reasonable here too.
+ */
+#define P9_XATTR_SIZE_MAX 65536
#else
#error Missing definition for P9_XATTR_SIZE_MAX for this host system
#endif
))
if host_os == 'darwin'
fs_ss.add(files('9p-util-darwin.c'))
+elif host_os == 'freebsd'
+ fs_ss.add(files('9p-util-freebsd.c'))
elif host_os == 'linux'
fs_ss.add(files('9p-util-linux.c'))
endif
# define ENOATTR ENODATA
# endif
# ifndef CONFIG_WIN32
-# include <sys/xattr.h>
+# ifdef CONFIG_FREEBSD
+# include <sys/extattr.h>
+# else
+# include <sys/xattr.h>
+# endif
# endif
#endif
.allowed()
have_virtfs = get_option('virtfs') \
- .require(host_os == 'linux' or host_os == 'darwin',
- error_message: 'virtio-9p (virtfs) requires Linux or macOS') \
- .require(host_os == 'linux' or cc.has_function('pthread_fchdir_np'),
+ .require(host_os == 'linux' or host_os == 'darwin' or host_os == 'freebsd',
+ error_message: 'virtio-9p (virtfs) requires Linux or macOS or FreeBSD') \
+ .require(host_os != 'darwin' or cc.has_function('pthread_fchdir_np'),
error_message: 'virtio-9p (virtfs) on macOS requires the presence of pthread_fchdir_np') \
- .require(host_os == 'darwin' or libattr.found(),
+ .require(host_os != 'linux' or libattr.found(),
error_message: 'virtio-9p (virtfs) on Linux requires libattr-devel') \
.disable_auto_if(not have_tools and not have_system) \
.allowed()