From: Arvin Schnell Date: Wed, 12 Jan 2011 13:45:30 +0000 (+0100) Subject: - added functions to compare files and directories X-Git-Tag: v0.1.3~557 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=040ba4cdf448ab22624e998cdd195ad7d379ff5a;p=thirdparty%2Fsnapper.git - added functions to compare files and directories --- diff --git a/examples/.gitignore b/examples/.gitignore index 648188c8..629d65b3 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -2,3 +2,5 @@ List.o List Create.o Create +CmpDirs.o +CmpDirs diff --git a/examples/CmpDirs.cc b/examples/CmpDirs.cc new file mode 100644 index 00000000..4200e225 --- /dev/null +++ b/examples/CmpDirs.cc @@ -0,0 +1,30 @@ + +#include +#include + +#include + +using namespace snapper; +using namespace std; + + +void +log(const string& name, unsigned int status) +{ + cout << statusToString(status) << " " << name << endl; +} + + +int +main(int argc, char** argv) +{ + if (argc != 3) + { + cerr << "usage: CmdDirs path1 path2\n"; + exit(EXIT_FAILURE); + } + + cmpDirs(argv[1], argv[2], log); + + exit(EXIT_SUCCESS); +} diff --git a/examples/Makefile.am b/examples/Makefile.am index d8df7443..10dac5c9 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -1,20 +1,22 @@ # -# Makefile.am for libsnapper/examples +# Makefile.am for snapper/examples # INCLUDES = -I$(top_srcdir) LDADD = ../snapper/libsnapper.la -noinst_PROGRAMS = List Create +noinst_PROGRAMS = List Create CmpDirs List_SOURCES = List.cc Create_SOURCES = Create.cc +CmpDirs_SOURCES = CmpDirs.cc + exampledir = $(docdir)/examples -example_DATA = List.cc Create.cc +example_DATA = List.cc Create.cc CmpDirs.cc EXTRA_DIST = $(example_DATA) Makefile.example diff --git a/snapper/AppUtil.h b/snapper/AppUtil.h index 7cbaf19b..20a25287 100644 --- a/snapper/AppUtil.h +++ b/snapper/AppUtil.h @@ -48,6 +48,8 @@ bool setStatMode(const string& Path_Cv, mode_t val ); list glob(const string& path, int flags); + bool readlink(const string& path, string& buf); + template void classic(StreamType& stream) { diff --git a/snapper/Files.cc b/snapper/Files.cc new file mode 100644 index 00000000..0ba77394 --- /dev/null +++ b/snapper/Files.cc @@ -0,0 +1,395 @@ +/* + * Copyright (c) 2011 Novell, 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, contact Novell, Inc. + * + * To contact Novell about this file by physical or electronic mail, you may + * find current contact information at www.novell.com. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "snapper/AppUtil.h" +#include "snapper/Files.h" +#include "snapper/SnapperInterface.h" + + +namespace snapper +{ + using namespace std; + + + bool + cmpFilesContentReg(const string& fullname1, struct stat stat1, const string& fullname2, + struct stat stat2) + { + if (stat1.st_mtime == stat2.st_mtime) + return true; + + if (stat1.st_size != stat2.st_size) + return false; + + if (stat1.st_size == 0 && stat2.st_size == 0) + return true; + + if ((stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino)) + return true; + + int fd1 = open(fullname1.c_str(), O_RDONLY | O_NOATIME); + assert(fd1 >= 0); + posix_fadvise(fd1, 0, stat1.st_size, POSIX_FADV_SEQUENTIAL); + + int fd2 = open(fullname2.c_str(), O_RDONLY | O_NOATIME); + assert(fd2 >= 0); + posix_fadvise(fd2, 0, stat2.st_size, POSIX_FADV_SEQUENTIAL); + + static_assert(sizeof(off_t) >= 8, "off_t is too small"); + + const off_t block_size = 4096; + + char block1[block_size]; + char block2[block_size]; + + bool equal = true; + + off_t length = stat1.st_size; + while (length > 0) + { + off_t t = min(block_size, length); + + int r1 = read(fd1, block1, t); + assert(r1 == t); + + int r2 = read(fd2, block2, t); + assert(r2 == t); + + if (memcmp(block1, block2, t) != 0) + { + equal = false; + break; + } + + length -= t; + } + + close(fd1); + close(fd2); + + return equal; + } + + + bool + cmpFilesContentLnk(const string& fullname1, struct stat stat1, const string& fullname2, + struct stat stat2) + { + if (stat1.st_mtime == stat2.st_mtime) + return true; + + string tmp1; + bool r1 = readlink(fullname1, tmp1); + assert(r1); + + string tmp2; + bool r2 = readlink(fullname2, tmp2); + assert(r2); + + return tmp1 == tmp2; + } + + + bool + cmpFilesContent(const string& fullname1, struct stat stat1, const string& fullname2, + struct stat stat2) + { + assert((stat1.st_mode & S_IFMT) == (stat2.st_mode & S_IFMT)); + + switch (stat1.st_mode & S_IFMT) + { + case S_IFREG: + return cmpFilesContentReg(fullname1, stat1, fullname2, stat2); + + case S_IFLNK: + return cmpFilesContentLnk(fullname1, stat1, fullname2, stat2); + + default: + return true; + } + } + + + unsigned int + cmpFiles(const string& fullname1, struct stat stat1, const string& fullname2, struct stat stat2) + { + unsigned int status = 0; + + if ((stat1.st_mode & S_IFMT) != (stat2.st_mode & S_IFMT)) + { + status |= TYPE; + } + else + { + if (!cmpFilesContent(fullname1, stat1, fullname2, stat2)) + status |= CONTENT; + } + + if ((stat1.st_mode ^ stat2.st_mode) & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | + S_ISGID | S_ISVTX)) + { + status |= PERMISSIONS; + } + + if (stat1.st_uid != stat2.st_uid) + { + status |= USER; + } + + if (stat1.st_gid != stat2.st_gid) + { + status |= GROUP; + } + + return status; + } + + + unsigned int + cmpFiles(const string& fullname1, const string& fullname2) + { + struct stat stat1; + int r1 = stat(fullname1.c_str(), &stat1); + + struct stat stat2; + int r2 = stat(fullname2.c_str(), &stat2); + + if (r1 != 0 && r2 == 0) + return CREATED; + + if (r1 == 0 && r2 != 0) + return DELETED; + + return cmpFiles(fullname1, stat1, fullname2, stat2); + } + + + bool + filter(const string& name) + { + if (name == "/snapshots") + return true; + + return false; + } + + + vector + readDirectory(const string& base_path, const string& path) + { + vector ret; + + int fd = open((base_path + path).c_str(), O_RDONLY | O_NOATIME); + assert(fd >= 0); + + DIR* dp = fdopendir(fd); + assert(dp != NULL); + + struct dirent* ep; + while ((ep = readdir(dp))) + { + if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0) + continue; + + string fullname = path + "/" + ep->d_name; + if (filter(fullname)) + continue; + + ret.push_back(ep->d_name); + } + + closedir(dp); + + sort(ret.begin(), ret.end()); + + return ret; + } + + + void + listSubdirs(const string& base_path, const string& path, unsigned int status, + void(*cb)(const string& name, unsigned int status)) + { + vector tmp = readDirectory(base_path, path); + + for (vector::const_iterator it1 = tmp.begin(); it1 != tmp.end(); ++it1) + { + (*cb)(path + "/" + *it1, status); + + struct stat stat; + int r = lstat((base_path + path + "/" + *it1).c_str(), &stat); + assert(r == 0); + + if (S_ISDIR(stat.st_mode)) + listSubdirs(base_path, path + "/" + *it1, status, cb); + } + } + + + void + cmpDirsWorker(const string& base_path1, const string& base_path2, const string& path, + void(*cb)(const string& name, unsigned int status)); + + + void + lonesome(const string& base_path, const string& path, const string& name, unsigned int status, + void(*cb)(const string& name, unsigned int status)) + { + (*cb)(path + "/" + name, status); + + struct stat stat; + int r = lstat((base_path + path + "/" + name).c_str(), &stat); + assert(r == 0); + + if (S_ISDIR(stat.st_mode)) + listSubdirs(base_path, path + "/" + name, status, cb); + } + + + void + twosome(const string& base_path1, const string& base_path2, const string& path, + const string& name, void(*cb)(const string& name, unsigned int status)) + { + string fullname1 = base_path1 + path + "/" + name; + struct stat stat1; + int r1 = lstat(fullname1.c_str(), &stat1); + assert(r1 == 0); + + string fullname2 = base_path2 + path + "/" + name; + struct stat stat2; + int r2 = lstat(fullname2.c_str(), &stat2); + assert(r2 == 0); + + unsigned int status = cmpFiles(fullname1, stat1, fullname2, stat2); + + if (status != 0) + { + (*cb)(path + "/" + name, status); + } + + if (!(status & TYPE)) + { + if (S_ISDIR(stat1.st_mode)) + cmpDirsWorker(base_path1, base_path2, path + "/" + name, cb); + } + else + { + if (S_ISDIR(stat1.st_mode)) + listSubdirs(base_path1, path + "/" + name, DELETED, cb); + + if (S_ISDIR(stat2.st_mode)) + listSubdirs(base_path2, path + "/" + name, CREATED, cb); + } + } + + + void + cmpDirsWorker(const string& base_path1, const string& base_path2, const string& path, + void(*cb)(const string& name, unsigned int status)) + { + const vector pre = readDirectory(base_path1, path); + vector::const_iterator first1 = pre.begin(); + vector::const_iterator last1 = pre.end(); + + const vector post = readDirectory(base_path2, path); + vector::const_iterator first2 = post.begin(); + vector::const_iterator last2 = post.end(); + + while (first1 != last1 || first2 != last2) + { + if (first1 == last1) + { + assert(*first1 != *first2); + lonesome(base_path2, path, *first2, CREATED, cb); + ++first2; + } + else if (first2 == last2) + { + assert(*first1 != *first2); + lonesome(base_path1, path, *first1, DELETED, cb); + ++first1; + } + else if (*first2 < *first1) + { + assert(*first1 != *first2); + lonesome(base_path2, path, *first2, CREATED, cb); + ++first2; + } + else if (*first1 < *first2) + { + assert(*first1 != *first2); + lonesome(base_path1, path, *first1, DELETED, cb); + ++first1; + } + else + { + assert(*first1 == *first2); + twosome(base_path1, base_path2, path, *first1, cb); + ++first1; + ++first2; + } + } + } + + + void + cmpDirs(const string& path1, const string& path2, void(*cb)(const string& name, + unsigned int status)) + { + cmpDirsWorker(path1, path2, "", cb); + } + + + string + statusToString(unsigned int status) + { + string ret; + + if (status & CREATED) + ret += "+"; + else if (status & DELETED) + ret += "-"; + else if (status & TYPE) + ret += "t"; + else if (status & CONTENT) + ret += "c"; + else + ret += "."; + + ret += status & PERMISSIONS ? "p" : "."; + ret += status & USER ? "u" : "."; + ret += status & GROUP ? "g" : "."; + + return ret; + } + +} diff --git a/snapper/Files.h b/snapper/Files.h new file mode 100644 index 00000000..1672ad22 --- /dev/null +++ b/snapper/Files.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2011 Novell, 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, contact Novell, Inc. + * + * To contact Novell about this file by physical or electronic mail, you may + * find current contact information at www.novell.com. + */ + + +#ifndef FILES_H +#define FILES_H + + +#include + + +namespace snapper +{ + using std::string; + + + unsigned int + cmpFiles(const string& name1, const string& name2); + + void + cmpDirs(const string& path1, const string& path2, void(*cb)(const string& name, + unsigned int status)); + + string + statusToString(unsigned int status); + +} + + +#endif diff --git a/snapper/Makefile.am b/snapper/Makefile.am index d1cbbbe2..d6b85f0c 100644 --- a/snapper/Makefile.am +++ b/snapper/Makefile.am @@ -1,15 +1,18 @@ # -# Makefile.am for libsnapper/snapper +# Makefile.am for snapper/snapper # lib_LTLIBRARIES = libsnapper.la +AM_CXXFLAGS = -D_FILE_OFFSET_BITS=64 + libsnapper_la_SOURCES = \ SnapperInterface.h \ Snapper.cc Snapper.h \ XmlFile.cc XmlFile.h \ Enum.cc Enum.h \ AppUtil.cc AppUtil.h \ + Files.cc Files.h \ SnapperTmpl.h libsnapper_la_LDFLAGS = -version-info 2:0 diff --git a/snapper/SnapperInterface.h b/snapper/SnapperInterface.h index 94b9ec5c..7a4f68b3 100644 --- a/snapper/SnapperInterface.h +++ b/snapper/SnapperInterface.h @@ -75,7 +75,8 @@ namespace snapper enum StatusFlags { - CREATED = 1, DELETED = 2, TYPE = 4, CONTENT = 8, PERMISSIONS = 16, OWNER = 32 + CREATED = 1, DELETED = 2, TYPE = 4, CONTENT = 8, PERMISSIONS = 16, USER = 32, + GROUP = 64 }; enum Cmp