For now, only three APIs are implemented:
virFileGetACL to retrieve permission for a specific user
virFileSetACL for setting requested permissions for a specific user,
virFileRemoveACL to remove those permissions.
---
diff to v4:
-drop errno setting
diff to v3:
-set errno=ENOSYS when building without WITH_ATTR for easier check within callee.
-ACL mask is deleted prior recalc as after removing our entry, mask may be not
required anymore.
diff to v2:
-Introduced m4 macro to check for libacl
-new virFileGetACL API
-ACL mask recalc offloaded to libacl
configure.ac | 2 +
libvirt.spec.in | 1 +
m4/virt-acl.m4 | 9 +++
src/Makefile.am | 4 +-
src/libvirt_private.syms | 3 +
src/util/virfile.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++
src/util/virfile.h | 14 ++++
7 files changed, 221 insertions(+), 2 deletions(-)
create mode 100644 m4/virt-acl.m4
diff --git a/configure.ac b/configure.ac
index 9d366e9..4e6627c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -142,6 +142,7 @@ AC_MSG_RESULT([$VERSION_SCRIPT_FLAGS])
LIBVIRT_COMPILE_WARNINGS
+LIBVIRT_CHECK_ACL
LIBVIRT_CHECK_APPARMOR
LIBVIRT_CHECK_ATTR
LIBVIRT_CHECK_AUDIT
@@ -2462,6 +2463,7 @@ fi
AC_MSG_NOTICE([])
AC_MSG_NOTICE([Libraries])
AC_MSG_NOTICE([])
+LIBVIRT_RESULT_ACL
LIBVIRT_RESULT_APPARMOR
LIBVIRT_RESULT_ATTR
LIBVIRT_RESULT_AUDIT
diff --git a/libvirt.spec.in b/libvirt.spec.in
index 9fb753a..222674d 100644
--- a/libvirt.spec.in
+++ b/libvirt.spec.in
@@ -411,6 +411,7 @@ BuildRequires: gettext
BuildRequires: libtasn1-devel
BuildRequires: gnutls-devel
BuildRequires: libattr-devel
+BuildRequires: libacl-devel
%if 0%{?fedora} >= 12 || 0%{?rhel} >= 6
# for augparse, optionally used in testing
BuildRequires: augeas
diff --git a/m4/virt-acl.m4 b/m4/virt-acl.m4
new file mode 100644
index 0000000..7f16dca
--- /dev/null
+++ b/m4/virt-acl.m4
@@ -0,0 +1,9 @@
+dnl The libacl.so library
+
+AC_DEFUN([LIBVIRT_CHECK_ACL],[
+ LIBVIRT_CHECK_LIB([ACL], [acl], [acl_init], [sys/acl.h])
+])
+
+AC_DEFUN([LIBVIRT_RESULT_ACL],[
+ LIBVIRT_RESULT_LIB([ACL])
+])
diff --git a/src/Makefile.am b/src/Makefile.am
index 3f69d39..921ef94 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -768,11 +768,11 @@ libvirt_util_la_SOURCES = \
$(UTIL_SOURCES)
libvirt_util_la_CFLAGS = $(CAPNG_CFLAGS) $(YAJL_CFLAGS) $(LIBNL_CFLAGS) \
$(AM_CFLAGS) $(AUDIT_CFLAGS) $(DEVMAPPER_CFLAGS) \
- $(DBUS_CFLAGS) $(LDEXP_LIBM) $(NUMACTL_CFLAGS)
+ $(DBUS_CFLAGS) $(LDEXP_LIBM) $(NUMACTL_CFLAGS) $(ACL_CFLAGS)
libvirt_util_la_LIBADD = $(CAPNG_LIBS) $(YAJL_LIBS) $(LIBNL_LIBS) \
$(THREAD_LIBS) $(AUDIT_LIBS) $(DEVMAPPER_LIBS) \
$(LIB_CLOCK_GETTIME) $(DBUS_LIBS) $(MSCOM_LIBS) $(LIBXML_LIBS) \
- $(SECDRIVER_LIBS) $(NUMACTL_LIBS)
+ $(SECDRIVER_LIBS) $(NUMACTL_LIBS) $(ACL_LIBS)
noinst_LTLIBRARIES += libvirt_conf.la
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index fd57fa0..db20591 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1253,10 +1253,13 @@ virFileClose;
virFileDirectFdFlag;
virFileFclose;
virFileFdopen;
+virFileGetACL;
virFileGetAttr;
virFileLoopDeviceAssociate;
+virFileRemoveACL;
virFileRemoveAttr;
virFileRewrite;
+virFileSetACL;
virFileSetAttr;
virFileTouch;
virFileUpdatePerm;
diff --git a/src/util/virfile.c b/src/util/virfile.c
index 2409db4..f8f0e20 100644
--- a/src/util/virfile.c
+++ b/src/util/virfile.c
@@ -41,6 +41,10 @@
# include <attr/xattr.h>
#endif
+#ifdef WITH_ACL
+# include <acl/libacl.h>
+#endif
+
#include "vircommand.h"
#include "configmake.h"
#include "viralloc.h"
@@ -749,3 +753,189 @@ virFileRemoveAttr(const char *file ATTRIBUTE_UNUSED,
return -1;
}
#endif /* WITH_ATTR */
+
+#ifdef WITH_ACL
+static acl_entry_t
+virFileACLFindEntry(acl_t acl, acl_tag_t type, id_t id)
+{
+ acl_entry_t ent;
+ acl_tag_t e_type;
+ id_t *e_id_p;
+
+ /* acl_get_entry returns 1 if there's an entry in @acl */
+ if (acl_get_entry(acl, ACL_FIRST_ENTRY, &ent) != 1)
+ return NULL;
+
+ do {
+ acl_get_tag_type(ent, &e_type);
+ if (e_type == type) {
+ if (id == ACL_UNDEFINED_ID)
+ return ent;
+
+ if (!(e_id_p = acl_get_qualifier(ent)))
+ return NULL;
+ if (*e_id_p == id) {
+ acl_free(e_id_p);
+ return ent;
+ }
+ acl_free(e_id_p);
+ }
+ } while (acl_get_entry(acl, ACL_NEXT_ENTRY, &ent) == 1);
+
+ return NULL;
+}
+
+static void
+virFileACLSetPerms(acl_entry_t ent, mode_t perms)
+{
+ acl_permset_t set;
+
+ acl_get_permset(ent, &set);
+ if (perms & S_IRUSR)
+ acl_add_perm(set, ACL_READ);
+ else
+ acl_delete_perm(set, ACL_READ);
+ if (perms & S_IWUSR)
+ acl_add_perm(set, ACL_WRITE);
+ else
+ acl_delete_perm(set, ACL_WRITE);
+ if (perms & S_IXUSR)
+ acl_add_perm(set, ACL_EXECUTE);
+ else
+ acl_delete_perm(set, ACL_EXECUTE);
+}
+
+static void
+virFileACLGetPerms(acl_entry_t ent, mode_t *perms)
+{
+ acl_permset_t set;
+
+ *perms = 0;
+ acl_get_permset(ent, &set);
+ if (acl_get_perm(set, ACL_READ))
+ *perms |= S_IRUSR;
+ if (acl_get_perm(set, ACL_WRITE))
+ *perms |= S_IWUSR;
+ if (acl_get_perm(set, ACL_EXECUTE))
+ *perms |= S_IXUSR;
+}
+
+static int
+virFileACLSetOrRemove(const char *path,
+ uid_t user,
+ mode_t perms,
+ bool set)
+{
+ int ret = -1;
+ acl_t acl;
+ acl_entry_t ent;
+
+ if (!(acl = acl_get_file(path, ACL_TYPE_ACCESS))) {
+ virReportSystemError(errno, _("Unable to get ACL on %s"), path);
+ return ret;
+ }
+
+ ent = virFileACLFindEntry(acl, ACL_USER, user);
+ if (set) {
+ if (!ent && acl_create_entry(&acl, &ent) < 0) {
+ virReportSystemError(errno, "%s", _("Unable to create ACL
entity"));
+ goto cleanup;
+ }
+ acl_set_tag_type(ent, ACL_USER);
+ acl_set_qualifier(ent, &user);
+
+ virFileACLSetPerms(ent, perms);
+
+ } else if (ent) {
+ if (acl_delete_entry(acl, ent) < 0) {
+ virReportSystemError(errno, "%s", _("Unable to delete ACL
entity"));
+ goto cleanup;
+ }
+ }
+
+ if ((ent = virFileACLFindEntry(acl, ACL_MASK, ACL_UNDEFINED_ID)) &&
+ acl_delete_entry(acl, ent) < 0) {
+ virReportSystemError(errno, "%s", _("Unable to delete ACL
mask"));
+ goto cleanup;
+ }
+
+ if (acl_equiv_mode(acl, NULL) && acl_calc_mask(&acl) < 0) {
+ virReportSystemError(errno, "%s", _("Unable to calculate ACL
mask"));
+ goto cleanup;
+ }
+
+ if (acl_set_file(path, ACL_TYPE_ACCESS, acl) < 0) {
+ virReportSystemError(errno, _("Unable to set ACL on %s"), path);
+ goto cleanup;
+ }
+
+ ret = 0;
+cleanup:
+ acl_free(acl);
+ return ret;
+}
+
+int
+virFileSetACL(const char *file,
+ uid_t user,
+ mode_t perms)
+{
+ return virFileACLSetOrRemove(file, user, perms, true);
+}
+
+int
+virFileRemoveACL(const char *file,
+ uid_t user)
+{
+ return virFileACLSetOrRemove(file, user, 0, false);
+}
+
+int
+virFileGetACL(const char *file,
+ uid_t user,
+ mode_t *perms)
+{
+ acl_t acl;
+ acl_entry_t ent;
+
+ if (!(acl = acl_get_file(file, ACL_TYPE_ACCESS))) {
+ virReportSystemError(errno, _("Unable to get ACL on %s"), file);
+ return -1;
+ }
+
+ if ((ent = virFileACLFindEntry(acl, ACL_USER, user)))
+ virFileACLGetPerms(ent, perms);
+ else
+ *perms = 0;
+
+ acl_free(acl);
+ return 0;
+}
+
+#else /* WITH_ACL */
+
+int
+virFileSetACL(const char *file ATTRIBUTE_UNUSED,
+ uid_t user ATTRIBUTE_UNUSED,
+ mode_t perms ATTRIBUTE_UNUSED)
+{
+ virReportSystemError(ENOSYS, "%s", _("Unable to set ACL"));
+ return -1;
+}
+
+int
+virFileRemoveACL(const char *file ATTRIBUTE_UNUSED,
+ uid_t user ATTRIBUTE_UNUSED)
+{
+ virReportSystemError(ENOSYS, "%s", _("Unable to remove ACL"));
+ return -1;
+}
+int
+virFileGetACL(const char *file ATTRIBUTE_UNUSED,
+ uid_t user ATTRIBUTE_UNUSED,
+ mode_t *perms ATTRIBUTE_UNUSED)
+{
+ virReportSystemError(ENOSYS, "%s", _("Unable to get ACL"));
+ return -1;
+}
+#endif /* WITH_ACL */
diff --git a/src/util/virfile.h b/src/util/virfile.h
index 9e0adf6..2a322b5 100644
--- a/src/util/virfile.h
+++ b/src/util/virfile.h
@@ -122,4 +122,18 @@ int virFileRemoveAttr(const char *file,
const char *name)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+int virFileSetACL(const char *file,
+ uid_t user,
+ mode_t perms)
+ ATTRIBUTE_NONNULL(1);
+
+int virFileGetACL(const char *file,
+ uid_t user,
+ mode_t *perms)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3);
+
+int virFileRemoveACL(const char *file,
+ uid_t user)
+ ATTRIBUTE_NONNULL(1);
+
#endif /* __VIR_FILES_H */
--
1.8.1.5