On Fri, Mar 15, 2013 at 03:12:02PM +0100, Michal Privoznik wrote: > 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 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 | 193 +++++++++++++++++++++++++++++++++++++++++++++++ > src/util/virfile.h | 14 ++++ > 7 files changed, 224 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 0c0dfb3..0ddc128 100644 > --- a/src/Makefile.am > +++ b/src/Makefile.am > @@ -764,11 +764,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) > + $(DBUS_CFLAGS) $(LDEXP_LIBM) $(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) > + $(SECDRIVER_LIBS) $(ACL_LIBS) > > > noinst_LTLIBRARIES += libvirt_conf.la > diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms > index 5a2cbe8..e1ec774 100644 > --- a/src/libvirt_private.syms > +++ b/src/libvirt_private.syms > @@ -1252,10 +1252,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 be50e83..7f50328 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" > @@ -752,3 +756,192 @@ 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) > +{ > + errno = ENOSYS; As with previous patch, NACK to setting 'errno' here. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :| -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list