aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml73
-rw-r--r--.travis.yml16
-rw-r--r--MAINTAINERS20
-rw-r--r--Makefile10
-rw-r--r--Makefile.objs8
-rw-r--r--Makefile.target2
-rw-r--r--authz/Makefile.objs7
-rw-r--r--authz/base.c82
-rw-r--r--authz/list.c271
-rw-r--r--authz/listfile.c283
-rw-r--r--authz/pamacct.c148
-rw-r--r--authz/simple.c115
-rw-r--r--authz/trace-events18
-rw-r--r--block/backup.c5
-rw-r--r--block/block-backend.c13
-rw-r--r--block/commit.c7
-rw-r--r--block/io.c89
-rw-r--r--block/parallels.c13
-rw-r--r--block/qcow.c21
-rw-r--r--block/qcow2.c12
-rw-r--r--block/qed-table.c16
-rw-r--r--block/qed.c31
-rw-r--r--block/stream.c7
-rw-r--r--block/vmdk.c7
-rwxr-xr-xconfigure54
-rw-r--r--crypto/tlssession.c35
-rw-r--r--crypto/trace-events2
-rw-r--r--hw/block/virtio-blk.c245
-rw-r--r--hw/core/machine.c2
-rw-r--r--hw/ide/atapi.c14
-rw-r--r--hw/ide/core.c19
-rw-r--r--hw/net/virtio-net.c31
-rw-r--r--hw/usb/dev-mtp.c281
-rw-r--r--hw/usb/trace-events2
-rw-r--r--hw/virtio/virtio.c15
-rw-r--r--include/authz/base.h112
-rw-r--r--include/authz/list.h106
-rw-r--r--include/authz/listfile.h111
-rw-r--r--include/authz/pamacct.h100
-rw-r--r--include/authz/simple.h84
-rw-r--r--include/hw/ide/internal.h3
-rw-r--r--include/hw/virtio/virtio-blk.h6
-rw-r--r--include/hw/virtio/virtio.h15
-rw-r--r--include/qemu/acl.h66
-rw-r--r--include/qemu/filemonitor.h128
-rw-r--r--include/qemu/iov.h64
-rw-r--r--migration/block.c10
-rw-r--r--monitor.c179
-rw-r--r--qapi/Makefile.objs2
-rw-r--r--qapi/authz.json58
-rw-r--r--qapi/qapi-schema.json1
-rw-r--r--qemu-img.c10
-rw-r--r--qemu-options.hx105
-rw-r--r--qom/object.c12
-rw-r--r--qom/object_interfaces.c16
-rw-r--r--tests/Makefile.include22
-rw-r--r--tests/cdrom-test.c10
-rw-r--r--tests/docker/dockerfiles/debian-amd64.docker1
-rw-r--r--tests/docker/dockerfiles/debian9.docker4
-rw-r--r--tests/test-authz-list.c159
-rw-r--r--tests/test-authz-listfile.c195
-rw-r--r--tests/test-authz-pam.c124
-rw-r--r--tests/test-authz-simple.c50
-rw-r--r--tests/test-bdrv-drain.c29
-rw-r--r--tests/test-crypto-tlssession.c15
-rw-r--r--tests/test-io-channel-tls.c16
-rw-r--r--tests/test-util-filemonitor.c685
-rw-r--r--tests/virtio-blk-test.c127
-rw-r--r--ui/vnc-auth-sasl.c23
-rw-r--r--ui/vnc-auth-sasl.h5
-rw-r--r--ui/vnc-auth-vencrypt.c2
-rw-r--r--ui/vnc-ws.c2
-rw-r--r--ui/vnc.c37
-rw-r--r--ui/vnc.h4
-rw-r--r--util/Makefile.objs4
-rw-r--r--util/acl.c179
-rw-r--r--util/filemonitor-inotify.c339
-rw-r--r--util/filemonitor-stub.c59
-rw-r--r--util/trace-events9
79 files changed, 4396 insertions, 866 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000..79d02cf740
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,73 @@
+before_script:
+ - apt-get update -qq
+ - apt-get install -y -qq flex bison libglib2.0-dev libpixman-1-dev genisoimage
+
+build-system1:
+ script:
+ - apt-get install -y -qq libgtk-3-dev libvte-dev nettle-dev libcacard-dev
+ libusb-dev libvde-dev libspice-protocol-dev libgl1-mesa-dev
+ - ./configure --enable-werror --target-list="aarch64-softmmu alpha-softmmu
+ cris-softmmu hppa-softmmu lm32-softmmu moxie-softmmu microblazeel-softmmu
+ mips64el-softmmu m68k-softmmu ppc-softmmu riscv64-softmmu sparc-softmmu"
+ - make -j2
+ - make -j2 check
+
+build-system2:
+ script:
+ - apt-get install -y -qq libsdl2-dev libgcrypt-dev libbrlapi-dev libaio-dev
+ libfdt-dev liblzo2-dev librdmacm-dev libibverbs-dev libibumad-dev
+ - ./configure --enable-werror --target-list="tricore-softmmu unicore32-softmmu
+ microblaze-softmmu mips-softmmu riscv32-softmmu s390x-softmmu sh4-softmmu
+ sparc64-softmmu x86_64-softmmu xtensa-softmmu nios2-softmmu or1k-softmmu"
+ - make -j2
+ - make -j2 check
+
+build-disabled:
+ script:
+ - ./configure --enable-werror --disable-rdma --disable-slirp --disable-curl
+ --disable-capstone --disable-live-block-migration --disable-glusterfs
+ --disable-replication --disable-coroutine-pool --disable-smartcard
+ --disable-guest-agent --disable-curses --disable-libxml2 --disable-tpm
+ --disable-qom-cast-debug --disable-spice --disable-vhost-vsock
+ --disable-vhost-net --disable-vhost-crypto --disable-vhost-user
+ --target-list="i386-softmmu ppc64-softmmu mips64-softmmu i386-linux-user"
+ - make -j2
+ - make -j2 check-qtest SPEED=slow
+
+build-tcg-disabled:
+ script:
+ - apt-get install -y -qq clang libgtk-3-dev libbluetooth-dev libusb-dev
+ - ./configure --cc=clang --enable-werror --disable-tcg --audio-drv-list=""
+ - make -j2
+ - make check-unit
+ - make check-qapi-schema
+ - cd tests/qemu-iotests/
+ - ./check -raw 001 002 003 004 005 008 009 010 011 012 021 025 032 033 048
+ 052 063 077 086 101 104 106 113 147 148 150 151 152 157 159 160
+ 163 170 171 183 184 192 194 197 205 208 215 221 222 226 227 236
+ - ./check -qcow2 001 002 003 004 005 007 008 009 010 011 012 013 017 018 019
+ 020 021 022 024 025 027 028 029 031 032 033 034 035 036 037 038
+ 039 040 042 043 046 047 048 049 050 051 052 053 054 056 057 058
+ 060 061 062 063 065 066 067 068 069 071 072 073 074 079 080 082
+ 085 086 089 090 091 095 096 097 098 099 102 103 104 105 107 108
+ 110 111 114 117 120 122 124 126 127 129 130 132 133 134 137 138
+ 139 140 141 142 143 144 145 147 150 151 152 154 155 156 157 158
+ 161 165 170 172 174 176 177 179 184 186 187 190 192 194 195 196
+ 197 200 202 203 205 208 209 214 215 216 217 218 222 226 227 229 234
+
+build-user:
+ script:
+ - ./configure --enable-werror --disable-system --disable-guest-agent
+ --disable-capstone --disable-slirp --disable-fdt
+ - make -j2
+ - make run-tcg-tests-i386-linux-user run-tcg-tests-x86_64-linux-user
+
+build-clang:
+ script:
+ - apt-get install -y -qq clang libsdl2-dev
+ xfslibs-dev libiscsi-dev libnfs-dev libseccomp-dev gnutls-dev librbd-dev
+ - ./configure --cc=clang --cxx=clang++ --enable-werror
+ --target-list="alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu
+ ppc-softmmu s390x-softmmu x86_64-softmmu arm-linux-user"
+ - make -j2
+ - make -j2 check
diff --git a/.travis.yml b/.travis.yml
index baa06b976a..cca57f4314 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -86,11 +86,16 @@ matrix:
- env:
- - CONFIG="--enable-debug --enable-debug-tcg"
+ - CONFIG="--enable-debug --enable-debug-tcg --disable-user"
+ # TCG debug can be run just on it's own and is mostly agnostic to user/softmmu distinctions
- env:
- - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb --disable-user"
+ - CONFIG="--enable-debug-tcg --disable-system"
+
+
+ - env:
+ - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-libusb --disable-user --disable-replication"
- env:
@@ -174,13 +179,6 @@ matrix:
compiler: clang
- - env:
- - CONFIG="--target-list=i386-softmmu,ppc-softmmu,ppc64-softmmu,m68k-softmmu,x86_64-softmmu"
- os: osx
- osx_image: xcode10
- compiler: clang
-
-
# Python builds
- env:
- CONFIG="--target-list=x86_64-softmmu"
diff --git a/MAINTAINERS b/MAINTAINERS
index e7f69f31ed..8f9f9d7c7d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2079,6 +2079,14 @@ F: io/
F: include/io/
F: tests/test-io-*
+User authorization
+M: Daniel P. Berrange <berrange@redhat.com>
+S: Maintained
+F: authz/
+F: qapi/authz.json
+F: include/authz/
+F: tests/test-authz-*
+
Sockets
M: Daniel P. Berrange <berrange@redhat.com>
M: Gerd Hoffmann <kraxel@redhat.com>
@@ -2087,6 +2095,13 @@ F: include/qemu/sockets.h
F: util/qemu-sockets.c
F: qapi/sockets.json
+File monitor
+M: Daniel P. Berrange <berrange@redhat.com>
+S: Odd fixes
+F: util/filemonitor*.c
+F: include/qemu/filemonitor.h
+F: tests/test-util-filemonitor.c
+
Throttling infrastructure
M: Alberto Garcia <berto@igalia.com>
S: Supported
@@ -2474,6 +2489,11 @@ S: Maintained
F: .cirrus.yml
W: https://cirrus-ci.com/github/qemu/qemu
+GitLab Continuous Integration
+M: Thomas Huth <thuth@redhat.com>
+S: Maintained
+F: .gitlab-ci.yml
+
Guest Test Compilation Support
M: Alex Bennée <alex.bennee@linaro.org>
R: Philippe Mathieu-Daudé <f4bug@amsat.org>
diff --git a/Makefile b/Makefile
index a6de28677f..7fa04e0821 100644
--- a/Makefile
+++ b/Makefile
@@ -359,6 +359,7 @@ endif
dummy := $(call unnest-vars,, \
stub-obj-y \
+ authz-obj-y \
chardev-obj-y \
util-obj-y \
qga-obj-y \
@@ -423,6 +424,7 @@ qemu-options.def: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS))
SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES))
+$(SOFTMMU_SUBDIR_RULES): $(authz-obj-y)
$(SOFTMMU_SUBDIR_RULES): $(block-obj-y)
$(SOFTMMU_SUBDIR_RULES): $(crypto-obj-y)
$(SOFTMMU_SUBDIR_RULES): $(io-obj-y)
@@ -485,9 +487,9 @@ COMMON_LDADDS = libqemuutil.a
qemu-img.o: qemu-img-cmds.h
-qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
-qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
-qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
+qemu-img$(EXESUF): qemu-img.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
+qemu-nbd$(EXESUF): qemu-nbd.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
+qemu-io$(EXESUF): qemu-io.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS)
@@ -498,7 +500,7 @@ qemu-edid$(EXESUF): qemu-edid.o hw/display/edid-generate.o $(COMMON_LDADDS)
fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS)
fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap
-scsi/qemu-pr-helper$(EXESUF): scsi/qemu-pr-helper.o scsi/utils.o $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
+scsi/qemu-pr-helper$(EXESUF): scsi/qemu-pr-helper.o scsi/utils.o $(authz-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
ifdef CONFIG_MPATH
scsi/qemu-pr-helper$(EXESUF): LIBS += -ludev -lmultipath -lmpathpersist
endif
diff --git a/Makefile.objs b/Makefile.objs
index 5fb022d7ad..6e91ee5674 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -1,12 +1,17 @@
#######################################################################
# Common libraries for tools and emulators
-stub-obj-y = stubs/ crypto/
+stub-obj-y = stubs/ util/ crypto/
util-obj-y = util/ qobject/ qapi/
chardev-obj-y = chardev/
slirp-obj-$(CONFIG_SLIRP) = slirp/
#######################################################################
+# authz-obj-y is code used by both qemu system emulation and qemu-img
+
+authz-obj-y = authz/
+
+#######################################################################
# block-obj-y is code used by both qemu system emulation and qemu-img
block-obj-y += nbd/
@@ -125,6 +130,7 @@ trace-events-subdirs =
trace-events-subdirs += accel/kvm
trace-events-subdirs += accel/tcg
trace-events-subdirs += audio
+trace-events-subdirs += authz
trace-events-subdirs += block
trace-events-subdirs += chardev
trace-events-subdirs += crypto
diff --git a/Makefile.target b/Makefile.target
index d6ce549388..3b79e7074c 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -179,6 +179,7 @@ include $(SRC_PATH)/Makefile.objs
dummy := $(call unnest-vars,,target-obj-y)
target-obj-y-save := $(target-obj-y)
dummy := $(call unnest-vars,.., \
+ authz-obj-y \
block-obj-y \
block-obj-m \
chardev-obj-y \
@@ -193,6 +194,7 @@ target-obj-y := $(target-obj-y-save)
all-obj-y += $(common-obj-y)
all-obj-y += $(target-obj-y)
all-obj-y += $(qom-obj-y)
+all-obj-$(CONFIG_SOFTMMU) += $(authz-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-obj-y)
all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
diff --git a/authz/Makefile.objs b/authz/Makefile.objs
new file mode 100644
index 0000000000..ed7b273596
--- /dev/null
+++ b/authz/Makefile.objs
@@ -0,0 +1,7 @@
+authz-obj-y += base.o
+authz-obj-y += simple.o
+authz-obj-y += list.o
+authz-obj-y += listfile.o
+authz-obj-$(CONFIG_AUTH_PAM) += pamacct.o
+
+pamacct.o-libs = -lpam
diff --git a/authz/base.c b/authz/base.c
new file mode 100644
index 0000000000..110dfa4195
--- /dev/null
+++ b/authz/base.c
@@ -0,0 +1,82 @@
+/*
+ * QEMU authorization framework base class
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "authz/base.h"
+#include "authz/trace.h"
+
+bool qauthz_is_allowed(QAuthZ *authz,
+ const char *identity,
+ Error **errp)
+{
+ QAuthZClass *cls = QAUTHZ_GET_CLASS(authz);
+ bool allowed;
+
+ allowed = cls->is_allowed(authz, identity, errp);
+ trace_qauthz_is_allowed(authz, identity, allowed);
+
+ return allowed;
+}
+
+
+bool qauthz_is_allowed_by_id(const char *authzid,
+ const char *identity,
+ Error **errp)
+{
+ QAuthZ *authz;
+ Object *obj;
+ Object *container;
+
+ container = object_get_objects_root();
+ obj = object_resolve_path_component(container,
+ authzid);
+ if (!obj) {
+ error_setg(errp, "Cannot find QAuthZ object ID %s",
+ authzid);
+ return false;
+ }
+
+ if (!object_dynamic_cast(obj, TYPE_QAUTHZ)) {
+ error_setg(errp, "Object '%s' is not a QAuthZ subclass",
+ authzid);
+ return false;
+ }
+
+ authz = QAUTHZ(obj);
+
+ return qauthz_is_allowed(authz, identity, errp);
+}
+
+
+static const TypeInfo authz_info = {
+ .parent = TYPE_OBJECT,
+ .name = TYPE_QAUTHZ,
+ .instance_size = sizeof(QAuthZ),
+ .class_size = sizeof(QAuthZClass),
+ .abstract = true,
+};
+
+static void qauthz_register_types(void)
+{
+ type_register_static(&authz_info);
+}
+
+type_init(qauthz_register_types)
+
diff --git a/authz/list.c b/authz/list.c
new file mode 100644
index 0000000000..dc6b0fec13
--- /dev/null
+++ b/authz/list.c
@@ -0,0 +1,271 @@
+/*
+ * QEMU access control list authorization driver
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "authz/list.h"
+#include "authz/trace.h"
+#include "qom/object_interfaces.h"
+#include "qapi/qapi-visit-authz.h"
+
+static bool qauthz_list_is_allowed(QAuthZ *authz,
+ const char *identity,
+ Error **errp)
+{
+ QAuthZList *lauthz = QAUTHZ_LIST(authz);
+ QAuthZListRuleList *rules = lauthz->rules;
+
+ while (rules) {
+ QAuthZListRule *rule = rules->value;
+ QAuthZListFormat format = rule->has_format ? rule->format :
+ QAUTHZ_LIST_FORMAT_EXACT;
+
+ trace_qauthz_list_check_rule(authz, rule->match, identity,
+ format, rule->policy);
+ switch (format) {
+ case QAUTHZ_LIST_FORMAT_EXACT:
+ if (g_str_equal(rule->match, identity)) {
+ return rule->policy == QAUTHZ_LIST_POLICY_ALLOW;
+ }
+ break;
+ case QAUTHZ_LIST_FORMAT_GLOB:
+ if (g_pattern_match_simple(rule->match, identity)) {
+ return rule->policy == QAUTHZ_LIST_POLICY_ALLOW;
+ }
+ break;
+ default:
+ g_warn_if_reached();
+ return false;
+ }
+ rules = rules->next;
+ }
+
+ trace_qauthz_list_default_policy(authz, identity, lauthz->policy);
+ return lauthz->policy == QAUTHZ_LIST_POLICY_ALLOW;
+}
+
+
+static void
+qauthz_list_prop_set_policy(Object *obj,
+ int value,
+ Error **errp G_GNUC_UNUSED)
+{
+ QAuthZList *lauthz = QAUTHZ_LIST(obj);
+
+ lauthz->policy = value;
+}
+
+
+static int
+qauthz_list_prop_get_policy(Object *obj,
+ Error **errp G_GNUC_UNUSED)
+{
+ QAuthZList *lauthz = QAUTHZ_LIST(obj);
+
+ return lauthz->policy;
+}
+
+
+static void
+qauthz_list_prop_get_rules(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ QAuthZList *lauthz = QAUTHZ_LIST(obj);
+
+ visit_type_QAuthZListRuleList(v, name, &lauthz->rules, errp);
+}
+
+static void
+qauthz_list_prop_set_rules(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ QAuthZList *lauthz = QAUTHZ_LIST(obj);
+ QAuthZListRuleList *oldrules;
+
+ oldrules = lauthz->rules;
+ visit_type_QAuthZListRuleList(v, name, &lauthz->rules, errp);
+
+ qapi_free_QAuthZListRuleList(oldrules);
+}
+
+
+static void
+qauthz_list_finalize(Object *obj)
+{
+ QAuthZList *lauthz = QAUTHZ_LIST(obj);
+
+ qapi_free_QAuthZListRuleList(lauthz->rules);
+}
+
+
+static void
+qauthz_list_class_init(ObjectClass *oc, void *data)
+{
+ QAuthZClass *authz = QAUTHZ_CLASS(oc);
+
+ object_class_property_add_enum(oc, "policy",
+ "QAuthZListPolicy",
+ &QAuthZListPolicy_lookup,
+ qauthz_list_prop_get_policy,
+ qauthz_list_prop_set_policy,
+ NULL);
+
+ object_class_property_add(oc, "rules", "QAuthZListRule",
+ qauthz_list_prop_get_rules,
+ qauthz_list_prop_set_rules,
+ NULL, NULL, NULL);
+
+ authz->is_allowed = qauthz_list_is_allowed;
+}
+
+
+QAuthZList *qauthz_list_new(const char *id,
+ QAuthZListPolicy policy,
+ Error **errp)
+{
+ return QAUTHZ_LIST(
+ object_new_with_props(TYPE_QAUTHZ_LIST,
+ object_get_objects_root(),
+ id, errp,
+ "policy", QAuthZListPolicy_str(policy),
+ NULL));
+}
+
+ssize_t qauthz_list_append_rule(QAuthZList *auth,
+ const char *match,
+ QAuthZListPolicy policy,
+ QAuthZListFormat format,
+ Error **errp)
+{
+ QAuthZListRule *rule;
+ QAuthZListRuleList *rules, *tmp;
+ size_t i = 0;
+
+ rule = g_new0(QAuthZListRule, 1);
+ rule->policy = policy;
+ rule->match = g_strdup(match);
+ rule->format = format;
+ rule->has_format = true;
+
+ tmp = g_new0(QAuthZListRuleList, 1);
+ tmp->value = rule;
+
+ rules = auth->rules;
+ if (rules) {
+ while (rules->next) {
+ i++;
+ rules = rules->next;
+ }
+ rules->next = tmp;
+ return i + 1;
+ } else {
+ auth->rules = tmp;
+ return 0;
+ }
+}
+
+
+ssize_t qauthz_list_insert_rule(QAuthZList *auth,
+ const char *match,
+ QAuthZListPolicy policy,
+ QAuthZListFormat format,
+ size_t index,
+ Error **errp)
+{
+ QAuthZListRule *rule;
+ QAuthZListRuleList *rules, *tmp;
+ size_t i = 0;
+
+ rule = g_new0(QAuthZListRule, 1);
+ rule->policy = policy;
+ rule->match = g_strdup(match);
+ rule->format = format;
+ rule->has_format = true;
+
+ tmp = g_new0(QAuthZListRuleList, 1);
+ tmp->value = rule;
+
+ rules = auth->rules;
+ if (rules && index > 0) {
+ while (rules->next && i < (index - 1)) {
+ i++;
+ rules = rules->next;
+ }
+ tmp->next = rules->next;
+ rules->next = tmp;
+ return i + 1;
+ } else {
+ tmp->next = auth->rules;
+ auth->rules = tmp;
+ return 0;
+ }
+}
+
+
+ssize_t qauthz_list_delete_rule(QAuthZList *auth, const char *match)
+{
+ QAuthZListRule *rule;
+ QAuthZListRuleList *rules, *prev;
+ size_t i = 0;
+
+ prev = NULL;
+ rules = auth->rules;
+ while (rules) {
+ rule = rules->value;
+ if (g_str_equal(rule->match, match)) {
+ if (prev) {
+ prev->next = rules->next;
+ } else {
+ auth->rules = rules->next;
+ }
+ rules->next = NULL;
+ qapi_free_QAuthZListRuleList(rules);
+ return i;
+ }
+ prev = rules;
+ rules = rules->next;
+ i++;
+ }
+
+ return -1;
+}
+
+
+static const TypeInfo qauthz_list_info = {
+ .parent = TYPE_QAUTHZ,
+ .name = TYPE_QAUTHZ_LIST,
+ .instance_size = sizeof(QAuthZList),
+ .instance_finalize = qauthz_list_finalize,
+ .class_size = sizeof(QAuthZListClass),
+ .class_init = qauthz_list_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_USER_CREATABLE },
+ { }
+ }
+};
+
+
+static void
+qauthz_list_register_types(void)
+{
+ type_register_static(&qauthz_list_info);
+}
+
+
+type_init(qauthz_list_register_types);
diff --git a/authz/listfile.c b/authz/listfile.c
new file mode 100644
index 0000000000..d4579767e7
--- /dev/null
+++ b/authz/listfile.c
@@ -0,0 +1,283 @@
+/*
+ * QEMU access control list file authorization driver
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "authz/listfile.h"
+#include "authz/trace.h"
+#include "qemu/error-report.h"
+#include "qemu/main-loop.h"
+#include "qemu/sockets.h"
+#include "qemu/filemonitor.h"
+#include "qom/object_interfaces.h"
+#include "qapi/qapi-visit-authz.h"
+#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject.h"
+#include "qapi/qmp/qerror.h"
+#include "qapi/qobject-input-visitor.h"
+
+
+static bool
+qauthz_list_file_is_allowed(QAuthZ *authz,
+ const char *identity,
+ Error **errp)
+{
+ QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(authz);
+ if (fauthz->list) {
+ return qauthz_is_allowed(fauthz->list, identity, errp);
+ }
+
+ return false;
+}
+
+
+static QAuthZ *
+qauthz_list_file_load(QAuthZListFile *fauthz, Error **errp)
+{
+ GError *err = NULL;
+ gchar *content = NULL;
+ gsize len;
+ QObject *obj = NULL;
+ QDict *pdict;
+ Visitor *v = NULL;
+ QAuthZ *ret = NULL;
+
+ trace_qauthz_list_file_load(fauthz, fauthz->filename);
+ if (!g_file_get_contents(fauthz->filename, &content, &len, &err)) {
+ error_setg(errp, "Unable to read '%s': %s",
+ fauthz->filename, err->message);
+ goto cleanup;
+ }
+
+ obj = qobject_from_json(content, errp);
+ if (!obj) {
+ goto cleanup;
+ }
+
+ pdict = qobject_to(QDict, obj);
+ if (!pdict) {
+ error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "obj", "dict");
+ goto cleanup;
+ }
+
+ v = qobject_input_visitor_new(obj);
+
+ ret = (QAuthZ *)user_creatable_add_type(TYPE_QAUTHZ_LIST,
+ NULL, pdict, v, errp);
+
+ cleanup:
+ visit_free(v);
+ qobject_unref(obj);
+ if (err) {
+ g_error_free(err);
+ }
+ g_free(content);
+ return ret;
+}
+
+
+static void
+qauthz_list_file_event(int wd G_GNUC_UNUSED,
+ QFileMonitorEvent ev G_GNUC_UNUSED,
+ const char *name G_GNUC_UNUSED,
+ void *opaque)
+{
+ QAuthZListFile *fauthz = opaque;
+ Error *err = NULL;
+
+ if (ev != QFILE_MONITOR_EVENT_MODIFIED &&
+ ev != QFILE_MONITOR_EVENT_CREATED) {
+ return;
+ }
+
+ object_unref(OBJECT(fauthz->list));
+ fauthz->list = qauthz_list_file_load(fauthz, &err);
+ trace_qauthz_list_file_refresh(fauthz,
+ fauthz->filename, fauthz->list ? 1 : 0);
+ if (!fauthz->list) {
+ error_report_err(err);
+ }
+}
+
+static void
+qauthz_list_file_complete(UserCreatable *uc, Error **errp)
+{
+ QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(uc);
+ gchar *dir = NULL, *file = NULL;
+
+ fauthz->list = qauthz_list_file_load(fauthz, errp);
+
+ if (!fauthz->refresh) {
+ return;
+ }
+
+ fauthz->file_monitor = qemu_file_monitor_new(errp);
+ if (!fauthz->file_monitor) {
+ return;
+ }
+
+ dir = g_path_get_dirname(fauthz->filename);
+ if (g_str_equal(dir, ".")) {
+ error_setg(errp, "Filename must be an absolute path");
+ goto cleanup;
+ }
+ file = g_path_get_basename(fauthz->filename);
+ if (g_str_equal(file, ".")) {
+ error_setg(errp, "Path has no trailing filename component");
+ goto cleanup;
+ }
+
+ fauthz->file_watch = qemu_file_monitor_add_watch(
+ fauthz->file_monitor, dir, file,
+ qauthz_list_file_event, fauthz, errp);
+ if (fauthz->file_watch < 0) {
+ goto cleanup;
+ }
+
+ cleanup:
+ g_free(file);
+ g_free(dir);
+}
+
+
+static void
+qauthz_list_file_prop_set_filename(Object *obj,
+ const char *value,
+ Error **errp G_GNUC_UNUSED)
+{
+ QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
+
+ g_free(fauthz->filename);
+ fauthz->filename = g_strdup(value);
+}
+
+
+static char *
+qauthz_list_file_prop_get_filename(Object *obj,
+ Error **errp G_GNUC_UNUSED)
+{
+ QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
+
+ return g_strdup(fauthz->filename);
+}
+
+
+static void
+qauthz_list_file_prop_set_refresh(Object *obj,
+ bool value,
+ Error **errp G_GNUC_UNUSED)
+{
+ QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
+
+ fauthz->refresh = value;
+}
+
+
+static bool
+qauthz_list_file_prop_get_refresh(Object *obj,
+ Error **errp G_GNUC_UNUSED)
+{
+ QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
+
+ return fauthz->refresh;
+}
+
+
+static void
+qauthz_list_file_finalize(Object *obj)
+{
+ QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
+
+ object_unref(OBJECT(fauthz->list));
+ g_free(fauthz->filename);
+ qemu_file_monitor_free(fauthz->file_monitor);
+}
+
+
+static void
+qauthz_list_file_class_init(ObjectClass *oc, void *data)
+{
+ UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
+ QAuthZClass *authz = QAUTHZ_CLASS(oc);
+
+ ucc->complete = qauthz_list_file_complete;
+
+ object_class_property_add_str(oc, "filename",
+ qauthz_list_file_prop_get_filename,
+ qauthz_list_file_prop_set_filename,
+ NULL);
+ object_class_property_add_bool(oc, "refresh",
+ qauthz_list_file_prop_get_refresh,
+ qauthz_list_file_prop_set_refresh,
+ NULL);
+
+ authz->is_allowed = qauthz_list_file_is_allowed;
+}
+
+
+static void
+qauthz_list_file_init(Object *obj)
+{
+ QAuthZListFile *authz = QAUTHZ_LIST_FILE(obj);
+
+ authz->file_watch = -1;
+#ifdef CONFIG_INOTIFY1
+ authz->refresh = TRUE;
+#endif
+}
+
+
+QAuthZListFile *qauthz_list_file_new(const char *id,
+ const char *filename,
+ bool refresh,
+ Error **errp)
+{
+ return QAUTHZ_LIST_FILE(
+ object_new_with_props(TYPE_QAUTHZ_LIST_FILE,
+ object_get_objects_root(),
+ id, errp,
+ "filename", filename,
+ "refresh", refresh ? "yes" : "no",
+ NULL));
+}
+
+
+static const TypeInfo qauthz_list_file_info = {
+ .parent = TYPE_QAUTHZ,
+ .name = TYPE_QAUTHZ_LIST_FILE,
+ .instance_init = qauthz_list_file_init,
+ .instance_size = sizeof(QAuthZListFile),
+ .instance_finalize = qauthz_list_file_finalize,
+ .class_size = sizeof(QAuthZListFileClass),
+ .class_init = qauthz_list_file_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_USER_CREATABLE },
+ { }
+ }
+};
+
+
+static void
+qauthz_list_file_register_types(void)
+{
+ type_register_static(&qauthz_list_file_info);
+}
+
+
+type_init(qauthz_list_file_register_types);
diff --git a/authz/pamacct.c b/authz/pamacct.c
new file mode 100644
index 0000000000..5038358cdc
--- /dev/null
+++ b/authz/pamacct.c
@@ -0,0 +1,148 @@
+/*
+ * QEMU PAM authorization driver
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "authz/pamacct.h"
+#include "authz/trace.h"
+#include "qom/object_interfaces.h"
+
+#include <security/pam_appl.h>
+
+
+static bool qauthz_pam_is_allowed(QAuthZ *authz,
+ const char *identity,
+ Error **errp)
+{
+ QAuthZPAM *pauthz = QAUTHZ_PAM(authz);
+ const struct pam_conv pam_conversation = { 0 };
+ pam_handle_t *pamh = NULL;
+ int ret;
+
+ trace_qauthz_pam_check(authz, identity, pauthz->service);
+ ret = pam_start(pauthz->service,
+ identity,
+ &pam_conversation,
+ &pamh);
+ if (ret != PAM_SUCCESS) {
+ error_setg(errp, "Unable to start PAM transaction: %s",
+ pam_strerror(NULL, ret));
+ return false;
+ }
+
+ ret = pam_acct_mgmt(pamh, PAM_SILENT);
+ pam_end(pamh, ret);
+ if (ret != PAM_SUCCESS) {
+ error_setg(errp, "Unable to authorize user '%s': %s",
+ identity, pam_strerror(pamh, ret));
+ return false;
+ }
+
+ return true;
+}
+
+
+static void
+qauthz_pam_prop_set_service(Object *obj,
+ const char *service,
+ Error **errp G_GNUC_UNUSED)
+{
+ QAuthZPAM *pauthz = QAUTHZ_PAM(obj);
+
+ g_free(pauthz->service);
+ pauthz->service = g_strdup(service);
+}
+
+
+static char *
+qauthz_pam_prop_get_service(Object *obj,
+ Error **errp G_GNUC_UNUSED)
+{
+ QAuthZPAM *pauthz = QAUTHZ_PAM(obj);
+
+ return g_strdup(pauthz->service);
+}
+
+
+static void
+qauthz_pam_complete(UserCreatable *uc, Error **errp)
+{
+}
+
+
+static void
+qauthz_pam_finalize(Object *obj)
+{
+ QAuthZPAM *pauthz = QAUTHZ_PAM(obj);
+
+ g_free(pauthz->service);
+}
+
+
+static void
+qauthz_pam_class_init(ObjectClass *oc, void *data)
+{
+ UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
+ QAuthZClass *authz = QAUTHZ_CLASS(oc);
+
+ ucc->complete = qauthz_pam_complete;
+ authz->is_allowed = qauthz_pam_is_allowed;
+
+ object_class_property_add_str(oc, "service",
+ qauthz_pam_prop_get_service,
+ qauthz_pam_prop_set_service,
+ NULL);
+}
+
+
+QAuthZPAM *qauthz_pam_new(const char *id,
+ const char *service,
+ Error **errp)
+{
+ return QAUTHZ_PAM(
+ object_new_with_props(TYPE_QAUTHZ_PAM,
+ object_get_objects_root(),
+ id, errp,
+ "service", service,
+ NULL));
+}
+
+
+static const TypeInfo qauthz_pam_info = {
+ .parent = TYPE_QAUTHZ,
+ .name = TYPE_QAUTHZ_PAM,
+ .instance_size = sizeof(QAuthZPAM),
+ .instance_finalize = qauthz_pam_finalize,
+ .class_size = sizeof(QAuthZPAMClass),
+ .class_init = qauthz_pam_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_USER_CREATABLE },
+ { }
+ }
+};
+
+
+static void
+qauthz_pam_register_types(void)
+{
+ type_register_static(&qauthz_pam_info);
+}
+
+
+type_init(qauthz_pam_register_types);
diff --git a/authz/simple.c b/authz/simple.c
new file mode 100644
index 0000000000..8ab718803e
--- /dev/null
+++ b/authz/simple.c
@@ -0,0 +1,115 @@
+/*
+ * QEMU simple authorization driver
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "authz/simple.h"
+#include "authz/trace.h"
+#include "qom/object_interfaces.h"
+
+static bool qauthz_simple_is_allowed(QAuthZ *authz,
+ const char *identity,
+ Error **errp)
+{
+ QAuthZSimple *sauthz = QAUTHZ_SIMPLE(authz);
+
+ trace_qauthz_simple_is_allowed(authz, sauthz->identity, identity);
+ return g_str_equal(identity, sauthz->identity);
+}
+
+static void
+qauthz_simple_prop_set_identity(Object *obj,
+ const char *value,
+ Error **errp G_GNUC_UNUSED)
+{
+ QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj);
+
+ g_free(sauthz->identity);
+ sauthz->identity = g_strdup(value);
+}
+
+
+static char *
+qauthz_simple_prop_get_identity(Object *obj,
+ Error **errp G_GNUC_UNUSED)
+{
+ QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj);
+
+ return g_strdup(sauthz->identity);
+}
+
+
+static void
+qauthz_simple_finalize(Object *obj)
+{
+ QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj);
+
+ g_free(sauthz->identity);
+}
+
+
+static void
+qauthz_simple_class_init(ObjectClass *oc, void *data)
+{
+ QAuthZClass *authz = QAUTHZ_CLASS(oc);
+
+ authz->is_allowed = qauthz_simple_is_allowed;
+
+ object_class_property_add_str(oc, "identity",
+ qauthz_simple_prop_get_identity,
+ qauthz_simple_prop_set_identity,
+ NULL);
+}
+
+
+QAuthZSimple *qauthz_simple_new(const char *id,
+ const char *identity,
+ Error **errp)
+{
+ return QAUTHZ_SIMPLE(
+ object_new_with_props(TYPE_QAUTHZ_SIMPLE,
+ object_get_objects_root(),
+ id, errp,
+ "identity", identity,
+ NULL));
+}
+
+
+static const TypeInfo qauthz_simple_info = {
+ .parent = TYPE_QAUTHZ,
+ .name = TYPE_QAUTHZ_SIMPLE,
+ .instance_size = sizeof(QAuthZSimple),
+ .instance_finalize = qauthz_simple_finalize,
+ .class_size = sizeof(QAuthZSimpleClass),
+ .class_init = qauthz_simple_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_USER_CREATABLE },
+ { }
+ }
+};
+
+
+static void
+qauthz_simple_register_types(void)
+{
+ type_register_static(&qauthz_simple_info);
+}
+
+
+type_init(qauthz_simple_register_types);
diff --git a/authz/trace-events b/authz/trace-events
new file mode 100644
index 0000000000..72c411927d
--- /dev/null
+++ b/authz/trace-events
@@ -0,0 +1,18 @@
+# See docs/devel/tracing.txt for syntax documentation.
+
+# authz/base.c
+qauthz_is_allowed(void *authz, const char *identity, bool allowed) "AuthZ %p check identity=%s allowed=%d"
+
+# auth/simple.c
+qauthz_simple_is_allowed(void *authz, const char *wantidentity, const char *gotidentity) "AuthZ simple %p check want identity=%s got identity=%s"
+
+# auth/list.c
+qauthz_list_check_rule(void *authz, const char *identity, const char *rule, int format, int policy) "AuthZ list %p check rule=%s identity=%s format=%d policy=%d"
+qauthz_list_default_policy(void *authz, const char *identity, int policy) "AuthZ list %p default identity=%s policy=%d"
+
+# auth/listfile.c
+qauthz_list_file_load(void *authz, const char *filename) "AuthZ file %p load filename=%s"
+qauthz_list_file_refresh(void *authz, const char *filename, int success) "AuthZ file %p load filename=%s success=%d"
+
+# auth/pam.c
+qauthz_pam_check(void *authz, const char *identity, const char *service) "AuthZ PAM %p identity=%s service=%s"
diff --git a/block/backup.c b/block/backup.c
index 435414e964..9988753249 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -107,7 +107,6 @@ static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job,
void **bounce_buffer)
{
int ret;
- struct iovec iov;
QEMUIOVector qiov;
BlockBackend *blk = job->common.blk;
int nbytes;
@@ -119,9 +118,7 @@ static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job,
if (!*bounce_buffer) {
*bounce_buffer = blk_blockalign(blk, job->cluster_size);
}
- iov.iov_base = *bounce_buffer;
- iov.iov_len = nbytes;
- qemu_iovec_init_external(&qiov, &iov, 1);
+ qemu_iovec_init_buf(&qiov, *bounce_buffer, nbytes);
ret = blk_co_preadv(blk, start, qiov.size, &qiov, read_flags);
if (ret < 0) {
diff --git a/block/block-backend.c b/block/block-backend.c
index 0219555f89..edad02a0f2 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -1204,17 +1204,8 @@ static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf,
int64_t bytes, CoroutineEntry co_entry,
BdrvRequestFlags flags)
{
- QEMUIOVector qiov;
- struct iovec iov;
- BlkRwCo rwco;
-
- iov = (struct iovec) {
- .iov_base = buf,
- .iov_len = bytes,
- };
- qemu_iovec_init_external(&qiov, &iov, 1);
-
- rwco = (BlkRwCo) {
+ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes);
+ BlkRwCo rwco = {
.blk = blk,
.offset = offset,
.iobuf = &qiov,
diff --git a/block/commit.c b/block/commit.c
index 385fb98527..3b46ca7f97 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -47,14 +47,9 @@ static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base,
void *buf)
{
int ret = 0;
- QEMUIOVector qiov;
- struct iovec iov = {
- .iov_base = buf,
- .iov_len = bytes,
- };
+ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes);
assert(bytes < SIZE_MAX);
- qemu_iovec_init_external(&qiov, &iov, 1);
ret = blk_co_preadv(bs, offset, qiov.size, &qiov, 0);
if (ret < 0) {
diff --git a/block/io.c b/block/io.c
index 213ca03d8d..2ba603c7bc 100644
--- a/block/io.c
+++ b/block/io.c
@@ -843,17 +843,13 @@ static int bdrv_prwv_co(BdrvChild *child, int64_t offset,
static int bdrv_rw_co(BdrvChild *child, int64_t sector_num, uint8_t *buf,
int nb_sectors, bool is_write, BdrvRequestFlags flags)
{
- QEMUIOVector qiov;
- struct iovec iov = {
- .iov_base = (void *)buf,
- .iov_len = nb_sectors * BDRV_SECTOR_SIZE,
- };
+ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf,
+ nb_sectors * BDRV_SECTOR_SIZE);
if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) {
return -EINVAL;
}
- qemu_iovec_init_external(&qiov, &iov, 1);
return bdrv_prwv_co(child, sector_num << BDRV_SECTOR_BITS,
&qiov, is_write, flags);
}
@@ -880,13 +876,8 @@ int bdrv_write(BdrvChild *child, int64_t sector_num,
int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset,
int bytes, BdrvRequestFlags flags)
{
- QEMUIOVector qiov;
- struct iovec iov = {
- .iov_base = NULL,
- .iov_len = bytes,
- };
+ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, bytes);
- qemu_iovec_init_external(&qiov, &iov, 1);
return bdrv_prwv_co(child, offset, &qiov, true,
BDRV_REQ_ZERO_WRITE | flags);
}
@@ -950,17 +941,12 @@ int bdrv_preadv(BdrvChild *child, int64_t offset, QEMUIOVector *qiov)
int bdrv_pread(BdrvChild *child, int64_t offset, void *buf, int bytes)
{
- QEMUIOVector qiov;
- struct iovec iov = {
- .iov_base = (void *)buf,
- .iov_len = bytes,
- };
+ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes);
if (bytes < 0) {
return -EINVAL;
}
- qemu_iovec_init_external(&qiov, &iov, 1);
return bdrv_preadv(child, offset, &qiov);
}
@@ -978,17 +964,12 @@ int bdrv_pwritev(BdrvChild *child, int64_t offset, QEMUIOVector *qiov)
int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, int bytes)
{
- QEMUIOVector qiov;
- struct iovec iov = {
- .iov_base = (void *) buf,
- .iov_len = bytes,
- };
+ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes);
if (bytes < 0) {
return -EINVAL;
}
- qemu_iovec_init_external(&qiov, &iov, 1);
return bdrv_pwritev(child, offset, &qiov);
}
@@ -1165,7 +1146,6 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child,
void *bounce_buffer;
BlockDriver *drv = bs->drv;
- struct iovec iov;
QEMUIOVector local_qiov;
int64_t cluster_offset;
int64_t cluster_bytes;
@@ -1230,9 +1210,8 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child,
if (ret <= 0) {
/* Must copy-on-read; use the bounce buffer */
- iov.iov_base = bounce_buffer;
- iov.iov_len = pnum = MIN(pnum, MAX_BOUNCE_BUFFER);
- qemu_iovec_init_external(&local_qiov, &iov, 1);
+ pnum = MIN(pnum, MAX_BOUNCE_BUFFER);
+ qemu_iovec_init_buf(&local_qiov, bounce_buffer, pnum);
ret = bdrv_driver_preadv(bs, cluster_offset, pnum,
&local_qiov, 0);
@@ -1477,7 +1456,7 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
{
BlockDriver *drv = bs->drv;
QEMUIOVector qiov;
- struct iovec iov = {0};
+ void *buf = NULL;
int ret = 0;
bool need_flush = false;
int head = 0;
@@ -1547,16 +1526,14 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
need_flush = true;
}
num = MIN(num, max_transfer);
- iov.iov_len = num;
- if (iov.iov_base == NULL) {
- iov.iov_base = qemu_try_blockalign(bs, num);
- if (iov.iov_base == NULL) {
+ if (buf == NULL) {
+ buf = qemu_try_blockalign0(bs, num);
+ if (buf == NULL) {
ret = -ENOMEM;
goto fail;
}
- memset(iov.iov_base, 0, num);
}
- qemu_iovec_init_external(&qiov, &iov, 1);
+ qemu_iovec_init_buf(&qiov, buf, num);
ret = bdrv_driver_pwritev(bs, offset, num, &qiov, write_flags);
@@ -1564,8 +1541,8 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
* all future requests.
*/
if (num < max_transfer) {
- qemu_vfree(iov.iov_base);
- iov.iov_base = NULL;
+ qemu_vfree(buf);
+ buf = NULL;
}
}
@@ -1577,7 +1554,7 @@ fail:
if (ret == 0 && need_flush) {
ret = bdrv_co_flush(bs);
}
- qemu_vfree(iov.iov_base);
+ qemu_vfree(buf);
return ret;
}
@@ -1763,7 +1740,6 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child,
BlockDriverState *bs = child->bs;
uint8_t *buf = NULL;
QEMUIOVector local_qiov;
- struct iovec iov;
uint64_t align = bs->bl.request_alignment;
unsigned int head_padding_bytes, tail_padding_bytes;
int ret = 0;
@@ -1775,11 +1751,7 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child,
assert(flags & BDRV_REQ_ZERO_WRITE);
if (head_padding_bytes || tail_padding_bytes) {
buf = qemu_blockalign(bs, align);
- iov = (struct iovec) {
- .iov_base = buf,
- .iov_len = align,
- };
- qemu_iovec_init_external(&local_qiov, &iov, 1);
+ qemu_iovec_init_buf(&local_qiov, buf, align);
}
if (head_padding_bytes) {
uint64_t zero_bytes = MIN(bytes, align - head_padding_bytes);
@@ -1885,17 +1857,12 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
if (offset & (align - 1)) {
QEMUIOVector head_qiov;
- struct iovec head_iov;
mark_request_serialising(&req, align);
wait_serialising_requests(&req);
head_buf = qemu_blockalign(bs, align);
- head_iov = (struct iovec) {
- .iov_base = head_buf,
- .iov_len = align,
- };
- qemu_iovec_init_external(&head_qiov, &head_iov, 1);
+ qemu_iovec_init_buf(&head_qiov, head_buf, align);
bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD);
ret = bdrv_aligned_preadv(child, &req, offset & ~(align - 1), align,
@@ -1924,7 +1891,6 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
if ((offset + bytes) & (align - 1)) {
QEMUIOVector tail_qiov;
- struct iovec tail_iov;
size_t tail_bytes;
bool waited;
@@ -1933,11 +1899,7 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
assert(!waited || !use_local_qiov);
tail_buf = qemu_blockalign(bs, align);
- tail_iov = (struct iovec) {
- .iov_base = tail_buf,
- .iov_len = align,
- };
- qemu_iovec_init_external(&tail_qiov, &tail_iov, 1);
+ qemu_iovec_init_buf(&tail_qiov, tail_buf, align);
bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL);
ret = bdrv_aligned_preadv(child, &req, (offset + bytes) & ~(align - 1),
@@ -2468,15 +2430,9 @@ bdrv_rw_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos,
int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf,
int64_t pos, int size)
{
- QEMUIOVector qiov;
- struct iovec iov = {
- .iov_base = (void *) buf,
- .iov_len = size,
- };
+ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, size);
int ret;
- qemu_iovec_init_external(&qiov, &iov, 1);
-
ret = bdrv_writev_vmstate(bs, &qiov, pos);
if (ret < 0) {
return ret;
@@ -2493,14 +2449,9 @@ int bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos)
int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf,
int64_t pos, int size)
{
- QEMUIOVector qiov;
- struct iovec iov = {
- .iov_base = buf,
- .iov_len = size,
- };
+ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, size);
int ret;
- qemu_iovec_init_external(&qiov, &iov, 1);
ret = bdrv_readv_vmstate(bs, &qiov, pos);
if (ret < 0) {
return ret;
diff --git a/block/parallels.c b/block/parallels.c
index cc9445879d..15bc97b759 100644
--- a/block/parallels.c
+++ b/block/parallels.c
@@ -220,23 +220,20 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num,
if (bs->backing) {
int64_t nb_cow_sectors = to_allocate * s->tracks;
int64_t nb_cow_bytes = nb_cow_sectors << BDRV_SECTOR_BITS;
- QEMUIOVector qiov;
- struct iovec iov = {
- .iov_len = nb_cow_bytes,
- .iov_base = qemu_blockalign(bs, nb_cow_bytes)
- };
- qemu_iovec_init_external(&qiov, &iov, 1);
+ QEMUIOVector qiov =
+ QEMU_IOVEC_INIT_BUF(qiov, qemu_blockalign(bs, nb_cow_bytes),
+ nb_cow_bytes);
ret = bdrv_co_preadv(bs->backing, idx * s->tracks * BDRV_SECTOR_SIZE,
nb_cow_bytes, &qiov, 0);
if (ret < 0) {
- qemu_vfree(iov.iov_base);
+ qemu_vfree(qemu_iovec_buf(&qiov));
return ret;
}
ret = bdrv_co_pwritev(bs->file, s->data_end * BDRV_SECTOR_SIZE,
nb_cow_bytes, &qiov, 0);
- qemu_vfree(iov.iov_base);
+ qemu_vfree(qemu_iovec_buf(&qiov));
if (ret < 0) {
return ret;
}
diff --git a/block/qcow.c b/block/qcow.c
index 25d2025fd0..10d2cf14b3 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -631,7 +631,6 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
int offset_in_cluster;
int ret = 0, n;
uint64_t cluster_offset;
- struct iovec hd_iov;
QEMUIOVector hd_qiov;
uint8_t *buf;
void *orig_buf;
@@ -664,9 +663,7 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
if (!cluster_offset) {
if (bs->backing) {
/* read from the base image */
- hd_iov.iov_base = (void *)buf;
- hd_iov.iov_len = n;
- qemu_iovec_init_external(&hd_qiov, &hd_iov, 1);
+ qemu_iovec_init_buf(&hd_qiov, buf, n);
qemu_co_mutex_unlock(&s->lock);
/* qcow2 emits this on bs->file instead of bs->backing */
BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
@@ -691,9 +688,7 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
ret = -EIO;
break;
}
- hd_iov.iov_base = (void *)buf;
- hd_iov.iov_len = n;
- qemu_iovec_init_external(&hd_qiov, &hd_iov, 1);
+ qemu_iovec_init_buf(&hd_qiov, buf, n);
qemu_co_mutex_unlock(&s->lock);
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
ret = bdrv_co_preadv(bs->file, cluster_offset + offset_in_cluster,
@@ -736,7 +731,6 @@ static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset,
int offset_in_cluster;
uint64_t cluster_offset;
int ret = 0, n;
- struct iovec hd_iov;
QEMUIOVector hd_qiov;
uint8_t *buf;
void *orig_buf;
@@ -782,9 +776,7 @@ static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset,
}
}
- hd_iov.iov_base = (void *)buf;
- hd_iov.iov_len = n;
- qemu_iovec_init_external(&hd_qiov, &hd_iov, 1);
+ qemu_iovec_init_buf(&hd_qiov, buf, n);
qemu_co_mutex_unlock(&s->lock);
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
ret = bdrv_co_pwritev(bs->file, cluster_offset + offset_in_cluster,
@@ -1065,7 +1057,6 @@ qcow_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
{
BDRVQcowState *s = bs->opaque;
QEMUIOVector hd_qiov;
- struct iovec iov;
z_stream strm;
int ret, out_len;
uint8_t *buf, *out_buf;
@@ -1131,11 +1122,7 @@ qcow_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
}
cluster_offset &= s->cluster_offset_mask;
- iov = (struct iovec) {
- .iov_base = out_buf,
- .iov_len = out_len,
- };
- qemu_iovec_init_external(&hd_qiov, &iov, 1);
+ qemu_iovec_init_buf(&hd_qiov, out_buf, out_len);
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_COMPRESSED);
ret = bdrv_co_pwritev(bs->file, cluster_offset, out_len, &hd_qiov, 0);
if (ret < 0) {
diff --git a/block/qcow2.c b/block/qcow2.c
index 1de5e24613..7fb2730f09 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -3898,7 +3898,6 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
{
BDRVQcow2State *s = bs->opaque;
QEMUIOVector hd_qiov;
- struct iovec iov;
int ret;
size_t out_len;
uint8_t *buf, *out_buf;
@@ -3964,11 +3963,7 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
goto fail;
}
- iov = (struct iovec) {
- .iov_base = out_buf,
- .iov_len = out_len,
- };
- qemu_iovec_init_external(&hd_qiov, &iov, 1);
+ qemu_iovec_init_buf(&hd_qiov, out_buf, out_len);
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_COMPRESSED);
ret = bdrv_co_pwritev(bs->file, cluster_offset, out_len, &hd_qiov, 0);
@@ -3994,7 +3989,6 @@ qcow2_co_preadv_compressed(BlockDriverState *bs,
int ret = 0, csize, nb_csectors;
uint64_t coffset;
uint8_t *buf, *out_buf;
- struct iovec iov;
QEMUIOVector local_qiov;
int offset_in_cluster = offset_into_cluster(s, offset);
@@ -4006,9 +4000,7 @@ qcow2_co_preadv_compressed(BlockDriverState *bs,
if (!buf) {
return -ENOMEM;
}
- iov.iov_base = buf;
- iov.iov_len = csize;
- qemu_iovec_init_external(&local_qiov, &iov, 1);
+ qemu_iovec_init_buf(&local_qiov, buf, csize);
out_buf = qemu_blockalign(bs, s->cluster_size);
diff --git a/block/qed-table.c b/block/qed-table.c
index 7df5680adb..c497bd4aec 100644
--- a/block/qed-table.c
+++ b/block/qed-table.c
@@ -21,16 +21,11 @@
/* Called with table_lock held. */
static int qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table)
{
- QEMUIOVector qiov;
+ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(
+ qiov, table->offsets, s->header.cluster_size * s->header.table_size);
int noffsets;
int i, ret;
- struct iovec iov = {
- .iov_base = table->offsets,
- .iov_len = s->header.cluster_size * s->header.table_size,
- };
- qemu_iovec_init_external(&qiov, &iov, 1);
-
trace_qed_read_table(s, offset, table);
qemu_co_mutex_unlock(&s->table_lock);
@@ -71,7 +66,6 @@ static int qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
unsigned int sector_mask = BDRV_SECTOR_SIZE / sizeof(uint64_t) - 1;
unsigned int start, end, i;
QEDTable *new_table;
- struct iovec iov;
QEMUIOVector qiov;
size_t len_bytes;
int ret;
@@ -85,11 +79,7 @@ static int qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
len_bytes = (end - start) * sizeof(uint64_t);
new_table = qemu_blockalign(s->bs, len_bytes);
- iov = (struct iovec) {
- .iov_base = new_table->offsets,
- .iov_len = len_bytes,
- };
- qemu_iovec_init_external(&qiov, &iov, 1);
+ qemu_iovec_init_buf(&qiov, new_table->offsets, len_bytes);
/* Byteswap table */
for (i = start; i < end; i++) {
diff --git a/block/qed.c b/block/qed.c
index 81a1bedd41..89af05d524 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -113,18 +113,13 @@ static int coroutine_fn qed_write_header(BDRVQEDState *s)
int nsectors = DIV_ROUND_UP(sizeof(QEDHeader), BDRV_SECTOR_SIZE);
size_t len = nsectors * BDRV_SECTOR_SIZE;
uint8_t *buf;
- struct iovec iov;
QEMUIOVector qiov;
int ret;
assert(s->allocating_acb || s->allocating_write_reqs_plugged);
buf = qemu_blockalign(s->bs, len);
- iov = (struct iovec) {
- .iov_base = buf,
- .iov_len = len,
- };
- qemu_iovec_init_external(&qiov, &iov, 1);
+ qemu_iovec_init_buf(&qiov, buf, len);
ret = bdrv_co_preadv(s->bs->file, 0, qiov.size, &qiov, 0);
if (ret < 0) {
@@ -916,7 +911,6 @@ static int coroutine_fn qed_copy_from_backing_file(BDRVQEDState *s,
{
QEMUIOVector qiov;
QEMUIOVector *backing_qiov = NULL;
- struct iovec iov;
int ret;
/* Skip copy entirely if there is no work to do */
@@ -924,11 +918,7 @@ static int coroutine_fn qed_copy_from_backing_file(BDRVQEDState *s,
return 0;
}
- iov = (struct iovec) {
- .iov_base = qemu_blockalign(s->bs, len),
- .iov_len = len,
- };
- qemu_iovec_init_external(&qiov, &iov, 1);
+ qemu_iovec_init_buf(&qiov, qemu_blockalign(s->bs, len), len);
ret = qed_read_backing_file(s, pos, &qiov, &backing_qiov);
@@ -949,7 +939,7 @@ static int coroutine_fn qed_copy_from_backing_file(BDRVQEDState *s,
}
ret = 0;
out:
- qemu_vfree(iov.iov_base);
+ qemu_vfree(qemu_iovec_buf(&qiov));
return ret;
}
@@ -1450,8 +1440,12 @@ static int coroutine_fn bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs,
BdrvRequestFlags flags)
{
BDRVQEDState *s = bs->opaque;
- QEMUIOVector qiov;
- struct iovec iov;
+
+ /*
+ * Zero writes start without an I/O buffer. If a buffer becomes necessary
+ * then it will be allocated during request processing.
+ */
+ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, bytes);
/* Fall back if the request is not aligned */
if (qed_offset_into_cluster(s, offset) ||
@@ -1459,13 +1453,6 @@ static int coroutine_fn bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs,
return -ENOTSUP;
}
- /* Zero writes start without an I/O buffer. If a buffer becomes necessary
- * then it will be allocated during request processing.
- */
- iov.iov_base = NULL;
- iov.iov_len = bytes;
-
- qemu_iovec_init_external(&qiov, &iov, 1);
return qed_co_request(bs, offset >> BDRV_SECTOR_BITS, &qiov,
bytes >> BDRV_SECTOR_BITS,
QED_AIOCB_WRITE | QED_AIOCB_ZERO);
diff --git a/block/stream.c b/block/stream.c
index 7a49ac0992..e14579ff80 100644
--- a/block/stream.c
+++ b/block/stream.c
@@ -41,14 +41,9 @@ static int coroutine_fn stream_populate(BlockBackend *blk,
int64_t offset, uint64_t bytes,
void *buf)
{
- struct iovec iov = {
- .iov_base = buf,
- .iov_len = bytes,
- };
- QEMUIOVector qiov;
+ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes);
assert(bytes < SIZE_MAX);
- qemu_iovec_init_external(&qiov, &iov, 1);
/* Copy-on-read the unallocated clusters */
return blk_co_preadv(blk, offset, qiov.size, &qiov, BDRV_REQ_COPY_ON_READ);
diff --git a/block/vmdk.c b/block/vmdk.c
index f4e68aa00b..d8c0c50390 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -1378,7 +1378,6 @@ static int vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset,
VmdkGrainMarker *data = NULL;
uLongf buf_len;
QEMUIOVector local_qiov;
- struct iovec iov;
int64_t write_offset;
int64_t write_end_sector;
@@ -1406,11 +1405,7 @@ static int vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset,
data->size = cpu_to_le32(buf_len);
n_bytes = buf_len + sizeof(VmdkGrainMarker);
- iov = (struct iovec) {
- .iov_base = data,
- .iov_len = n_bytes,
- };
- qemu_iovec_init_external(&local_qiov, &iov, 1);
+ qemu_iovec_init_buf(&local_qiov, data, n_bytes);
BLKDBG_EVENT(extent->file, BLKDBG_WRITE_COMPRESSED);
} else {
diff --git a/configure b/configure
index 05d72f1c56..694088a4ec 100755
--- a/configure
+++ b/configure
@@ -463,6 +463,7 @@ gnutls=""
nettle=""
gcrypt=""
gcrypt_hmac="no"
+auth_pam=""
vte=""
virglrenderer=""
tpm="yes"
@@ -1381,6 +1382,10 @@ for opt do
;;
--enable-gcrypt) gcrypt="yes"
;;
+ --disable-auth-pam) auth_pam="no"
+ ;;
+ --enable-auth-pam) auth_pam="yes"
+ ;;
--enable-rdma) rdma="yes"
;;
--disable-rdma) rdma="no"
@@ -1707,6 +1712,7 @@ disabled with --disable-FEATURE, default is enabled if available:
gnutls GNUTLS cryptography support
nettle nettle cryptography support
gcrypt libgcrypt cryptography support
+ auth-pam PAM access control
sdl SDL UI
sdl_image SDL Image support for icons
gtk gtk UI
@@ -2865,6 +2871,33 @@ fi
##########################################
+# PAM probe
+
+if test "$auth_pam" != "no"; then
+ cat > $TMPC <<EOF
+#include <security/pam_appl.h>
+#include <stdio.h>
+int main(void) {
+ const char *service_name = "qemu";
+ const char *user = "frank";
+ const struct pam_conv *pam_conv = NULL;
+ pam_handle_t *pamh = NULL;
+ pam_start(service_name, user, pam_conv, &pamh);
+ return 0;
+}
+EOF
+ if compile_prog "" "-lpam" ; then
+ auth_pam=yes
+ else
+ if test "$auth_pam" = "yes"; then
+ feature_not_found "PAM" "Install PAM development package"
+ else
+ auth_pam=no
+ fi
+ fi
+fi
+
+##########################################
# getifaddrs (for tests/test-io-channel-socket )
have_ifaddrs_h=yes
@@ -3172,20 +3205,6 @@ if test "$xkbcommon" != "no" ; then
fi
fi
-##########################################
-# fnmatch() probe, used for ACL routines
-fnmatch="no"
-cat > $TMPC << EOF
-#include <fnmatch.h>
-int main(void)
-{
- fnmatch("foo", "foo", 0);
- return 0;
-}
-EOF
-if compile_prog "" "" ; then
- fnmatch="yes"
-fi
##########################################
# xfsctl() probe, used for file-posix.c
@@ -6091,6 +6110,7 @@ echo "GNUTLS support $gnutls"
echo "libgcrypt $gcrypt"
echo "nettle $nettle $(echo_version $nettle $nettle_version)"
echo "libtasn1 $tasn1"
+echo "PAM $auth_pam"
echo "curses support $curses"
echo "virgl support $virglrenderer $(echo_version $virglrenderer $virgl_version)"
echo "curl support $curl"
@@ -6382,9 +6402,6 @@ if test "$xkbcommon" = "yes" ; then
echo "XKBCOMMON_CFLAGS=$xkbcommon_cflags" >> $config_host_mak
echo "XKBCOMMON_LIBS=$xkbcommon_libs" >> $config_host_mak
fi
-if test "$fnmatch" = "yes" ; then
- echo "CONFIG_FNMATCH=y" >> $config_host_mak
-fi
if test "$xfs" = "yes" ; then
echo "CONFIG_XFS=y" >> $config_host_mak
fi
@@ -6550,6 +6567,9 @@ fi
if test "$tasn1" = "yes" ; then
echo "CONFIG_TASN1=y" >> $config_host_mak
fi
+if test "$auth_pam" = "yes" ; then
+ echo "CONFIG_AUTH_PAM=y" >> $config_host_mak
+fi
if test "$have_ifaddrs_h" = "yes" ; then
echo "HAVE_IFADDRS_H=y" >> $config_host_mak
fi
diff --git a/crypto/tlssession.c b/crypto/tlssession.c
index 0dedd4af52..c3a920dfe8 100644
--- a/crypto/tlssession.c
+++ b/crypto/tlssession.c
@@ -24,7 +24,7 @@
#include "crypto/tlscredspsk.h"
#include "crypto/tlscredsx509.h"
#include "qapi/error.h"
-#include "qemu/acl.h"
+#include "authz/base.h"
#include "trace.h"
#ifdef CONFIG_GNUTLS
@@ -37,7 +37,7 @@ struct QCryptoTLSSession {
QCryptoTLSCreds *creds;
gnutls_session_t handle;
char *hostname;
- char *aclname;
+ char *authzid;
bool handshakeComplete;
QCryptoTLSSessionWriteFunc writeFunc;
QCryptoTLSSessionReadFunc readFunc;
@@ -56,7 +56,7 @@ qcrypto_tls_session_free(QCryptoTLSSession *session)
gnutls_deinit(session->handle);
g_free(session->hostname);
g_free(session->peername);
- g_free(session->aclname);
+ g_free(session->authzid);
object_unref(OBJECT(session->creds));
g_free(session);
}
@@ -95,7 +95,7 @@ qcrypto_tls_session_pull(void *opaque, void *buf, size_t len)
QCryptoTLSSession *
qcrypto_tls_session_new(QCryptoTLSCreds *creds,
const char *hostname,
- const char *aclname,
+ const char *authzid,
QCryptoTLSCredsEndpoint endpoint,
Error **errp)
{
@@ -105,13 +105,13 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds,
session = g_new0(QCryptoTLSSession, 1);
trace_qcrypto_tls_session_new(
session, creds, hostname ? hostname : "<none>",
- aclname ? aclname : "<none>", endpoint);
+ authzid ? authzid : "<none>", endpoint);
if (hostname) {
session->hostname = g_strdup(hostname);
}
- if (aclname) {
- session->aclname = g_strdup(aclname);
+ if (authzid) {
+ session->authzid = g_strdup(authzid);
}
session->creds = creds;
object_ref(OBJECT(creds));
@@ -262,6 +262,7 @@ qcrypto_tls_session_check_certificate(QCryptoTLSSession *session,
unsigned int nCerts, i;
time_t now;
gnutls_x509_crt_t cert = NULL;
+ Error *err = NULL;
now = time(NULL);
if (now == ((time_t)-1)) {
@@ -349,19 +350,17 @@ qcrypto_tls_session_check_certificate(QCryptoTLSSession *session,
gnutls_strerror(ret));
goto error;
}
- if (session->aclname) {
- qemu_acl *acl = qemu_acl_find(session->aclname);
- int allow;
- if (!acl) {
- error_setg(errp, "Cannot find ACL %s",
- session->aclname);
+ if (session->authzid) {
+ bool allow;
+
+ allow = qauthz_is_allowed_by_id(session->authzid,
+ session->peername, &err);
+ if (err) {
+ error_propagate(errp, err);
goto error;
}
-
- allow = qemu_acl_party_is_allowed(acl, session->peername);
-
if (!allow) {
- error_setg(errp, "TLS x509 ACL check for %s is denied",
+ error_setg(errp, "TLS x509 authz check for %s is denied",
session->peername);
goto error;
}
@@ -555,7 +554,7 @@ qcrypto_tls_session_get_peer_name(QCryptoTLSSession *session)
QCryptoTLSSession *
qcrypto_tls_session_new(QCryptoTLSCreds *creds G_GNUC_UNUSED,
const char *hostname G_GNUC_UNUSED,
- const char *aclname G_GNUC_UNUSED,
+ const char *authzid G_GNUC_UNUSED,
QCryptoTLSCredsEndpoint endpoint G_GNUC_UNUSED,
Error **errp)
{
diff --git a/crypto/trace-events b/crypto/trace-events
index 597389b73c..a38ad7b787 100644
--- a/crypto/trace-events
+++ b/crypto/trace-events
@@ -19,5 +19,5 @@ qcrypto_tls_creds_x509_load_cert(void *creds, int isServer, const char *file) "T
qcrypto_tls_creds_x509_load_cert_list(void *creds, const char *file) "TLS creds x509 load cert list creds=%p file=%s"
# crypto/tlssession.c
-qcrypto_tls_session_new(void *session, void *creds, const char *hostname, const char *aclname, int endpoint) "TLS session new session=%p creds=%p hostname=%s aclname=%s endpoint=%d"
+qcrypto_tls_session_new(void *session, void *creds, const char *hostname, const char *authzid, int endpoint) "TLS session new session=%p creds=%p hostname=%s authzid=%s endpoint=%d"
qcrypto_tls_session_check_creds(void *session, const char *status) "TLS session check creds session=%p status=%s"
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index e11e6e45d0..0cc3c590b9 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -28,9 +28,28 @@
#include "hw/virtio/virtio-bus.h"
#include "hw/virtio/virtio-access.h"
-/* We don't support discard yet, hide associated config fields. */
+/* Config size before the discard support (hide associated config fields) */
#define VIRTIO_BLK_CFG_SIZE offsetof(struct virtio_blk_config, \
max_discard_sectors)
+/*
+ * Starting from the discard feature, we can use this array to properly
+ * set the config size depending on the features enabled.
+ */
+static VirtIOFeature feature_sizes[] = {
+ {.flags = 1ULL << VIRTIO_BLK_F_DISCARD,
+ .end = virtio_endof(struct virtio_blk_config, discard_sector_alignment)},
+ {.flags = 1ULL << VIRTIO_BLK_F_WRITE_ZEROES,
+ .end = virtio_endof(struct virtio_blk_config, write_zeroes_may_unmap)},
+ {}
+};
+
+static void virtio_blk_set_config_size(VirtIOBlock *s, uint64_t host_features)
+{
+ s->config_size = MAX(VIRTIO_BLK_CFG_SIZE,
+ virtio_feature_get_config_size(feature_sizes, host_features));
+
+ assert(s->config_size <= sizeof(struct virtio_blk_config));
+}
static void virtio_blk_init_request(VirtIOBlock *s, VirtQueue *vq,
VirtIOBlockReq *req)
@@ -65,7 +84,7 @@ static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status)
}
static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error,
- bool is_read)
+ bool is_read, bool acct_failed)
{
VirtIOBlock *s = req->dev;
BlockErrorAction action = blk_get_error_action(s->blk, is_read, error);
@@ -78,7 +97,9 @@ static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error,
s->rq = req;
} else if (action == BLOCK_ERROR_ACTION_REPORT) {
virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
- block_acct_failed(blk_get_stats(s->blk), &req->acct);
+ if (acct_failed) {
+ block_acct_failed(blk_get_stats(s->blk), &req->acct);
+ }
virtio_blk_free_request(req);
}
@@ -116,7 +137,7 @@ static void virtio_blk_rw_complete(void *opaque, int ret)
* the memory until the request is completed (which will
* happen on the other side of the migration).
*/
- if (virtio_blk_handle_rw_error(req, -ret, is_read)) {
+ if (virtio_blk_handle_rw_error(req, -ret, is_read, true)) {
continue;
}
}
@@ -135,7 +156,7 @@ static void virtio_blk_flush_complete(void *opaque, int ret)
aio_context_acquire(blk_get_aio_context(s->conf.conf.blk));
if (ret) {
- if (virtio_blk_handle_rw_error(req, -ret, 0)) {
+ if (virtio_blk_handle_rw_error(req, -ret, 0, true)) {
goto out;
}
}
@@ -148,6 +169,30 @@ out:
aio_context_release(blk_get_aio_context(s->conf.conf.blk));
}
+static void virtio_blk_discard_write_zeroes_complete(void *opaque, int ret)
+{
+ VirtIOBlockReq *req = opaque;
+ VirtIOBlock *s = req->dev;
+ bool is_write_zeroes = (virtio_ldl_p(VIRTIO_DEVICE(s), &req->out.type) &
+ ~VIRTIO_BLK_T_BARRIER) == VIRTIO_BLK_T_WRITE_ZEROES;
+
+ aio_context_acquire(blk_get_aio_context(s->conf.conf.blk));
+ if (ret) {
+ if (virtio_blk_handle_rw_error(req, -ret, false, is_write_zeroes)) {
+ goto out;
+ }
+ }
+
+ virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
+ if (is_write_zeroes) {
+ block_acct_done(blk_get_stats(s->blk), &req->acct);
+ }
+ virtio_blk_free_request(req);
+
+out:
+ aio_context_release(blk_get_aio_context(s->conf.conf.blk));
+}
+
#ifdef __linux__
typedef struct {
@@ -243,7 +288,7 @@ static int virtio_blk_handle_scsi_req(VirtIOBlockReq *req)
*/
scsi = (void *)elem->in_sg[elem->in_num - 2].iov_base;
- if (!blk->conf.scsi) {
+ if (!virtio_has_feature(blk->host_features, VIRTIO_BLK_F_SCSI)) {
status = VIRTIO_BLK_S_UNSUPP;
goto fail;
}
@@ -481,6 +526,84 @@ static bool virtio_blk_sect_range_ok(VirtIOBlock *dev,
return true;
}
+static uint8_t virtio_blk_handle_discard_write_zeroes(VirtIOBlockReq *req,
+ struct virtio_blk_discard_write_zeroes *dwz_hdr, bool is_write_zeroes)
+{
+ VirtIOBlock *s = req->dev;
+ VirtIODevice *vdev = VIRTIO_DEVICE(s);
+ uint64_t sector;
+ uint32_t num_sectors, flags, max_sectors;
+ uint8_t err_status;
+ int bytes;
+
+ sector = virtio_ldq_p(vdev, &dwz_hdr->sector);
+ num_sectors = virtio_ldl_p(vdev, &dwz_hdr->num_sectors);
+ flags = virtio_ldl_p(vdev, &dwz_hdr->flags);
+ max_sectors = is_write_zeroes ? s->conf.max_write_zeroes_sectors :
+ s->conf.max_discard_sectors;
+
+ /*
+ * max_sectors is at most BDRV_REQUEST_MAX_SECTORS, this check
+ * make us sure that "num_sectors << BDRV_SECTOR_BITS" can fit in
+ * the integer variable.
+ */
+ if (unlikely(num_sectors > max_sectors)) {
+ err_status = VIRTIO_BLK_S_IOERR;
+ goto err;
+ }
+
+ bytes = num_sectors << BDRV_SECTOR_BITS;
+
+ if (unlikely(!virtio_blk_sect_range_ok(s, sector, bytes))) {
+ err_status = VIRTIO_BLK_S_IOERR;
+ goto err;
+ }
+
+ /*
+ * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for discard
+ * and write zeroes commands if any unknown flag is set.
+ */
+ if (unlikely(flags & ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) {
+ err_status = VIRTIO_BLK_S_UNSUPP;
+ goto err;
+ }
+
+ if (is_write_zeroes) { /* VIRTIO_BLK_T_WRITE_ZEROES */
+ int blk_aio_flags = 0;
+
+ if (flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP) {
+ blk_aio_flags |= BDRV_REQ_MAY_UNMAP;
+ }
+
+ block_acct_start(blk_get_stats(s->blk), &req->acct, bytes,
+ BLOCK_ACCT_WRITE);
+
+ blk_aio_pwrite_zeroes(s->blk, sector << BDRV_SECTOR_BITS,
+ bytes, blk_aio_flags,
+ virtio_blk_discard_write_zeroes_complete, req);
+ } else { /* VIRTIO_BLK_T_DISCARD */
+ /*
+ * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for
+ * discard commands if the unmap flag is set.
+ */
+ if (unlikely(flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) {
+ err_status = VIRTIO_BLK_S_UNSUPP;
+ goto err;
+ }
+
+ blk_aio_pdiscard(s->blk, sector << BDRV_SECTOR_BITS, bytes,
+ virtio_blk_discard_write_zeroes_complete, req);
+ }
+
+ return VIRTIO_BLK_S_OK;
+
+err:
+ if (is_write_zeroes) {
+ block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_WRITE);
+ }
+ return err_status;
+}
+
static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
{
uint32_t type;
@@ -582,6 +705,47 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
virtio_blk_free_request(req);
break;
}
+ /*
+ * VIRTIO_BLK_T_DISCARD and VIRTIO_BLK_T_WRITE_ZEROES are defined with
+ * VIRTIO_BLK_T_OUT flag set. We masked this flag in the switch statement,
+ * so we must mask it for these requests, then we will check if it is set.
+ */
+ case VIRTIO_BLK_T_DISCARD & ~VIRTIO_BLK_T_OUT:
+ case VIRTIO_BLK_T_WRITE_ZEROES & ~VIRTIO_BLK_T_OUT:
+ {
+ struct virtio_blk_discard_write_zeroes dwz_hdr;
+ size_t out_len = iov_size(out_iov, out_num);
+ bool is_write_zeroes = (type & ~VIRTIO_BLK_T_BARRIER) ==
+ VIRTIO_BLK_T_WRITE_ZEROES;
+ uint8_t err_status;
+
+ /*
+ * Unsupported if VIRTIO_BLK_T_OUT is not set or the request contains
+ * more than one segment.
+ */
+ if (unlikely(!(type & VIRTIO_BLK_T_OUT) ||
+ out_len > sizeof(dwz_hdr))) {
+ virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);
+ virtio_blk_free_request(req);
+ return 0;
+ }
+
+ if (unlikely(iov_to_buf(out_iov, out_num, 0, &dwz_hdr,
+ sizeof(dwz_hdr)) != sizeof(dwz_hdr))) {
+ virtio_error(vdev, "virtio-blk discard/write_zeroes header"
+ " too short");
+ return -1;
+ }
+
+ err_status = virtio_blk_handle_discard_write_zeroes(req, &dwz_hdr,
+ is_write_zeroes);
+ if (err_status != VIRTIO_BLK_S_OK) {
+ virtio_blk_req_complete(req, err_status);
+ virtio_blk_free_request(req);
+ }
+
+ break;
+ }
default:
virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);
virtio_blk_free_request(req);
@@ -765,8 +929,25 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config)
blkcfg.alignment_offset = 0;
blkcfg.wce = blk_enable_write_cache(s->blk);
virtio_stw_p(vdev, &blkcfg.num_queues, s->conf.num_queues);
- memcpy(config, &blkcfg, VIRTIO_BLK_CFG_SIZE);
- QEMU_BUILD_BUG_ON(VIRTIO_BLK_CFG_SIZE > sizeof(blkcfg));
+ if (virtio_has_feature(s->host_features, VIRTIO_BLK_F_DISCARD)) {
+ virtio_stl_p(vdev, &blkcfg.max_discard_sectors,
+ s->conf.max_discard_sectors);
+ virtio_stl_p(vdev, &blkcfg.discard_sector_alignment,
+ blk_size >> BDRV_SECTOR_BITS);
+ /*
+ * We support only one segment per request since multiple segments
+ * are not widely used and there are no userspace APIs that allow
+ * applications to submit multiple segments in a single call.
+ */
+ virtio_stl_p(vdev, &blkcfg.max_discard_seg, 1);
+ }
+ if (virtio_has_feature(s->host_features, VIRTIO_BLK_F_WRITE_ZEROES)) {
+ virtio_stl_p(vdev, &blkcfg.max_write_zeroes_sectors,
+ s->conf.max_write_zeroes_sectors);
+ blkcfg.write_zeroes_may_unmap = 1;
+ virtio_stl_p(vdev, &blkcfg.max_write_zeroes_seg, 1);
+ }
+ memcpy(config, &blkcfg, s->config_size);
}
static void virtio_blk_set_config(VirtIODevice *vdev, const uint8_t *config)
@@ -774,8 +955,7 @@ static void virtio_blk_set_config(VirtIODevice *vdev, const uint8_t *config)
VirtIOBlock *s = VIRTIO_BLK(vdev);
struct virtio_blk_config blkcfg;
- memcpy(&blkcfg, config, VIRTIO_BLK_CFG_SIZE);
- QEMU_BUILD_BUG_ON(VIRTIO_BLK_CFG_SIZE > sizeof(blkcfg));
+ memcpy(&blkcfg, config, s->config_size);
aio_context_acquire(blk_get_aio_context(s->blk));
blk_set_enable_write_cache(s->blk, blkcfg.wce != 0);
@@ -787,12 +967,15 @@ static uint64_t virtio_blk_get_features(VirtIODevice *vdev, uint64_t features,
{
VirtIOBlock *s = VIRTIO_BLK(vdev);
+ /* Firstly sync all virtio-blk possible supported features */
+ features |= s->host_features;
+
virtio_add_feature(&features, VIRTIO_BLK_F_SEG_MAX);
virtio_add_feature(&features, VIRTIO_BLK_F_GEOMETRY);
virtio_add_feature(&features, VIRTIO_BLK_F_TOPOLOGY);
virtio_add_feature(&features, VIRTIO_BLK_F_BLK_SIZE);
if (virtio_has_feature(features, VIRTIO_F_VERSION_1)) {
- if (s->conf.scsi) {
+ if (virtio_has_feature(s->host_features, VIRTIO_BLK_F_SCSI)) {
error_setg(errp, "Please set scsi=off for virtio-blk devices in order to use virtio 1.0");
return 0;
}
@@ -801,9 +984,6 @@ static uint64_t virtio_blk_get_features(VirtIODevice *vdev, uint64_t features,
virtio_add_feature(&features, VIRTIO_BLK_F_SCSI);
}
- if (s->conf.config_wce) {
- virtio_add_feature(&features, VIRTIO_BLK_F_CONFIG_WCE);
- }
if (blk_enable_write_cache(s->blk)) {
virtio_add_feature(&features, VIRTIO_BLK_F_WCE);
}
@@ -958,7 +1138,28 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
return;
}
- virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK, VIRTIO_BLK_CFG_SIZE);
+ if (virtio_has_feature(s->host_features, VIRTIO_BLK_F_DISCARD) &&
+ (!conf->max_discard_sectors ||
+ conf->max_discard_sectors > BDRV_REQUEST_MAX_SECTORS)) {
+ error_setg(errp, "invalid max-discard-sectors property (%" PRIu32 ")"
+ ", must be between 1 and %d",
+ conf->max_discard_sectors, (int)BDRV_REQUEST_MAX_SECTORS);
+ return;
+ }
+
+ if (virtio_has_feature(s->host_features, VIRTIO_BLK_F_WRITE_ZEROES) &&
+ (!conf->max_write_zeroes_sectors ||
+ conf->max_write_zeroes_sectors > BDRV_REQUEST_MAX_SECTORS)) {
+ error_setg(errp, "invalid max-write-zeroes-sectors property (%" PRIu32
+ "), must be between 1 and %d",
+ conf->max_write_zeroes_sectors,
+ (int)BDRV_REQUEST_MAX_SECTORS);
+ return;
+ }
+
+ virtio_blk_set_config_size(s, s->host_features);
+
+ virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK, s->config_size);
s->blk = conf->conf.blk;
s->rq = NULL;
@@ -1017,9 +1218,11 @@ static Property virtio_blk_properties[] = {
DEFINE_BLOCK_ERROR_PROPERTIES(VirtIOBlock, conf.conf),
DEFINE_BLOCK_CHS_PROPERTIES(VirtIOBlock, conf.conf),
DEFINE_PROP_STRING("serial", VirtIOBlock, conf.serial),
- DEFINE_PROP_BIT("config-wce", VirtIOBlock, conf.config_wce, 0, true),
+ DEFINE_PROP_BIT64("config-wce", VirtIOBlock, host_features,
+ VIRTIO_BLK_F_CONFIG_WCE, true),
#ifdef __linux__
- DEFINE_PROP_BIT("scsi", VirtIOBlock, conf.scsi, 0, false),
+ DEFINE_PROP_BIT64("scsi", VirtIOBlock, host_features,
+ VIRTIO_BLK_F_SCSI, false),
#endif
DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0,
true),
@@ -1027,6 +1230,14 @@ static Property virtio_blk_properties[] = {
DEFINE_PROP_UINT16("queue-size", VirtIOBlock, conf.queue_size, 128),
DEFINE_PROP_LINK("iothread", VirtIOBlock, conf.iothread, TYPE_IOTHREAD,
IOThread *),
+ DEFINE_PROP_BIT64("discard", VirtIOBlock, host_features,
+ VIRTIO_BLK_F_DISCARD, true),
+ DEFINE_PROP_BIT64("write-zeroes", VirtIOBlock, host_features,
+ VIRTIO_BLK_F_WRITE_ZEROES, true),
+ DEFINE_PROP_UINT32("max-discard-sectors", VirtIOBlock,
+ conf.max_discard_sectors, BDRV_REQUEST_MAX_SECTORS),
+ DEFINE_PROP_UINT32("max-write-zeroes-sectors", VirtIOBlock,
+ conf.max_write_zeroes_sectors, BDRV_REQUEST_MAX_SECTORS),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 077fbd182a..766ca5899d 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -33,6 +33,8 @@ GlobalProperty hw_compat_3_1[] = {
{ "usb-kbd", "serial", "42" },
{ "usb-mouse", "serial", "42" },
{ "usb-kbd", "serial", "42" },
+ { "virtio-blk-device", "discard", "false" },
+ { "virtio-blk-device", "write-zeroes", "false" },
};
const size_t hw_compat_3_1_len = G_N_ELEMENTS(hw_compat_3_1);
diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c
index 39e473f9c2..1b0f66cc08 100644
--- a/hw/ide/atapi.c
+++ b/hw/ide/atapi.c
@@ -174,16 +174,15 @@ static void cd_read_sector_cb(void *opaque, int ret)
static int cd_read_sector(IDEState *s)
{
+ void *buf;
+
if (s->cd_sector_size != 2048 && s->cd_sector_size != 2352) {
block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_READ);
return -EINVAL;
}
- s->iov.iov_base = (s->cd_sector_size == 2352) ?
- s->io_buffer + 16 : s->io_buffer;
-
- s->iov.iov_len = ATAPI_SECTOR_SIZE;
- qemu_iovec_init_external(&s->qiov, &s->iov, 1);
+ buf = (s->cd_sector_size == 2352) ? s->io_buffer + 16 : s->io_buffer;
+ qemu_iovec_init_buf(&s->qiov, buf, ATAPI_SECTOR_SIZE);
trace_cd_read_sector(s->lba);
@@ -421,9 +420,8 @@ static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret)
data_offset = 0;
}
trace_ide_atapi_cmd_read_dma_cb_aio(s, s->lba, n);
- s->bus->dma->iov.iov_base = (void *)(s->io_buffer + data_offset);
- s->bus->dma->iov.iov_len = n * ATAPI_SECTOR_SIZE;
- qemu_iovec_init_external(&s->bus->dma->qiov, &s->bus->dma->iov, 1);
+ qemu_iovec_init_buf(&s->bus->dma->qiov, s->io_buffer + data_offset,
+ n * ATAPI_SECTOR_SIZE);
s->bus->dma->aiocb = ide_buffered_readv(s, (int64_t)s->lba << 2,
&s->bus->dma->qiov, n * 4,
diff --git a/hw/ide/core.c b/hw/ide/core.c
index 84832008b8..6afadf894f 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -629,13 +629,15 @@ static void ide_buffered_readv_cb(void *opaque, int ret)
IDEBufferedRequest *req = opaque;
if (!req->orphaned) {
if (!ret) {
- qemu_iovec_from_buf(req->original_qiov, 0, req->iov.iov_base,
+ assert(req->qiov.size == req->original_qiov->size);
+ qemu_iovec_from_buf(req->original_qiov, 0,
+ req->qiov.local_iov.iov_base,
req->original_qiov->size);
}
req->original_cb(req->original_opaque, ret);
}
QLIST_REMOVE(req, list);
- qemu_vfree(req->iov.iov_base);
+ qemu_vfree(qemu_iovec_buf(&req->qiov));
g_free(req);
}
@@ -660,9 +662,8 @@ BlockAIOCB *ide_buffered_readv(IDEState *s, int64_t sector_num,
req->original_qiov = iov;
req->original_cb = cb;
req->original_opaque = opaque;
- req->iov.iov_base = qemu_blockalign(blk_bs(s->blk), iov->size);
- req->iov.iov_len = iov->size;
- qemu_iovec_init_external(&req->qiov, &req->iov, 1);
+ qemu_iovec_init_buf(&req->qiov, blk_blockalign(s->blk, iov->size),
+ iov->size);
aioreq = blk_aio_preadv(s->blk, sector_num << BDRV_SECTOR_BITS,
&req->qiov, 0, ide_buffered_readv_cb, req);
@@ -774,9 +775,7 @@ static void ide_sector_read(IDEState *s)
return;
}
- s->iov.iov_base = s->io_buffer;
- s->iov.iov_len = n * BDRV_SECTOR_SIZE;
- qemu_iovec_init_external(&s->qiov, &s->iov, 1);
+ qemu_iovec_init_buf(&s->qiov, s->io_buffer, n * BDRV_SECTOR_SIZE);
block_acct_start(blk_get_stats(s->blk), &s->acct,
n * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
@@ -1045,9 +1044,7 @@ static void ide_sector_write(IDEState *s)
return;
}
- s->iov.iov_base = s->io_buffer;
- s->iov.iov_len = n * BDRV_SECTOR_SIZE;
- qemu_iovec_init_external(&s->qiov, &s->iov, 1);
+ qemu_iovec_init_buf(&s->qiov, s->io_buffer, n * BDRV_SECTOR_SIZE);
block_acct_start(blk_get_stats(s->blk), &s->acct,
n * BDRV_SECTOR_SIZE, BLOCK_ACCT_WRITE);
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 3f319ef723..6e6b146022 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -82,29 +82,17 @@ static inline __virtio16 *virtio_net_rsc_ext_num_dupacks(
#endif
-/*
- * Calculate the number of bytes up to and including the given 'field' of
- * 'container'.
- */
-#define endof(container, field) \
- (offsetof(container, field) + sizeof_field(container, field))
-
-typedef struct VirtIOFeature {
- uint64_t flags;
- size_t end;
-} VirtIOFeature;
-
static VirtIOFeature feature_sizes[] = {
{.flags = 1ULL << VIRTIO_NET_F_MAC,
- .end = endof(struct virtio_net_config, mac)},
+ .end = virtio_endof(struct virtio_net_config, mac)},
{.flags = 1ULL << VIRTIO_NET_F_STATUS,
- .end = endof(struct virtio_net_config, status)},
+ .end = virtio_endof(struct virtio_net_config, status)},
{.flags = 1ULL << VIRTIO_NET_F_MQ,
- .end = endof(struct virtio_net_config, max_virtqueue_pairs)},
+ .end = virtio_endof(struct virtio_net_config, max_virtqueue_pairs)},
{.flags = 1ULL << VIRTIO_NET_F_MTU,
- .end = endof(struct virtio_net_config, mtu)},
+ .end = virtio_endof(struct virtio_net_config, mtu)},
{.flags = 1ULL << VIRTIO_NET_F_SPEED_DUPLEX,
- .end = endof(struct virtio_net_config, duplex)},
+ .end = virtio_endof(struct virtio_net_config, duplex)},
{}
};
@@ -2580,15 +2568,10 @@ static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx,
static void virtio_net_set_config_size(VirtIONet *n, uint64_t host_features)
{
- int i, config_size = 0;
virtio_add_feature(&host_features, VIRTIO_NET_F_MAC);
- for (i = 0; feature_sizes[i].flags != 0; i++) {
- if (host_features & feature_sizes[i].flags) {
- config_size = MAX(feature_sizes[i].end, config_size);
- }
- }
- n->config_size = config_size;
+ n->config_size = virtio_feature_get_config_size(feature_sizes,
+ host_features);
}
void virtio_net_set_netclient_name(VirtIONet *n, const char *name,
diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c
index f1d20fa1b9..4ee4fc5a89 100644
--- a/hw/usb/dev-mtp.c
+++ b/hw/usb/dev-mtp.c
@@ -11,17 +11,16 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
+#include "qemu/error-report.h"
#include <wchar.h>
#include <dirent.h>
#include <sys/statvfs.h>
-#ifdef CONFIG_INOTIFY1
-#include <sys/inotify.h>
-#include "qemu/main-loop.h"
-#endif
+
#include "qemu-common.h"
#include "qemu/iov.h"
+#include "qemu/filemonitor.h"
#include "trace.h"
#include "hw/usb.h"
#include "desc.h"
@@ -132,7 +131,6 @@ enum {
EP_EVENT,
};
-#ifdef CONFIG_INOTIFY1
typedef struct MTPMonEntry MTPMonEntry;
struct MTPMonEntry {
@@ -141,7 +139,6 @@ struct MTPMonEntry {
QTAILQ_ENTRY(MTPMonEntry) next;
};
-#endif
struct MTPControl {
uint16_t code;
@@ -172,10 +169,8 @@ struct MTPObject {
char *name;
char *path;
struct stat stat;
-#ifdef CONFIG_INOTIFY1
- /* inotify watch cookie */
- int watchfd;
-#endif
+ /* file monitor watch id */
+ int watchid;
MTPObject *parent;
uint32_t nchildren;
QLIST_HEAD(, MTPObject) children;
@@ -198,11 +193,8 @@ struct MTPState {
bool readonly;
QTAILQ_HEAD(, MTPObject) objects;
-#ifdef CONFIG_INOTIFY1
- /* inotify descriptor */
- int inotifyfd;
+ QFileMonitor *file_monitor;
QTAILQ_HEAD(, MTPMonEntry) events;
-#endif
/* Responder is expecting a write operation */
bool write_pending;
struct {
@@ -383,7 +375,7 @@ static const USBDesc desc = {
/* ----------------------------------------------------------------------- */
static MTPObject *usb_mtp_object_alloc(MTPState *s, uint32_t handle,
- MTPObject *parent, char *name)
+ MTPObject *parent, const char *name)
{
MTPObject *o = g_new0(MTPObject, 1);
@@ -391,6 +383,7 @@ static MTPObject *usb_mtp_object_alloc(MTPState *s, uint32_t handle,
goto ignore;
}
+ o->watchid = -1;
o->handle = handle;
o->parent = parent;
o->name = g_strdup(name);
@@ -437,6 +430,10 @@ static void usb_mtp_object_free(MTPState *s, MTPObject *o)
trace_usb_mtp_object_free(s->dev.addr, o->handle, o->path);
+ if (o->watchid != -1 && s->file_monitor) {
+ qemu_file_monitor_remove_watch(s->file_monitor, o->path, o->watchid);
+ }
+
QTAILQ_REMOVE(&s->objects, o, next);
if (o->parent) {
QLIST_REMOVE(o, list);
@@ -465,7 +462,7 @@ static MTPObject *usb_mtp_object_lookup(MTPState *s, uint32_t handle)
}
static MTPObject *usb_mtp_add_child(MTPState *s, MTPObject *o,
- char *name)
+ const char *name)
{
MTPObject *child =
usb_mtp_object_alloc(s, s->next_handle++, o, name);
@@ -484,10 +481,14 @@ static MTPObject *usb_mtp_add_child(MTPState *s, MTPObject *o,
}
static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent,
- char *name, int len)
+ const char *name, int len)
{
MTPObject *iter;
+ if (len == -1) {
+ len = strlen(name);
+ }
+
QLIST_FOREACH(iter, &parent->children, list) {
if (strncmp(iter->name, name, len) == 0) {
return iter;
@@ -497,13 +498,12 @@ static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent,
return NULL;
}
-#ifdef CONFIG_INOTIFY1
-static MTPObject *usb_mtp_object_lookup_wd(MTPState *s, int wd)
+static MTPObject *usb_mtp_object_lookup_id(MTPState *s, int id)
{
MTPObject *iter;
QTAILQ_FOREACH(iter, &s->objects, next) {
- if (iter->watchfd == wd) {
+ if (iter->watchid == id) {
return iter;
}
}
@@ -511,160 +511,103 @@ static MTPObject *usb_mtp_object_lookup_wd(MTPState *s, int wd)
return NULL;
}
-static void inotify_watchfn(void *arg)
+static void file_monitor_event(int id,
+ QFileMonitorEvent ev,
+ const char *name,
+ void *opaque)
{
- MTPState *s = arg;
- ssize_t bytes;
- /* From the man page: atleast one event can be read */
- int pos;
- char buf[sizeof(struct inotify_event) + NAME_MAX + 1];
-
- for (;;) {
- bytes = read(s->inotifyfd, buf, sizeof(buf));
- pos = 0;
-
- if (bytes <= 0) {
- /* Better luck next time */
+ MTPState *s = opaque;
+ MTPObject *parent = usb_mtp_object_lookup_id(s, id);
+ MTPMonEntry *entry = NULL;
+ MTPObject *o;
+
+ if (!parent) {
+ return;
+ }
+
+ switch (ev) {
+ case QFILE_MONITOR_EVENT_CREATED:
+ if (usb_mtp_object_lookup_name(parent, name, -1)) {
+ /* Duplicate create event */
return;
}
+ entry = g_new0(MTPMonEntry, 1);
+ entry->handle = s->next_handle;
+ entry->event = EVT_OBJ_ADDED;
+ o = usb_mtp_add_child(s, parent, name);
+ if (!o) {
+ g_free(entry);
+ return;
+ }
+ trace_usb_mtp_file_monitor_event(s->dev.addr, name, "Obj Added");
+ break;
+ case QFILE_MONITOR_EVENT_DELETED:
/*
- * TODO: Ignore initiator initiated events.
- * For now we are good because the store is RO
+ * The kernel issues a IN_IGNORED event
+ * when a dir containing a watchpoint is
+ * deleted, so we don't have to delete the
+ * watchpoint
*/
- while (bytes > 0) {
- char *p = buf + pos;
- struct inotify_event *event = (struct inotify_event *)p;
- int watchfd = 0;
- uint32_t mask = event->mask & (IN_CREATE | IN_DELETE |
- IN_MODIFY | IN_IGNORED);
- MTPObject *parent = usb_mtp_object_lookup_wd(s, event->wd);
- MTPMonEntry *entry = NULL;
- MTPObject *o;
-
- pos = pos + sizeof(struct inotify_event) + event->len;
- bytes = bytes - pos;
-
- if (!parent) {
- continue;
- }
-
- switch (mask) {
- case IN_CREATE:
- if (usb_mtp_object_lookup_name
- (parent, event->name, event->len)) {
- /* Duplicate create event */
- continue;
- }
- entry = g_new0(MTPMonEntry, 1);
- entry->handle = s->next_handle;
- entry->event = EVT_OBJ_ADDED;
- o = usb_mtp_add_child(s, parent, event->name);
- if (!o) {
- g_free(entry);
- continue;
- }
- o->watchfd = watchfd;
- trace_usb_mtp_inotify_event(s->dev.addr, event->name,
- event->mask, "Obj Added");
- break;
-
- case IN_DELETE:
- /*
- * The kernel issues a IN_IGNORED event
- * when a dir containing a watchpoint is
- * deleted, so we don't have to delete the
- * watchpoint
- */
- o = usb_mtp_object_lookup_name(parent, event->name, event->len);
- if (!o) {
- continue;
- }
- entry = g_new0(MTPMonEntry, 1);
- entry->handle = o->handle;
- entry->event = EVT_OBJ_REMOVED;
- trace_usb_mtp_inotify_event(s->dev.addr, o->path,
- event->mask, "Obj Deleted");
- usb_mtp_object_free(s, o);
- break;
-
- case IN_MODIFY:
- o = usb_mtp_object_lookup_name(parent, event->name, event->len);
- if (!o) {
- continue;
- }
- entry = g_new0(MTPMonEntry, 1);
- entry->handle = o->handle;
- entry->event = EVT_OBJ_INFO_CHANGED;
- trace_usb_mtp_inotify_event(s->dev.addr, o->path,
- event->mask, "Obj Modified");
- break;
-
- case IN_IGNORED:
- trace_usb_mtp_inotify_event(s->dev.addr, parent->path,
- event->mask, "Obj parent dir ignored");
- break;
-
- default:
- fprintf(stderr, "usb-mtp: failed to parse inotify event\n");
- continue;
- }
-
- if (entry) {
- QTAILQ_INSERT_HEAD(&s->events, entry, next);
- }
+ o = usb_mtp_object_lookup_name(parent, name, -1);
+ if (!o) {
+ return;
}
- }
-}
+ entry = g_new0(MTPMonEntry, 1);
+ entry->handle = o->handle;
+ entry->event = EVT_OBJ_REMOVED;
+ trace_usb_mtp_file_monitor_event(s->dev.addr, o->path, "Obj Deleted");
+ usb_mtp_object_free(s, o);
+ break;
-static int usb_mtp_inotify_init(MTPState *s)
-{
- int fd;
+ case QFILE_MONITOR_EVENT_MODIFIED:
+ o = usb_mtp_object_lookup_name(parent, name, -1);
+ if (!o) {
+ return;
+ }
+ entry = g_new0(MTPMonEntry, 1);
+ entry->handle = o->handle;
+ entry->event = EVT_OBJ_INFO_CHANGED;
+ trace_usb_mtp_file_monitor_event(s->dev.addr, o->path, "Obj Modified");
+ break;
- fd = inotify_init1(IN_NONBLOCK);
- if (fd == -1) {
- return 1;
- }
+ case QFILE_MONITOR_EVENT_IGNORED:
+ trace_usb_mtp_file_monitor_event(s->dev.addr, parent->path,
+ "Obj parent dir ignored");
+ break;
- QTAILQ_INIT(&s->events);
- s->inotifyfd = fd;
+ case QFILE_MONITOR_EVENT_ATTRIBUTES:
+ break;
- qemu_set_fd_handler(fd, inotify_watchfn, NULL, s);
+ default:
+ g_assert_not_reached();
+ }
- return 0;
+ if (entry) {
+ QTAILQ_INSERT_HEAD(&s->events, entry, next);
+ }
}
-static void usb_mtp_inotify_cleanup(MTPState *s)
+static void usb_mtp_file_monitor_cleanup(MTPState *s)
{
MTPMonEntry *e, *p;
- if (!s->inotifyfd) {
- return;
- }
-
- qemu_set_fd_handler(s->inotifyfd, NULL, NULL, s);
- close(s->inotifyfd);
-
QTAILQ_FOREACH_SAFE(e, &s->events, next, p) {
QTAILQ_REMOVE(&s->events, e, next);
g_free(e);
}
-}
-static int usb_mtp_add_watch(int inotifyfd, char *path)
-{
- uint32_t mask = IN_CREATE | IN_DELETE | IN_MODIFY |
- IN_ISDIR;
-
- return inotify_add_watch(inotifyfd, path, mask);
+ qemu_file_monitor_free(s->file_monitor);
+ s->file_monitor = NULL;
}
-#endif
+
static void usb_mtp_object_readdir(MTPState *s, MTPObject *o)
{
struct dirent *entry;
DIR *dir;
int fd;
+ Error *err = NULL;
if (o->have_children) {
return;
@@ -680,16 +623,21 @@ static void usb_mtp_object_readdir(MTPState *s, MTPObject *o)
close(fd);
return;
}
-#ifdef CONFIG_INOTIFY1
- int watchfd = usb_mtp_add_watch(s->inotifyfd, o->path);
- if (watchfd == -1) {
- fprintf(stderr, "usb-mtp: failed to add watch for %s\n", o->path);
- } else {
- trace_usb_mtp_inotify_event(s->dev.addr, o->path,
- 0, "Watch Added");
- o->watchfd = watchfd;
+
+ if (s->file_monitor) {
+ int id = qemu_file_monitor_add_watch(s->file_monitor, o->path, NULL,
+ file_monitor_event, s, &err);
+ if (id == -1) {
+ error_report("usb-mtp: failed to add watch for %s: %s", o->path,
+ error_get_pretty(err));
+ error_free(err);
+ } else {
+ trace_usb_mtp_file_monitor_event(s->dev.addr, o->path,
+ "Watch Added");
+ o->watchid = id;
+ }
}
-#endif
+
while ((entry = readdir(dir)) != NULL) {
usb_mtp_add_child(s, o, entry->d_name);
}
@@ -1197,13 +1145,11 @@ enum {
/* Assumes that children, if any, have been already freed */
static void usb_mtp_object_free_one(MTPState *s, MTPObject *o)
{
-#ifndef CONFIG_INOTIFY1
assert(o->nchildren == 0);
QTAILQ_REMOVE(&s->objects, o, next);
g_free(o->name);
g_free(o->path);
g_free(o);
-#endif
}
static int usb_mtp_deletefn(MTPState *s, MTPObject *o, uint32_t trans)
@@ -1302,6 +1248,7 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
MTPData *data_in = NULL;
MTPObject *o = NULL;
uint32_t nres = 0, res0 = 0;
+ Error *err = NULL;
/* sanity checks */
if (c->code >= CMD_CLOSE_SESSION && s->session == 0) {
@@ -1329,19 +1276,21 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
trace_usb_mtp_op_open_session(s->dev.addr);
s->session = c->argv[0];
usb_mtp_object_alloc(s, s->next_handle++, NULL, s->root);
-#ifdef CONFIG_INOTIFY1
- if (usb_mtp_inotify_init(s)) {
- fprintf(stderr, "usb-mtp: file monitoring init failed\n");
+
+ s->file_monitor = qemu_file_monitor_new(&err);
+ if (err) {
+ error_report("usb-mtp: file monitoring init failed: %s",
+ error_get_pretty(err));
+ error_free(err);
+ } else {
+ QTAILQ_INIT(&s->events);
}
-#endif
break;
case CMD_CLOSE_SESSION:
trace_usb_mtp_op_close_session(s->dev.addr);
s->session = 0;
s->next_handle = 0;
-#ifdef CONFIG_INOTIFY1
- usb_mtp_inotify_cleanup(s);
-#endif
+ usb_mtp_file_monitor_cleanup(s);
usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects));
assert(QTAILQ_EMPTY(&s->objects));
break;
@@ -1554,9 +1503,7 @@ static void usb_mtp_handle_reset(USBDevice *dev)
trace_usb_mtp_reset(s->dev.addr);
-#ifdef CONFIG_INOTIFY1
- usb_mtp_inotify_cleanup(s);
-#endif
+ usb_mtp_file_monitor_cleanup(s);
usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects));
s->session = 0;
usb_mtp_data_free(s->data_in);
@@ -2027,7 +1974,6 @@ static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p)
}
break;
case EP_EVENT:
-#ifdef CONFIG_INOTIFY1
if (!QTAILQ_EMPTY(&s->events)) {
struct MTPMonEntry *e = QTAILQ_LAST(&s->events);
uint32_t handle;
@@ -2051,7 +1997,6 @@ static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p)
g_free(e);
return;
}
-#endif
p->status = USB_RET_NAK;
return;
default:
diff --git a/hw/usb/trace-events b/hw/usb/trace-events
index 2c18770ca5..99b1e8b8ce 100644
--- a/hw/usb/trace-events
+++ b/hw/usb/trace-events
@@ -237,7 +237,7 @@ usb_mtp_op_unknown(int dev, uint32_t code) "dev %d, command code 0x%x"
usb_mtp_object_alloc(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s"
usb_mtp_object_free(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s"
usb_mtp_add_child(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s"
-usb_mtp_inotify_event(int dev, const char *path, uint32_t mask, const char *s) "dev %d, path %s mask 0x%x event %s"
+usb_mtp_file_monitor_event(int dev, const char *path, const char *s) "dev %d, path %s event %s"
# hw/usb/host-libusb.c
usb_host_open_started(int bus, int addr) "dev %d:%d"
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index a1ff647a66..2626a895cb 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -2036,6 +2036,21 @@ int virtio_set_features(VirtIODevice *vdev, uint64_t val)
return ret;
}
+size_t virtio_feature_get_config_size(VirtIOFeature *feature_sizes,
+ uint64_t host_features)
+{
+ size_t config_size = 0;
+ int i;
+
+ for (i = 0; feature_sizes[i].flags != 0; i++) {
+ if (host_features & feature_sizes[i].flags) {
+ config_size = MAX(feature_sizes[i].end, config_size);
+ }
+ }
+
+ return config_size;
+}
+
int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
{
int i, ret;
diff --git a/include/authz/base.h b/include/authz/base.h
new file mode 100644
index 0000000000..77dcd54c4c
--- /dev/null
+++ b/include/authz/base.h
@@ -0,0 +1,112 @@
+/*
+ * QEMU authorization framework base class
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef QAUTHZ_BASE_H__
+#define QAUTHZ_BASE_H__
+
+#include "qemu-common.h"
+#include "qapi/error.h"
+#include "qom/object.h"
+
+
+#define TYPE_QAUTHZ "authz"
+
+#define QAUTHZ_CLASS(klass) \
+ OBJECT_CLASS_CHECK(QAuthZClass, (klass), \
+ TYPE_QAUTHZ)
+#define QAUTHZ_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(QAuthZClass, (obj), \
+ TYPE_QAUTHZ)
+#define QAUTHZ(obj) \
+ INTERFACE_CHECK(QAuthZ, (obj), \
+ TYPE_QAUTHZ)
+
+typedef struct QAuthZ QAuthZ;
+typedef struct QAuthZClass QAuthZClass;
+
+/**
+ * QAuthZ:
+ *
+ * The QAuthZ class defines an API contract to be used
+ * for providing an authorization driver for services
+ * with user identities.
+ */
+
+struct QAuthZ {
+ Object parent_obj;
+};
+
+
+struct QAuthZClass {
+ ObjectClass parent_class;
+
+ bool (*is_allowed)(QAuthZ *authz,
+ const char *identity,
+ Error **errp);
+};
+
+
+/**
+ * qauthz_is_allowed:
+ * @authz: the authorization object
+ * @identity: the user identity to authorize
+ * @errp: pointer to a NULL initialized error object
+ *
+ * Check if a user @identity is authorized. If an error
+ * occurs this method will return false to indicate
+ * denial, as well as setting @errp to contain the details.
+ * Callers are recommended to treat the denial and error
+ * scenarios identically. Specifically the error info in
+ * @errp should never be fed back to the user being
+ * authorized, it is merely for benefit of administrator
+ * debugging.
+ *
+ * Returns: true if @identity is authorized, false if denied or if
+ * an error occurred.
+ */
+bool qauthz_is_allowed(QAuthZ *authz,
+ const char *identity,
+ Error **errp);
+
+
+/**
+ * qauthz_is_allowed_by_id:
+ * @authzid: ID of the authorization object
+ * @identity: the user identity to authorize
+ * @errp: pointer to a NULL initialized error object
+ *
+ * Check if a user @identity is authorized. If an error
+ * occurs this method will return false to indicate
+ * denial, as well as setting @errp to contain the details.
+ * Callers are recommended to treat the denial and error
+ * scenarios identically. Specifically the error info in
+ * @errp should never be fed back to the user being
+ * authorized, it is merely for benefit of administrator
+ * debugging.
+ *
+ * Returns: true if @identity is authorized, false if denied or if
+ * an error occurred.
+ */
+bool qauthz_is_allowed_by_id(const char *authzid,
+ const char *identity,
+ Error **errp);
+
+#endif /* QAUTHZ_BASE_H__ */
+
diff --git a/include/authz/list.h b/include/authz/list.h
new file mode 100644
index 0000000000..a7225a747c
--- /dev/null
+++ b/include/authz/list.h
@@ -0,0 +1,106 @@
+/*
+ * QEMU list authorization driver
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef QAUTHZ_LIST_H__
+#define QAUTHZ_LIST_H__
+
+#include "authz/base.h"
+#include "qapi/qapi-types-authz.h"
+
+#define TYPE_QAUTHZ_LIST "authz-list"
+
+#define QAUTHZ_LIST_CLASS(klass) \
+ OBJECT_CLASS_CHECK(QAuthZListClass, (klass), \
+ TYPE_QAUTHZ_LIST)
+#define QAUTHZ_LIST_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(QAuthZListClass, (obj), \
+ TYPE_QAUTHZ_LIST)
+#define QAUTHZ_LIST(obj) \
+ INTERFACE_CHECK(QAuthZList, (obj), \
+ TYPE_QAUTHZ_LIST)
+
+typedef struct QAuthZList QAuthZList;
+typedef struct QAuthZListClass QAuthZListClass;
+
+
+/**
+ * QAuthZList:
+ *
+ * This authorization driver provides a list mechanism
+ * for granting access by matching user names against a
+ * list of globs. Each match rule has an associated policy
+ * and a catch all policy applies if no rule matches
+ *
+ * To create an instance of this class via QMP:
+ *
+ * {
+ * "execute": "object-add",
+ * "arguments": {
+ * "qom-type": "authz-list",
+ * "id": "authz0",
+ * "props": {
+ * "rules": [
+ * { "match": "fred", "policy": "allow", "format": "exact" },
+ * { "match": "bob", "policy": "allow", "format": "exact" },
+ * { "match": "danb", "policy": "deny", "format": "exact" },
+ * { "match": "dan*", "policy": "allow", "format": "glob" }
+ * ],
+ * "policy": "deny"
+ * }
+ * }
+ * }
+ *
+ */
+struct QAuthZList {
+ QAuthZ parent_obj;
+
+ QAuthZListPolicy policy;
+ QAuthZListRuleList *rules;
+};
+
+
+struct QAuthZListClass {
+ QAuthZClass parent_class;
+};
+
+
+QAuthZList *qauthz_list_new(const char *id,
+ QAuthZListPolicy policy,
+ Error **errp);
+
+ssize_t qauthz_list_append_rule(QAuthZList *auth,
+ const char *match,
+ QAuthZListPolicy policy,
+ QAuthZListFormat format,
+ Error **errp);
+
+ssize_t qauthz_list_insert_rule(QAuthZList *auth,
+ const char *match,
+ QAuthZListPolicy policy,
+ QAuthZListFormat format,
+ size_t index,
+ Error **errp);
+
+ssize_t qauthz_list_delete_rule(QAuthZList *auth,
+ const char *match);
+
+
+#endif /* QAUTHZ_LIST_H__ */
+
diff --git a/include/authz/listfile.h b/include/authz/listfile.h
new file mode 100644
index 0000000000..bcc8d80743
--- /dev/null
+++ b/include/authz/listfile.h
@@ -0,0 +1,111 @@
+/*
+ * QEMU list file authorization driver
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef QAUTHZ_LIST_FILE_H__
+#define QAUTHZ_LIST_FILE_H__
+
+#include "authz/list.h"
+#include "qapi/qapi-types-authz.h"
+#include "qemu/filemonitor.h"
+
+#define TYPE_QAUTHZ_LIST_FILE "authz-list-file"
+
+#define QAUTHZ_LIST_FILE_CLASS(klass) \
+ OBJECT_CLASS_CHECK(QAuthZListFileClass, (klass), \
+ TYPE_QAUTHZ_LIST_FILE)
+#define QAUTHZ_LIST_FILE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(QAuthZListFileClass, (obj), \
+ TYPE_QAUTHZ_LIST_FILE)
+#define QAUTHZ_LIST_FILE(obj) \
+ INTERFACE_CHECK(QAuthZListFile, (obj), \
+ TYPE_QAUTHZ_LIST_FILE)
+
+typedef struct QAuthZListFile QAuthZListFile;
+typedef struct QAuthZListFileClass QAuthZListFileClass;
+
+
+/**
+ * QAuthZListFile:
+ *
+ * This authorization driver provides a file mechanism
+ * for granting access by matching user names against a
+ * file of globs. Each match rule has an associated policy
+ * and a catch all policy applies if no rule matches
+ *
+ * To create an instance of this class via QMP:
+ *
+ * {
+ * "execute": "object-add",
+ * "arguments": {
+ * "qom-type": "authz-list-file",
+ * "id": "authz0",
+ * "props": {
+ * "filename": "/etc/qemu/myvm-vnc.acl",
+ * "refresh": true
+ * }
+ * }
+ * }
+ *
+ * If 'refresh' is 'yes', inotify is used to monitor for changes
+ * to the file and auto-reload the rules.
+ *
+ * The myvm-vnc.acl file should contain the parameters for
+ * the QAuthZList object in JSON format:
+ *
+ * {
+ * "rules": [
+ * { "match": "fred", "policy": "allow", "format": "exact" },
+ * { "match": "bob", "policy": "allow", "format": "exact" },
+ * { "match": "danb", "policy": "deny", "format": "exact" },
+ * { "match": "dan*", "policy": "allow", "format": "glob" }
+ * ],
+ * "policy": "deny"
+ * }
+ *
+ * The object can be created on the command line using
+ *
+ * -object authz-list-file,id=authz0,\
+ * filename=/etc/qemu/myvm-vnc.acl,refresh=yes
+ *
+ */
+struct QAuthZListFile {
+ QAuthZ parent_obj;
+
+ QAuthZ *list;
+ char *filename;
+ bool refresh;
+ QFileMonitor *file_monitor;
+ int file_watch;
+};
+
+
+struct QAuthZListFileClass {
+ QAuthZClass parent_class;
+};
+
+
+QAuthZListFile *qauthz_list_file_new(const char *id,
+ const char *filename,
+ bool refresh,
+ Error **errp);
+
+
+#endif /* QAUTHZ_LIST_FILE_H__ */
+
diff --git a/include/authz/pamacct.h b/include/authz/pamacct.h
new file mode 100644
index 0000000000..6e3046e528
--- /dev/null
+++ b/include/authz/pamacct.h
@@ -0,0 +1,100 @@
+/*
+ * QEMU PAM authorization driver
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef QAUTHZ_PAM_H__
+#define QAUTHZ_PAM_H__
+
+#include "authz/base.h"
+
+
+#define TYPE_QAUTHZ_PAM "authz-pam"
+
+#define QAUTHZ_PAM_CLASS(klass) \
+ OBJECT_CLASS_CHECK(QAuthZPAMClass, (klass), \
+ TYPE_QAUTHZ_PAM)
+#define QAUTHZ_PAM_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(QAuthZPAMClass, (obj), \
+ TYPE_QAUTHZ_PAM)
+#define QAUTHZ_PAM(obj) \
+ INTERFACE_CHECK(QAuthZPAM, (obj), \
+ TYPE_QAUTHZ_PAM)
+
+typedef struct QAuthZPAM QAuthZPAM;
+typedef struct QAuthZPAMClass QAuthZPAMClass;
+
+
+/**
+ * QAuthZPAM:
+ *
+ * This authorization driver provides a PAM mechanism
+ * for granting access by matching user names against a
+ * list of globs. Each match rule has an associated policy
+ * and a catch all policy applies if no rule matches
+ *
+ * To create an instance of this class via QMP:
+ *
+ * {
+ * "execute": "object-add",
+ * "arguments": {
+ * "qom-type": "authz-pam",
+ * "id": "authz0",
+ * "parameters": {
+ * "service": "qemu-vnc-tls"
+ * }
+ * }
+ * }
+ *
+ * The driver only uses the PAM "account" verification
+ * subsystem. The above config would require a config
+ * file /etc/pam.d/qemu-vnc-tls. For a simple file
+ * lookup it would contain
+ *
+ * account requisite pam_listfile.so item=user sense=allow \
+ * file=/etc/qemu/vnc.allow
+ *
+ * The external file would then contain a list of usernames.
+ * If x509 cert was being used as the username, a suitable
+ * entry would match the distinguish name:
+ *
+ * CN=laptop.berrange.com,O=Berrange Home,L=London,ST=London,C=GB
+ *
+ * On the command line it can be created using
+ *
+ * -object authz-pam,id=authz0,service=qemu-vnc-tls
+ *
+ */
+struct QAuthZPAM {
+ QAuthZ parent_obj;
+
+ char *service;
+};
+
+
+struct QAuthZPAMClass {
+ QAuthZClass parent_class;
+};
+
+
+QAuthZPAM *qauthz_pam_new(const char *id,
+ const char *service,
+ Error **errp);
+
+
+#endif /* QAUTHZ_PAM_H__ */
diff --git a/include/authz/simple.h b/include/authz/simple.h
new file mode 100644
index 0000000000..ef13958269
--- /dev/null
+++ b/include/authz/simple.h
@@ -0,0 +1,84 @@
+/*
+ * QEMU simple authorization driver
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef QAUTHZ_SIMPLE_H__
+#define QAUTHZ_SIMPLE_H__
+
+#include "authz/base.h"
+
+#define TYPE_QAUTHZ_SIMPLE "authz-simple"
+
+#define QAUTHZ_SIMPLE_CLASS(klass) \
+ OBJECT_CLASS_CHECK(QAuthZSimpleClass, (klass), \
+ TYPE_QAUTHZ_SIMPLE)
+#define QAUTHZ_SIMPLE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(QAuthZSimpleClass, (obj), \
+ TYPE_QAUTHZ_SIMPLE)
+#define QAUTHZ_SIMPLE(obj) \
+ INTERFACE_CHECK(QAuthZSimple, (obj), \
+ TYPE_QAUTHZ_SIMPLE)
+
+typedef struct QAuthZSimple QAuthZSimple;
+typedef struct QAuthZSimpleClass QAuthZSimpleClass;
+
+
+/**
+ * QAuthZSimple:
+ *
+ * This authorization driver provides a simple mechanism
+ * for granting access based on an exact matched username.
+ *
+ * To create an instance of this class via QMP:
+ *
+ * {
+ * "execute": "object-add",
+ * "arguments": {
+ * "qom-type": "authz-simple",
+ * "id": "authz0",
+ * "props": {
+ * "identity": "fred"
+ * }
+ * }
+ * }
+ *
+ * Or via the command line
+ *
+ * -object authz-simple,id=authz0,identity=fred
+ *
+ */
+struct QAuthZSimple {
+ QAuthZ parent_obj;
+
+ char *identity;
+};
+
+
+struct QAuthZSimpleClass {
+ QAuthZClass parent_class;
+};
+
+
+QAuthZSimple *qauthz_simple_new(const char *id,
+ const char *identity,
+ Error **errp);
+
+
+#endif /* QAUTHZ_SIMPLE_H__ */
+
diff --git a/include/hw/ide/internal.h b/include/hw/ide/internal.h
index 880413ddc7..8efd03132b 100644
--- a/include/hw/ide/internal.h
+++ b/include/hw/ide/internal.h
@@ -346,7 +346,6 @@ extern const char *IDE_DMA_CMD_lookup[IDE_DMA__COUNT];
typedef struct IDEBufferedRequest {
QLIST_ENTRY(IDEBufferedRequest) list;
- struct iovec iov;
QEMUIOVector qiov;
QEMUIOVector *original_qiov;
BlockCompletionFunc *original_cb;
@@ -405,7 +404,6 @@ struct IDEState {
int atapi_dma; /* true if dma is requested for the packet cmd */
BlockAcctCookie acct;
BlockAIOCB *pio_aiocb;
- struct iovec iov;
QEMUIOVector qiov;
QLIST_HEAD(, IDEBufferedRequest) buffered_requests;
/* ATA DMA state */
@@ -457,7 +455,6 @@ struct IDEDMAOps {
struct IDEDMA {
const struct IDEDMAOps *ops;
- struct iovec iov;
QEMUIOVector qiov;
BlockAIOCB *aiocb;
};
diff --git a/include/hw/virtio/virtio-blk.h b/include/hw/virtio/virtio-blk.h
index 5117431d96..cddcfbebe9 100644
--- a/include/hw/virtio/virtio-blk.h
+++ b/include/hw/virtio/virtio-blk.h
@@ -35,11 +35,11 @@ struct VirtIOBlkConf
BlockConf conf;
IOThread *iothread;
char *serial;
- uint32_t scsi;
- uint32_t config_wce;
uint32_t request_merging;
uint16_t num_queues;
uint16_t queue_size;
+ uint32_t max_discard_sectors;
+ uint32_t max_write_zeroes_sectors;
};
struct VirtIOBlockDataPlane;
@@ -57,6 +57,8 @@ typedef struct VirtIOBlock {
bool dataplane_disabled;
bool dataplane_started;
struct VirtIOBlockDataPlane *dataplane;
+ uint64_t host_features;
+ size_t config_size;
} VirtIOBlock;
typedef struct VirtIOBlockReq {
diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
index 9c1fa07d6d..ce9516236a 100644
--- a/include/hw/virtio/virtio.h
+++ b/include/hw/virtio/virtio.h
@@ -37,6 +37,21 @@ static inline hwaddr vring_align(hwaddr addr,
return QEMU_ALIGN_UP(addr, align);
}
+/*
+ * Calculate the number of bytes up to and including the given 'field' of
+ * 'container'.
+ */
+#define virtio_endof(container, field) \
+ (offsetof(container, field) + sizeof_field(container, field))
+
+typedef struct VirtIOFeature {
+ uint64_t flags;
+ size_t end;
+} VirtIOFeature;
+
+size_t virtio_feature_get_config_size(VirtIOFeature *features,
+ uint64_t host_features);
+
typedef struct VirtQueue VirtQueue;
#define VIRTQUEUE_MAX_SIZE 1024
diff --git a/include/qemu/acl.h b/include/qemu/acl.h
deleted file mode 100644
index 73d2a71c8d..0000000000
--- a/include/qemu/acl.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * QEMU access control list management
- *
- * Copyright (C) 2009 Red Hat, Inc
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#ifndef QEMU_ACL_H
-#define QEMU_ACL_H
-
-#include "qemu/queue.h"
-
-typedef struct qemu_acl_entry qemu_acl_entry;
-typedef struct qemu_acl qemu_acl;
-
-struct qemu_acl_entry {
- char *match;
- int deny;
-
- QTAILQ_ENTRY(qemu_acl_entry) next;
-};
-
-struct qemu_acl {
- char *aclname;
- unsigned int nentries;
- QTAILQ_HEAD(,qemu_acl_entry) entries;
- int defaultDeny;
-};
-
-qemu_acl *qemu_acl_init(const char *aclname);
-
-qemu_acl *qemu_acl_find(const char *aclname);
-
-int qemu_acl_party_is_allowed(qemu_acl *acl,
- const char *party);
-
-void qemu_acl_reset(qemu_acl *acl);
-
-int qemu_acl_append(qemu_acl *acl,
- int deny,
- const char *match);
-int qemu_acl_insert(qemu_acl *acl,
- int deny,
- const char *match,
- int index);
-int qemu_acl_remove(qemu_acl *acl,
- const char *match);
-
-#endif /* QEMU_ACL_H */
diff --git a/include/qemu/filemonitor.h b/include/qemu/filemonitor.h
new file mode 100644
index 0000000000..cd031832ed
--- /dev/null
+++ b/include/qemu/filemonitor.h
@@ -0,0 +1,128 @@
+/*
+ * QEMU file monitor helper
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef QEMU_FILE_MONITOR_H
+#define QEMU_FILE_MONITOR_H
+
+#include "qemu-common.h"
+
+
+typedef struct QFileMonitor QFileMonitor;
+
+typedef enum {
+ /* File has been created in a dir */
+ QFILE_MONITOR_EVENT_CREATED,
+ /* File has been modified in a dir */
+ QFILE_MONITOR_EVENT_MODIFIED,
+ /* File has been deleted in a dir */
+ QFILE_MONITOR_EVENT_DELETED,
+ /* File has attributes changed */
+ QFILE_MONITOR_EVENT_ATTRIBUTES,
+ /* Dir is no longer being monitored (due to deletion) */
+ QFILE_MONITOR_EVENT_IGNORED,
+} QFileMonitorEvent;
+
+
+/**
+ * QFileMonitorHandler:
+ * @id: id from qemu_file_monitor_add_watch()
+ * @event: the file change that occurred
+ * @filename: the name of the file affected
+ * @opaque: opaque data provided to qemu_file_monitor_add_watch()
+ *
+ * Invoked whenever a file changes. If @event is
+ * QFILE_MONITOR_EVENT_IGNORED, @filename will be
+ * empty.
+ *
+ */
+typedef void (*QFileMonitorHandler)(int id,
+ QFileMonitorEvent event,
+ const char *filename,
+ void *opaque);
+
+/**
+ * qemu_file_monitor_new:
+ * @errp: pointer to a NULL-initialized error object
+ *
+ * Create a handle for a file monitoring object.
+ *
+ * This object does locking internally to enable it to be
+ * safe to use from multiple threads
+ *
+ * If the platform does not support file monitoring, an
+ * error will be reported. Likewise if file monitoring
+ * is supported, but cannot be initialized
+ *
+ * Currently this is implemented on Linux platforms with
+ * the inotify subsystem.
+ *
+ * Returns: the new monitoring object, or NULL on error
+ */
+QFileMonitor *qemu_file_monitor_new(Error **errp);
+
+/**
+ * qemu_file_monitor_free:
+ * @mon: the file monitor context
+ *
+ * Free resources associated with the file monitor,
+ * including any currently registered watches.
+ */
+void qemu_file_monitor_free(QFileMonitor *mon);
+
+/**
+ * qemu_file_monitor_add_watch:
+ * @mon: the file monitor context
+ * @dirpath: the directory whose contents to watch
+ * @filename: optional filename to filter on
+ * @cb: the function to invoke when @dirpath has changes
+ * @opaque: data to pass to @cb
+ * @errp: pointer to a NULL-initialized error object
+ *
+ * Register to receive notifications of changes
+ * in the directory @dirpath. All files in the
+ * directory will be monitored. If the caller is
+ * only interested in one specific file, @filename
+ * can be used to filter events.
+ *
+ * Returns: a positive integer watch ID, or -1 on error
+ */
+int qemu_file_monitor_add_watch(QFileMonitor *mon,
+ const char *dirpath,
+ const char *filename,
+ QFileMonitorHandler cb,
+ void *opaque,
+ Error **errp);
+
+/**
+ * qemu_file_monitor_remove_watch:
+ * @mon: the file monitor context
+ * @dirpath: the directory whose contents to unwatch
+ * @id: id of the watch to remove
+ *
+ * Removes the file monitoring watch @id, associated
+ * with the directory @dirpath. This must never be
+ * called from a QFileMonitorHandler callback, or a
+ * deadlock will result.
+ */
+void qemu_file_monitor_remove_watch(QFileMonitor *mon,
+ const char *dirpath,
+ int id);
+
+#endif /* QEMU_FILE_MONITOR_H */
diff --git a/include/qemu/iov.h b/include/qemu/iov.h
index 5f433c7768..48b45987b7 100644
--- a/include/qemu/iov.h
+++ b/include/qemu/iov.h
@@ -133,10 +133,70 @@ size_t iov_discard_back(struct iovec *iov, unsigned int *iov_cnt,
typedef struct QEMUIOVector {
struct iovec *iov;
int niov;
- int nalloc;
- size_t size;
+
+ /*
+ * For external @iov (qemu_iovec_init_external()) or allocated @iov
+ * (qemu_iovec_init()), @size is the cumulative size of iovecs and
+ * @local_iov is invalid and unused.
+ *
+ * For embedded @iov (QEMU_IOVEC_INIT_BUF() or qemu_iovec_init_buf()),
+ * @iov is equal to &@local_iov, and @size is valid, as it has same
+ * offset and type as @local_iov.iov_len, which is guaranteed by
+ * static assertion below.
+ *
+ * @nalloc is always valid and is -1 both for embedded and external
+ * cases. It is included in the union only to ensure the padding prior
+ * to the @size field will not result in a 0-length array.
+ */
+ union {
+ struct {
+ int nalloc;
+ struct iovec local_iov;
+ };
+ struct {
+ char __pad[sizeof(int) + offsetof(struct iovec, iov_len)];
+ size_t size;
+ };
+ };
} QEMUIOVector;
+QEMU_BUILD_BUG_ON(offsetof(QEMUIOVector, size) !=
+ offsetof(QEMUIOVector, local_iov.iov_len));
+
+#define QEMU_IOVEC_INIT_BUF(self, buf, len) \
+{ \
+ .iov = &(self).local_iov, \
+ .niov = 1, \
+ .nalloc = -1, \
+ .local_iov = { \
+ .iov_base = (void *)(buf), /* cast away const */ \
+ .iov_len = (len), \
+ }, \
+}
+
+/*
+ * qemu_iovec_init_buf
+ *
+ * Initialize embedded QEMUIOVector.
+ *
+ * Note: "const" is used over @buf pointer to make it simple to pass
+ * const pointers, appearing in read functions. Then this "const" is
+ * cast away by QEMU_IOVEC_INIT_BUF().
+ */
+static inline void qemu_iovec_init_buf(QEMUIOVector *qiov,
+ const void *buf, size_t len)
+{
+ *qiov = (QEMUIOVector) QEMU_IOVEC_INIT_BUF(*qiov, buf, len);
+}
+
+static inline void *qemu_iovec_buf(QEMUIOVector *qiov)
+{
+ /* Only supports embedded iov */
+ assert(qiov->nalloc == -1 && qiov->iov == &qiov->local_iov);
+
+ return qiov->local_iov.iov_base;
+}
+
void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint);
void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov);
void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len);
diff --git a/migration/block.c b/migration/block.c
index 0e24e18d13..83c633fb3f 100644
--- a/migration/block.c
+++ b/migration/block.c
@@ -83,7 +83,6 @@ typedef struct BlkMigBlock {
BlkMigDevState *bmds;
int64_t sector;
int nr_sectors;
- struct iovec iov;
QEMUIOVector qiov;
BlockAIOCB *aiocb;
@@ -314,9 +313,7 @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds)
blk->sector = cur_sector;
blk->nr_sectors = nr_sectors;
- blk->iov.iov_base = blk->buf;
- blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE;
- qemu_iovec_init_external(&blk->qiov, &blk->iov, 1);
+ qemu_iovec_init_buf(&blk->qiov, blk->buf, nr_sectors * BDRV_SECTOR_SIZE);
blk_mig_lock();
block_mig_state.submitted++;
@@ -556,9 +553,8 @@ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds,
blk->nr_sectors = nr_sectors;
if (is_async) {
- blk->iov.iov_base = blk->buf;
- blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE;
- qemu_iovec_init_external(&blk->qiov, &blk->iov, 1);
+ qemu_iovec_init_buf(&blk->qiov, blk->buf,
+ nr_sectors * BDRV_SECTOR_SIZE);
blk->aiocb = blk_aio_preadv(bmds->blk,
sector * BDRV_SECTOR_SIZE,
diff --git a/monitor.c b/monitor.c
index 33ccbf3957..defa129319 100644
--- a/monitor.c
+++ b/monitor.c
@@ -51,7 +51,8 @@
#include "sysemu/balloon.h"
#include "qemu/timer.h"
#include "sysemu/hw_accel.h"
-#include "qemu/acl.h"
+#include "authz/list.h"
+#include "qapi/util.h"
#include "sysemu/tpm.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qerror.h"
@@ -2016,93 +2017,148 @@ static void hmp_wavcapture(Monitor *mon, const QDict *qdict)
QLIST_INSERT_HEAD (&capture_head, s, entries);
}
-static qemu_acl *find_acl(Monitor *mon, const char *name)
+static QAuthZList *find_auth(Monitor *mon, const char *name)
{
- qemu_acl *acl = qemu_acl_find(name);
+ Object *obj;
+ Object *container;
- if (!acl) {
+ container = object_get_objects_root();
+ obj = object_resolve_path_component(container, name);
+ if (!obj) {
monitor_printf(mon, "acl: unknown list '%s'\n", name);
+ return NULL;
}
- return acl;
+
+ return QAUTHZ_LIST(obj);
}
static void hmp_acl_show(Monitor *mon, const QDict *qdict)
{
const char *aclname = qdict_get_str(qdict, "aclname");
- qemu_acl *acl = find_acl(mon, aclname);
- qemu_acl_entry *entry;
- int i = 0;
-
- if (acl) {
- monitor_printf(mon, "policy: %s\n",
- acl->defaultDeny ? "deny" : "allow");
- QTAILQ_FOREACH(entry, &acl->entries, next) {
- i++;
- monitor_printf(mon, "%d: %s %s\n", i,
- entry->deny ? "deny" : "allow", entry->match);
- }
+ QAuthZList *auth = find_auth(mon, aclname);
+ QAuthZListRuleList *rules;
+ size_t i = 0;
+
+ if (!auth) {
+ return;
+ }
+
+ monitor_printf(mon, "policy: %s\n",
+ QAuthZListPolicy_str(auth->policy));
+
+ rules = auth->rules;
+ while (rules) {
+ QAuthZListRule *rule = rules->value;
+ i++;
+ monitor_printf(mon, "%zu: %s %s\n", i,
+ QAuthZListPolicy_str(rule->policy),
+ rule->match);
+ rules = rules->next;
}
}
static void hmp_acl_reset(Monitor *mon, const QDict *qdict)
{
const char *aclname = qdict_get_str(qdict, "aclname");
- qemu_acl *acl = find_acl(mon, aclname);
+ QAuthZList *auth = find_auth(mon, aclname);
- if (acl) {
- qemu_acl_reset(acl);
- monitor_printf(mon, "acl: removed all rules\n");
+ if (!auth) {
+ return;
}
+
+ auth->policy = QAUTHZ_LIST_POLICY_DENY;
+ qapi_free_QAuthZListRuleList(auth->rules);
+ auth->rules = NULL;
+ monitor_printf(mon, "acl: removed all rules\n");
}
static void hmp_acl_policy(Monitor *mon, const QDict *qdict)
{
const char *aclname = qdict_get_str(qdict, "aclname");
const char *policy = qdict_get_str(qdict, "policy");
- qemu_acl *acl = find_acl(mon, aclname);
+ QAuthZList *auth = find_auth(mon, aclname);
+ int val;
+ Error *err = NULL;
- if (acl) {
- if (strcmp(policy, "allow") == 0) {
- acl->defaultDeny = 0;
+ if (!auth) {
+ return;
+ }
+
+ val = qapi_enum_parse(&QAuthZListPolicy_lookup,
+ policy,
+ QAUTHZ_LIST_POLICY_DENY,
+ &err);
+ if (err) {
+ error_free(err);
+ monitor_printf(mon, "acl: unknown policy '%s', "
+ "expected 'deny' or 'allow'\n", policy);
+ } else {
+ auth->policy = val;
+ if (auth->policy == QAUTHZ_LIST_POLICY_ALLOW) {
monitor_printf(mon, "acl: policy set to 'allow'\n");
- } else if (strcmp(policy, "deny") == 0) {
- acl->defaultDeny = 1;
- monitor_printf(mon, "acl: policy set to 'deny'\n");
} else {
- monitor_printf(mon, "acl: unknown policy '%s', "
- "expected 'deny' or 'allow'\n", policy);
+ monitor_printf(mon, "acl: policy set to 'deny'\n");
}
}
}
+static QAuthZListFormat hmp_acl_get_format(const char *match)
+{
+ if (strchr(match, '*')) {
+ return QAUTHZ_LIST_FORMAT_GLOB;
+ } else {
+ return QAUTHZ_LIST_FORMAT_EXACT;
+ }
+}
+
static void hmp_acl_add(Monitor *mon, const QDict *qdict)
{
const char *aclname = qdict_get_str(qdict, "aclname");
const char *match = qdict_get_str(qdict, "match");
- const char *policy = qdict_get_str(qdict, "policy");
+ const char *policystr = qdict_get_str(qdict, "policy");
int has_index = qdict_haskey(qdict, "index");
int index = qdict_get_try_int(qdict, "index", -1);
- qemu_acl *acl = find_acl(mon, aclname);
- int deny, ret;
-
- if (acl) {
- if (strcmp(policy, "allow") == 0) {
- deny = 0;
- } else if (strcmp(policy, "deny") == 0) {
- deny = 1;
- } else {
- monitor_printf(mon, "acl: unknown policy '%s', "
- "expected 'deny' or 'allow'\n", policy);
- return;
- }
- if (has_index)
- ret = qemu_acl_insert(acl, deny, match, index);
- else
- ret = qemu_acl_append(acl, deny, match);
- if (ret < 0)
- monitor_printf(mon, "acl: unable to add acl entry\n");
- else
- monitor_printf(mon, "acl: added rule at position %d\n", ret);
+ QAuthZList *auth = find_auth(mon, aclname);
+ Error *err = NULL;
+ QAuthZListPolicy policy;
+ QAuthZListFormat format;
+ size_t i = 0;
+
+ if (!auth) {
+ return;
+ }
+
+ policy = qapi_enum_parse(&QAuthZListPolicy_lookup,
+ policystr,
+ QAUTHZ_LIST_POLICY_DENY,
+ &err);
+ if (err) {
+ error_free(err);
+ monitor_printf(mon, "acl: unknown policy '%s', "
+ "expected 'deny' or 'allow'\n", policystr);
+ return;
+ }
+
+ format = hmp_acl_get_format(match);
+
+ if (has_index && index == 0) {
+ monitor_printf(mon, "acl: unable to add acl entry\n");
+ return;
+ }
+
+ if (has_index) {
+ i = qauthz_list_insert_rule(auth, match, policy,
+ format, index - 1, &err);
+ } else {
+ i = qauthz_list_append_rule(auth, match, policy,
+ format, &err);
+ }
+ if (err) {
+ monitor_printf(mon, "acl: unable to add rule: %s",
+ error_get_pretty(err));
+ error_free(err);
+ } else {
+ monitor_printf(mon, "acl: added rule at position %zu\n", i + 1);
}
}
@@ -2110,15 +2166,18 @@ static void hmp_acl_remove(Monitor *mon, const QDict *qdict)
{
const char *aclname = qdict_get_str(qdict, "aclname");
const char *match = qdict_get_str(qdict, "match");
- qemu_acl *acl = find_acl(mon, aclname);
- int ret;
+ QAuthZList *auth = find_auth(mon, aclname);
+ ssize_t i = 0;
- if (acl) {
- ret = qemu_acl_remove(acl, match);
- if (ret < 0)
- monitor_printf(mon, "acl: no matching acl entry\n");
- else
- monitor_printf(mon, "acl: removed rule at position %d\n", ret);
+ if (!auth) {
+ return;
+ }
+
+ i = qauthz_list_delete_rule(auth, match);
+ if (i >= 0) {
+ monitor_printf(mon, "acl: removed rule at position %zu\n", i + 1);
+ } else {
+ monitor_printf(mon, "acl: no matching acl entry\n");
}
}
diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
index 87e4df1660..77acca0209 100644
--- a/qapi/Makefile.objs
+++ b/qapi/Makefile.objs
@@ -5,7 +5,7 @@ util-obj-y += opts-visitor.o qapi-clone-visitor.o
util-obj-y += qmp-event.o
util-obj-y += qapi-util.o
-QAPI_COMMON_MODULES = block-core block char common crypto introspect
+QAPI_COMMON_MODULES = authz block-core block char common crypto introspect
QAPI_COMMON_MODULES += job migration misc net rdma rocker run-state
QAPI_COMMON_MODULES += sockets tpm trace transaction ui
QAPI_TARGET_MODULES = target
diff --git a/qapi/authz.json b/qapi/authz.json
new file mode 100644
index 0000000000..1c836a3abd
--- /dev/null
+++ b/qapi/authz.json
@@ -0,0 +1,58 @@
+# -*- Mode: Python -*-
+#
+# QAPI authz definitions
+
+##
+# @QAuthZListPolicy:
+#
+# The authorization policy result
+#
+# @deny: deny access
+# @allow: allow access
+#
+# Since: 4.0
+##
+{ 'enum': 'QAuthZListPolicy',
+ 'prefix': 'QAUTHZ_LIST_POLICY',
+ 'data': ['deny', 'allow']}
+
+##
+# @QAuthZListFormat:
+#
+# The authorization policy match format
+#
+# @exact: an exact string match
+# @glob: string with ? and * shell wildcard support
+#
+# Since: 4.0
+##
+{ 'enum': 'QAuthZListFormat',
+ 'prefix': 'QAUTHZ_LIST_FORMAT',
+ 'data': ['exact', 'glob']}
+
+##
+# @QAuthZListRule:
+#
+# A single authorization rule.
+#
+# @match: a string or glob to match against a user identity
+# @policy: the result to return if @match evaluates to true
+# @format: the format of the @match rule (default 'exact')
+#
+# Since: 4.0
+##
+{ 'struct': 'QAuthZListRule',
+ 'data': {'match': 'str',
+ 'policy': 'QAuthZListPolicy',
+ '*format': 'QAuthZListFormat'}}
+
+##
+# @QAuthZListRuleListHack:
+#
+# Not exposed via QMP; hack to generate QAuthZListRuleList
+# for use internally by the code.
+#
+# Since: 4.0
+##
+{ 'struct': 'QAuthZListRuleListHack',
+ 'data': { 'unused': ['QAuthZListRule'] } }
diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json
index db61bfd688..a34899c626 100644
--- a/qapi/qapi-schema.json
+++ b/qapi/qapi-schema.json
@@ -92,6 +92,7 @@
{ 'include': 'rocker.json' }
{ 'include': 'tpm.json' }
{ 'include': 'ui.json' }
+{ 'include': 'authz.json' }
{ 'include': 'migration.json' }
{ 'include': 'transaction.json' }
{ 'include': 'trace.json' }
diff --git a/qemu-img.c b/qemu-img.c
index ae0025926c..660c01898e 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -1670,7 +1670,6 @@ static int coroutine_fn convert_co_read(ImgConvertState *s, int64_t sector_num,
{
int n, ret;
QEMUIOVector qiov;
- struct iovec iov;
assert(nb_sectors <= s->buf_sectors);
while (nb_sectors > 0) {
@@ -1686,9 +1685,7 @@ static int coroutine_fn convert_co_read(ImgConvertState *s, int64_t sector_num,
bs_sectors = s->src_sectors[src_cur];
n = MIN(nb_sectors, bs_sectors - (sector_num - src_cur_offset));
- iov.iov_base = buf;
- iov.iov_len = n << BDRV_SECTOR_BITS;
- qemu_iovec_init_external(&qiov, &iov, 1);
+ qemu_iovec_init_buf(&qiov, buf, n << BDRV_SECTOR_BITS);
ret = blk_co_preadv(
blk, (sector_num - src_cur_offset) << BDRV_SECTOR_BITS,
@@ -1712,7 +1709,6 @@ static int coroutine_fn convert_co_write(ImgConvertState *s, int64_t sector_num,
{
int ret;
QEMUIOVector qiov;
- struct iovec iov;
while (nb_sectors > 0) {
int n = nb_sectors;
@@ -1740,9 +1736,7 @@ static int coroutine_fn convert_co_write(ImgConvertState *s, int64_t sector_num,
(s->compressed &&
!buffer_is_zero(buf, n * BDRV_SECTOR_SIZE)))
{
- iov.iov_base = buf;
- iov.iov_len = n << BDRV_SECTOR_BITS;
- qemu_iovec_init_external(&qiov, &iov, 1);
+ qemu_iovec_init_buf(&qiov, buf, n << BDRV_SECTOR_BITS);
ret = blk_co_pwritev(s->target, sector_num << BDRV_SECTOR_BITS,
n << BDRV_SECTOR_BITS, &qiov, flags);
diff --git a/qemu-options.hx b/qemu-options.hx
index c843126ebd..1cf9aac1fe 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -4365,6 +4365,111 @@ e.g to launch a SEV guest
.....
@end example
+
+
+@item -object authz-simple,id=@var{id},identity=@var{string}
+
+Create an authorization object that will control access to network services.
+
+The @option{identity} parameter is identifies the user and its format
+depends on the network service that authorization object is associated
+with. For authorizing based on TLS x509 certificates, the identity must
+be the x509 distinguished name. Note that care must be taken to escape
+any commas in the distinguished name.
+
+An example authorization object to validate a x509 distinguished name
+would look like:
+@example
+ # $QEMU \
+ ...
+ -object 'authz-simple,id=auth0,identity=CN=laptop.example.com,,O=Example Org,,L=London,,ST=London,,C=GB' \
+ ...
+@end example
+
+Note the use of quotes due to the x509 distinguished name containing
+whitespace, and escaping of ','.
+
+@item -object authz-listfile,id=@var{id},filename=@var{path},refresh=@var{yes|no}
+
+Create an authorization object that will control access to network services.
+
+The @option{filename} parameter is the fully qualified path to a file
+containing the access control list rules in JSON format.
+
+An example set of rules that match against SASL usernames might look
+like:
+
+@example
+ @{
+ "rules": [
+ @{ "match": "fred", "policy": "allow", "format": "exact" @},
+ @{ "match": "bob", "policy": "allow", "format": "exact" @},
+ @{ "match": "danb", "policy": "deny", "format": "glob" @},
+ @{ "match": "dan*", "policy": "allow", "format": "exact" @},
+ ],
+ "policy": "deny"
+ @}
+@end example
+
+When checking access the object will iterate over all the rules and
+the first rule to match will have its @option{policy} value returned
+as the result. If no rules match, then the default @option{policy}
+value is returned.
+
+The rules can either be an exact string match, or they can use the
+simple UNIX glob pattern matching to allow wildcards to be used.
+
+If @option{refresh} is set to true the file will be monitored
+and automatically reloaded whenever its content changes.
+
+As with the @code{authz-simple} object, the format of the identity
+strings being matched depends on the network service, but is usually
+a TLS x509 distinguished name, or a SASL username.
+
+An example authorization object to validate a SASL username
+would look like:
+@example
+ # $QEMU \
+ ...
+ -object authz-simple,id=auth0,filename=/etc/qemu/vnc-sasl.acl,refresh=yes
+ ...
+@end example
+
+@item -object authz-pam,id=@var{id},service=@var{string}
+
+Create an authorization object that will control access to network services.
+
+The @option{service} parameter provides the name of a PAM service to use
+for authorization. It requires that a file @code{/etc/pam.d/@var{service}}
+exist to provide the configuration for the @code{account} subsystem.
+
+An example authorization object to validate a TLS x509 distinguished
+name would look like:
+
+@example
+ # $QEMU \
+ ...
+ -object authz-pam,id=auth0,service=qemu-vnc
+ ...
+@end example
+
+There would then be a corresponding config file for PAM at
+@code{/etc/pam.d/qemu-vnc} that contains:
+
+@example
+account requisite pam_listfile.so item=user sense=allow \
+ file=/etc/qemu/vnc.allow
+@end example
+
+Finally the @code{/etc/qemu/vnc.allow} file would contain
+the list of x509 distingished names that are permitted
+access
+
+@example
+CN=laptop.example.com,O=Example Home,L=London,ST=London,C=GB
+@end example
+
+
@end table
ETEXI
diff --git a/qom/object.c b/qom/object.c
index b8c732063b..05a8567041 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -646,16 +646,20 @@ Object *object_new_with_propv(const char *typename,
goto error;
}
- object_property_add_child(parent, id, obj, &local_err);
- if (local_err) {
- goto error;
+ if (id != NULL) {
+ object_property_add_child(parent, id, obj, &local_err);
+ if (local_err) {
+ goto error;
+ }
}
uc = (UserCreatable *)object_dynamic_cast(obj, TYPE_USER_CREATABLE);
if (uc) {
user_creatable_complete(uc, &local_err);
if (local_err) {
- object_unparent(obj);
+ if (id != NULL) {
+ object_unparent(obj);
+ }
goto error;
}
}
diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
index db85d1eb75..cb5809934a 100644
--- a/qom/object_interfaces.c
+++ b/qom/object_interfaces.c
@@ -75,16 +75,20 @@ Object *user_creatable_add_type(const char *type, const char *id,
goto out;
}
- object_property_add_child(object_get_objects_root(),
- id, obj, &local_err);
- if (local_err) {
- goto out;
+ if (id != NULL) {
+ object_property_add_child(object_get_objects_root(),
+ id, obj, &local_err);
+ if (local_err) {
+ goto out;
+ }
}
user_creatable_complete(USER_CREATABLE(obj), &local_err);
if (local_err) {
- object_property_del(object_get_objects_root(),
- id, &error_abort);
+ if (id != NULL) {
+ object_property_del(object_get_objects_root(),
+ id, &error_abort);
+ }
goto out;
}
out:
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 992378e031..370de59974 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -115,7 +115,12 @@ ifneq (,$(findstring qemu-ga,$(TOOLS)))
check-unit-$(land,$(CONFIG_LINUX),$(CONFIG_VIRTIO_SERIAL)) += tests/test-qga$(EXESUF)
endif
check-unit-y += tests/test-timed-average$(EXESUF)
+check-unit-$(CONFIG_INOTIFY1) += tests/test-util-filemonitor$(EXESUF)
check-unit-y += tests/test-util-sockets$(EXESUF)
+check-unit-y += tests/test-authz-simple$(EXESUF)
+check-unit-y += tests/test-authz-list$(EXESUF)
+check-unit-y += tests/test-authz-listfile$(EXESUF)
+check-unit-$(CONFIG_AUTH_PAM) += tests/test-authz-pam$(EXESUF)
check-unit-y += tests/test-io-task$(EXESUF)
check-unit-y += tests/test-io-channel-socket$(EXESUF)
check-unit-y += tests/test-io-channel-file$(EXESUF)
@@ -533,9 +538,10 @@ test-qom-obj-y = $(qom-obj-y) $(test-util-obj-y)
test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \
tests/test-qapi-introspect.o \
$(test-qom-obj-y)
-benchmark-crypto-obj-y = $(crypto-obj-y) $(test-qom-obj-y)
-test-crypto-obj-y = $(crypto-obj-y) $(test-qom-obj-y)
+benchmark-crypto-obj-y = $(authz-obj-y) $(crypto-obj-y) $(test-qom-obj-y)
+test-crypto-obj-y = $(authz-obj-y) $(crypto-obj-y) $(test-qom-obj-y)
test-io-obj-y = $(io-obj-y) $(test-crypto-obj-y)
+test-authz-obj-y = $(test-qom-obj-y) $(authz-obj-y)
test-block-obj-y = $(block-obj-y) $(test-io-obj-y) tests/iothread.o
tests/check-qnum$(EXESUF): tests/check-qnum.o $(test-util-obj-y)
@@ -659,8 +665,14 @@ tests/test-crypto-tlssession$(EXESUF): tests/test-crypto-tlssession.o \
tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o \
tests/crypto-tls-psk-helpers.o \
$(test-crypto-obj-y)
+tests/test-util-filemonitor$(EXESUF): tests/test-util-filemonitor.o \
+ $(test-util-obj-y)
tests/test-util-sockets$(EXESUF): tests/test-util-sockets.o \
tests/socket-helpers.o $(test-util-obj-y)
+tests/test-authz-simple$(EXESUF): tests/test-authz-simple.o $(test-authz-obj-y)
+tests/test-authz-list$(EXESUF): tests/test-authz-list.o $(test-authz-obj-y)
+tests/test-authz-listfile$(EXESUF): tests/test-authz-listfile.o $(test-authz-obj-y)
+tests/test-authz-pam$(EXESUF): tests/test-authz-pam.o $(test-authz-obj-y)
tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y)
tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \
tests/io-channel-helpers.o tests/socket-helpers.o $(test-io-obj-y)
@@ -898,11 +910,9 @@ $(FP_TEST_BIN):
"BUILD", "$(notdir $@)")
# The full test suite can take a bit of time, default to a quick run
-ifeq ($(SPEED), quick)
+# "-l 2 -r all" can take more than a day for some operations and is best
+# run manually
FP_TL=-l 1
-else
-FP_TL=-l 2 -r all
-endif
# $1 = tests, $2 = description
test-softfloat = $(call quiet-command, \
diff --git a/tests/cdrom-test.c b/tests/cdrom-test.c
index 14bd981336..05611da648 100644
--- a/tests/cdrom-test.c
+++ b/tests/cdrom-test.c
@@ -132,8 +132,14 @@ static void add_x86_tests(void)
qtest_add_data_func("cdrom/boot/virtio-scsi",
"-device virtio-scsi -device scsi-cd,drive=cdr "
"-blockdev file,node-name=cdr,filename=", test_cdboot);
- qtest_add_data_func("cdrom/boot/isapc", "-M isapc "
- "-drive if=ide,media=cdrom,file=", test_cdboot);
+ /*
+ * Unstable CI test under load
+ * See https://lists.gnu.org/archive/html/qemu-devel/2019-02/msg05509.html
+ */
+ if (g_test_slow()) {
+ qtest_add_data_func("cdrom/boot/isapc", "-M isapc "
+ "-drive if=ide,media=cdrom,file=", test_cdboot);
+ }
qtest_add_data_func("cdrom/boot/am53c974",
"-device am53c974 -device scsi-cd,drive=cd1 "
"-drive if=none,id=cd1,format=raw,file=", test_cdboot);
diff --git a/tests/docker/dockerfiles/debian-amd64.docker b/tests/docker/dockerfiles/debian-amd64.docker
index 954fcf9606..d770a11a52 100644
--- a/tests/docker/dockerfiles/debian-amd64.docker
+++ b/tests/docker/dockerfiles/debian-amd64.docker
@@ -33,6 +33,7 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \
apt-get install -y --no-install-recommends \
linux-headers-amd64
RUN git clone https://github.com/luigirizzo/netmap.git /usr/src/netmap
+RUN cd /usr/src/netmap && git checkout v11.3
RUN cd /usr/src/netmap/LINUX && ./configure --no-drivers --no-apps --kernel-dir=$(ls -d /usr/src/linux-headers-*-amd64) && make install
ENV QEMU_CONFIGURE_OPTS --enable-netmap
diff --git a/tests/docker/dockerfiles/debian9.docker b/tests/docker/dockerfiles/debian9.docker
index 154ae2a455..5f23a35404 100644
--- a/tests/docker/dockerfiles/debian9.docker
+++ b/tests/docker/dockerfiles/debian9.docker
@@ -13,8 +13,8 @@ FROM debian:stretch-slim
RUN cat /etc/apt/sources.list | sed "s/^deb\ /deb-src /" >> /etc/apt/sources.list
# Install common build utilities
-RUN apt update
-RUN DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata
+RUN apt-get update && \
+ DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata
RUN DEBIAN_FRONTEND=noninteractive eatmydata \
apt install -y --no-install-recommends \
bison \
diff --git a/tests/test-authz-list.c b/tests/test-authz-list.c
new file mode 100644
index 0000000000..24347a6ac3
--- /dev/null
+++ b/tests/test-authz-list.c
@@ -0,0 +1,159 @@
+/*
+ * QEMU list file authorization object tests
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+
+#include "authz/list.h"
+
+static void test_authz_default_deny(void)
+{
+ QAuthZList *auth = qauthz_list_new("auth0",
+ QAUTHZ_LIST_POLICY_DENY,
+ &error_abort);
+
+ g_assert(!qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+
+ object_unparent(OBJECT(auth));
+}
+
+static void test_authz_default_allow(void)
+{
+ QAuthZList *auth = qauthz_list_new("auth0",
+ QAUTHZ_LIST_POLICY_ALLOW,
+ &error_abort);
+
+ g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+
+ object_unparent(OBJECT(auth));
+}
+
+static void test_authz_explicit_deny(void)
+{
+ QAuthZList *auth = qauthz_list_new("auth0",
+ QAUTHZ_LIST_POLICY_ALLOW,
+ &error_abort);
+
+ qauthz_list_append_rule(auth, "fred", QAUTHZ_LIST_POLICY_DENY,
+ QAUTHZ_LIST_FORMAT_EXACT, &error_abort);
+
+ g_assert(!qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+
+ object_unparent(OBJECT(auth));
+}
+
+static void test_authz_explicit_allow(void)
+{
+ QAuthZList *auth = qauthz_list_new("auth0",
+ QAUTHZ_LIST_POLICY_DENY,
+ &error_abort);
+
+ qauthz_list_append_rule(auth, "fred", QAUTHZ_LIST_POLICY_ALLOW,
+ QAUTHZ_LIST_FORMAT_EXACT, &error_abort);
+
+ g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+
+ object_unparent(OBJECT(auth));
+}
+
+
+static void test_authz_complex(void)
+{
+ QAuthZList *auth = qauthz_list_new("auth0",
+ QAUTHZ_LIST_POLICY_DENY,
+ &error_abort);
+
+ qauthz_list_append_rule(auth, "fred", QAUTHZ_LIST_POLICY_ALLOW,
+ QAUTHZ_LIST_FORMAT_EXACT, &error_abort);
+ qauthz_list_append_rule(auth, "bob", QAUTHZ_LIST_POLICY_ALLOW,
+ QAUTHZ_LIST_FORMAT_EXACT, &error_abort);
+ qauthz_list_append_rule(auth, "dan", QAUTHZ_LIST_POLICY_DENY,
+ QAUTHZ_LIST_FORMAT_EXACT, &error_abort);
+ qauthz_list_append_rule(auth, "dan*", QAUTHZ_LIST_POLICY_ALLOW,
+ QAUTHZ_LIST_FORMAT_GLOB, &error_abort);
+
+ g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+ g_assert(qauthz_is_allowed(QAUTHZ(auth), "bob", &error_abort));
+ g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort));
+ g_assert(qauthz_is_allowed(QAUTHZ(auth), "danb", &error_abort));
+
+ object_unparent(OBJECT(auth));
+}
+
+static void test_authz_add_remove(void)
+{
+ QAuthZList *auth = qauthz_list_new("auth0",
+ QAUTHZ_LIST_POLICY_ALLOW,
+ &error_abort);
+
+ g_assert_cmpint(qauthz_list_append_rule(auth, "fred",
+ QAUTHZ_LIST_POLICY_ALLOW,
+ QAUTHZ_LIST_FORMAT_EXACT,
+ &error_abort),
+ ==, 0);
+ g_assert_cmpint(qauthz_list_append_rule(auth, "bob",
+ QAUTHZ_LIST_POLICY_ALLOW,
+ QAUTHZ_LIST_FORMAT_EXACT,
+ &error_abort),
+ ==, 1);
+ g_assert_cmpint(qauthz_list_append_rule(auth, "dan",
+ QAUTHZ_LIST_POLICY_DENY,
+ QAUTHZ_LIST_FORMAT_EXACT,
+ &error_abort),
+ ==, 2);
+ g_assert_cmpint(qauthz_list_append_rule(auth, "frank",
+ QAUTHZ_LIST_POLICY_DENY,
+ QAUTHZ_LIST_FORMAT_EXACT,
+ &error_abort),
+ ==, 3);
+
+ g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort));
+
+ g_assert_cmpint(qauthz_list_delete_rule(auth, "dan"),
+ ==, 2);
+
+ g_assert(qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort));
+
+ g_assert_cmpint(qauthz_list_insert_rule(auth, "dan",
+ QAUTHZ_LIST_POLICY_DENY,
+ QAUTHZ_LIST_FORMAT_EXACT,
+ 2,
+ &error_abort),
+ ==, 2);
+
+ g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort));
+
+ object_unparent(OBJECT(auth));
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ module_call_init(MODULE_INIT_QOM);
+
+ g_test_add_func("/auth/list/default/deny", test_authz_default_deny);
+ g_test_add_func("/auth/list/default/allow", test_authz_default_allow);
+ g_test_add_func("/auth/list/explicit/deny", test_authz_explicit_deny);
+ g_test_add_func("/auth/list/explicit/allow", test_authz_explicit_allow);
+ g_test_add_func("/auth/list/complex", test_authz_complex);
+ g_test_add_func("/auth/list/add-remove", test_authz_add_remove);
+
+ return g_test_run();
+}
diff --git a/tests/test-authz-listfile.c b/tests/test-authz-listfile.c
new file mode 100644
index 0000000000..1e452fef6d
--- /dev/null
+++ b/tests/test-authz-listfile.c
@@ -0,0 +1,195 @@
+/*
+ * QEMU list authorization object tests
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/main-loop.h"
+#include "authz/listfile.h"
+
+static char *workdir;
+
+static gchar *qemu_authz_listfile_test_save(const gchar *name,
+ const gchar *cfg)
+{
+ gchar *path = g_strdup_printf("%s/default-deny.cfg", workdir);
+ GError *gerr = NULL;
+
+ if (!g_file_set_contents(path, cfg, -1, &gerr)) {
+ g_printerr("Unable to save config %s: %s\n",
+ path, gerr->message);
+ g_error_free(gerr);
+ g_free(path);
+ rmdir(workdir);
+ abort();
+ }
+
+ return path;
+}
+
+static void test_authz_default_deny(void)
+{
+ gchar *file = qemu_authz_listfile_test_save(
+ "default-deny.cfg",
+ "{ \"policy\": \"deny\" }");
+ Error *local_err = NULL;
+
+ QAuthZListFile *auth = qauthz_list_file_new("auth0",
+ file, false,
+ &local_err);
+ unlink(file);
+ g_free(file);
+ g_assert(local_err == NULL);
+ g_assert(!qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+
+ object_unparent(OBJECT(auth));
+}
+
+static void test_authz_default_allow(void)
+{
+ gchar *file = qemu_authz_listfile_test_save(
+ "default-allow.cfg",
+ "{ \"policy\": \"allow\" }");
+ Error *local_err = NULL;
+
+ QAuthZListFile *auth = qauthz_list_file_new("auth0",
+ file, false,
+ &local_err);
+ unlink(file);
+ g_free(file);
+ g_assert(local_err == NULL);
+ g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+
+ object_unparent(OBJECT(auth));
+}
+
+static void test_authz_explicit_deny(void)
+{
+ gchar *file = qemu_authz_listfile_test_save(
+ "explicit-deny.cfg",
+ "{ \"rules\": [ "
+ " { \"match\": \"fred\","
+ " \"policy\": \"deny\","
+ " \"format\": \"exact\" } ],"
+ " \"policy\": \"allow\" }");
+ Error *local_err = NULL;
+
+ QAuthZListFile *auth = qauthz_list_file_new("auth0",
+ file, false,
+ &local_err);
+ unlink(file);
+ g_free(file);
+ g_assert(local_err == NULL);
+
+ g_assert(!qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+
+ object_unparent(OBJECT(auth));
+}
+
+static void test_authz_explicit_allow(void)
+{
+ gchar *file = qemu_authz_listfile_test_save(
+ "explicit-allow.cfg",
+ "{ \"rules\": [ "
+ " { \"match\": \"fred\","
+ " \"policy\": \"allow\","
+ " \"format\": \"exact\" } ],"
+ " \"policy\": \"deny\" }");
+ Error *local_err = NULL;
+
+ QAuthZListFile *auth = qauthz_list_file_new("auth0",
+ file, false,
+ &local_err);
+ unlink(file);
+ g_free(file);
+ g_assert(local_err == NULL);
+
+ g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+
+ object_unparent(OBJECT(auth));
+}
+
+
+static void test_authz_complex(void)
+{
+ gchar *file = qemu_authz_listfile_test_save(
+ "complex.cfg",
+ "{ \"rules\": [ "
+ " { \"match\": \"fred\","
+ " \"policy\": \"allow\","
+ " \"format\": \"exact\" },"
+ " { \"match\": \"bob\","
+ " \"policy\": \"allow\","
+ " \"format\": \"exact\" },"
+ " { \"match\": \"dan\","
+ " \"policy\": \"deny\","
+ " \"format\": \"exact\" },"
+ " { \"match\": \"dan*\","
+ " \"policy\": \"allow\","
+ " \"format\": \"glob\" } ],"
+ " \"policy\": \"deny\" }");
+
+ Error *local_err = NULL;
+
+ QAuthZListFile *auth = qauthz_list_file_new("auth0",
+ file, false,
+ &local_err);
+ unlink(file);
+ g_free(file);
+ g_assert(local_err == NULL);
+
+ g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+ g_assert(qauthz_is_allowed(QAUTHZ(auth), "bob", &error_abort));
+ g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort));
+ g_assert(qauthz_is_allowed(QAUTHZ(auth), "danb", &error_abort));
+
+ object_unparent(OBJECT(auth));
+}
+
+
+int main(int argc, char **argv)
+{
+ int ret;
+ GError *gerr = NULL;
+
+ g_test_init(&argc, &argv, NULL);
+
+ module_call_init(MODULE_INIT_QOM);
+
+ workdir = g_dir_make_tmp("qemu-test-authz-listfile-XXXXXX",
+ &gerr);
+ if (!workdir) {
+ g_printerr("Unable to create temporary dir: %s\n",
+ gerr->message);
+ g_error_free(gerr);
+ abort();
+ }
+
+ g_test_add_func("/auth/list/default/deny", test_authz_default_deny);
+ g_test_add_func("/auth/list/default/allow", test_authz_default_allow);
+ g_test_add_func("/auth/list/explicit/deny", test_authz_explicit_deny);
+ g_test_add_func("/auth/list/explicit/allow", test_authz_explicit_allow);
+ g_test_add_func("/auth/list/complex", test_authz_complex);
+
+ ret = g_test_run();
+
+ rmdir(workdir);
+ g_free(workdir);
+
+ return ret;
+}
diff --git a/tests/test-authz-pam.c b/tests/test-authz-pam.c
new file mode 100644
index 0000000000..93d5ac8bbf
--- /dev/null
+++ b/tests/test-authz-pam.c
@@ -0,0 +1,124 @@
+/*
+ * QEMU PAM authorization object tests
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "authz/pamacct.h"
+
+#include <security/pam_appl.h>
+
+static bool failauth;
+
+/*
+ * These two functions are exported by libpam.so.
+ *
+ * By defining them again here, our impls are resolved
+ * by the linker instead of those in libpam.so
+ *
+ * The test suite is thus isolated from the host system
+ * PAM setup, so we can do predictable test scenarios
+ */
+int
+pam_start(const char *service_name, const char *user,
+ const struct pam_conv *pam_conversation,
+ pam_handle_t **pamh)
+{
+ failauth = true;
+ if (!g_str_equal(service_name, "qemu-vnc")) {
+ return PAM_AUTH_ERR;
+ }
+
+ if (g_str_equal(user, "fred")) {
+ failauth = false;
+ }
+
+ return PAM_SUCCESS;
+}
+
+
+int
+pam_acct_mgmt(pam_handle_t *pamh, int flags)
+{
+ if (failauth) {
+ return PAM_AUTH_ERR;
+ }
+
+ return PAM_SUCCESS;
+}
+
+
+static void test_authz_unknown_service(void)
+{
+ Error *local_err = NULL;
+ QAuthZPAM *auth = qauthz_pam_new("auth0",
+ "qemu-does-not-exist",
+ &error_abort);
+
+ g_assert_nonnull(auth);
+
+ g_assert_false(qauthz_is_allowed(QAUTHZ(auth), "fred", &local_err));
+
+ error_free_or_abort(&local_err);
+ object_unparent(OBJECT(auth));
+}
+
+
+static void test_authz_good_user(void)
+{
+ QAuthZPAM *auth = qauthz_pam_new("auth0",
+ "qemu-vnc",
+ &error_abort);
+
+ g_assert_nonnull(auth);
+
+ g_assert_true(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+
+ object_unparent(OBJECT(auth));
+}
+
+
+static void test_authz_bad_user(void)
+{
+ Error *local_err = NULL;
+ QAuthZPAM *auth = qauthz_pam_new("auth0",
+ "qemu-vnc",
+ &error_abort);
+
+ g_assert_nonnull(auth);
+
+ g_assert_false(qauthz_is_allowed(QAUTHZ(auth), "bob", &local_err));
+
+ error_free_or_abort(&local_err);
+ object_unparent(OBJECT(auth));
+}
+
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ module_call_init(MODULE_INIT_QOM);
+
+ g_test_add_func("/auth/pam/unknown-service", test_authz_unknown_service);
+ g_test_add_func("/auth/pam/good-user", test_authz_good_user);
+ g_test_add_func("/auth/pam/bad-user", test_authz_bad_user);
+
+ return g_test_run();
+}
diff --git a/tests/test-authz-simple.c b/tests/test-authz-simple.c
new file mode 100644
index 0000000000..2cf14fb87e
--- /dev/null
+++ b/tests/test-authz-simple.c
@@ -0,0 +1,50 @@
+/*
+ * QEMU simple authorization object testing
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+
+#include "authz/simple.h"
+
+
+static void test_authz_simple(void)
+{
+ QAuthZSimple *authz = qauthz_simple_new("authz0",
+ "cthulu",
+ &error_abort);
+
+ g_assert(!qauthz_is_allowed(QAUTHZ(authz), "cthul", &error_abort));
+ g_assert(qauthz_is_allowed(QAUTHZ(authz), "cthulu", &error_abort));
+ g_assert(!qauthz_is_allowed(QAUTHZ(authz), "cthuluu", &error_abort));
+ g_assert(!qauthz_is_allowed(QAUTHZ(authz), "fred", &error_abort));
+
+ object_unparent(OBJECT(authz));
+}
+
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+ module_call_init(MODULE_INIT_QOM);
+
+ g_test_add_func("/authz/simple", test_authz_simple);
+
+ return g_test_run();
+}
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
index 1b1f6c17a5..eda90750eb 100644
--- a/tests/test-bdrv-drain.c
+++ b/tests/test-bdrv-drain.c
@@ -204,12 +204,7 @@ static void test_drv_cb_common(enum drain_type drain_type, bool recursive)
BlockAIOCB *acb;
int aio_ret;
- QEMUIOVector qiov;
- struct iovec iov = {
- .iov_base = NULL,
- .iov_len = 0,
- };
- qemu_iovec_init_external(&qiov, &iov, 1);
+ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, 0);
blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
@@ -670,12 +665,7 @@ static void test_iothread_common(enum drain_type drain_type, int drain_thread)
AioContext *ctx_a = iothread_get_aio_context(a);
AioContext *ctx_b = iothread_get_aio_context(b);
- QEMUIOVector qiov;
- struct iovec iov = {
- .iov_base = NULL,
- .iov_len = 0,
- };
- qemu_iovec_init_external(&qiov, &iov, 1);
+ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, 0);
/* bdrv_drain_all() may only be called from the main loop thread */
if (drain_type == BDRV_DRAIN_ALL && drain_thread != 0) {
@@ -1148,13 +1138,7 @@ static void coroutine_fn test_co_delete_by_drain(void *opaque)
BlockDriverState *bs = blk_bs(blk);
BDRVTestTopState *tts = bs->opaque;
void *buffer = g_malloc(65536);
- QEMUIOVector qiov;
- struct iovec iov = {
- .iov_base = buffer,
- .iov_len = 65536,
- };
-
- qemu_iovec_init_external(&qiov, &iov, 1);
+ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buffer, 65536);
/* Pretend some internal write operation from parent to child.
* Important: We have to read from the child, not from the parent!
@@ -1365,12 +1349,7 @@ static void test_detach_indirect(bool by_parent_cb)
BdrvChild *child_a, *child_b;
BlockAIOCB *acb;
- QEMUIOVector qiov;
- struct iovec iov = {
- .iov_base = NULL,
- .iov_len = 0,
- };
- qemu_iovec_init_external(&qiov, &iov, 1);
+ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, 0);
if (!by_parent_cb) {
detach_by_driver_cb_role = child_file;
diff --git a/tests/test-crypto-tlssession.c b/tests/test-crypto-tlssession.c
index 6fa9950afb..15212ec276 100644
--- a/tests/test-crypto-tlssession.c
+++ b/tests/test-crypto-tlssession.c
@@ -28,7 +28,7 @@
#include "qom/object_interfaces.h"
#include "qapi/error.h"
#include "qemu/sockets.h"
-#include "qemu/acl.h"
+#include "authz/list.h"
#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT
@@ -229,7 +229,7 @@ static void test_crypto_tls_session_x509(const void *opaque)
QCryptoTLSCreds *serverCreds;
QCryptoTLSSession *clientSess = NULL;
QCryptoTLSSession *serverSess = NULL;
- qemu_acl *acl;
+ QAuthZList *auth;
const char * const *wildcards;
int channel[2];
bool clientShake = false;
@@ -285,11 +285,15 @@ static void test_crypto_tls_session_x509(const void *opaque)
SERVER_CERT_DIR);
g_assert(serverCreds != NULL);
- acl = qemu_acl_init("tlssessionacl");
- qemu_acl_reset(acl);
+ auth = qauthz_list_new("tlssessionacl",
+ QAUTHZ_LIST_POLICY_DENY,
+ &error_abort);
wildcards = data->wildcards;
while (wildcards && *wildcards) {
- qemu_acl_append(acl, 0, *wildcards);
+ qauthz_list_append_rule(auth, *wildcards,
+ QAUTHZ_LIST_POLICY_ALLOW,
+ QAUTHZ_LIST_FORMAT_GLOB,
+ &error_abort);
wildcards++;
}
@@ -377,6 +381,7 @@ static void test_crypto_tls_session_x509(const void *opaque)
object_unparent(OBJECT(serverCreds));
object_unparent(OBJECT(clientCreds));
+ object_unparent(OBJECT(auth));
qcrypto_tls_session_free(serverSess);
qcrypto_tls_session_free(clientSess);
diff --git a/tests/test-io-channel-tls.c b/tests/test-io-channel-tls.c
index 4900c6d433..43b707eba7 100644
--- a/tests/test-io-channel-tls.c
+++ b/tests/test-io-channel-tls.c
@@ -29,8 +29,8 @@
#include "io-channel-helpers.h"
#include "crypto/init.h"
#include "crypto/tlscredsx509.h"
-#include "qemu/acl.h"
#include "qapi/error.h"
+#include "authz/list.h"
#include "qom/object_interfaces.h"
#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT
@@ -113,7 +113,7 @@ static void test_io_channel_tls(const void *opaque)
QIOChannelTLS *serverChanTLS;
QIOChannelSocket *clientChanSock;
QIOChannelSocket *serverChanSock;
- qemu_acl *acl;
+ QAuthZList *auth;
const char * const *wildcards;
int channel[2];
struct QIOChannelTLSHandshakeData clientHandshake = { false, false };
@@ -161,11 +161,15 @@ static void test_io_channel_tls(const void *opaque)
SERVER_CERT_DIR);
g_assert(serverCreds != NULL);
- acl = qemu_acl_init("channeltlsacl");
- qemu_acl_reset(acl);
+ auth = qauthz_list_new("channeltlsacl",
+ QAUTHZ_LIST_POLICY_DENY,
+ &error_abort);
wildcards = data->wildcards;
while (wildcards && *wildcards) {
- qemu_acl_append(acl, 0, *wildcards);
+ qauthz_list_append_rule(auth, *wildcards,
+ QAUTHZ_LIST_POLICY_ALLOW,
+ QAUTHZ_LIST_FORMAT_GLOB,
+ &error_abort);
wildcards++;
}
@@ -253,6 +257,8 @@ static void test_io_channel_tls(const void *opaque)
object_unref(OBJECT(serverChanSock));
object_unref(OBJECT(clientChanSock));
+ object_unparent(OBJECT(auth));
+
close(channel[0]);
close(channel[1]);
}
diff --git a/tests/test-util-filemonitor.c b/tests/test-util-filemonitor.c
new file mode 100644
index 0000000000..5d95cea5ee
--- /dev/null
+++ b/tests/test-util-filemonitor.c
@@ -0,0 +1,685 @@
+/*
+ * Tests for util/filemonitor-*.c
+ *
+ * Copyright 2018 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/main-loop.h"
+#include "qapi/error.h"
+#include "qemu/filemonitor.h"
+
+#include <utime.h>
+
+enum {
+ QFILE_MONITOR_TEST_OP_CREATE,
+ QFILE_MONITOR_TEST_OP_APPEND,
+ QFILE_MONITOR_TEST_OP_TRUNC,
+ QFILE_MONITOR_TEST_OP_RENAME,
+ QFILE_MONITOR_TEST_OP_TOUCH,
+ QFILE_MONITOR_TEST_OP_UNLINK,
+};
+
+typedef struct {
+ int type;
+ const char *filesrc;
+ const char *filedst;
+} QFileMonitorTestOp;
+
+typedef struct {
+ const char *file;
+} QFileMonitorTestWatch;
+
+typedef struct {
+ gsize nwatches;
+ const QFileMonitorTestWatch *watches;
+
+ gsize nops;
+ const QFileMonitorTestOp *ops;
+} QFileMonitorTestPlan;
+
+typedef struct {
+ int id;
+ QFileMonitorEvent event;
+ char *filename;
+} QFileMonitorTestRecord;
+
+
+typedef struct {
+ QemuMutex lock;
+ GList *records;
+} QFileMonitorTestData;
+
+static QemuMutex evlock;
+static bool evstopping;
+static bool evrunning;
+
+/*
+ * Main function for a background thread that is
+ * running the event loop during the test
+ */
+static void *
+qemu_file_monitor_test_event_loop(void *opaque G_GNUC_UNUSED)
+{
+ qemu_mutex_lock(&evlock);
+
+ while (!evstopping) {
+ qemu_mutex_unlock(&evlock);
+ main_loop_wait(true);
+ qemu_mutex_lock(&evlock);
+ }
+
+ evrunning = false;
+ qemu_mutex_unlock(&evlock);
+ return NULL;
+}
+
+
+/*
+ * File monitor event handler which simply maintains
+ * an ordered list of all events that it receives
+ */
+static void
+qemu_file_monitor_test_handler(int id,
+ QFileMonitorEvent event,
+ const char *filename,
+ void *opaque)
+{
+ QFileMonitorTestData *data = opaque;
+ QFileMonitorTestRecord *rec = g_new0(QFileMonitorTestRecord, 1);
+
+ rec->id = id;
+ rec->event = event;
+ rec->filename = g_strdup(filename);
+
+ qemu_mutex_lock(&data->lock);
+ data->records = g_list_append(data->records, rec);
+ qemu_mutex_unlock(&data->lock);
+}
+
+
+static void
+qemu_file_monitor_test_record_free(QFileMonitorTestRecord *rec)
+{
+ g_free(rec->filename);
+ g_free(rec);
+}
+
+
+/*
+ * Get the next event record that has been received by
+ * the file monitor event handler. Since events are
+ * emitted in the background thread running the event
+ * loop, we can't assume there is a record available
+ * immediately. Thus we will sleep for upto 5 seconds
+ * to wait for the event to be queued for us.
+ */
+static QFileMonitorTestRecord *
+qemu_file_monitor_test_next_record(QFileMonitorTestData *data)
+{
+ GTimer *timer = g_timer_new();
+ QFileMonitorTestRecord *record = NULL;
+ GList *tmp;
+
+ qemu_mutex_lock(&data->lock);
+ while (!data->records && g_timer_elapsed(timer, NULL) < 5) {
+ qemu_mutex_unlock(&data->lock);
+ usleep(10 * 1000);
+ qemu_mutex_lock(&data->lock);
+ }
+ if (data->records) {
+ record = data->records->data;
+ tmp = data->records;
+ data->records = g_list_remove_link(data->records, tmp);
+ g_list_free(tmp);
+ }
+ qemu_mutex_unlock(&data->lock);
+
+ g_timer_destroy(timer);
+ return record;
+}
+
+
+/*
+ * Check whether the event record we retrieved matches
+ * data we were expecting to see for the event
+ */
+static bool
+qemu_file_monitor_test_expect(QFileMonitorTestData *data,
+ int id,
+ QFileMonitorEvent event,
+ const char *filename)
+{
+ QFileMonitorTestRecord *rec;
+ bool ret = false;
+
+ rec = qemu_file_monitor_test_next_record(data);
+
+ if (!rec) {
+ g_printerr("Missing event watch id %d event %d file %s\n",
+ id, event, filename);
+ return false;
+ }
+
+ if (id != rec->id) {
+ g_printerr("Expected watch id %d but got %d\n", id, rec->id);
+ goto cleanup;
+ }
+
+ if (event != rec->event) {
+ g_printerr("Expected event %d but got %d\n", event, rec->event);
+ goto cleanup;
+ }
+
+ if (!g_str_equal(filename, rec->filename)) {
+ g_printerr("Expected filename %s but got %s\n",
+ filename, rec->filename);
+ goto cleanup;
+ }
+
+ ret = true;
+
+ cleanup:
+ qemu_file_monitor_test_record_free(rec);
+ return ret;
+}
+
+
+static void
+test_file_monitor_events(const void *opaque)
+{
+ const QFileMonitorTestPlan *plan = opaque;
+ Error *local_err = NULL;
+ GError *gerr = NULL;
+ QFileMonitor *mon = qemu_file_monitor_new(&local_err);
+ QemuThread th;
+ GTimer *timer;
+ gchar *dir = NULL;
+ int err = -1;
+ gsize i, j;
+ char *pathsrc = NULL;
+ char *pathdst = NULL;
+ QFileMonitorTestData data;
+
+ qemu_mutex_init(&data.lock);
+ data.records = NULL;
+
+ /*
+ * The file monitor needs the main loop running in
+ * order to receive events from inotify. We must
+ * thus spawn a background thread to run an event
+ * loop impl, while this thread triggers the
+ * actual file operations we're testing
+ */
+ evrunning = 1;
+ evstopping = 0;
+ qemu_thread_create(&th, "event-loop",
+ qemu_file_monitor_test_event_loop, NULL,
+ QEMU_THREAD_JOINABLE);
+
+ if (local_err) {
+ g_printerr("File monitoring not available: %s",
+ error_get_pretty(local_err));
+ error_free(local_err);
+ return;
+ }
+
+ dir = g_dir_make_tmp("test-util-filemonitor-XXXXXX",
+ &gerr);
+ if (!dir) {
+ g_printerr("Unable to create tmp dir %s",
+ gerr->message);
+ g_error_free(gerr);
+ abort();
+ }
+
+ /*
+ * First register all the directory / file watches
+ * we're interested in seeing events against
+ */
+ for (i = 0; i < plan->nwatches; i++) {
+ int watchid;
+ watchid = qemu_file_monitor_add_watch(mon,
+ dir,
+ plan->watches[i].file,
+ qemu_file_monitor_test_handler,
+ &data,
+ &local_err);
+ if (watchid < 0) {
+ g_printerr("Unable to add watch %s",
+ error_get_pretty(local_err));
+ goto cleanup;
+ }
+ }
+
+
+ /*
+ * Now invoke all the file operations (create,
+ * delete, rename, chmod, etc). These operations
+ * will trigger the various file monitor events
+ */
+ for (i = 0; i < plan->nops; i++) {
+ const QFileMonitorTestOp *op = &(plan->ops[i]);
+ int fd;
+ struct utimbuf ubuf;
+
+ pathsrc = g_strdup_printf("%s/%s", dir, op->filesrc);
+ if (op->filedst) {
+ pathdst = g_strdup_printf("%s/%s", dir, op->filedst);
+ }
+
+ switch (op->type) {
+ case QFILE_MONITOR_TEST_OP_CREATE:
+ fd = open(pathsrc, O_WRONLY | O_CREAT, 0700);
+ if (fd < 0) {
+ g_printerr("Unable to create %s: %s",
+ pathsrc, strerror(errno));
+ goto cleanup;
+ }
+ close(fd);
+ break;
+
+ case QFILE_MONITOR_TEST_OP_APPEND:
+ fd = open(pathsrc, O_WRONLY | O_APPEND, 0700);
+ if (fd < 0) {
+ g_printerr("Unable to open %s: %s",
+ pathsrc, strerror(errno));
+ goto cleanup;
+ }
+
+ if (write(fd, "Hello World", 10) != 10) {
+ g_printerr("Unable to write %s: %s",
+ pathsrc, strerror(errno));
+ close(fd);
+ goto cleanup;
+ }
+ close(fd);
+ break;
+
+ case QFILE_MONITOR_TEST_OP_TRUNC:
+ if (truncate(pathsrc, 4) < 0) {
+ g_printerr("Unable to truncate %s: %s",
+ pathsrc, strerror(errno));
+ goto cleanup;
+ }
+ break;
+
+ case QFILE_MONITOR_TEST_OP_RENAME:
+ if (rename(pathsrc, pathdst) < 0) {
+ g_printerr("Unable to rename %s to %s: %s",
+ pathsrc, pathdst, strerror(errno));
+ goto cleanup;
+ }
+ break;
+
+ case QFILE_MONITOR_TEST_OP_UNLINK:
+ if (unlink(pathsrc) < 0) {
+ g_printerr("Unable to unlink %s: %s",
+ pathsrc, strerror(errno));
+ goto cleanup;
+ }
+ break;
+
+ case QFILE_MONITOR_TEST_OP_TOUCH:
+ ubuf.actime = 1024;
+ ubuf.modtime = 1025;
+ if (utime(pathsrc, &ubuf) < 0) {
+ g_printerr("Unable to touch %s: %s",
+ pathsrc, strerror(errno));
+ goto cleanup;
+ }
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+
+ g_free(pathsrc);
+ g_free(pathdst);
+ pathsrc = pathdst = NULL;
+ }
+
+
+ /*
+ * Finally validate that we have received all the events
+ * we expect to see for the combination of watches and
+ * file operations
+ */
+ for (i = 0; i < plan->nops; i++) {
+ const QFileMonitorTestOp *op = &(plan->ops[i]);
+
+ switch (op->type) {
+ case QFILE_MONITOR_TEST_OP_CREATE:
+ for (j = 0; j < plan->nwatches; j++) {
+ if (plan->watches[j].file &&
+ !g_str_equal(plan->watches[j].file, op->filesrc))
+ continue;
+
+ if (!qemu_file_monitor_test_expect(
+ &data, j, QFILE_MONITOR_EVENT_CREATED, op->filesrc))
+ goto cleanup;
+ }
+ break;
+
+ case QFILE_MONITOR_TEST_OP_APPEND:
+ case QFILE_MONITOR_TEST_OP_TRUNC:
+ for (j = 0; j < plan->nwatches; j++) {
+ if (plan->watches[j].file &&
+ !g_str_equal(plan->watches[j].file, op->filesrc))
+ continue;
+
+ if (!qemu_file_monitor_test_expect(
+ &data, j, QFILE_MONITOR_EVENT_MODIFIED, op->filesrc))
+ goto cleanup;
+ }
+ break;
+
+ case QFILE_MONITOR_TEST_OP_RENAME:
+ for (j = 0; j < plan->nwatches; j++) {
+ if (plan->watches[j].file &&
+ !g_str_equal(plan->watches[j].file, op->filesrc))
+ continue;
+
+ if (!qemu_file_monitor_test_expect(
+ &data, j, QFILE_MONITOR_EVENT_DELETED, op->filesrc))
+ goto cleanup;
+ }
+
+ for (j = 0; j < plan->nwatches; j++) {
+ if (plan->watches[j].file &&
+ !g_str_equal(plan->watches[j].file, op->filedst))
+ continue;
+
+ if (!qemu_file_monitor_test_expect(
+ &data, j, QFILE_MONITOR_EVENT_CREATED, op->filedst))
+ goto cleanup;
+ }
+ break;
+
+ case QFILE_MONITOR_TEST_OP_TOUCH:
+ for (j = 0; j < plan->nwatches; j++) {
+ if (plan->watches[j].file &&
+ !g_str_equal(plan->watches[j].file, op->filesrc))
+ continue;
+
+ if (!qemu_file_monitor_test_expect(
+ &data, j, QFILE_MONITOR_EVENT_ATTRIBUTES, op->filesrc))
+ goto cleanup;
+ }
+ break;
+
+ case QFILE_MONITOR_TEST_OP_UNLINK:
+ for (j = 0; j < plan->nwatches; j++) {
+ if (plan->watches[j].file &&
+ !g_str_equal(plan->watches[j].file, op->filesrc))
+ continue;
+
+ if (!qemu_file_monitor_test_expect(
+ &data, j, QFILE_MONITOR_EVENT_DELETED, op->filesrc))
+ goto cleanup;
+ }
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+ }
+
+ err = 0;
+
+ cleanup:
+ g_free(pathsrc);
+ g_free(pathdst);
+
+ qemu_mutex_lock(&evlock);
+ evstopping = 1;
+ timer = g_timer_new();
+ while (evrunning && g_timer_elapsed(timer, NULL) < 5) {
+ qemu_mutex_unlock(&evlock);
+ usleep(10 * 1000);
+ qemu_mutex_lock(&evlock);
+ }
+ qemu_mutex_unlock(&evlock);
+
+ if (g_timer_elapsed(timer, NULL) >= 5) {
+ g_printerr("Event loop failed to quit after 5 seconds\n");
+ }
+ g_timer_destroy(timer);
+
+ for (i = 0; i < plan->nops; i++) {
+ const QFileMonitorTestOp *op = &(plan->ops[i]);
+ pathsrc = g_strdup_printf("%s/%s", dir, op->filesrc);
+ unlink(pathsrc);
+ g_free(pathsrc);
+ if (op->filedst) {
+ pathdst = g_strdup_printf("%s/%s", dir, op->filedst);
+ unlink(pathdst);
+ g_free(pathdst);
+ }
+ }
+
+ qemu_file_monitor_free(mon);
+ g_list_foreach(data.records,
+ (GFunc)qemu_file_monitor_test_record_free, NULL);
+ g_list_free(data.records);
+ qemu_mutex_destroy(&data.lock);
+ if (dir) {
+ rmdir(dir);
+ }
+ g_free(dir);
+ g_assert(err == 0);
+}
+
+
+/*
+ * Set of structs which define which file name patterns
+ * we're trying to watch against. NULL, means all files
+ * in the directory
+ */
+static const QFileMonitorTestWatch watches_any[] = {
+ { NULL },
+};
+
+static const QFileMonitorTestWatch watches_one[] = {
+ { "one.txt" },
+};
+
+static const QFileMonitorTestWatch watches_two[] = {
+ { "two.txt" },
+};
+
+static const QFileMonitorTestWatch watches_many[] = {
+ { NULL },
+ { "one.txt" },
+ { "two.txt" },
+};
+
+
+/*
+ * Various sets of file operations we're going to
+ * trigger and validate events for
+ */
+static const QFileMonitorTestOp ops_create_one[] = {
+ { .type = QFILE_MONITOR_TEST_OP_CREATE,
+ .filesrc = "one.txt", }
+};
+
+static const QFileMonitorTestOp ops_delete_one[] = {
+ { .type = QFILE_MONITOR_TEST_OP_CREATE,
+ .filesrc = "one.txt", },
+ { .type = QFILE_MONITOR_TEST_OP_UNLINK,
+ .filesrc = "one.txt", }
+};
+
+static const QFileMonitorTestOp ops_create_many[] = {
+ { .type = QFILE_MONITOR_TEST_OP_CREATE,
+ .filesrc = "one.txt", },
+ { .type = QFILE_MONITOR_TEST_OP_CREATE,
+ .filesrc = "two.txt", },
+ { .type = QFILE_MONITOR_TEST_OP_CREATE,
+ .filesrc = "three.txt", }
+};
+
+static const QFileMonitorTestOp ops_rename_one[] = {
+ { .type = QFILE_MONITOR_TEST_OP_CREATE,
+ .filesrc = "one.txt", },
+ { .type = QFILE_MONITOR_TEST_OP_RENAME,
+ .filesrc = "one.txt", .filedst = "two.txt" }
+};
+
+static const QFileMonitorTestOp ops_rename_many[] = {
+ { .type = QFILE_MONITOR_TEST_OP_CREATE,
+ .filesrc = "one.txt", },
+ { .type = QFILE_MONITOR_TEST_OP_CREATE,
+ .filesrc = "two.txt", },
+ { .type = QFILE_MONITOR_TEST_OP_RENAME,
+ .filesrc = "one.txt", .filedst = "two.txt" }
+};
+
+static const QFileMonitorTestOp ops_append_one[] = {
+ { .type = QFILE_MONITOR_TEST_OP_CREATE,
+ .filesrc = "one.txt", },
+ { .type = QFILE_MONITOR_TEST_OP_APPEND,
+ .filesrc = "one.txt", },
+};
+
+static const QFileMonitorTestOp ops_trunc_one[] = {
+ { .type = QFILE_MONITOR_TEST_OP_CREATE,
+ .filesrc = "one.txt", },
+ { .type = QFILE_MONITOR_TEST_OP_TRUNC,
+ .filesrc = "one.txt", },
+};
+
+static const QFileMonitorTestOp ops_touch_one[] = {
+ { .type = QFILE_MONITOR_TEST_OP_CREATE,
+ .filesrc = "one.txt", },
+ { .type = QFILE_MONITOR_TEST_OP_TOUCH,
+ .filesrc = "one.txt", },
+};
+
+
+/*
+ * No we define data sets for the combinatorial
+ * expansion of file watches and operation sets
+ */
+#define PLAN_DATA(o, w) \
+ static const QFileMonitorTestPlan plan_ ## o ## _ ## w = { \
+ .nops = G_N_ELEMENTS(ops_ ##o), \
+ .ops = ops_ ##o, \
+ .nwatches = G_N_ELEMENTS(watches_ ##w), \
+ .watches = watches_ ## w, \
+ }
+
+PLAN_DATA(create_one, any);
+PLAN_DATA(create_one, one);
+PLAN_DATA(create_one, two);
+PLAN_DATA(create_one, many);
+
+PLAN_DATA(delete_one, any);
+PLAN_DATA(delete_one, one);
+PLAN_DATA(delete_one, two);
+PLAN_DATA(delete_one, many);
+
+PLAN_DATA(create_many, any);
+PLAN_DATA(create_many, one);
+PLAN_DATA(create_many, two);
+PLAN_DATA(create_many, many);
+
+PLAN_DATA(rename_one, any);
+PLAN_DATA(rename_one, one);
+PLAN_DATA(rename_one, two);
+PLAN_DATA(rename_one, many);
+
+PLAN_DATA(rename_many, any);
+PLAN_DATA(rename_many, one);
+PLAN_DATA(rename_many, two);
+PLAN_DATA(rename_many, many);
+
+PLAN_DATA(append_one, any);
+PLAN_DATA(append_one, one);
+PLAN_DATA(append_one, two);
+PLAN_DATA(append_one, many);
+
+PLAN_DATA(trunc_one, any);
+PLAN_DATA(trunc_one, one);
+PLAN_DATA(trunc_one, two);
+PLAN_DATA(trunc_one, many);
+
+PLAN_DATA(touch_one, any);
+PLAN_DATA(touch_one, one);
+PLAN_DATA(touch_one, two);
+PLAN_DATA(touch_one, many);
+
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ qemu_init_main_loop(&error_abort);
+
+ qemu_mutex_init(&evlock);
+
+ /*
+ * Register test cases for the combinatorial
+ * expansion of file watches and operation sets
+ */
+ #define PLAN_REGISTER(o, w) \
+ g_test_add_data_func("/util/filemonitor/" # o "/" # w, \
+ &plan_ ## o ## _ ## w, test_file_monitor_events)
+
+ PLAN_REGISTER(create_one, any);
+ PLAN_REGISTER(create_one, one);
+ PLAN_REGISTER(create_one, two);
+ PLAN_REGISTER(create_one, many);
+
+ PLAN_REGISTER(delete_one, any);
+ PLAN_REGISTER(delete_one, one);
+ PLAN_REGISTER(delete_one, two);
+ PLAN_REGISTER(delete_one, many);
+
+ PLAN_REGISTER(create_many, any);
+ PLAN_REGISTER(create_many, one);
+ PLAN_REGISTER(create_many, two);
+ PLAN_REGISTER(create_many, many);
+
+ PLAN_REGISTER(rename_one, any);
+ PLAN_REGISTER(rename_one, one);
+ PLAN_REGISTER(rename_one, two);
+ PLAN_REGISTER(rename_one, many);
+
+ PLAN_REGISTER(rename_many, any);
+ PLAN_REGISTER(rename_many, one);
+ PLAN_REGISTER(rename_many, two);
+ PLAN_REGISTER(rename_many, many);
+
+ PLAN_REGISTER(append_one, any);
+ PLAN_REGISTER(append_one, one);
+ PLAN_REGISTER(append_one, two);
+ PLAN_REGISTER(append_one, many);
+
+ PLAN_REGISTER(trunc_one, any);
+ PLAN_REGISTER(trunc_one, one);
+ PLAN_REGISTER(trunc_one, two);
+ PLAN_REGISTER(trunc_one, many);
+
+ PLAN_REGISTER(touch_one, any);
+ PLAN_REGISTER(touch_one, one);
+ PLAN_REGISTER(touch_one, two);
+ PLAN_REGISTER(touch_one, many);
+
+ return g_test_run();
+}
diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c
index 04c608764b..8d2fc9c710 100644
--- a/tests/virtio-blk-test.c
+++ b/tests/virtio-blk-test.c
@@ -46,6 +46,12 @@ typedef struct QVirtioBlkReq {
uint8_t status;
} QVirtioBlkReq;
+#ifdef HOST_WORDS_BIGENDIAN
+const bool host_is_big_endian = true;
+#else
+const bool host_is_big_endian; /* false */
+#endif
+
static char *drive_create(void)
{
int fd, ret;
@@ -125,12 +131,6 @@ static QVirtioPCIDevice *virtio_blk_pci_init(QPCIBus *bus, int slot)
static inline void virtio_blk_fix_request(QVirtioDevice *d, QVirtioBlkReq *req)
{
-#ifdef HOST_WORDS_BIGENDIAN
- const bool host_is_big_endian = true;
-#else
- const bool host_is_big_endian = false;
-#endif
-
if (qvirtio_is_big_endian(d) != host_is_big_endian) {
req->type = bswap32(req->type);
req->ioprio = bswap32(req->ioprio);
@@ -138,13 +138,37 @@ static inline void virtio_blk_fix_request(QVirtioDevice *d, QVirtioBlkReq *req)
}
}
+
+static inline void virtio_blk_fix_dwz_hdr(QVirtioDevice *d,
+ struct virtio_blk_discard_write_zeroes *dwz_hdr)
+{
+ if (qvirtio_is_big_endian(d) != host_is_big_endian) {
+ dwz_hdr->sector = bswap64(dwz_hdr->sector);
+ dwz_hdr->num_sectors = bswap32(dwz_hdr->num_sectors);
+ dwz_hdr->flags = bswap32(dwz_hdr->flags);
+ }
+}
+
static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioDevice *d,
QVirtioBlkReq *req, uint64_t data_size)
{
uint64_t addr;
uint8_t status = 0xFF;
- g_assert_cmpuint(data_size % 512, ==, 0);
+ switch (req->type) {
+ case VIRTIO_BLK_T_IN:
+ case VIRTIO_BLK_T_OUT:
+ g_assert_cmpuint(data_size % 512, ==, 0);
+ break;
+ case VIRTIO_BLK_T_DISCARD:
+ case VIRTIO_BLK_T_WRITE_ZEROES:
+ g_assert_cmpuint(data_size %
+ sizeof(struct virtio_blk_discard_write_zeroes), ==, 0);
+ break;
+ default:
+ g_assert_cmpuint(data_size, ==, 0);
+ }
+
addr = guest_alloc(alloc, sizeof(*req) + data_size);
virtio_blk_fix_request(d, req);
@@ -231,6 +255,95 @@ static void test_basic(QVirtioDevice *dev, QGuestAllocator *alloc,
guest_free(alloc, req_addr);
+ if (features & (1u << VIRTIO_BLK_F_WRITE_ZEROES)) {
+ struct virtio_blk_discard_write_zeroes dwz_hdr;
+ void *expected;
+
+ /*
+ * WRITE_ZEROES request on the same sector of previous test where
+ * we wrote "TEST".
+ */
+ req.type = VIRTIO_BLK_T_WRITE_ZEROES;
+ req.data = (char *) &dwz_hdr;
+ dwz_hdr.sector = 0;
+ dwz_hdr.num_sectors = 1;
+ dwz_hdr.flags = 0;
+
+ virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
+
+ req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
+
+ free_head = qvirtqueue_add(vq, req_addr, 16, false, true);
+ qvirtqueue_add(vq, req_addr + 16, sizeof(dwz_hdr), false, true);
+ qvirtqueue_add(vq, req_addr + 16 + sizeof(dwz_hdr), 1, true, false);
+
+ qvirtqueue_kick(dev, vq, free_head);
+
+ qvirtio_wait_used_elem(dev, vq, free_head, NULL,
+ QVIRTIO_BLK_TIMEOUT_US);
+ status = readb(req_addr + 16 + sizeof(dwz_hdr));
+ g_assert_cmpint(status, ==, 0);
+
+ guest_free(alloc, req_addr);
+
+ /* Read request to check if the sector contains all zeroes */
+ req.type = VIRTIO_BLK_T_IN;
+ req.ioprio = 1;
+ req.sector = 0;
+ req.data = g_malloc0(512);
+
+ req_addr = virtio_blk_request(alloc, dev, &req, 512);
+
+ g_free(req.data);
+
+ free_head = qvirtqueue_add(vq, req_addr, 16, false, true);
+ qvirtqueue_add(vq, req_addr + 16, 512, true, true);
+ qvirtqueue_add(vq, req_addr + 528, 1, true, false);
+
+ qvirtqueue_kick(dev, vq, free_head);
+
+ qvirtio_wait_used_elem(dev, vq, free_head, NULL,
+ QVIRTIO_BLK_TIMEOUT_US);
+ status = readb(req_addr + 528);
+ g_assert_cmpint(status, ==, 0);
+
+ data = g_malloc(512);
+ expected = g_malloc0(512);
+ memread(req_addr + 16, data, 512);
+ g_assert_cmpmem(data, 512, expected, 512);
+ g_free(expected);
+ g_free(data);
+
+ guest_free(alloc, req_addr);
+ }
+
+ if (features & (1u << VIRTIO_BLK_F_DISCARD)) {
+ struct virtio_blk_discard_write_zeroes dwz_hdr;
+
+ req.type = VIRTIO_BLK_T_DISCARD;
+ req.data = (char *) &dwz_hdr;
+ dwz_hdr.sector = 0;
+ dwz_hdr.num_sectors = 1;
+ dwz_hdr.flags = 0;
+
+ virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
+
+ req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
+
+ free_head = qvirtqueue_add(vq, req_addr, 16, false, true);
+ qvirtqueue_add(vq, req_addr + 16, sizeof(dwz_hdr), false, true);
+ qvirtqueue_add(vq, req_addr + 16 + sizeof(dwz_hdr), 1, true, false);
+
+ qvirtqueue_kick(dev, vq, free_head);
+
+ qvirtio_wait_used_elem(dev, vq, free_head, NULL,
+ QVIRTIO_BLK_TIMEOUT_US);
+ status = readb(req_addr + 16 + sizeof(dwz_hdr));
+ g_assert_cmpint(status, ==, 0);
+
+ guest_free(alloc, req_addr);
+ }
+
if (features & (1u << VIRTIO_F_ANY_LAYOUT)) {
/* Write and read with 2 descriptor layout */
/* Write request */
diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c
index 3751a777a4..7b2b09f242 100644
--- a/ui/vnc-auth-sasl.c
+++ b/ui/vnc-auth-sasl.c
@@ -24,6 +24,7 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
+#include "authz/base.h"
#include "vnc.h"
#include "trace.h"
@@ -146,13 +147,14 @@ size_t vnc_client_read_sasl(VncState *vs)
static int vnc_auth_sasl_check_access(VncState *vs)
{
const void *val;
- int err;
- int allow;
+ int rv;
+ Error *err = NULL;
+ bool allow;
- err = sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val);
- if (err != SASL_OK) {
+ rv = sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val);
+ if (rv != SASL_OK) {
trace_vnc_auth_fail(vs, vs->auth, "Cannot fetch SASL username",
- sasl_errstring(err, NULL, NULL));
+ sasl_errstring(rv, NULL, NULL));
return -1;
}
if (val == NULL) {
@@ -163,12 +165,19 @@ static int vnc_auth_sasl_check_access(VncState *vs)
vs->sasl.username = g_strdup((const char*)val);
trace_vnc_auth_sasl_username(vs, vs->sasl.username);
- if (vs->vd->sasl.acl == NULL) {
+ if (vs->vd->sasl.authzid == NULL) {
trace_vnc_auth_sasl_acl(vs, 1);
return 0;
}
- allow = qemu_acl_party_is_allowed(vs->vd->sasl.acl, vs->sasl.username);
+ allow = qauthz_is_allowed_by_id(vs->vd->sasl.authzid,
+ vs->sasl.username, &err);
+ if (err) {
+ trace_vnc_auth_fail(vs, vs->auth, "Error from authz",
+ error_get_pretty(err));
+ error_free(err);
+ return -1;
+ }
trace_vnc_auth_sasl_acl(vs, allow);
return allow ? 0 : -1;
diff --git a/ui/vnc-auth-sasl.h b/ui/vnc-auth-sasl.h
index 2ae224ee3a..fb55fe04ca 100644
--- a/ui/vnc-auth-sasl.h
+++ b/ui/vnc-auth-sasl.h
@@ -30,8 +30,8 @@
typedef struct VncStateSASL VncStateSASL;
typedef struct VncDisplaySASL VncDisplaySASL;
-#include "qemu/acl.h"
#include "qemu/main-loop.h"
+#include "authz/base.h"
struct VncStateSASL {
sasl_conn_t *conn;
@@ -60,7 +60,8 @@ struct VncStateSASL {
};
struct VncDisplaySASL {
- qemu_acl *acl;
+ QAuthZ *authz;
+ char *authzid;
};
void vnc_sasl_client_cleanup(VncState *vs);
diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c
index d99ea362c1..f072e16ace 100644
--- a/ui/vnc-auth-vencrypt.c
+++ b/ui/vnc-auth-vencrypt.c
@@ -109,7 +109,7 @@ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len
tls = qio_channel_tls_new_server(
vs->ioc,
vs->vd->tlscreds,
- vs->vd->tlsaclname,
+ vs->vd->tlsauthzid,
&err);
if (!tls) {
trace_vnc_auth_fail(vs, vs->auth, "TLS setup failed",
diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c
index 950f1cd2ac..95c9703c72 100644
--- a/ui/vnc-ws.c
+++ b/ui/vnc-ws.c
@@ -62,7 +62,7 @@ gboolean vncws_tls_handshake_io(QIOChannel *ioc G_GNUC_UNUSED,
tls = qio_channel_tls_new_server(
vs->ioc,
vs->vd->tlscreds,
- vs->vd->tlsaclname,
+ vs->vd->tlsauthzid,
&err);
if (!tls) {
VNC_DEBUG("Failed to setup TLS %s\n", error_get_pretty(err));
diff --git a/ui/vnc.c b/ui/vnc.c
index 7e0710ed8f..da4a21d4ce 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -33,7 +33,7 @@
#include "qemu/option.h"
#include "qemu/sockets.h"
#include "qemu/timer.h"
-#include "qemu/acl.h"
+#include "authz/list.h"
#include "qemu/config-file.h"
#include "qapi/qapi-emit-events.h"
#include "qapi/qapi-events-ui.h"
@@ -3229,12 +3229,24 @@ static void vnc_display_close(VncDisplay *vd)
object_unparent(OBJECT(vd->tlscreds));
vd->tlscreds = NULL;
}
- g_free(vd->tlsaclname);
- vd->tlsaclname = NULL;
+ if (vd->tlsauthz) {
+ object_unparent(OBJECT(vd->tlsauthz));
+ vd->tlsauthz = NULL;
+ }
+ g_free(vd->tlsauthzid);
+ vd->tlsauthzid = NULL;
if (vd->lock_key_sync) {
qemu_remove_led_event_handler(vd->led);
vd->led = NULL;
}
+#ifdef CONFIG_VNC_SASL
+ if (vd->sasl.authz) {
+ object_unparent(OBJECT(vd->sasl.authz));
+ vd->sasl.authz = NULL;
+ }
+ g_free(vd->sasl.authzid);
+ vd->sasl.authzid = NULL;
+#endif
}
int vnc_display_password(const char *id, const char *password)
@@ -3887,23 +3899,24 @@ void vnc_display_open(const char *id, Error **errp)
if (acl) {
if (strcmp(vd->id, "default") == 0) {
- vd->tlsaclname = g_strdup("vnc.x509dname");
+ vd->tlsauthzid = g_strdup("vnc.x509dname");
} else {
- vd->tlsaclname = g_strdup_printf("vnc.%s.x509dname", vd->id);
+ vd->tlsauthzid = g_strdup_printf("vnc.%s.x509dname", vd->id);
}
- qemu_acl_init(vd->tlsaclname);
+ vd->tlsauthz = QAUTHZ(qauthz_list_new(vd->tlsauthzid,
+ QAUTHZ_LIST_POLICY_DENY,
+ &error_abort));
}
#ifdef CONFIG_VNC_SASL
if (acl && sasl) {
- char *aclname;
-
if (strcmp(vd->id, "default") == 0) {
- aclname = g_strdup("vnc.username");
+ vd->sasl.authzid = g_strdup("vnc.username");
} else {
- aclname = g_strdup_printf("vnc.%s.username", vd->id);
+ vd->sasl.authzid = g_strdup_printf("vnc.%s.username", vd->id);
}
- vd->sasl.acl = qemu_acl_init(aclname);
- g_free(aclname);
+ vd->sasl.authz = QAUTHZ(qauthz_list_new(vd->sasl.authzid,
+ QAUTHZ_LIST_POLICY_DENY,
+ &error_abort));
}
#endif
diff --git a/ui/vnc.h b/ui/vnc.h
index 81daa7a0eb..ee3da08f4a 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -39,6 +39,7 @@
#include "io/channel-socket.h"
#include "io/channel-tls.h"
#include "io/net-listener.h"
+#include "authz/base.h"
#include <zlib.h>
#include "keymaps.h"
@@ -178,7 +179,8 @@ struct VncDisplay
bool lossy;
bool non_adaptive;
QCryptoTLSCreds *tlscreds;
- char *tlsaclname;
+ QAuthZ *tlsauthz;
+ char *tlsauthzid;
#ifdef CONFIG_VNC_SASL
VncDisplaySASL sasl;
#endif
diff --git a/util/Makefile.objs b/util/Makefile.objs
index 0820923c18..0808575e3e 100644
--- a/util/Makefile.objs
+++ b/util/Makefile.objs
@@ -20,7 +20,6 @@ util-obj-y += envlist.o path.o module.o
util-obj-y += host-utils.o
util-obj-y += bitmap.o bitops.o hbitmap.o
util-obj-y += fifo8.o
-util-obj-y += acl.o
util-obj-y += cacheinfo.o
util-obj-y += error.o qemu-error.o
util-obj-y += id.o
@@ -50,5 +49,8 @@ util-obj-y += range.o
util-obj-y += stats64.o
util-obj-y += systemd.o
util-obj-y += iova-tree.o
+util-obj-$(CONFIG_INOTIFY1) += filemonitor-inotify.o
util-obj-$(CONFIG_LINUX) += vfio-helpers.o
util-obj-$(CONFIG_OPENGL) += drm.o
+
+stub-obj-y += filemonitor-stub.o
diff --git a/util/acl.c b/util/acl.c
deleted file mode 100644
index c105addadc..0000000000
--- a/util/acl.c
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * QEMU access control list management
- *
- * Copyright (C) 2009 Red Hat, Inc
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-
-#include "qemu/osdep.h"
-#include "qemu-common.h"
-#include "qemu/acl.h"
-
-#ifdef CONFIG_FNMATCH
-#include <fnmatch.h>
-#endif
-
-
-static unsigned int nacls = 0;
-static qemu_acl **acls = NULL;
-
-
-
-qemu_acl *qemu_acl_find(const char *aclname)
-{
- int i;
- for (i = 0 ; i < nacls ; i++) {
- if (strcmp(acls[i]->aclname, aclname) == 0)
- return acls[i];
- }
-
- return NULL;
-}
-
-qemu_acl *qemu_acl_init(const char *aclname)
-{
- qemu_acl *acl;
-
- acl = qemu_acl_find(aclname);
- if (acl)
- return acl;
-
- acl = g_malloc(sizeof(*acl));
- acl->aclname = g_strdup(aclname);
- /* Deny by default, so there is no window of "open
- * access" between QEMU starting, and the user setting
- * up ACLs in the monitor */
- acl->defaultDeny = 1;
-
- acl->nentries = 0;
- QTAILQ_INIT(&acl->entries);
-
- acls = g_realloc(acls, sizeof(*acls) * (nacls +1));
- acls[nacls] = acl;
- nacls++;
-
- return acl;
-}
-
-int qemu_acl_party_is_allowed(qemu_acl *acl,
- const char *party)
-{
- qemu_acl_entry *entry;
-
- QTAILQ_FOREACH(entry, &acl->entries, next) {
-#ifdef CONFIG_FNMATCH
- if (fnmatch(entry->match, party, 0) == 0)
- return entry->deny ? 0 : 1;
-#else
- /* No fnmatch, so fallback to exact string matching
- * instead of allowing wildcards */
- if (strcmp(entry->match, party) == 0)
- return entry->deny ? 0 : 1;
-#endif
- }
-
- return acl->defaultDeny ? 0 : 1;
-}
-
-
-void qemu_acl_reset(qemu_acl *acl)
-{
- qemu_acl_entry *entry, *next_entry;
-
- /* Put back to deny by default, so there is no window
- * of "open access" while the user re-initializes the
- * access control list */
- acl->defaultDeny = 1;
- QTAILQ_FOREACH_SAFE(entry, &acl->entries, next, next_entry) {
- QTAILQ_REMOVE(&acl->entries, entry, next);
- g_free(entry->match);
- g_free(entry);
- }
- acl->nentries = 0;
-}
-
-
-int qemu_acl_append(qemu_acl *acl,
- int deny,
- const char *match)
-{
- qemu_acl_entry *entry;
-
- entry = g_malloc(sizeof(*entry));
- entry->match = g_strdup(match);
- entry->deny = deny;
-
- QTAILQ_INSERT_TAIL(&acl->entries, entry, next);
- acl->nentries++;
-
- return acl->nentries;
-}
-
-
-int qemu_acl_insert(qemu_acl *acl,
- int deny,
- const char *match,
- int index)
-{
- qemu_acl_entry *tmp;
- int i = 0;
-
- if (index <= 0)
- return -1;
- if (index > acl->nentries) {
- return qemu_acl_append(acl, deny, match);
- }
-
- QTAILQ_FOREACH(tmp, &acl->entries, next) {
- i++;
- if (i == index) {
- qemu_acl_entry *entry;
- entry = g_malloc(sizeof(*entry));
- entry->match = g_strdup(match);
- entry->deny = deny;
-
- QTAILQ_INSERT_BEFORE(tmp, entry, next);
- acl->nentries++;
- break;
- }
- }
-
- return i;
-}
-
-int qemu_acl_remove(qemu_acl *acl,
- const char *match)
-{
- qemu_acl_entry *entry;
- int i = 0;
-
- QTAILQ_FOREACH(entry, &acl->entries, next) {
- i++;
- if (strcmp(entry->match, match) == 0) {
- QTAILQ_REMOVE(&acl->entries, entry, next);
- acl->nentries--;
- g_free(entry->match);
- g_free(entry);
- return i;
- }
- }
- return -1;
-}
diff --git a/util/filemonitor-inotify.c b/util/filemonitor-inotify.c
new file mode 100644
index 0000000000..3a72be037f
--- /dev/null
+++ b/util/filemonitor-inotify.c
@@ -0,0 +1,339 @@
+/*
+ * QEMU file monitor Linux inotify impl
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/filemonitor.h"
+#include "qemu/main-loop.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "trace.h"
+
+#include <sys/inotify.h>
+
+struct QFileMonitor {
+ int fd;
+
+ QemuMutex lock; /* protects dirs & idmap */
+ GHashTable *dirs; /* dirname => QFileMonitorDir */
+ GHashTable *idmap; /* inotify ID => dirname */
+};
+
+
+typedef struct {
+ int id; /* watch ID */
+ char *filename; /* optional filter */
+ QFileMonitorHandler cb;
+ void *opaque;
+} QFileMonitorWatch;
+
+
+typedef struct {
+ char *path;
+ int id; /* inotify ID */
+ int nextid; /* watch ID counter */
+ GArray *watches; /* QFileMonitorWatch elements */
+} QFileMonitorDir;
+
+
+static void qemu_file_monitor_watch(void *arg)
+{
+ QFileMonitor *mon = arg;
+ char buf[4096]
+ __attribute__ ((aligned(__alignof__(struct inotify_event))));
+ int used = 0;
+ int len;
+
+ qemu_mutex_lock(&mon->lock);
+
+ if (mon->fd == -1) {
+ qemu_mutex_unlock(&mon->lock);
+ return;
+ }
+
+ len = read(mon->fd, buf, sizeof(buf));
+
+ if (len < 0) {
+ if (errno != EAGAIN) {
+ error_report("Failure monitoring inotify FD '%s',"
+ "disabling events", strerror(errno));
+ goto cleanup;
+ }
+
+ /* no more events right now */
+ goto cleanup;
+ }
+
+ /* Loop over all events in the buffer */
+ while (used < len) {
+ struct inotify_event *ev =
+ (struct inotify_event *)(buf + used);
+ const char *name = ev->len ? ev->name : "";
+ QFileMonitorDir *dir = g_hash_table_lookup(mon->idmap,
+ GINT_TO_POINTER(ev->wd));
+ uint32_t iev = ev->mask &
+ (IN_CREATE | IN_MODIFY | IN_DELETE | IN_IGNORED |
+ IN_MOVED_TO | IN_MOVED_FROM | IN_ATTRIB);
+ int qev;
+ gsize i;
+
+ used += sizeof(struct inotify_event) + ev->len;
+
+ if (!dir) {
+ continue;
+ }
+
+ /*
+ * During a rename operation, the old name gets
+ * IN_MOVED_FROM and the new name gets IN_MOVED_TO.
+ * To simplify life for callers, we turn these into
+ * DELETED and CREATED events
+ */
+ switch (iev) {
+ case IN_CREATE:
+ case IN_MOVED_TO:
+ qev = QFILE_MONITOR_EVENT_CREATED;
+ break;
+ case IN_MODIFY:
+ qev = QFILE_MONITOR_EVENT_MODIFIED;
+ break;
+ case IN_DELETE:
+ case IN_MOVED_FROM:
+ qev = QFILE_MONITOR_EVENT_DELETED;
+ break;
+ case IN_ATTRIB:
+ qev = QFILE_MONITOR_EVENT_ATTRIBUTES;
+ break;
+ case IN_IGNORED:
+ qev = QFILE_MONITOR_EVENT_IGNORED;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ trace_qemu_file_monitor_event(mon, dir->path, name, ev->mask, dir->id);
+ for (i = 0; i < dir->watches->len; i++) {
+ QFileMonitorWatch *watch = &g_array_index(dir->watches,
+ QFileMonitorWatch,
+ i);
+
+ if (watch->filename == NULL ||
+ (name && g_str_equal(watch->filename, name))) {
+ trace_qemu_file_monitor_dispatch(mon, dir->path, name,
+ qev, watch->cb,
+ watch->opaque, watch->id);
+ watch->cb(watch->id, qev, name, watch->opaque);
+ }
+ }
+ }
+
+ cleanup:
+ qemu_mutex_unlock(&mon->lock);
+}
+
+
+static void
+qemu_file_monitor_dir_free(void *data)
+{
+ QFileMonitorDir *dir = data;
+ gsize i;
+
+ for (i = 0; i < dir->watches->len; i++) {
+ QFileMonitorWatch *watch = &g_array_index(dir->watches,
+ QFileMonitorWatch, i);
+ g_free(watch->filename);
+ }
+ g_array_unref(dir->watches);
+ g_free(dir->path);
+ g_free(dir);
+}
+
+
+QFileMonitor *
+qemu_file_monitor_new(Error **errp)
+{
+ int fd;
+ QFileMonitor *mon;
+
+ fd = inotify_init1(IN_NONBLOCK);
+ if (fd < 0) {
+ error_setg_errno(errp, errno,
+ "Unable to initialize inotify");
+ return NULL;
+ }
+
+ mon = g_new0(QFileMonitor, 1);
+ qemu_mutex_init(&mon->lock);
+ mon->fd = fd;
+
+ mon->dirs = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
+ qemu_file_monitor_dir_free);
+ mon->idmap = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+ trace_qemu_file_monitor_new(mon, mon->fd);
+
+ return mon;
+}
+
+static gboolean
+qemu_file_monitor_free_idle(void *opaque)
+{
+ QFileMonitor *mon = opaque;
+
+ if (!mon) {
+ return G_SOURCE_REMOVE;
+ }
+
+ qemu_mutex_lock(&mon->lock);
+
+ g_hash_table_unref(mon->idmap);
+ g_hash_table_unref(mon->dirs);
+
+ qemu_mutex_unlock(&mon->lock);
+
+ qemu_mutex_destroy(&mon->lock);
+ g_free(mon);
+
+ return G_SOURCE_REMOVE;
+}
+
+void
+qemu_file_monitor_free(QFileMonitor *mon)
+{
+ if (!mon) {
+ return;
+ }
+
+ qemu_mutex_lock(&mon->lock);
+ if (mon->fd != -1) {
+ qemu_set_fd_handler(mon->fd, NULL, NULL, NULL);
+ close(mon->fd);
+ mon->fd = -1;
+ }
+ qemu_mutex_unlock(&mon->lock);
+
+ /*
+ * Can't free it yet, because another thread
+ * may be running event loop, so the inotify
+ * callback might be pending. Using an idle
+ * source ensures we'll only free after the
+ * pending callback is done
+ */
+ g_idle_add((GSourceFunc)qemu_file_monitor_free_idle, mon);
+}
+
+int
+qemu_file_monitor_add_watch(QFileMonitor *mon,
+ const char *dirpath,
+ const char *filename,
+ QFileMonitorHandler cb,
+ void *opaque,
+ Error **errp)
+{
+ QFileMonitorDir *dir;
+ QFileMonitorWatch watch;
+ int ret = -1;
+
+ qemu_mutex_lock(&mon->lock);
+ dir = g_hash_table_lookup(mon->dirs, dirpath);
+ if (!dir) {
+ int rv = inotify_add_watch(mon->fd, dirpath,
+ IN_CREATE | IN_DELETE | IN_MODIFY |
+ IN_MOVED_TO | IN_MOVED_FROM | IN_ATTRIB);
+
+ if (rv < 0) {
+ error_setg_errno(errp, errno, "Unable to watch '%s'", dirpath);
+ goto cleanup;
+ }
+
+ trace_qemu_file_monitor_enable_watch(mon, dirpath, rv);
+
+ dir = g_new0(QFileMonitorDir, 1);
+ dir->path = g_strdup(dirpath);
+ dir->id = rv;
+ dir->watches = g_array_new(FALSE, TRUE, sizeof(QFileMonitorWatch));
+
+ g_hash_table_insert(mon->dirs, dir->path, dir);
+ g_hash_table_insert(mon->idmap, GINT_TO_POINTER(rv), dir);
+
+ if (g_hash_table_size(mon->dirs) == 1) {
+ qemu_set_fd_handler(mon->fd, qemu_file_monitor_watch, NULL, mon);
+ }
+ }
+
+ watch.id = dir->nextid++;
+ watch.filename = g_strdup(filename);
+ watch.cb = cb;
+ watch.opaque = opaque;
+
+ g_array_append_val(dir->watches, watch);
+
+ trace_qemu_file_monitor_add_watch(mon, dirpath,
+ filename ? filename : "<none>",
+ cb, opaque, watch.id);
+
+ ret = watch.id;
+
+ cleanup:
+ qemu_mutex_unlock(&mon->lock);
+ return ret;
+}
+
+
+void qemu_file_monitor_remove_watch(QFileMonitor *mon,
+ const char *dirpath,
+ int id)
+{
+ QFileMonitorDir *dir;
+ gsize i;
+
+ qemu_mutex_lock(&mon->lock);
+
+ trace_qemu_file_monitor_remove_watch(mon, dirpath, id);
+
+ dir = g_hash_table_lookup(mon->dirs, dirpath);
+ if (!dir) {
+ goto cleanup;
+ }
+
+ for (i = 0; i < dir->watches->len; i++) {
+ QFileMonitorWatch *watch = &g_array_index(dir->watches,
+ QFileMonitorWatch, i);
+ if (watch->id == id) {
+ g_free(watch->filename);
+ g_array_remove_index(dir->watches, i);
+ break;
+ }
+ }
+
+ if (dir->watches->len == 0) {
+ inotify_rm_watch(mon->fd, dir->id);
+ trace_qemu_file_monitor_disable_watch(mon, dir->path, dir->id);
+
+ g_hash_table_remove(mon->idmap, GINT_TO_POINTER(dir->id));
+ g_hash_table_remove(mon->dirs, dir->path);
+
+ if (g_hash_table_size(mon->dirs) == 0) {
+ qemu_set_fd_handler(mon->fd, NULL, NULL, NULL);
+ }
+ }
+
+ cleanup:
+ qemu_mutex_unlock(&mon->lock);
+}
diff --git a/util/filemonitor-stub.c b/util/filemonitor-stub.c
new file mode 100644
index 0000000000..48268b2bb6
--- /dev/null
+++ b/util/filemonitor-stub.c
@@ -0,0 +1,59 @@
+/*
+ * QEMU file monitor stub impl
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/filemonitor.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+
+
+QFileMonitor *
+qemu_file_monitor_new(Error **errp)
+{
+ error_setg(errp, "File monitoring not available on this platform");
+ return NULL;
+}
+
+
+void
+qemu_file_monitor_free(QFileMonitor *mon G_GNUC_UNUSED)
+{
+}
+
+
+int
+qemu_file_monitor_add_watch(QFileMonitor *mon G_GNUC_UNUSED,
+ const char *dirpath G_GNUC_UNUSED,
+ const char *filename G_GNUC_UNUSED,
+ QFileMonitorHandler cb G_GNUC_UNUSED,
+ void *opaque G_GNUC_UNUSED,
+ Error **errp)
+{
+ error_setg(errp, "File monitoring not available on this platform");
+ return -1;
+}
+
+
+void
+qemu_file_monitor_remove_watch(QFileMonitor *mon G_GNUC_UNUSED,
+ const char *dirpath G_GNUC_UNUSED,
+ int id G_GNUC_UNUSED)
+{
+}
diff --git a/util/trace-events b/util/trace-events
index 79569b7fdf..ff19b253e2 100644
--- a/util/trace-events
+++ b/util/trace-events
@@ -21,6 +21,15 @@ buffer_move_empty(const char *buf, size_t len, const char *from) "%s: %zd bytes
buffer_move(const char *buf, size_t len, const char *from) "%s: %zd bytes from %s"
buffer_free(const char *buf, size_t len) "%s: capacity %zd"
+# util/filemonitor.c
+qemu_file_monitor_add_watch(void *mon, const char *dirpath, const char *filename, void *cb, void *opaque, int id) "File monitor %p add watch dir='%s' file='%s' cb=%p opaque=%p id=%u"
+qemu_file_monitor_remove_watch(void *mon, const char *dirpath, int id) "File monitor %p remove watch dir='%s' id=%u"
+qemu_file_monitor_new(void *mon, int fd) "File monitor %p created fd=%d"
+qemu_file_monitor_enable_watch(void *mon, const char *dirpath, int id) "File monitor %p enable watch dir='%s' id=%u"
+qemu_file_monitor_disable_watch(void *mon, const char *dirpath, int id) "Fle monitor %p disable watch dir='%s' id=%u"
+qemu_file_monitor_event(void *mon, const char *dirpath, const char *filename, int mask, unsigned int id) "File monitor %p event dir='%s' file='%s' mask=0x%x id=%u"
+qemu_file_monitor_dispatch(void *mon, const char *dirpath, const char *filename, int ev, void *cb, void *opaque, unsigned int id) "File monitor %p dispatch dir='%s' file='%s' ev=%d cb=%p opaque=%p id=%u"
+
# util/qemu-coroutine.c
qemu_aio_coroutine_enter(void *ctx, void *from, void *to, void *opaque) "ctx %p from %p to %p opaque %p"
qemu_coroutine_yield(void *from, void *to) "from %p to %p"