From: Ondrej Kozina Date: Wed, 16 Jan 2013 17:07:39 +0000 (+0100) Subject: - initial support for detecting extended attributes changes in between two snapshots X-Git-Tag: v0.1.3~18^2~50 X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=4dbfde44fc8727d4e143fc0fb270759afac03c4d;p=thirdparty%2Fsnapper.git - initial support for detecting extended attributes changes in between two snapshots --- diff --git a/snapper/Compare.cc b/snapper/Compare.cc index c4bb61ee..837a2c0b 100644 --- a/snapper/Compare.cc +++ b/snapper/Compare.cc @@ -35,6 +35,7 @@ #include "snapper/File.h" #include "snapper/Compare.h" #include "snapper/Exception.h" +#include "snapper/XAttributes.h" namespace snapper @@ -174,7 +175,19 @@ namespace snapper cmpFiles(const SFile& file1, const struct stat& stat1, const SFile& file2, const struct stat& stat2) { - unsigned int status = 0; + unsigned int status = 0; + + /* + * NOTE: + * + * if both ctimes are in match, files should be same + * ctime can be altered only by some debugfs tool and + * on umounted fs (ext2,3,4...). So this should be safe + * unless root or CAP_SYS_ADMIN played with low-level fs + * utilities + */ + if ((stat1.st_ctime == stat2.st_ctime)) + return status; if ((stat1.st_mode & S_IFMT) != (stat2.st_mode & S_IFMT)) { @@ -202,7 +215,17 @@ namespace snapper status |= GROUP; } - return status; + // TODO: decide what to do w/ i.e. file1.xaSupported() + // and !file2.xaSupported() at the same time + if (file1.xaSupported() && file2.xaSupported()) + { + if (!cmpFilesXattrs(file1, file2)) + { + status |= XATTRS; + } + } + + return status; } @@ -439,4 +462,26 @@ namespace snapper y2mil("stopwatch " << stopwatch << " for comparing directories"); } + bool + cmpFilesXattrs(const SFile& file1, const SFile& file2) + { + int fd1 = file1.open(O_RDONLY | O_NOFOLLOW | O_NOATIME | O_CLOEXEC); + if (fd1 < 0) + { + y2err("open failed path:" << file1.fullname() << " errno:" << errno); + throw IOErrorException(); + } + + int fd2 = file2.open(O_RDONLY | O_NOFOLLOW | O_NOATIME | O_CLOEXEC); + if (fd1 < 0) + { + y2err("open failed path:" << file2.fullname() << " errno:" << errno); + throw IOErrorException(); + } + + XAttributes xa(fd1), xb(fd2); + close(fd1); + close(fd2); + return (xa == xb); + } } diff --git a/snapper/Compare.h b/snapper/Compare.h index 4f390117..2975280c 100644 --- a/snapper/Compare.h +++ b/snapper/Compare.h @@ -48,6 +48,9 @@ namespace snapper void cmpDirs(const SDir& dir1, const SDir& dir2, cmpdirs_cb_t cb); + bool + cmpFilesXattrs(const SFile&, const SFile&); + } diff --git a/snapper/File.cc b/snapper/File.cc index 16361175..113a94f0 100644 --- a/snapper/File.cc +++ b/snapper/File.cc @@ -646,6 +646,8 @@ namespace snapper ret += status & USER ? "u" : "."; ret += status & GROUP ? "g" : "."; + ret += status & XATTRS ? "x" : "."; + return ret; } @@ -684,6 +686,12 @@ namespace snapper ret |= GROUP; } + if (str.length() >= 5) + { + if (str[4] == 'x') + ret |= XATTRS; + } + return ret; } diff --git a/snapper/File.h b/snapper/File.h index 0e6268db..439f12b6 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 + GROUP = 64, XATTRS = 128 }; enum Cmp diff --git a/snapper/FileUtils.cc b/snapper/FileUtils.cc index e0ea3cd4..59f2b1ce 100644 --- a/snapper/FileUtils.cc +++ b/snapper/FileUtils.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -60,6 +61,8 @@ namespace snapper y2err("not a directory path:" << base_path); throw IOErrorException(); } + + setXaStatus(); } @@ -84,11 +87,13 @@ namespace snapper close(dirfd); throw IOErrorException(); } + + setXaStatus(); } SDir::SDir(const SDir& dir) - : base_path(dir.base_path), path(dir.path) + : base_path(dir.base_path), path(dir.path), xastatus(dir.xastatus) { dirfd = dup(dir.dirfd); if (dirfd == -1) @@ -104,6 +109,7 @@ namespace snapper { if (this != &dir) { + xastatus = dir.xastatus; ::close(dirfd); dirfd = dup(dir.dirfd); if (dirfd == -1) @@ -374,6 +380,35 @@ namespace snapper return -1; } + bool + SDir::xaSupported(void) const + { + return (xastatus == XA_SUPPORTED); + } + + void + SDir::setXaStatus(void) + { + xastatus = XA_UNKNOWN; + + ssize_t ret = flistxattr(dirfd, NULL, 0); + if (ret < 0) + { + if (errno == ENOTSUP) + { + xastatus = XA_UNSUPPORTED; + } + else + { + y2err("Couldn't get extended attributes status for " << base_path << "/" << path << stringerror(errno)); + throw IOErrorException(); + } + } + else + { + xastatus = XA_SUPPORTED; + } + } SFile::SFile(const SDir& dir, const string& name) : dir(dir), name(name) @@ -410,4 +445,9 @@ namespace snapper return dir.readlink(name, buf); } + bool + SFile::xaSupported(void) const + { + return dir.xaSupported(); + } } diff --git a/snapper/FileUtils.h b/snapper/FileUtils.h index b3aba356..fba6da52 100644 --- a/snapper/FileUtils.h +++ b/snapper/FileUtils.h @@ -33,7 +33,12 @@ namespace snapper { using std::string; using std::vector; - + + enum XaAttrsStatus { + XA_UNKNOWN, + XA_UNSUPPORTED, + XA_SUPPORTED + }; class SDir { @@ -75,13 +80,16 @@ namespace snapper int rename(const string& oldname, const string& newname) const; int mktemp(string& name) const; + bool xaSupported() const; private: + void setXaStatus(); const string base_path; const string path; int dirfd; + int xastatus; }; @@ -97,7 +105,7 @@ namespace snapper int stat(struct stat* buf, int flags) const; int open(int flags) const; int readlink(string& buf) const; - + bool xaSupported() const; private: const SDir& dir; diff --git a/snapper/Makefile.am b/snapper/Makefile.am index f0e8e656..a32e4ea2 100644 --- a/snapper/Makefile.am +++ b/snapper/Makefile.am @@ -25,6 +25,7 @@ libsnapper_la_SOURCES = \ SystemCmd.cc SystemCmd.h \ AsciiFile.cc AsciiFile.h \ Regex.cc Regex.h \ + XAttributes.cc Xattributes.h \ Exception.h \ SnapperTmpl.h \ SnapperTypes.h \ diff --git a/snapper/XAttributes.cc b/snapper/XAttributes.cc new file mode 100644 index 00000000..697ede1d --- /dev/null +++ b/snapper/XAttributes.cc @@ -0,0 +1,177 @@ +/* + * Copyright (c) [2013] 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 "XAttributes.h" + +#include +#include +#include +#include +#include +#include + +#include "snapper/Exception.h" + +namespace snapper +{ + XAttributes::XAttributes() + { + xamap = new xa_map_t; + } + + XAttributes::XAttributes(int fd) + { + // TODO: change to debug + std::cout << "starting XattrsContainer(int)" << std::endl; + ssize_t size = flistxattr(fd, NULL, 0); + if (size < 0) + { + std::cerr << "errno: " << errno << std::endl; + throw IOErrorException(); + } + + // +1 to cover size == 0 + char *names = new char[size + 1]; + + size = flistxattr(fd, names, size); + if (size < 0) + { + std::cerr << "errno: " << errno << std::endl; + throw IOErrorException(); + } + + // TODO: change to debug + std::cout << "names list size is: " << size << std::endl; + + int pos = 0; + + xamap = new xa_map_t; + + while (pos < size) + { + string name = string(names + pos); + // step beyond separating '\0' char + pos += name.length() + 1; + + ssize_t v_size = fgetxattr(fd, name.c_str(), NULL, 0); + if (v_size < 0) + { + std::cerr << "fgetxattr(0) failed w/ errno: " << errno << std::endl; + throw IOErrorException(); + } + + uint8_t *buffer = new uint8_t[v_size]; + + v_size = fgetxattr(fd, name.c_str(), buffer, v_size); + if (v_size < 0) + { + std::cerr << "fgetxattr(" << v_size << ") failed w/ errno: " << errno << std::endl; + throw IOErrorException(); + } + + xa_value_t xa_value(buffer, buffer + v_size); + + std::cout << "array size: " << v_size << ", xavalue size: " << xa_value.size() << std::endl; + + xamap->insert(xa_pair_t(name, xa_value)); + + delete[] buffer; + } + + delete[] names; + } + + XAttributes::XAttributes(const XAttributes &xa) + { + std::cout << "starting copy constructor XattrsContainer(const XattrsContainer&)" << std::endl; + xamap = new xa_map_t(*(xa.xamap)); + } + + XAttributes::~XAttributes() + { + delete xamap; + } + + XAttributes& + XAttributes::operator=(const XAttributes &xa) + { + if (this != &xa) + { + std::cout << "This is assignment operator XattrsContainer::operator=()" << std::endl; + delete this->xamap; + + this->xamap = new xa_map_t(*(xa.xamap)); + } + + return *this; + } + + bool + XAttributes::operator==(const XAttributes &xa) + { + std::cout << "operator==" << std::endl; + return *(this->xamap) == *(xa.xamap); + } + + ostream& + operator<<(ostream &out, const XAttributes &xa) + { + xa_map_citer it = xa.xamap->begin(); + + if (it == xa.xamap->end()) + { + out << "(XA container is empty)"; + return out; + } + + out << "XA container includes:" << std::endl; + + while (it != xa.xamap->end()) + { + out << "xa name: " << it->first << ", xa value: " << it->second << std::endl; + it++; + } + + return out; + } + + void + XAttributes::insert(const xa_pair_t &p) + { + pair ret = xamap->insert(p); + if (!ret.second) + std::cerr << "couldn't insert '" << p.first << ":" << "'" << p.second << "'" << std::endl; + + } + + ostream& + operator<<(ostream &out, const xa_value_t &xavalue) + { + char tmp[4]; + + for (xa_value_t::const_iterator cit = xavalue.begin(); cit != xavalue.end(); cit++) + { + sprintf(tmp, "%d", *cit); + out << tmp << ":"; + } + + return out; + } +} \ No newline at end of file diff --git a/snapper/XAttributes.h b/snapper/XAttributes.h new file mode 100644 index 00000000..cbfe030d --- /dev/null +++ b/snapper/XAttributes.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) [2013] 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_XATTRIBUTES_H +#define SNAPPER_XATTRIBUTES_H + +#include +#include +#include +#include + +#include + +namespace snapper +{ + using std::map; + using std::string; + using std::pair; + using std::ostream; + using std::vector; + + typedef vector xa_value_t; + typedef map xa_map_t; + typedef pair xa_pair_t; + + typedef xa_map_t::iterator xa_map_iter; + typedef xa_map_t::const_iterator xa_map_citer; + + class XAttributes + { + private: + xa_map_t *xamap; + public: + XAttributes(); + XAttributes(int); + XAttributes(const XAttributes&); + ~XAttributes(); + + XAttributes& operator=(const XAttributes&); + bool operator==(const XAttributes&); + + void insert(const xa_pair_t&); + + friend ostream& operator<<(ostream&, const XAttributes&); + }; + + ostream& operator<<(ostream&, const xa_value_t&); +} + +#endif