/*
- * Copyright (c) 2011 Novell, Inc.
+ * Copyright (c) [2011-2012] Novell, Inc.
*
* All Rights Reserved.
*
#include <sys/types.h>
#include <errno.h>
#include <unistd.h>
-#include <mntent.h>
+#include <boost/algorithm/string.hpp>
#include "snapper/Log.h"
#include "snapper/Filesystem.h"
#include "snapper/SnapperTmpl.h"
#include "snapper/SystemCmd.h"
#include "snapper/SnapperDefines.h"
+#include "snapper/Regex.h"
namespace snapper
{
+ bool
+ mount(const string& device, const string& mount_point, const string& mount_type,
+ const vector<string>& options)
+ {
+ string cmd_line = MOUNTBIN " -t " + mount_type + " --read-only";
+
+ if (!options.empty())
+ cmd_line += " -o " + boost::join(options, ",");
+
+ cmd_line += " " + quote(device) + " " + quote(mount_point);
+
+ SystemCmd cmd(cmd_line);
+ return cmd.retcode() == 0;
+ }
+
+
+ bool
+ umount(const string& mount_point)
+ {
+ SystemCmd cmd(UMOUNTBIN " " + quote(mount_point));
+ return cmd.retcode() == 0;
+ }
+
+
Filesystem*
Filesystem::create(const string& fstype, const string& subvolume)
{
- if (fstype == "btrfs")
- return new Btrfs(subvolume);
+ typedef Filesystem* (*func_t)(const string& fstype, const string& subvolume);
- if (fstype == "ext4")
- return new Ext4(subvolume);
+ static const func_t funcs[] = { &Btrfs::create, &Ext4::create, &Lvm::create, NULL };
+ for (const func_t* func = funcs; func != NULL; ++func)
+ {
+ Filesystem* fs = (*func)(fstype, subvolume);
+ if (fs)
+ return fs;
+ }
+
+ y2err("do not know about fstype '" << fstype << "'");
throw InvalidConfigException();
}
+ Filesystem*
+ Btrfs::create(const string& fstype, const string& subvolume)
+ {
+ if (fstype == "btrfs")
+ return new Btrfs(subvolume);
+
+ return NULL;
+ }
+
+
Btrfs::Btrfs(const string& subvolume)
: Filesystem(subvolume)
{
string
Btrfs::snapshotDir(unsigned int num) const
{
- return (subvolume == "/" ? "" : subvolume) + "/.snapshots/" +
- decString(num) + "/snapshot";
+ return (subvolume == "/" ? "" : subvolume) + "/.snapshots/" + decString(num) +
+ "/snapshot";
}
}
+ Filesystem*
+ Ext4::create(const string& fstype, const string& subvolume)
+ {
+ if (fstype == "ext4")
+ return new Ext4(subvolume);
+
+ return NULL;
+ }
+
+
Ext4::Ext4(const string& subvolume)
: Filesystem(subvolume)
{
void
Ext4::createConfig() const
{
- int r1 = mkdir((subvolume + "/.snapshots").c_str(), 700);
+ int r1 = mkdir((subvolume + "/.snapshots").c_str(), 0700);
if (r1 == 0)
{
SystemCmd cmd1(CHATTRBIN " +x " + quote(subvolume + "/.snapshots"));
throw CreateConfigFailedException("mkdir failed");
}
- int r2 = mkdir((subvolume + "/.snapshots/.info").c_str(), 700);
+ int r2 = mkdir((subvolume + "/.snapshots/.info").c_str(), 0700);
if (r2 == 0)
{
SystemCmd cmd2(CHATTRBIN " -x " + quote(subvolume + "/.snapshots/.info"));
bool
Ext4::isSnapshotMounted(unsigned int num) const
{
- FILE* f = setmntent("/etc/mtab", "r");
- if (!f)
- {
- y2err("setmntent failed");
- throw IsSnapshotMountedFailedException();
- }
-
bool mounted = false;
+ MtabData mtab_data;
- struct mntent* m;
- while ((m = getmntent(f)))
- {
- if (strcmp(m->mnt_type, "rootfs") == 0)
- continue;
-
- if (m->mnt_dir == snapshotDir(num))
- {
- mounted = true;
- break;
- }
- }
-
- endmntent(f);
+ if (!getMtabData(snapshotDir(num), mounted, mtab_data))
+ throw IsSnapshotMountedFailedException();
return mounted;
}
throw MountSnapshotFailedException();
}
- SystemCmd cmd2(MOUNTBIN " -t ext4 -r -o loop,noload " + quote(snapshotFile(num)) +
- " " + quote(snapshotDir(num)));
- if (cmd2.retcode() != 0)
+ vector<string> options;
+ options.push_back("noatime");
+ options.push_back("loop");
+ options.push_back("noload");
+
+ if (!mount(snapshotFile(num), snapshotDir(num), "ext4", options))
throw MountSnapshotFailedException();
}
if (!isSnapshotMounted(num))
return;
- SystemCmd cmd1(UMOUNTBIN " " + quote(snapshotDir(num)));
- if (cmd1.retcode() != 0)
+ if (!umount(snapshotDir(num)))
throw UmountSnapshotFailedException();
- SystemCmd cmd2(CHSNAPBIN " -n " + quote(snapshotFile(num)));
- if (cmd2.retcode() != 0)
+ SystemCmd cmd1(CHSNAPBIN " -n " + quote(snapshotFile(num)));
+ if (cmd1.retcode() != 0)
throw UmountSnapshotFailedException();
rmdir(snapshotDir(num).c_str());
return checkNormalFile(snapshotFile(num));
}
+
+ Filesystem*
+ Lvm::create(const string& fstype, const string& subvolume)
+ {
+ if (fstype == "lvm")
+ return new Lvm(subvolume, "auto");
+
+ Regex rx("^lvm\\(([_a-z0-9]+)\\)$");
+ if (rx.match(fstype))
+ return new Lvm(subvolume, rx.cap(1));
+
+ return NULL;
+ }
+
+
+ Lvm::Lvm(const string& subvolume, const string& mount_type)
+ : Filesystem(subvolume), mount_type(mount_type)
+ {
+ if (access(LVCREATE, X_OK) != 0)
+ {
+ throw ProgramNotInstalledException(LVCREATE " not installed");
+ }
+
+ if (!detectLvmNames())
+ {
+ throw InvalidConfigException();
+ }
+ }
+
+
+ void
+ Lvm::createConfig() const
+ {
+ int r1 = mkdir((subvolume + "/.snapshots").c_str(), 0700);
+ if (r1 != 0 && errno != EEXIST)
+ {
+ y2err("mkdir failed errno:" << errno << " (" << strerror(errno) << ")");
+ throw CreateConfigFailedException("mkdir failed");
+ }
+ }
+
+
+ void
+ Lvm::deleteConfig() const
+ {
+ int r1 = rmdir((subvolume + "/.snapshots").c_str());
+ if (r1 != 0)
+ {
+ y2err("rmdir failed errno:" << errno << " (" << strerror(errno) << ")");
+ throw DeleteConfigFailedException("rmdir failed");
+ }
+ }
+
+
+ string
+ Lvm::infosDir() const
+ {
+ return (subvolume == "/" ? "" : subvolume) + "/.snapshots";
+ }
+
+
+ string
+ Lvm::snapshotDir(unsigned int num) const
+ {
+ return (subvolume == "/" ? "" : subvolume) + "/.snapshots/" + decString(num) +
+ "/snapshot";
+ }
+
+
+ string
+ Lvm::snapshotLvName(unsigned int num) const
+ {
+ return lv_name + "-snapshot" + decString(num);
+ }
+
+
+ void
+ Lvm::createSnapshot(unsigned int num) const
+ {
+ sync(); // TODO looks like a bug that this is needed (with ext4)
+
+ SystemCmd cmd(LVCREATE " --snapshot --name " + quote(snapshotLvName(num)) + " " +
+ quote(vg_name + "/" + lv_name));
+ if (cmd.retcode() != 0)
+ throw CreateSnapshotFailedException();
+
+ int r1 = mkdir(snapshotDir(num).c_str(), 0700);
+ if (r1 != 0 && errno != EEXIST)
+ {
+ y2err("mkdir failed errno:" << errno << " (" << strerror(errno) << ")");
+ throw CreateSnapshotFailedException();
+ }
+ }
+
+
+ void
+ Lvm::deleteSnapshot(unsigned int num) const
+ {
+ SystemCmd cmd(LVREMOVE " --force " + quote(vg_name + "/" + snapshotLvName(num)));
+ if (cmd.retcode() != 0)
+ throw DeleteSnapshotFailedException();
+
+ rmdir(snapshotDir(num).c_str());
+ }
+
+
+ bool
+ Lvm::isSnapshotMounted(unsigned int num) const
+ {
+ bool mounted = false;
+ MtabData mtab_data;
+
+ if (!getMtabData(snapshotDir(num), mounted, mtab_data))
+ throw IsSnapshotMountedFailedException();
+
+ return mounted;
+ }
+
+
+ void
+ Lvm::mountSnapshot(unsigned int num) const
+ {
+ if (isSnapshotMounted(num))
+ return;
+
+ vector<string> options;
+ options.push_back("noatime");
+ if (mount_type == "xfs")
+ options.push_back("nouuid");
+
+ if (!mount(getDevice(num), snapshotDir(num), mount_type, options))
+ throw MountSnapshotFailedException();
+ }
+
+
+ void
+ Lvm::umountSnapshot(unsigned int num) const
+ {
+ if (!isSnapshotMounted(num))
+ return;
+
+ if (!umount(snapshotDir(num)))
+ throw UmountSnapshotFailedException();
+ }
+
+
+ bool
+ Lvm::checkSnapshot(unsigned int num) const
+ {
+ return checkAnything(getDevice(num));
+ }
+
+
+ bool
+ Lvm::detectLvmNames()
+ {
+ bool found = false;
+ MtabData mtab_data;
+
+ if (!getMtabData(subvolume, found, mtab_data))
+ return false;
+
+ if (!found)
+ {
+ y2err("logical volume not mounted");
+ return false;
+ }
+
+ Regex rx("^/dev/mapper/([^-]+)-([^-]+)$");
+ if (rx.match(mtab_data.device))
+ {
+ vg_name = boost::replace_all_copy(rx.cap(1), "--", "-");
+ lv_name = boost::replace_all_copy(rx.cap(2), "--", "-");
+ return true;
+ }
+
+ y2err("could not detect lvm names from '" << mtab_data.device << "'");
+ return false;
+ }
+
+
+ string
+ Lvm::getDevice(unsigned int num) const
+ {
+ return "/dev/mapper/" + boost::replace_all_copy(vg_name, "-", "--") + "-" +
+ boost::replace_all_copy(snapshotLvName(num), "-", "--");
+ }
+
}
/*
- * Copyright (c) 2011 Novell, Inc.
+ * Copyright (c) [2011-2012] Novell, Inc.
*
* All Rights Reserved.
*
static Filesystem* create(const string& fstype, const string& subvolume);
- virtual string name() const = 0;
+ virtual string fstype() const = 0;
virtual void createConfig() const = 0;
virtual void deleteConfig() const = 0;
{
public:
+ static Filesystem* create(const string& fstype, const string& subvolume);
+
Btrfs(const string& subvolume);
- virtual string name() const { return "btrfs"; }
+ virtual string fstype() const { return "btrfs"; }
virtual void createConfig() const;
virtual void deleteConfig() const;
{
public:
+ static Filesystem* create(const string& fstype, const string& subvolume);
+
Ext4(const string& subvolume);
- virtual string name() const { return "ext4"; }
+ virtual string fstype() const { return "ext4"; }
virtual void createConfig() const;
virtual void deleteConfig() const;
};
+
+ class Lvm : public Filesystem
+ {
+ public:
+
+ static Filesystem* create(const string& fstype, const string& subvolume);
+
+ Lvm(const string& subvolume, const string& mount_type);
+
+ virtual string fstype() const { return "lvm(" + mount_type + ")"; }
+
+ virtual void createConfig() const;
+ virtual void deleteConfig() const;
+
+ virtual string infosDir() const;
+ virtual string snapshotDir(unsigned int num) const;
+ virtual string snapshotLvName(unsigned int num) const;
+
+ virtual void createSnapshot(unsigned int num) const;
+ virtual void deleteSnapshot(unsigned int num) const;
+
+ virtual bool isSnapshotMounted(unsigned int num) const;
+ virtual void mountSnapshot(unsigned int num) const;
+ virtual void umountSnapshot(unsigned int num) const;
+
+ virtual bool checkSnapshot(unsigned int num) const;
+
+ private:
+
+ const string mount_type;
+
+ bool detectLvmNames();
+
+ string getDevice(unsigned int num) const;
+
+ string vg_name;
+ string lv_name;
+
+ };
+
}