]> git.ipfire.org Git - thirdparty/snapper.git/commitdiff
- initial support for detecting extended attributes changes in between two snapshots
authorOndrej Kozina <okozina@redhat.com>
Wed, 16 Jan 2013 17:07:39 +0000 (18:07 +0100)
committerOndrej Kozina <okozina@redhat.com>
Wed, 27 Feb 2013 16:24:56 +0000 (17:24 +0100)
snapper/Compare.cc
snapper/Compare.h
snapper/File.cc
snapper/File.h
snapper/FileUtils.cc
snapper/FileUtils.h
snapper/Makefile.am
snapper/XAttributes.cc [new file with mode: 0644]
snapper/XAttributes.h [new file with mode: 0644]

index c4bb61ee6ecce912e0b05d89f0a44f36e318ed25..837a2c0b2931d4f17a15d43de8bb45d11be8f64b 100644 (file)
@@ -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);
+    }
 }
index 4f3901175689132d2efbca1dc3fd334700596939..2975280cf3700bc8b06b69219bdf7e29b1e985bd 100644 (file)
@@ -48,6 +48,9 @@ namespace snapper
     void
     cmpDirs(const SDir& dir1, const SDir& dir2, cmpdirs_cb_t cb);
 
+    bool
+    cmpFilesXattrs(const SFile&, const SFile&);
+
 }
 
 
index 163611751a4c06931a17924df8c4b3b98829a71f..113a94f0a7898c501d3ff922aedfef7a55e2f6b4 100644 (file)
@@ -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;
     }
 
index 0e6268dba19e43fb326e0d49c36ba96606086b7b..439f12b6d1bb4e4384e3a9425e7bf3962e55a5fd 100644 (file)
@@ -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
index e0ea3cd43e6d21b89a65e33cd42b58027ef3cfe8..59f2b1ce4369b31995301c61a20696bced80b8a5 100644 (file)
@@ -23,6 +23,7 @@
 #include <string.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/xattr.h>
 #include <fcntl.h>
 #include <stddef.h>
 #include <dirent.h>
@@ -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();
+    }
 }
index b3aba356d80e23f76a8544a40062bb3aa7777fed..fba6da5299a800a4b036776255612543ac31327e 100644 (file)
@@ -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;
index f0e8e6561c37c4f2204f767f27f9158a5ed46e88..a32e4ea217fc9059dbf6e99ae8c5aa673387ac02 100644 (file)
@@ -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 (file)
index 0000000..697ede1
--- /dev/null
@@ -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 <cstdio>
+#include <attr/xattr.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#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<xa_map_citer, bool> 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 (file)
index 0000000..cbfe030
--- /dev/null
@@ -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 <map>
+#include <vector>
+#include <string>
+#include <iostream>
+
+#include <stdint.h>
+
+namespace snapper
+{
+       using std::map;
+       using std::string;
+       using std::pair;
+       using std::ostream;
+       using std::vector;
+
+       typedef vector<uint8_t> xa_value_t;
+       typedef map<string, xa_value_t> xa_map_t;
+       typedef pair<string, xa_value_t> 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