From: Ondrej Kozina Date: Mon, 24 Feb 2014 12:48:46 +0000 (+0100) Subject: - handle ACL modifications outside XAttributes X-Git-Tag: v0.2.2~18^2~2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=39b1edad1f803da1e70fdf516636b03ac9ebd93c;p=thirdparty%2Fsnapper.git - handle ACL modifications outside XAttributes --- diff --git a/snapper/Acls.cc b/snapper/Acls.cc new file mode 100644 index 00000000..83821cb2 --- /dev/null +++ b/snapper/Acls.cc @@ -0,0 +1,147 @@ +/* + * Copyright (c) [2014] Red Hat, Inc. + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * 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 program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include + +#include "snapper/Acls.h" +#include "snapper/AppUtil.h" +#include "snapper/Exception.h" +#include "snapper/Log.h" + +namespace snapper +{ + bool + is_acl_signature(const std::string& name) + { + for (std::vector::const_iterator cit = _acl_signatures.begin(); cit != _acl_signatures.end(); cit++) + { + if (name == *cit) + return true; + } + return false; + } + + Acls::Acls(const string& path) + : allowed_types(0x0), acl_access(NULL), acl_default(NULL) + { + struct stat buf; + + int fd = ::open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_NONBLOCK | O_NOATIME | + O_CLOEXEC); + if (fd < 0) + { + if (errno == ELOOP) + { + y2deb("can't read ACLs from symlink '" << path << "' itself"); + return; + } + + if (stat(path.c_str(), &buf) < 0) + { + y2err("stat failed errno: " << errno << " (" << stringerror(errno) << ")"); + throw AclException(); + } + } + else + { + if (fstat(fd, &buf) < 0) + { + y2err("fstat failed errno: " << errno << " (" << stringerror(errno) << ")"); + ::close(fd); + throw AclException(); + } + + acl_access = acl_get_fd(fd); + if (!acl_access) + { + y2err("acl_get_fd failed errno: " << errno << " (" << stringerror(errno) << ")"); + ::close(fd); + throw AclException(); + } + + ::close(fd); + allowed_types = ACL_TYPE_ACCESS; + } + + allowed_types |= (S_ISDIR(buf.st_mode)) ? ACL_TYPE_DEFAULT : 0x0; + + // in case open failed for some reason + if (!(allowed_types & ACL_TYPE_ACCESS)) + { + allowed_types |= ACL_TYPE_ACCESS; + acl_access = acl_get_file(path.c_str(), ACL_TYPE_ACCESS); + if (!acl_access) + { + y2err("acl_get_file failed errno: " << errno << " (" << stringerror(errno) << ")"); + throw AclException(); + } + } + + // ACL_TYPE_DEFAULT can't be read from fd + if (allowed_types & ACL_TYPE_DEFAULT) + { + acl_default = acl_get_file(path.c_str(), ACL_TYPE_DEFAULT); + if (!acl_default) + { + y2err("acl_get_file failed errno: " << errno << " (" << stringerror(errno) << ")"); + if (acl_free(acl_access)) + { + y2err("acl_free failed errno: " << errno << " (" << stringerror(errno) << ")"); + } + + throw AclException(); + } + } + } + + + Acls::~Acls() + { + if (acl_access) + acl_free(acl_access); + if (acl_default) + acl_free(acl_default); + } + + + void + Acls::serializeTo(const string& path) const + { + if (empty()) + return; + + if (acl_set_file(path.c_str(), ACL_TYPE_ACCESS, acl_access)) + { + y2err("acl_set_file failed errno: " << errno << " (" << stringerror(errno) << ")"); + throw AclException(); + } + + if (get_acl_types() & ACL_TYPE_DEFAULT) + { + if (acl_set_file(path.c_str(), ACL_TYPE_DEFAULT, acl_default)) + { + y2err("acl_set_file failed errno: " << errno << " (" << stringerror(errno) << ")"); + throw AclException(); + } + } + } +} diff --git a/snapper/Acls.h b/snapper/Acls.h new file mode 100644 index 00000000..6aa5d7ee --- /dev/null +++ b/snapper/Acls.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) [2014] Red Hat, Inc. + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * 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 program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef SNAPPER_ACLS_H +#define SNAPPER_ACLS_H + +#include +#include +#include + +#include + +#ifndef ENABLE_ACL_SIGNATURES +#define ENABLE_ACL_SIGNATURES ("system.posix_acl_access") \ + ("system.posix_acl_default") \ + ("trusted.SGI_ACL_FILE") \ + ("trusted.SGI_ACL_DEFAULT") +#endif + +namespace snapper +{ + using std::string; + + const std::vector _acl_signatures = boost::assign::list_of ENABLE_ACL_SIGNATURES; + + bool is_acl_signature(const string& name); + + class Acls + { + public: + + Acls(const string& path); + ~Acls(); + + acl_type_t get_acl_types() const { return allowed_types; } + bool empty() const { return allowed_types == 0x0; } + void serializeTo(const string& path) const; + + private: + + acl_type_t allowed_types; + acl_t acl_access; + acl_t acl_default; + }; + +} +#endif //SNAPPER_ACLS_H diff --git a/snapper/Btrfs.cc b/snapper/Btrfs.cc index 39442ea5..73356152 100644 --- a/snapper/Btrfs.cc +++ b/snapper/Btrfs.cc @@ -47,6 +47,7 @@ #include "snapper/Snapper.h" #include "snapper/SnapperTmpl.h" #include "snapper/SnapperDefines.h" +#include "snapper/Acls.h" namespace snapper @@ -450,10 +451,10 @@ namespace snapper if (status & CREATED) status = CREATED; if (status & DELETED) status = DELETED; - if (status & (CONTENT | PERMISSIONS | USER | GROUP | XATTRS)) + if (status & (CONTENT | PERMISSIONS | USER | GROUP | XATTRS | ACL)) { // TODO check for content sometimes not required - status &= ~(CONTENT | PERMISSIONS | USER | GROUP | XATTRS); + status &= ~(CONTENT | PERMISSIONS | USER | GROUP | XATTRS | ACL); string dirname = snapper::dirname(name); string basename = snapper::basename(name); @@ -546,7 +547,7 @@ namespace snapper else { node->status &= ~(CREATED | DELETED); - node->status |= CONTENT | PERMISSIONS | USER | GROUP | XATTRS; + node->status |= CONTENT | PERMISSIONS | USER | GROUP | XATTRS | ACL; } } @@ -679,7 +680,7 @@ namespace snapper else { node->status &= ~(CREATED | DELETED); - node->status |= CONTENT | PERMISSIONS | USER | GROUP | XATTRS; + node->status |= CONTENT | PERMISSIONS | USER | GROUP | XATTRS | ACL; } merge(processor, &it->second, from, to, x); @@ -697,7 +698,7 @@ namespace snapper else { node->status &= ~(CREATED | DELETED); - node->status |= CONTENT | PERMISSIONS | USER | GROUP | XATTRS; + node->status |= CONTENT | PERMISSIONS | USER | GROUP | XATTRS | ACL; } merge(processor, &it->second, from, to, x); @@ -854,6 +855,14 @@ namespace snapper tree_node* node = processor->files.insert(path); node->status |= XATTRS; + + if (is_acl_signature(name)) + { + #ifdef DEBUG_PROCESS + y2deb("adding acl flag, signature:'" << name << "'"); + #endif + node->status |= ACL; + } #endif return 0; @@ -872,6 +881,14 @@ namespace snapper tree_node* node = processor->files.insert(path); node->status |= XATTRS; + + if (is_acl_signature(name)) + { + #ifdef DEBUG_PROCESS + y2deb("adding acl flag, signature:'" << name << "'"); + #endif + node->status |= ACL; + } #endif return 0; diff --git a/snapper/Compare.cc b/snapper/Compare.cc index 9b62baab..fa1b771e 100644 --- a/snapper/Compare.cc +++ b/snapper/Compare.cc @@ -38,6 +38,7 @@ #include "snapper/Compare.h" #include "snapper/Exception.h" #include "snapper/XAttributes.h" +#include "snapper/Acls.h" namespace snapper @@ -222,10 +223,7 @@ namespace snapper #ifdef ENABLE_XATTRS if (file1.xaSupported() && file2.xaSupported()) { - if (!cmpFilesXattrs(file1, file2)) - { - status |= XATTRS; - } + status |= cmpFilesXattrs(file1, file2); } #endif @@ -467,19 +465,34 @@ namespace snapper } - bool + unsigned int cmpFilesXattrs(const SFile& file1, const SFile& file2) { try { XAttributes xa(file1); XAttributes xb(file2); - return xa == xb; + + if (xa == xb) + { + return 0; + } + else + { + unsigned int status = XATTRS; + + CompareAcls acl_a(xa); + CompareAcls acl_b(xb); + + status |= (acl_a == acl_b) ? 0 : ACL; + + return status; + } } catch (const XAttributesException& e) { - y2err("extended attributes compare failed"); - return false; + y2err("extended attributes or ACL compare failed"); + return (XATTRS | ACL); } } diff --git a/snapper/Compare.h b/snapper/Compare.h index 2975280c..0b56c85a 100644 --- a/snapper/Compare.h +++ b/snapper/Compare.h @@ -48,7 +48,9 @@ namespace snapper void cmpDirs(const SDir& dir1, const SDir& dir2, cmpdirs_cb_t cb); - bool + /* Compares the two files extended attributes and ACLs. + Returns 0 or XATTRS or (XATTRS | ACL) */ + unsigned int cmpFilesXattrs(const SFile&, const SFile&); } diff --git a/snapper/File.cc b/snapper/File.cc index bc40ba01..4e0fee5c 100644 --- a/snapper/File.cc +++ b/snapper/File.cc @@ -40,6 +40,7 @@ #include "snapper/Compare.h" #include "snapper/Exception.h" #include "snapper/XAttributes.h" +#include "snapper/Acls.h" namespace snapper @@ -540,6 +541,8 @@ namespace snapper XAModification xa_mod(xa_src, xa_dest); y2deb("xa_modmap(xa_dest) object: " << xa_mod); + xa_mod.filterOutAcls(); + xaCreated = xa_mod.getXaCreateNum(); xaDeleted = xa_mod.getXaDeleteNum(); xaReplaced = xa_mod.getXaReplaceNum(); @@ -556,6 +559,28 @@ namespace snapper return ret_val; } + + bool + File::modifyAcls() + { + bool ret_val; + + try + { + Acls acl(getAbsolutePath(LOC_PRE)); + acl.serializeTo(getAbsolutePath(LOC_SYSTEM)); + + ret_val = true; + } + catch (const SnapperException& e) + { + ret_val = false; + } + + return ret_val; + } + + XAUndoStatistic& operator+=(XAUndoStatistic &out, const XAUndoStatistic &src) { out.numCreate += src.numCreate; @@ -628,6 +653,12 @@ namespace snapper if (!modifyXattributes()) error = true; } + + if (getPreToPostStatus() & (ACL | TYPE | DELETED)) + { + if (!modifyAcls()) + error = true; + } #endif pre_to_system_status = (unsigned int) -1; @@ -727,7 +758,8 @@ namespace snapper ret += status & PERMISSIONS ? "p" : "."; ret += status & USER ? "u" : "."; ret += status & GROUP ? "g" : "."; - ret += status & XATTRS ? "x" : "."; + ret += status & XATTRS ? "x" : "."; + ret += status & ACL ? "a" : "."; return ret; } @@ -768,10 +800,16 @@ namespace snapper } if (str.length() >= 5) - { - if (str[4] == 'x') - ret |= XATTRS; - } + { + if (str[4] == 'x') + ret |= XATTRS; + } + + if (str.length() >= 6) + { + if (str[5] == 'a') + ret |= ACL; + } return ret; } diff --git a/snapper/File.h b/snapper/File.h index 19c3dc41..96c1e39e 100644 --- a/snapper/File.h +++ b/snapper/File.h @@ -39,7 +39,7 @@ namespace snapper enum StatusFlags { CREATED = 1, DELETED = 2, TYPE = 4, CONTENT = 8, PERMISSIONS = 16, USER = 32, - GROUP = 64, XATTRS = 128 + GROUP = 64, XATTRS = 128, ACL = 256 }; enum Cmp @@ -156,6 +156,7 @@ namespace snapper bool undo; bool modifyXattributes(); + bool modifyAcls(); unsigned int xaCreated; unsigned int xaDeleted; diff --git a/snapper/Makefile.am b/snapper/Makefile.am index a6975afa..8518f0f1 100644 --- a/snapper/Makefile.am +++ b/snapper/Makefile.am @@ -26,12 +26,12 @@ libsnapper_la_SOURCES = \ SystemCmd.cc SystemCmd.h \ AsciiFile.cc AsciiFile.h \ Regex.cc Regex.h \ + Acls.cc Acls.h \ Exception.h \ SnapperTmpl.h \ SnapperTypes.h \ SnapperDefines.h \ - Version.h \ - $(TMP_XA) + Version.h if ENABLE_BTRFS diff --git a/snapper/XAttributes.cc b/snapper/XAttributes.cc index 756e0a03..bcf8c6f6 100644 --- a/snapper/XAttributes.cc +++ b/snapper/XAttributes.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) [2013] Red Hat, Inc. + * Copyright (c) [2013-2014] Red Hat, Inc. * * All Rights Reserved. * @@ -29,15 +29,62 @@ #include #include #include +#include #include "snapper/AppUtil.h" #include "snapper/Exception.h" #include "snapper/Log.h" #include "snapper/XAttributes.h" +#include "snapper/Acls.h" namespace snapper { + struct FilterAclsHelper + { + FilterAclsHelper(const vector& acl_sigs) + : acl_sigs(acl_sigs) {} + + bool operator()(const xa_pair_t& pair) + { + for (vector::const_iterator cit = acl_sigs.begin(); cit != acl_sigs.end(); cit++) + if (pair.first == *cit) + return true; + return false; + } + + bool operator()(const string& name) + { + for (vector::const_iterator cit = acl_sigs.begin(); cit != acl_sigs.end(); cit++) + if (name == *cit) + return true; + return false; + } + + const vector& acl_sigs; + }; + + + struct InsertAclsHelper + { + InsertAclsHelper(xa_map_t& xamap, const vector& acl_sigs) + : map(xamap), acl_sigs(acl_sigs) {} + void operator()(const xa_pair_t& xapair) + { + for (vector::const_iterator cit = acl_sigs.begin(); cit != acl_sigs.end(); cit++) + { + if (*cit == xapair.first) + { + map.insert(xapair); + break; + } + } + } + + xa_map_t& map; + const vector& acl_sigs; + }; + XAttributes::XAttributes(const string &path) { @@ -73,6 +120,7 @@ namespace snapper // move beyond separating '\0' char pos += name.length() + 1; + ssize_t v_size = lgetxattr(path.c_str(), name.c_str(), NULL, 0); if (v_size < 0) { @@ -180,6 +228,7 @@ namespace snapper return (this == &xa) ? true : (this->xamap == xa.xamap); } + ostream& operator<<(ostream &out, const XAttributes &xa) { @@ -194,6 +243,7 @@ namespace snapper return out; } + ostream& operator<<(ostream &out, const xa_value_t &xavalue) { @@ -262,14 +312,17 @@ namespace snapper y2deb("adding create operation for " << src_cit->first); create_vec.push_back(xa_pair_t(src_cit->first, src_cit->second)); } + } + bool XAModification::empty() const { return create_vec.empty() && delete_vec.empty() && replace_vec.empty(); } + bool XAModification::serializeTo(const string &dest) const { @@ -336,24 +389,28 @@ namespace snapper return true; } + unsigned int XAModification::getXaCreateNum() const { return create_vec.size(); } + unsigned int XAModification::getXaDeleteNum() const { return delete_vec.size(); } + unsigned int XAModification::getXaReplaceNum() const { return replace_vec.size(); } + void XAModification::printTo(ostream& out, bool diff) const { @@ -381,16 +438,46 @@ namespace snapper } } + void XAModification::dumpDiffReport(ostream& out) const { printTo(out, true); } + ostream& operator<<(ostream &out, const XAModification &xa_mod) { xa_mod.printTo(out, false); return out; } + + + CompareAcls::CompareAcls(const XAttributes& xa) + { + std::for_each(xa.cbegin(), xa.cend(), InsertAclsHelper(xamap, _acl_signatures)); + } + + + bool + CompareAcls::operator==(const CompareAcls& acls) const + { + return (this == &acls) ? true : (this->xamap == acls.xamap); + } + + + void + XAModification::filterOutAcls() + { + FilterAclsHelper fhelper(_acl_signatures); + + create_vec.erase(std::remove_if(create_vec.begin(), create_vec.end(), fhelper), + create_vec.end()); + delete_vec.erase(std::remove_if(delete_vec.begin(), delete_vec.end(), fhelper), + delete_vec.end()); + replace_vec.erase(std::remove_if(replace_vec.begin(), replace_vec.end(), fhelper), + replace_vec.end()); + } + } diff --git a/snapper/XAttributes.h b/snapper/XAttributes.h index 70482108..2aa88b2e 100644 --- a/snapper/XAttributes.h +++ b/snapper/XAttributes.h @@ -1,5 +1,5 @@ /* - * Copyright (c) [2013] Red Hat, Inc. + * Copyright (c) [2013-2014] Red Hat, Inc. * * All Rights Reserved. * @@ -77,6 +77,7 @@ namespace snapper XAModification(const XAttributes&, const XAttributes&); bool empty() const; + void filterOutAcls(); bool serializeTo(const string&) const; unsigned int getXaCreateNum() const; @@ -92,6 +93,16 @@ namespace snapper ostream& operator<<(ostream&, const XAttributes&); ostream& operator<<(ostream&, const xa_value_t&); + + class CompareAcls + { + private: + xa_map_t xamap; + public: + CompareAcls(const XAttributes& xa); + + bool operator==(const CompareAcls&) const; + }; } diff --git a/testsuite-real/Makefile.am b/testsuite-real/Makefile.am index b183fb77..ec1de72c 100644 --- a/testsuite-real/Makefile.am +++ b/testsuite-real/Makefile.am @@ -9,7 +9,7 @@ INCLUDES = -I$(top_srcdir) LDADD = ../snapper/libsnapper.la if HAVE_XATTRS -TMP_XATST = xattrs1 xattrs2 xattrs3 +TMP_XATST = xattrs1 xattrs2 xattrs3 xattrs4 endif noinst_SCRIPTS = run-all @@ -38,6 +38,7 @@ error4_SOURCES = error4.cc common.h common.cc xattrs1_SOURCES = xattrs1.cc xattrs_utils.cc xattrs_utils.h common.h common.cc xattrs2_SOURCES = xattrs2.cc xattrs_utils.cc xattrs_utils.h common.h common.cc xattrs3_SOURCES = xattrs3.cc xattrs_utils.cc xattrs_utils.h common.h common.cc +xattrs4_SOURCES = xattrs4.cc xattrs_utils.cc xattrs_utils.h common.h common.cc EXTRA_DIST = $(noinst_SCRIPTS) diff --git a/testsuite-real/run-all b/testsuite-real/run-all index 0f9b6eda..51e60479 100755 --- a/testsuite-real/run-all +++ b/testsuite-real/run-all @@ -38,3 +38,4 @@ run error4 test -x xattrs1 && run xattrs1 test -x xattrs2 && run xattrs2 test -x xattrs3 && run xattrs3 +test -x xattrs4 && run xattrs4 diff --git a/testsuite-real/xattrs1.cc b/testsuite-real/xattrs1.cc index 2d9f360c..b784b25c 100644 --- a/testsuite-real/xattrs1.cc +++ b/testsuite-real/xattrs1.cc @@ -28,7 +28,8 @@ main() check_undo_statistics(0, 1, 0); - check_xa_undo_statistics(2, 1, 1); + // do not count ACLs + check_xa_undo_statistics(1, 1, 1); check_undo_errors(0, 0, 0); diff --git a/testsuite-real/xattrs4.cc b/testsuite-real/xattrs4.cc new file mode 100644 index 00000000..0d4db29c --- /dev/null +++ b/testsuite-real/xattrs4.cc @@ -0,0 +1,41 @@ + +#include "common.h" + +using namespace std; + +int +main() +{ + setup(); + + run_command("touch file1"); + run_command("mkdir dir1"); + run_command("mkdir no_default"); + run_command("setfacl -b file1"); + run_command("setfacl -k dir1"); + run_command("setfacl -k no_default"); + run_command("setfacl -m u:nobody:rw file1"); + run_command("setfacl -d -m u:nobody:w dir1"); + + first_snapshot(); + + run_command("setfacl -b file1"); + run_command("setfacl -k dir1"); + + second_snapshot(); + + undo(); + + check_undo_statistics(0, 2, 0); + + // do not count ACLs + check_xa_undo_statistics(0, 0, 0); + + check_undo_errors(0, 0, 0); + + check_first(); + + cleanup(); + + exit(EXIT_SUCCESS); +}