bool
filter(const string& name)
{
- if (name == "/snapshots")
+ if (name == "/.snapshots" || name == "/.snapshots-info")
return true;
return false;
if (getSnapper()->getCompareCallback())
getSnapper()->getCompareCallback()->start();
+ comparison->getSnapshot1()->mountFilesystemSnapshot();
+ comparison->getSnapshot2()->mountFilesystemSnapshot();
+
#if 1
cmpdirs_cb_t cb = AppendHelper(comparison, entries);
#else
if (invert)
swap(num1, num2);
- string input = getSnapper()->snapshotsDir() + "/" + decString(num2) + "/filelist-" +
+ string input = getSnapper()->infosDir() + "/" + decString(num2) + "/filelist-" +
decString(num1) + ".txt";
try
if (invert)
swap(num1, num2);
- string output = getSnapper()->snapshotsDir() + "/" + decString(num2) + "/filelist-" +
+ string output = getSnapper()->infosDir() + "/" + decString(num2) + "/filelist-" +
decString(num1) + ".txt";
string tmp_name = output + ".tmp-XXXXXX";
--- /dev/null
+/*
+ * 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 <sys/stat.h>
+#include <sys/types.h>
+
+#include "snapper/Filesystem.h"
+#include "snapper/Snapper.h"
+#include "snapper/SnapperTmpl.h"
+#include "snapper/SystemCmd.h"
+#include "snapper/SnapperDefines.h"
+
+
+namespace snapper
+{
+
+ string
+ Btrfs::infosDir() const
+ {
+ return snapper->subvolumeDir() + "/.snapshots";
+ }
+
+
+ string
+ Ext4::infosDir() const
+ {
+ return snapper->subvolumeDir() + "/.snapshots-info";
+ }
+
+
+ string
+ Btrfs::snapshotDir(unsigned int num) const
+ {
+ return snapper->subvolumeDir() + "/.snapshots/" + decString(num) + "/snapshot";
+ }
+
+
+ string
+ Ext4::snapshotDir(unsigned int num) const
+ {
+ return snapper->subvolumeDir() + "@" + decString(num);
+ }
+
+
+ void
+ Btrfs::createFilesystemSnapshot(unsigned int num) const
+ {
+ SystemCmd cmd(BTRFSBIN " subvolume snapshot " + snapper->subvolumeDir() + " " + snapshotDir(num));
+ if (cmd.retcode() != 0)
+ throw CreateSnapshotFailedException();
+ }
+
+
+ void
+ Ext4::createFilesystemSnapshot(unsigned int num) const
+ {
+ SystemCmd cmd1(TOUCHBIN " " "/mnt/.snapshots/" + decString(num));
+ if (cmd1.retcode() != 0)
+ throw CreateSnapshotFailedException();
+
+ SystemCmd cmd2(CHSNAPBIN " +S " "/mnt/.snapshots/" + decString(num));
+ if (cmd2.retcode() != 0)
+ throw CreateSnapshotFailedException();
+ }
+
+
+ void
+ Btrfs::deleteFilesystemSnapshot(unsigned int num) const
+ {
+ SystemCmd cmd(BTRFSBIN " subvolume delete " + snapshotDir(num));
+ if (cmd.retcode() != 0)
+ throw DeleteSnapshotFailedException();
+ }
+
+
+ void
+ Ext4::deleteFilesystemSnapshot(unsigned int num) const
+ {
+ // TODO
+ }
+
+
+ void
+ Btrfs::mountFilesystemSnapshot(unsigned int num) const
+ {
+ }
+
+
+ void
+ Ext4::mountFilesystemSnapshot(unsigned int num) const
+ {
+ SystemCmd cmd1(CHSNAPBIN " +n " "/mnt/.snapshots/" + decString(num));
+ if (cmd1.retcode() != 0)
+ throw CreateSnapshotFailedException();
+
+ mkdir(("/mnt@" + decString(num)).c_str(), 0666);
+
+ SystemCmd cmd2(MOUNTBIN " -t ext4 -r -o loop,noload " "/mnt/.snapshots/" + decString(num) + " "
+ "/mnt@" + decString(num));
+ if (cmd2.retcode() != 0)
+ throw CreateSnapshotFailedException();
+ }
+
+
+ void
+ Btrfs::umountFilesystemSnapshot(unsigned int num) const
+ {
+ }
+
+
+ void
+ Ext4::umountFilesystemSnapshot(unsigned int num) const
+ {
+ // TODO
+ }
+
+
+ bool
+ Btrfs::checkFilesystemSnapshot(unsigned int num) const
+ {
+ return checkDir(snapshotDir(num));
+ }
+
+
+ bool
+ Ext4::checkFilesystemSnapshot(unsigned int num) const
+ {
+ // TODO
+
+ return true;
+ }
+
+}
--- /dev/null
+/*
+ * 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 FILESYSTEM_H
+#define FILESYSTEM_H
+
+
+#include <string>
+
+
+namespace snapper
+{
+ using std::string;
+
+
+ class Snapper;
+
+
+ class Filesystem
+ {
+ public:
+
+ Filesystem(Snapper* snapper) : snapper(snapper) {}
+ virtual ~Filesystem() {}
+
+ virtual string name() const = 0;
+
+ virtual string infosDir() const = 0;
+ virtual string snapshotDir(unsigned int num) const = 0;
+
+ virtual void createFilesystemSnapshot(unsigned int num) const = 0;
+ virtual void deleteFilesystemSnapshot(unsigned int num) const = 0;
+
+ virtual void mountFilesystemSnapshot(unsigned int num) const = 0;
+ virtual void umountFilesystemSnapshot(unsigned int num) const = 0;
+
+ virtual bool checkFilesystemSnapshot(unsigned int num) const = 0;
+
+ protected:
+
+ Snapper* snapper;
+
+ };
+
+
+ class Btrfs : public Filesystem
+ {
+ public:
+
+ Btrfs(Snapper* snapper) : Filesystem(snapper) {}
+
+ virtual string name() const { return "btrfs"; }
+
+ virtual string infosDir() const;
+ virtual string snapshotDir(unsigned int num) const;
+
+ virtual void createFilesystemSnapshot(unsigned int num) const;
+ virtual void deleteFilesystemSnapshot(unsigned int num) const;
+
+ virtual void mountFilesystemSnapshot(unsigned int num) const;
+ virtual void umountFilesystemSnapshot(unsigned int num) const;
+
+ virtual bool checkFilesystemSnapshot(unsigned int num) const;
+
+ };
+
+
+ class Ext4 : public Filesystem
+ {
+ public:
+
+ Ext4(Snapper* snapper) : Filesystem(snapper) {}
+
+ virtual string name() const { return "ext4"; }
+
+ virtual string infosDir() const;
+ virtual string snapshotDir(unsigned int num) const;
+
+ virtual void createFilesystemSnapshot(unsigned int num) const;
+ virtual void deleteFilesystemSnapshot(unsigned int num) const;
+
+ virtual void mountFilesystemSnapshot(unsigned int num) const;
+ virtual void umountFilesystemSnapshot(unsigned int num) const;
+
+ virtual bool checkFilesystemSnapshot(unsigned int num) const;
+
+ };
+
+}
+
+
+#endif
Snapper.cc Snapper.h \
Snapshot.cc Snapshot.h \
Comparison.cc Comparison.h \
+ Filesystem.cc Filesystem.h \
File.cc File.h \
XmlFile.cc XmlFile.h \
Enum.cc Enum.h \
#include "snapper/Snapper.h"
#include "snapper/Comparison.h"
#include "snapper/AppUtil.h"
-#include "snapper/XmlFile.h"
#include "snapper/Enum.h"
+#include "snapper/Filesystem.h"
#include "snapper/SnapperTmpl.h"
#include "snapper/SystemCmd.h"
#include "snapper/SnapperDefines.h"
Snapper::Snapper(const string& config_name, bool disable_filters)
- : config_name(config_name), config(NULL), subvolume("/"), snapshots(this),
- compare_callback(NULL), rollback_callback(NULL)
+ : config_name(config_name), config(NULL), subvolume("/"), filesystem(NULL),
+ snapshots(this), compare_callback(NULL), rollback_callback(NULL)
{
y2mil("Snapper constructor");
y2mil("libsnapper version " VERSION);
y2mil("subvolume:" << subvolume);
+ filesystem = new Btrfs(this);
+
+ y2mil("filesystem:" << filesystem->name());
+
if (!disable_filters)
loadIgnorePatterns();
{
y2mil("Snapper destructor");
+ delete filesystem;
delete config;
}
}
- // Directory containing directories for all snapshots, e.g. "/snapshots"
- // or "/home/snapshots".
+ // Directory that contains the per snapshot directory with info files.
+ // For btrfs e.g. "/.snapshots" or "/home/.snapshots".
+ // For ext4 e.g. "/.snapshots-info" or "/home/.snapshots-info".
string
- Snapper::snapshotsDir() const
+ Snapper::infosDir() const
{
- if (subvolumeDir() == "/")
- return SNAPSHOTSDIR;
- else
- return subvolumeDir() + SNAPSHOTSDIR;
+ return filesystem->infosDir();
}
y2mil("num1:" << snapshot1->getNum() << " num2:" << snapshot2->getNum());
+ snapshot1->mountFilesystemSnapshot();
+ snapshot2->mountFilesystemSnapshot();
+
bool invert = snapshot1->getNum() > snapshot2->getNum();
if (invert)
string dir1 = snapshot1->snapshotDir();
string dir2 = snapshot2->snapshotDir();
- string output = snapshot2->baseDir() + "/filelist-" + decString(snapshot1->getNum()) +
+ string output = snapshot2->infoDir() + "/filelist-" + decString(snapshot1->getNum()) +
".txt";
SystemCmd(NICEBIN " -n 19 " IONICEBIN " -c 3 " COMPAREDIRSBIN " " + quote(dir1) + " " +
throw AddConfigFailedException("modifying config failed");
}
- SystemCmd cmd2(BTRFSBIN " subvolume create " + subvolume + SNAPSHOTSDIR);
+ SystemCmd cmd2(BTRFSBIN " subvolume create " + subvolume + "/.snapshots");
if (cmd2.retcode() != 0)
{
throw AddConfigFailedException("creating snapshot failed");
class SysconfigFile;
+ class Filesystem;
struct CompareCallback
~Snapper();
string subvolumeDir() const;
- string snapshotsDir() const;
+ string infosDir() const;
Snapshots& getSnapshots() { return snapshots; }
const Snapshots& getSnapshots() const { return snapshots; }
static void addConfig(const string& config_name, const string& subvolume,
const string& template_name);
+ const Filesystem* getFilesystem() const { return filesystem; }
+
private:
void filter1(list<Snapshots::iterator>& tmp, time_t min_age);
string subvolume;
+ Filesystem* filesystem;
+
vector<string> ignore_patterns;
Snapshots snapshots;
#define FILTERSDIR "/etc/snapper/filters"
-#define SNAPSHOTDIR "/snapshot"
-#define SNAPSHOTSDIR "/.snapshots"
-
#define BTRFSBIN "/sbin/btrfs"
+#define CHSNAPBIN "/sbin/chsnap"
+
#define COMPAREDIRSBIN "/usr/lib/snapper/bin/compare-dirs"
#define NICEBIN "/usr/bin/nice"
#define IONICEBIN "/usr/bin/ionice"
#define CPBIN "/bin/cp"
-
+#define TOUCHBIN "/usr/bin/touch"
#define DIFFBIN "/usr/bin/diff"
+#define MOUNTBIN "/bin/mount"
+
#endif
#include "snapper/Snapper.h"
#include "snapper/AppUtil.h"
#include "snapper/XmlFile.h"
+#include "snapper/Filesystem.h"
#include "snapper/Enum.h"
#include "snapper/SnapperTmpl.h"
#include "snapper/SystemCmd.h"
}
- // Directory where the info file is saved, e.g. "/snapshots/1" or
- // "/home/snapshots/1". Obviously not available for current.
+ // Directory where the info file is saved. Obviously not available for
+ // current.
+ // For btrfs e.g. "/.snapshots/1" or "/home/.snapshots/1".
+ // For ext4 e.g. "/.snapshots-info/1" or "/home/.snapshots-info/1".
string
- Snapshot::baseDir() const
+ Snapshot::infoDir() const
{
if (isCurrent())
throw IllegalSnapshotException();
- return snapper->snapshotsDir() + "/" + decString(num);
+ return snapper->infosDir() + "/" + decString(num);
}
- // Directory containing the actual snapshot, e.g. "/" or "/home" for
- // current and "/snapshots/1/snapshot" or "/home/snapshots/1/snapshot"
+ // Directory containing the actual content of the snapshot.
+ // For btrfs , e.g. "/" or "/home" for current and
+ // "/.snapshots/1/snapshot" or "/home/.snapshots/1/snapshot" otherwise.
+ // For ext4 , e.g. "/" or "/home" for current and "/@1" or "/home@1"
// otherwise.
string
Snapshot::snapshotDir() const
{
- if (num == 0)
+ if (isCurrent())
return snapper->subvolumeDir();
- else
- return baseDir() + SNAPSHOTDIR;
+
+ return snapper->getFilesystem()->snapshotDir(num);
}
void
Snapshots::read()
{
- list<string> infos = glob(snapper->snapshotsDir() + "/*/info.xml", GLOB_NOSORT);
+ list<string> infos = glob(snapper->infosDir() + "/*/info.xml", GLOB_NOSORT);
for (list<string>::const_iterator it = infos.begin(); it != infos.end(); ++it)
{
unsigned int num;
- it->substr(snapper->snapshotsDir().length() + 1) >> num;
+ it->substr(snapper->infosDir().length() + 1) >> num;
XmlFile file(*it);
const xmlNode* root = file.getRootElement();
getChildValue(node, "cleanup", snapshot.cleanup);
- if (!checkDir(snapshot.snapshotDir()))
+ if (!snapper->getFilesystem()->checkFilesystemSnapshot(num))
{
- y2err("snapshot directory does not exist. not adding snapshot " << num);
+ y2err("snapshot check failed. not adding snapshot " << num);
continue;
}
num = entries.rbegin()->num + 1;
int r;
- while ((r = mkdir((snapper->snapshotsDir() + "/" + decString(num)).c_str(), 0777)) == -1 &&
+ while ((r = mkdir((snapper->infosDir() + "/" + decString(num)).c_str(), 0777)) == -1 &&
errno == EEXIST)
++num;
if (!cleanup.empty())
setChildValue(node, "cleanup", cleanup);
- xml.save(baseDir() + "/info.xml");
+ xml.save(infoDir() + "/info.xml");
return true;
}
+ void
+ Snapshot::mountFilesystemSnapshot() const
+ {
+ snapper->getFilesystem()->mountFilesystemSnapshot(num);
+ }
+
+
+ void
+ Snapshot::umountFilesystemSnapshot() const
+ {
+ snapper->getFilesystem()->umountFilesystemSnapshot(num);
+ }
+
+
void
Snapshot::createFilesystemSnapshot() const
{
- SystemCmd cmd(BTRFSBIN " subvolume snapshot " + snapper->subvolumeDir() + " " + snapshotDir());
- if (cmd.retcode() != 0)
- throw CreateSnapshotFailedException();
+ snapper->getFilesystem()->createFilesystemSnapshot(num);
}
void
Snapshot::deleteFilesystemSnapshot() const
{
- SystemCmd cmd(BTRFSBIN " subvolume delete " + snapshotDir());
- if (cmd.retcode() != 0)
- throw DeleteSnapshotFailedException();
+ snapper->getFilesystem()->deleteFilesystemSnapshot(num);
}
snapshot->deleteFilesystemSnapshot();
- unlink((snapshot->baseDir() + "/info.xml").c_str());
+ unlink((snapshot->infoDir() + "/info.xml").c_str());
- list<string> tmp1 = glob(snapshot->baseDir() + "/filelist-*.txt", GLOB_NOSORT);
+ list<string> tmp1 = glob(snapshot->infoDir() + "/filelist-*.txt", GLOB_NOSORT);
for (list<string>::const_iterator it = tmp1.begin(); it != tmp1.end(); ++it)
unlink(it->c_str());
- list<string> tmp2 = glob(snapper->snapshotsDir() + "/*/filelist-" +
+ list<string> tmp2 = glob(snapper->infosDir() + "/*/filelist-" +
decString(snapshot->getNum()) + ".txt", GLOB_NOSORT);
for (list<string>::const_iterator it = tmp2.begin(); it != tmp2.end(); ++it)
unlink(it->c_str());
- rmdir(snapshot->baseDir().c_str());
+ rmdir(snapshot->infoDir().c_str());
entries.erase(snapshot);
}
void setCleanup(const string& cleanup);
string getCleanup() const { return cleanup; }
- string baseDir() const;
+ string infoDir() const;
string snapshotDir() const;
+ void mountFilesystemSnapshot() const;
+ void umountFilesystemSnapshot() const;
+
friend std::ostream& operator<<(std::ostream& s, const Snapshot& snapshot);
private:
string cleanup;
bool writeInfo() const;
+
void createFilesystemSnapshot() const;
void deleteFilesystemSnapshot() const;
void
setup()
{
- system("/usr/bin/find " SUBVOLUME " -mindepth 1 -maxdepth 1 -not -path " SUBVOLUME SNAPSHOTSDIR " "
+ system("/usr/bin/find " SUBVOLUME " -mindepth 1 -maxdepth 1 -not -path " SUBVOLUME "/.snapshots "
"-exec rm -r {} \\;");
initDefaultLogger();