]> git.ipfire.org Git - thirdparty/snapper.git/commitdiff
- split Filesystem.{h,cc}
authorArvin Schnell <aschnell@suse.de>
Wed, 16 Jan 2013 13:06:28 +0000 (14:06 +0100)
committerArvin Schnell <aschnell@suse.de>
Wed, 16 Jan 2013 13:06:28 +0000 (14:06 +0100)
configure.in
snapper/Btrfs.cc [new file with mode: 0644]
snapper/Btrfs.h [new file with mode: 0644]
snapper/Ext4.cc [new file with mode: 0644]
snapper/Ext4.h [new file with mode: 0644]
snapper/Filesystem.cc
snapper/Filesystem.h
snapper/Lvm.cc [new file with mode: 0644]
snapper/Lvm.h [new file with mode: 0644]
snapper/Makefile.am

index 19fb3e4fe0bc7ca50f399f0d619d61310e81e4fd..c4e7d9c615a2d5b8520e5ea5962f269426848ca2 100644 (file)
@@ -33,9 +33,20 @@ CXXFLAGS="${CXXFLAGS} -std=c++0x -DHAVE_CXX0X -Wall -Wextra -Wformat=2 -Wnon-vir
 
 fillupdir=/var/adm/fillup-templates
 
+AC_ARG_ENABLE([btrfs], AC_HELP_STRING([--disable-btrfs],[Disable Btrfs internal snapshots support]),
+               [with_btrfs=$enableval],[with_btrfs=yes])
+
+AM_CONDITIONAL(ENABLE_BTRFS, [test "x$with_btrfs" = "xyes"])
+
+if test "x$with_btrfs" = "xyes"; then
+       AC_DEFINE(ENABLE_BTRFS, 1, [Enable Btrfs internal snapshots support])
+fi
+
 AC_ARG_ENABLE([ext4], AC_HELP_STRING([--disable-ext4],[Disable ext4 snapshots support]),
                [with_ext4=$enableval],[with_ext4=yes])
 
+AM_CONDITIONAL(ENABLE_EXT4, [test "x$with_ext4" = "xyes"])
+
 if test "x$with_ext4" = "xyes"; then
        AC_DEFINE(ENABLE_EXT4, 1, [Enable Ext4 snapshots support])
 fi
@@ -43,18 +54,13 @@ fi
 AC_ARG_ENABLE([lvm], AC_HELP_STRING([--disable-lvm],[Disable LVM thinprovisioned snapshots support]),
                [with_lvm=$enableval],[with_lvm=yes])
 
-if test "x$with_lvm" = "xyes"; then
-       AC_DEFINE(ENABLE_LVM, 1, [Enable LVM thinprovisioned snapshots support])
-fi
-
-AC_ARG_ENABLE([btrfs], AC_HELP_STRING([--disable-btrfs],[Disable Btrfs internal snapshots support]),
-               [with_btrfs=$enableval],[with_btrfs=yes])
+AM_CONDITIONAL(ENABLE_LVM, [test "x$with_lvm" = "xyes"])
 
-if test "x$with_btrfs" = "xyes"; then
-       AC_DEFINE(ENABLE_BTRFS, 1, [Enable Btrfs internal snapshots support])
+if test "x$with_lvm" = "xyes"; then
+       AC_DEFINE(ENABLE_LVM, 1, [Enable LVM thin-provisioned snapshots support])
 fi
 
-if test "x$with_lvm" != "xyes" -a "x$with_ext4" != "xyes" -a "x$with_btrfs" != "xyes"; then
+if test "x$with_btrfs" != "xyes" -a "x$with_lvm" != "xyes" -a "x$with_ext4" != "xyes"; then
        AC_MSG_ERROR([You have to enable at least one snapshot type (remove some --disable-xxx parameter)])
 fi
 
diff --git a/snapper/Btrfs.cc b/snapper/Btrfs.cc
new file mode 100644 (file)
index 0000000..0da2ad1
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) [2011-2013] 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 <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <asm/types.h>
+#include <boost/algorithm/string.hpp>
+
+#include "snapper/Log.h"
+#include "snapper/Btrfs.h"
+#include "snapper/Snapper.h"
+#include "snapper/SnapperTmpl.h"
+#include "snapper/SnapperDefines.h"
+#include "config.h"
+
+
+#define BTRFS_IOCTL_MAGIC 0x94
+#define BTRFS_PATH_NAME_MAX 4087
+#define BTRFS_SUBVOL_NAME_MAX 4039
+#define BTRFS_SUBVOL_RDONLY (1ULL << 1)
+
+#define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, struct btrfs_ioctl_vol_args)
+#define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, struct btrfs_ioctl_vol_args)
+#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, struct btrfs_ioctl_vol_args)
+#define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, struct btrfs_ioctl_vol_args_v2)
+
+struct btrfs_ioctl_vol_args
+{
+    __s64 fd;
+    char name[BTRFS_PATH_NAME_MAX + 1];
+};
+
+struct btrfs_ioctl_vol_args_v2
+{
+    __s64 fd;
+    __u64 transid;
+    __u64 flags;
+    __u64 unused[4];
+    char name[BTRFS_SUBVOL_NAME_MAX + 1];
+};
+
+
+namespace snapper
+{
+
+    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)
+    {
+    }
+
+
+    void
+    Btrfs::createConfig() const
+    {
+       SDir subvolume_dir = openSubvolumeDir();
+
+       if (!create_subvolume(subvolume_dir.fd(), ".snapshots"))
+       {
+           y2err("create subvolume failed errno:" << errno << " (" << stringerror(errno) << ")");
+           throw CreateConfigFailedException("creating btrfs snapshot failed");
+       }
+    }
+
+
+    void
+    Btrfs::deleteConfig() const
+    {
+       SDir subvolume_dir = openSubvolumeDir();
+
+       if (!delete_subvolume(subvolume_dir.fd(), ".snapshots"))
+       {
+           y2err("delete subvolume failed errno:" << errno << " (" << stringerror(errno) << ")");
+           throw DeleteConfigFailedException("deleting btrfs snapshot failed");
+       }
+    }
+
+
+    string
+    Btrfs::snapshotDir(unsigned int num) const
+    {
+       return (subvolume == "/" ? "" : subvolume) + "/.snapshots/" + decString(num) +
+           "/snapshot";
+    }
+
+
+    SDir
+    Btrfs::openSubvolumeDir() const
+    {
+       SDir subvolume_dir = Filesystem::openSubvolumeDir();
+
+       struct stat stat;
+       if (subvolume_dir.stat(&stat) != 0)
+       {
+           throw IOErrorException();
+       }
+
+       if (!is_subvolume(stat))
+       {
+           y2err("subvolume is not a btrfs snapshot");
+           throw IOErrorException();
+       }
+
+       return subvolume_dir;
+    }
+
+
+    SDir
+    Btrfs::openInfosDir() const
+    {
+       SDir subvolume_dir = openSubvolumeDir();
+       SDir infos_dir(subvolume_dir, ".snapshots");
+
+       struct stat stat;
+       if (infos_dir.stat(&stat) != 0)
+       {
+           throw IOErrorException();
+       }
+
+       if (!is_subvolume(stat))
+       {
+           y2err(".snapshots is not a btrfs snapshot");
+           throw IOErrorException();
+       }
+
+       if (stat.st_uid != 0)
+       {
+           y2err(".snapshots must have owner root");
+           throw IOErrorException();
+       }
+
+       if (stat.st_gid != 0 && stat.st_mode & S_IWGRP)
+       {
+           y2err(".snapshots must have group root or must not be group-writable");
+           throw IOErrorException();
+       }
+
+       if (stat.st_mode & S_IWOTH)
+       {
+           y2err(".snapshots must not be world-writable");
+           throw IOErrorException();
+       }
+
+       return infos_dir;
+    }
+
+
+    SDir
+    Btrfs::openSnapshotDir(unsigned int num) const
+    {
+       SDir info_dir = openInfoDir(num);
+       SDir snapshot_dir(info_dir, "snapshot");
+
+       return snapshot_dir;
+    }
+
+
+    void
+    Btrfs::createSnapshot(unsigned int num) const
+    {
+       SDir subvolume_dir = openSubvolumeDir();
+       SDir info_dir = openInfoDir(num);
+
+       if (!create_snapshot(subvolume_dir.fd(), info_dir.fd(), "snapshot"))
+       {
+           y2err("create snapshot failed errno:" << errno << " (" << stringerror(errno) << ")");
+           throw CreateSnapshotFailedException();
+       }
+    }
+
+
+    void
+    Btrfs::deleteSnapshot(unsigned int num) const
+    {
+       SDir info_dir = openInfoDir(num);
+
+       if (!delete_subvolume(info_dir.fd(), "snapshot"))
+       {
+           y2err("delete snapshot failed errno:" << errno << " (" << stringerror(errno) << ")");
+           throw DeleteSnapshotFailedException();
+       }
+    }
+
+
+    bool
+    Btrfs::isSnapshotMounted(unsigned int num) const
+    {
+       return true;
+    }
+
+
+    void
+    Btrfs::mountSnapshot(unsigned int num) const
+    {
+    }
+
+
+    void
+    Btrfs::umountSnapshot(unsigned int num) const
+    {
+    }
+
+
+    bool
+    Btrfs::checkSnapshot(unsigned int num) const
+    {
+       try
+       {
+           SDir info_dir = openInfoDir(num);
+
+           struct stat stat;
+           int r = info_dir.stat("snapshot", &stat, AT_SYMLINK_NOFOLLOW);
+           return r == 0 && is_subvolume(stat);
+       }
+       catch (const IOErrorException& e)
+       {
+           return false;
+       }
+    }
+
+
+    bool
+    Btrfs::is_subvolume(const struct stat& stat) const
+    {
+       // see btrfsprogs source code
+       return stat.st_ino == 256 && S_ISDIR(stat.st_mode);
+    }
+
+
+    bool
+    Btrfs::create_subvolume(int fddst, const string& name) const
+    {
+       struct btrfs_ioctl_vol_args args;
+       memset(&args, 0, sizeof(args));
+
+       strncpy(args.name, name.c_str(), sizeof(args.name) - 1);
+
+       return ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args) == 0;
+    }
+
+
+    bool
+    Btrfs::create_snapshot(int fd, int fddst, const string& name) const
+    {
+       struct btrfs_ioctl_vol_args_v2 args_v2;
+       memset(&args_v2, 0, sizeof(args_v2));
+
+       args_v2.fd = fd;
+       args_v2.flags |= BTRFS_SUBVOL_RDONLY;
+       strncpy(args_v2.name, name.c_str(), sizeof(args_v2.name) - 1);
+
+       if (ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args_v2) == 0)
+           return true;
+       else if (errno != ENOTTY && errno != EINVAL)
+           return false;
+
+       struct btrfs_ioctl_vol_args args;
+       memset(&args, 0, sizeof(args));
+
+       args.fd = fd;
+       strncpy(args.name, name.c_str(), sizeof(args.name) - 1);
+
+       return ioctl(fddst, BTRFS_IOC_SNAP_CREATE, &args) == 0;
+    }
+
+
+    bool
+    Btrfs::delete_subvolume(int fd, const string& name) const
+    {
+       struct btrfs_ioctl_vol_args args;
+       memset(&args, 0, sizeof(args));
+
+       strncpy(args.name, name.c_str(), sizeof(args.name) - 1);
+
+       return ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args) == 0;
+    }
+
+}
diff --git a/snapper/Btrfs.h b/snapper/Btrfs.h
new file mode 100644 (file)
index 0000000..583e8fa
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) [2011-2013] 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 SNAPPER_BTRFS_H
+#define SNAPPER_BTRFS_H
+
+
+#include "snapper/Filesystem.h"
+#include "config.h"
+
+
+namespace snapper
+{
+
+    class Btrfs : public Filesystem
+    {
+    public:
+
+       static Filesystem* create(const string& fstype, const string& subvolume);
+
+       Btrfs(const string& subvolume);
+
+       virtual string fstype() const { return "btrfs"; }
+
+       virtual void createConfig() const;
+       virtual void deleteConfig() const;
+
+       virtual string snapshotDir(unsigned int num) const;
+
+       virtual SDir openSubvolumeDir() const;
+       virtual SDir openInfosDir() const;
+       virtual SDir openSnapshotDir(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:
+
+       bool is_subvolume(const struct stat& stat) const;
+
+       bool create_subvolume(int fddst, const string& name) const;
+       bool create_snapshot(int fd, int fddst, const string& name) const;
+       bool delete_subvolume(int fd, const string& name) const;
+
+    };
+
+}
+
+
+#endif
diff --git a/snapper/Ext4.cc b/snapper/Ext4.cc
new file mode 100644 (file)
index 0000000..a238b8b
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) [2011-2013] 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 <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <asm/types.h>
+#include <boost/algorithm/string.hpp>
+
+#include "snapper/Log.h"
+#include "snapper/Filesystem.h"
+#include "snapper/Ext4.h"
+#include "snapper/Snapper.h"
+#include "snapper/SnapperTmpl.h"
+#include "snapper/SystemCmd.h"
+#include "snapper/SnapperDefines.h"
+#include "config.h"
+
+
+namespace snapper
+{
+
+    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)
+    {
+       if (access(CHSNAPBIN, X_OK) != 0)
+       {
+           throw ProgramNotInstalledException(CHSNAPBIN " not installed");
+       }
+
+       if (access(CHATTRBIN, X_OK) != 0)
+       {
+           throw ProgramNotInstalledException(CHATTRBIN " not installed");
+       }
+
+       bool found = false;
+       MtabData mtab_data;
+
+       if (!getMtabData(subvolume, found, mtab_data))
+           throw InvalidConfigException();
+
+       if (!found)
+       {
+           y2err("filesystem not mounted");
+           throw InvalidConfigException();
+       }
+
+       mount_options = filter_mount_options(mtab_data.options);
+       mount_options.push_back("loop");
+       mount_options.push_back("noload");
+    }
+
+
+    void
+    Ext4::createConfig() const
+    {
+       int r1 = mkdir((subvolume + "/.snapshots").c_str(), 0700);
+       if (r1 == 0)
+       {
+           SystemCmd cmd1(CHATTRBIN " +x " + quote(subvolume + "/.snapshots"));
+           if (cmd1.retcode() != 0)
+               throw CreateConfigFailedException("chattr failed");
+       }
+       else if (errno != EEXIST)
+       {
+           y2err("mkdir failed errno:" << errno << " (" << stringerror(errno) << ")");
+           throw CreateConfigFailedException("mkdir failed");
+       }
+
+       int r2 = mkdir((subvolume + "/.snapshots/.info").c_str(), 0700);
+       if (r2 == 0)
+       {
+           SystemCmd cmd2(CHATTRBIN " -x " + quote(subvolume + "/.snapshots/.info"));
+           if (cmd2.retcode() != 0)
+               throw CreateConfigFailedException("chattr failed");
+       }
+       else if (errno != EEXIST)
+       {
+           y2err("mkdir failed errno:" << errno << " (" << stringerror(errno) << ")");
+           throw CreateConfigFailedException("mkdir failed");
+       }
+    }
+
+
+    void
+    Ext4::deleteConfig() const
+    {
+       int r1 = rmdir((subvolume + "/.snapshots/.info").c_str());
+       if (r1 != 0)
+       {
+           y2err("rmdir failed errno:" << errno << " (" << stringerror(errno) << ")");
+           throw DeleteConfigFailedException("rmdir failed");
+       }
+
+       int r2 = rmdir((subvolume + "/.snapshots").c_str());
+       if (r2 != 0)
+       {
+           y2err("rmdir failed errno:" << errno << " (" << stringerror(errno) << ")");
+           throw DeleteConfigFailedException("rmdir failed");
+       }
+    }
+
+
+    string
+    Ext4::snapshotDir(unsigned int num) const
+    {
+       return subvolume + "@" + decString(num);
+    }
+
+
+    string
+    Ext4::snapshotFile(unsigned int num) const
+    {
+       return (subvolume == "/" ? "" : subvolume) + "/.snapshots/" + decString(num);
+    }
+
+
+    SDir
+    Ext4::openInfosDir() const
+    {
+       // TODO
+    }
+
+
+    SDir
+    Ext4::openSnapshotDir(unsigned int num) const
+    {
+       // TODO
+    }
+
+
+    void
+    Ext4::createSnapshot(unsigned int num) const
+    {
+       SystemCmd cmd1(TOUCHBIN " " + quote(snapshotFile(num)));
+       if (cmd1.retcode() != 0)
+           throw CreateSnapshotFailedException();
+
+       SystemCmd cmd2(CHSNAPBIN " +S " + quote(snapshotFile(num)));
+       if (cmd2.retcode() != 0)
+           throw CreateSnapshotFailedException();
+    }
+
+
+    void
+    Ext4::deleteSnapshot(unsigned int num) const
+    {
+       SystemCmd cmd(CHSNAPBIN " -S " + quote(snapshotFile(num)));
+       if (cmd.retcode() != 0)
+           throw DeleteSnapshotFailedException();
+    }
+
+
+    bool
+    Ext4::isSnapshotMounted(unsigned int num) const
+    {
+       bool mounted = false;
+       MtabData mtab_data;
+
+       if (!getMtabData(snapshotDir(num), mounted, mtab_data))
+           throw IsSnapshotMountedFailedException();
+
+       return mounted;
+    }
+
+
+    void
+    Ext4::mountSnapshot(unsigned int num) const
+    {
+       if (isSnapshotMounted(num))
+           return;
+
+       SystemCmd cmd1(CHSNAPBIN " +n " + quote(snapshotFile(num)));
+       if (cmd1.retcode() != 0)
+           throw MountSnapshotFailedException();
+
+       int r1 = mkdir(snapshotDir(num).c_str(), 0755);
+       if (r1 != 0 && errno != EEXIST)
+       {
+           y2err("mkdir failed errno:" << errno << " (" << stringerror(errno) << ")");
+           throw MountSnapshotFailedException();
+       }
+
+       // if (!mount(snapshotFile(num), snapshotDir(num), "ext4", mount_options))
+       // throw MountSnapshotFailedException();
+    }
+
+
+    void
+    Ext4::umountSnapshot(unsigned int num) const
+    {
+       if (!isSnapshotMounted(num))
+           return;
+
+       // if (!umount(snapshotDir(num)))
+       // throw UmountSnapshotFailedException();
+
+       SystemCmd cmd1(CHSNAPBIN " -n " + quote(snapshotFile(num)));
+       if (cmd1.retcode() != 0)
+           throw UmountSnapshotFailedException();
+
+       rmdir(snapshotDir(num).c_str());
+    }
+
+
+    bool
+    Ext4::checkSnapshot(unsigned int num) const
+    {
+       struct stat stat;
+       int r1 = ::stat(snapshotFile(num).c_str(), &stat);
+       return r1 == 0 && S_ISREG(stat.st_mode);
+    }
+
+}
diff --git a/snapper/Ext4.h b/snapper/Ext4.h
new file mode 100644 (file)
index 0000000..aa4226b
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) [2011-2013] 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 SNAPPER_EXT4_H
+#define SNAPPER_EXT4_H
+
+
+#include "snapper/Filesystem.h"
+#include "config.h"
+
+
+namespace snapper
+{
+
+    class Ext4 : public Filesystem
+    {
+    public:
+
+       static Filesystem* create(const string& fstype, const string& subvolume);
+
+       Ext4(const string& subvolume);
+
+       virtual string fstype() const { return "ext4"; }
+
+       virtual void createConfig() const;
+       virtual void deleteConfig() const;
+
+       virtual string snapshotDir(unsigned int num) const;
+       virtual string snapshotFile(unsigned int num) const;
+
+       virtual SDir openInfosDir() const;
+       virtual SDir openSnapshotDir(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:
+
+       vector<string> mount_options;
+
+    };
+
+}
+
+
+#endif
index baf05341be8107b71451c7489542299a08df334f..4eec7ad6133aebe139bf18b0943be4e1b8b918a5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) [2011-2012] Novell, Inc.
+ * Copyright (c) [2011-2013] Novell, Inc.
  *
  * All Rights Reserved.
  *
 #include <unistd.h>
 #include <mntent.h>
 #include <fcntl.h>
-#include <sys/ioctl.h>
 #include <asm/types.h>
 #include <boost/algorithm/string.hpp>
 
 #include "snapper/Log.h"
 #include "snapper/Filesystem.h"
+#ifdef ENABLE_BTRFS
+#include "snapper/Btrfs.h"
+#endif
+#ifdef ENABLE_EXT4
+#include "snapper/Ext4.h"
+#endif
+#ifdef ENABLE_LVM
+#include "snapper/Lvm.h"
+#endif
 #include "snapper/Snapper.h"
 #include "snapper/SnapperTmpl.h"
-#include "snapper/SystemCmd.h"
 #include "snapper/SnapperDefines.h"
-#include "snapper/Regex.h"
 #include "config.h"
 
 
-#define BTRFS_IOCTL_MAGIC 0x94
-#define BTRFS_PATH_NAME_MAX 4087
-#define BTRFS_SUBVOL_NAME_MAX 4039
-#define BTRFS_SUBVOL_RDONLY (1ULL << 1)
-
-#define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, struct btrfs_ioctl_vol_args)
-#define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, struct btrfs_ioctl_vol_args)
-#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, struct btrfs_ioctl_vol_args)
-#define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, struct btrfs_ioctl_vol_args_v2)
-
-struct btrfs_ioctl_vol_args
-{
-    __s64 fd;
-    char name[BTRFS_PATH_NAME_MAX + 1];
-};
-
-struct btrfs_ioctl_vol_args_v2
-{
-    __s64 fd;
-    __u64 transid;
-    __u64 flags;
-    __u64 unused[4];
-    char name[BTRFS_SUBVOL_NAME_MAX + 1];
-};
-
-
 namespace snapper
 {
 
     vector<string>
-    filter_mount_options(const vector<string>& options)
+    Filesystem::filter_mount_options(const vector<string>& options)
     {
        static const char* ign_opt[] = {
            "ro", "rw",
@@ -91,7 +71,8 @@ namespace snapper
 
 
     bool
-    mount(const string& device, int fd, const string& mount_type, const vector<string>& options)
+    Filesystem::mount(const string& device, int fd, const string& mount_type,
+                     const vector<string>& options)
     {
        unsigned long mount_flags = MS_RDONLY | MS_NOEXEC | MS_NOSUID | MS_NODEV |
            MS_NOATIME | MS_NODIRATIME;
@@ -119,7 +100,7 @@ namespace snapper
 
 
     bool
-    umount(int fd, const string& mount_point)
+    Filesystem::umount(int fd, const string& mount_point)
     {
        int r1 = fchdir(fd);
        if (r1 != 0)
@@ -192,718 +173,4 @@ namespace snapper
        return info_dir;
     }
 
-
-#ifdef ENABLE_BTRFS
-    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)
-    {
-    }
-
-
-    void
-    Btrfs::createConfig() const
-    {
-       SDir subvolume_dir = openSubvolumeDir();
-
-       if (!create_subvolume(subvolume_dir.fd(), ".snapshots"))
-       {
-           y2err("create subvolume failed errno:" << errno << " (" << stringerror(errno) << ")");
-           throw CreateConfigFailedException("creating btrfs snapshot failed");
-       }
-    }
-
-
-    void
-    Btrfs::deleteConfig() const
-    {
-       SDir subvolume_dir = openSubvolumeDir();
-
-       if (!delete_subvolume(subvolume_dir.fd(), ".snapshots"))
-       {
-           y2err("delete subvolume failed errno:" << errno << " (" << stringerror(errno) << ")");
-           throw DeleteConfigFailedException("deleting btrfs snapshot failed");
-       }
-    }
-
-
-    string
-    Btrfs::snapshotDir(unsigned int num) const
-    {
-       return (subvolume == "/" ? "" : subvolume) + "/.snapshots/" + decString(num) +
-           "/snapshot";
-    }
-
-
-    SDir
-    Btrfs::openSubvolumeDir() const
-    {
-       SDir subvolume_dir = Filesystem::openSubvolumeDir();
-
-       struct stat stat;
-       if (subvolume_dir.stat(&stat) != 0)
-       {
-           throw IOErrorException();
-       }
-
-       if (!is_subvolume(stat))
-       {
-           y2err("subvolume is not a btrfs snapshot");
-           throw IOErrorException();
-       }
-
-       return subvolume_dir;
-    }
-
-
-    SDir
-    Btrfs::openInfosDir() const
-    {
-       SDir subvolume_dir = openSubvolumeDir();
-       SDir infos_dir(subvolume_dir, ".snapshots");
-
-       struct stat stat;
-       if (infos_dir.stat(&stat) != 0)
-       {
-           throw IOErrorException();
-       }
-
-       if (!is_subvolume(stat))
-       {
-           y2err(".snapshots is not a btrfs snapshot");
-           throw IOErrorException();
-       }
-
-       if (stat.st_uid != 0)
-       {
-           y2err(".snapshots must have owner root");
-           throw IOErrorException();
-       }
-
-       if (stat.st_gid != 0 && stat.st_mode & S_IWGRP)
-       {
-           y2err(".snapshots must have group root or must not be group-writable");
-           throw IOErrorException();
-       }
-
-       if (stat.st_mode & S_IWOTH)
-       {
-           y2err(".snapshots must not be world-writable");
-           throw IOErrorException();
-       }
-
-       return infos_dir;
-    }
-
-
-    SDir
-    Btrfs::openSnapshotDir(unsigned int num) const
-    {
-       SDir info_dir = openInfoDir(num);
-       SDir snapshot_dir(info_dir, "snapshot");
-
-       return snapshot_dir;
-    }
-
-
-    void
-    Btrfs::createSnapshot(unsigned int num) const
-    {
-       SDir subvolume_dir = openSubvolumeDir();
-       SDir info_dir = openInfoDir(num);
-
-       if (!create_snapshot(subvolume_dir.fd(), info_dir.fd(), "snapshot"))
-       {
-           y2err("create snapshot failed errno:" << errno << " (" << stringerror(errno) << ")");
-           throw CreateSnapshotFailedException();
-       }
-    }
-
-
-    void
-    Btrfs::deleteSnapshot(unsigned int num) const
-    {
-       SDir info_dir = openInfoDir(num);
-
-       if (!delete_subvolume(info_dir.fd(), "snapshot"))
-       {
-           y2err("delete snapshot failed errno:" << errno << " (" << stringerror(errno) << ")");
-           throw DeleteSnapshotFailedException();
-       }
-    }
-
-
-    bool
-    Btrfs::isSnapshotMounted(unsigned int num) const
-    {
-       return true;
-    }
-
-
-    void
-    Btrfs::mountSnapshot(unsigned int num) const
-    {
-    }
-
-
-    void
-    Btrfs::umountSnapshot(unsigned int num) const
-    {
-    }
-
-
-    bool
-    Btrfs::checkSnapshot(unsigned int num) const
-    {
-       try
-       {
-           SDir info_dir = openInfoDir(num);
-
-           struct stat stat;
-           int r = info_dir.stat("snapshot", &stat, AT_SYMLINK_NOFOLLOW);
-           return r == 0 && is_subvolume(stat);
-       }
-       catch (const IOErrorException& e)
-       {
-           return false;
-       }
-    }
-
-
-    bool
-    Btrfs::is_subvolume(const struct stat& stat) const
-    {
-       // see btrfsprogs source code
-       return stat.st_ino == 256 && S_ISDIR(stat.st_mode);
-    }
-
-
-    bool
-    Btrfs::create_subvolume(int fddst, const string& name) const
-    {
-       struct btrfs_ioctl_vol_args args;
-       memset(&args, 0, sizeof(args));
-
-       strncpy(args.name, name.c_str(), sizeof(args.name) - 1);
-
-       return ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args) == 0;
-    }
-
-
-    bool
-    Btrfs::create_snapshot(int fd, int fddst, const string& name) const
-    {
-       struct btrfs_ioctl_vol_args_v2 args_v2;
-       memset(&args_v2, 0, sizeof(args_v2));
-
-       args_v2.fd = fd;
-       args_v2.flags |= BTRFS_SUBVOL_RDONLY;
-       strncpy(args_v2.name, name.c_str(), sizeof(args_v2.name) - 1);
-
-       if (ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args_v2) == 0)
-           return true;
-       else if (errno != ENOTTY && errno != EINVAL)
-           return false;
-
-       struct btrfs_ioctl_vol_args args;
-       memset(&args, 0, sizeof(args));
-
-       args.fd = fd;
-       strncpy(args.name, name.c_str(), sizeof(args.name) - 1);
-
-       return ioctl(fddst, BTRFS_IOC_SNAP_CREATE, &args) == 0;
-    }
-
-
-    bool
-    Btrfs::delete_subvolume(int fd, const string& name) const
-    {
-       struct btrfs_ioctl_vol_args args;
-       memset(&args, 0, sizeof(args));
-
-       strncpy(args.name, name.c_str(), sizeof(args.name) - 1);
-
-       return ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args) == 0;
-    }
-    // ENABLE_BTRFS
-#endif
-
-
-#ifdef ENABLE_EXT4
-    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)
-    {
-       if (access(CHSNAPBIN, X_OK) != 0)
-       {
-           throw ProgramNotInstalledException(CHSNAPBIN " not installed");
-       }
-
-       if (access(CHATTRBIN, X_OK) != 0)
-       {
-           throw ProgramNotInstalledException(CHATTRBIN " not installed");
-       }
-
-       bool found = false;
-       MtabData mtab_data;
-
-       if (!getMtabData(subvolume, found, mtab_data))
-           throw InvalidConfigException();
-
-       if (!found)
-       {
-           y2err("filesystem not mounted");
-           throw InvalidConfigException();
-       }
-
-       mount_options = filter_mount_options(mtab_data.options);
-       mount_options.push_back("loop");
-       mount_options.push_back("noload");
-    }
-
-
-    void
-    Ext4::createConfig() const
-    {
-       int r1 = mkdir((subvolume + "/.snapshots").c_str(), 0700);
-       if (r1 == 0)
-       {
-           SystemCmd cmd1(CHATTRBIN " +x " + quote(subvolume + "/.snapshots"));
-           if (cmd1.retcode() != 0)
-               throw CreateConfigFailedException("chattr failed");
-       }
-       else if (errno != EEXIST)
-       {
-           y2err("mkdir failed errno:" << errno << " (" << stringerror(errno) << ")");
-           throw CreateConfigFailedException("mkdir failed");
-       }
-
-       int r2 = mkdir((subvolume + "/.snapshots/.info").c_str(), 0700);
-       if (r2 == 0)
-       {
-           SystemCmd cmd2(CHATTRBIN " -x " + quote(subvolume + "/.snapshots/.info"));
-           if (cmd2.retcode() != 0)
-               throw CreateConfigFailedException("chattr failed");
-       }
-       else if (errno != EEXIST)
-       {
-           y2err("mkdir failed errno:" << errno << " (" << stringerror(errno) << ")");
-           throw CreateConfigFailedException("mkdir failed");
-       }
-    }
-
-
-    void
-    Ext4::deleteConfig() const
-    {
-       int r1 = rmdir((subvolume + "/.snapshots/.info").c_str());
-       if (r1 != 0)
-       {
-           y2err("rmdir failed errno:" << errno << " (" << stringerror(errno) << ")");
-           throw DeleteConfigFailedException("rmdir failed");
-       }
-
-       int r2 = rmdir((subvolume + "/.snapshots").c_str());
-       if (r2 != 0)
-       {
-           y2err("rmdir failed errno:" << errno << " (" << stringerror(errno) << ")");
-           throw DeleteConfigFailedException("rmdir failed");
-       }
-    }
-
-
-    string
-    Ext4::snapshotDir(unsigned int num) const
-    {
-       return subvolume + "@" + decString(num);
-    }
-
-
-    string
-    Ext4::snapshotFile(unsigned int num) const
-    {
-       return (subvolume == "/" ? "" : subvolume) + "/.snapshots/" + decString(num);
-    }
-
-
-    SDir
-    Ext4::openInfosDir() const
-    {
-       // TODO
-    }
-
-
-    SDir
-    Ext4::openSnapshotDir(unsigned int num) const
-    {
-       // TODO
-    }
-
-
-    void
-    Ext4::createSnapshot(unsigned int num) const
-    {
-       SystemCmd cmd1(TOUCHBIN " " + quote(snapshotFile(num)));
-       if (cmd1.retcode() != 0)
-           throw CreateSnapshotFailedException();
-
-       SystemCmd cmd2(CHSNAPBIN " +S " + quote(snapshotFile(num)));
-       if (cmd2.retcode() != 0)
-           throw CreateSnapshotFailedException();
-    }
-
-
-    void
-    Ext4::deleteSnapshot(unsigned int num) const
-    {
-       SystemCmd cmd(CHSNAPBIN " -S " + quote(snapshotFile(num)));
-       if (cmd.retcode() != 0)
-           throw DeleteSnapshotFailedException();
-    }
-
-
-    bool
-    Ext4::isSnapshotMounted(unsigned int num) const
-    {
-       bool mounted = false;
-       MtabData mtab_data;
-
-       if (!getMtabData(snapshotDir(num), mounted, mtab_data))
-           throw IsSnapshotMountedFailedException();
-
-       return mounted;
-    }
-
-
-    void
-    Ext4::mountSnapshot(unsigned int num) const
-    {
-       if (isSnapshotMounted(num))
-           return;
-
-       SystemCmd cmd1(CHSNAPBIN " +n " + quote(snapshotFile(num)));
-       if (cmd1.retcode() != 0)
-           throw MountSnapshotFailedException();
-
-       int r1 = mkdir(snapshotDir(num).c_str(), 0755);
-       if (r1 != 0 && errno != EEXIST)
-       {
-           y2err("mkdir failed errno:" << errno << " (" << stringerror(errno) << ")");
-           throw MountSnapshotFailedException();
-       }
-
-       // if (!mount(snapshotFile(num), snapshotDir(num), "ext4", mount_options))
-       // throw MountSnapshotFailedException();
-    }
-
-
-    void
-    Ext4::umountSnapshot(unsigned int num) const
-    {
-       if (!isSnapshotMounted(num))
-           return;
-
-       // if (!umount(snapshotDir(num)))
-       // throw UmountSnapshotFailedException();
-
-       SystemCmd cmd1(CHSNAPBIN " -n " + quote(snapshotFile(num)));
-       if (cmd1.retcode() != 0)
-           throw UmountSnapshotFailedException();
-
-       rmdir(snapshotDir(num).c_str());
-    }
-
-
-    bool
-    Ext4::checkSnapshot(unsigned int num) const
-    {
-       struct stat stat;
-       int r1 = ::stat(snapshotFile(num).c_str(), &stat);
-       return r1 == 0 && S_ISREG(stat.st_mode);
-    }
-    // ENABLE_EXT4
-#endif
-
-
-#ifdef ENABLE_LVM
-    Filesystem*
-    Lvm::create(const string& fstype, const string& subvolume)
-    {
-       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(LVCREATEBIN, X_OK) != 0)
-       {
-           throw ProgramNotInstalledException(LVCREATEBIN " not installed");
-       }
-
-       if (access(LVSBIN, X_OK) != 0)
-       {
-           throw ProgramNotInstalledException(LVSBIN " not installed");
-       }
-
-       bool found = false;
-       MtabData mtab_data;
-
-       if (!getMtabData(subvolume, found, mtab_data))
-           throw InvalidConfigException();
-
-       if (!found)
-       {
-           y2err("filesystem not mounted");
-           throw InvalidConfigException();
-       }
-
-       if (!detectThinVolumeNames(mtab_data))
-           throw InvalidConfigException();
-
-       mount_options = filter_mount_options(mtab_data.options);
-       if (mount_type == "xfs")
-           mount_options.push_back("nouuid");
-    }
-
-
-    void
-    Lvm::createConfig() const
-    {
-       SDir subvolume_dir = openSubvolumeDir();
-
-       int r1 = subvolume_dir.mkdir(".snapshots", 0750);
-       if (r1 != 0 && errno != EEXIST)
-       {
-           y2err("mkdir failed errno:" << errno << " (" << strerror(errno) << ")");
-           throw CreateConfigFailedException("mkdir failed");
-       }
-    }
-
-
-    void
-    Lvm::deleteConfig() const
-    {
-       SDir subvolume_dir = openSubvolumeDir();
-
-       int r1 = subvolume_dir.unlink(".snapshots", AT_REMOVEDIR);
-       if (r1 != 0)
-       {
-           y2err("rmdir failed errno:" << errno << " (" << strerror(errno) << ")");
-           throw DeleteConfigFailedException("rmdir failed");
-       }
-    }
-
-
-    string
-    Lvm::snapshotDir(unsigned int num) const
-    {
-       return (subvolume == "/" ? "" : subvolume) + "/.snapshots/" + decString(num) +
-           "/snapshot";
-    }
-
-
-    SDir
-    Lvm::openInfosDir() const
-    {
-       SDir subvolume_dir = openSubvolumeDir();
-       SDir infos_dir(subvolume_dir, ".snapshots");
-
-       struct stat stat;
-       if (infos_dir.stat(&stat) != 0)
-       {
-           throw IOErrorException();
-       }
-
-       if (stat.st_uid != 0)
-       {
-           y2err(".snapshots must have owner root");
-           throw IOErrorException();
-       }
-
-       if (stat.st_gid != 0 && stat.st_mode & S_IWGRP)
-       {
-           y2err(".snapshots must have group root or must not be group-writable");
-           throw IOErrorException();
-       }
-
-       if (stat.st_mode & S_IWOTH)
-       {
-           y2err(".snapshots must not be world-writable");
-           throw IOErrorException();
-       }
-
-       return infos_dir;
-    }
-
-
-    SDir
-    Lvm::openSnapshotDir(unsigned int num) const
-    {
-       SDir info_dir = openInfoDir(num);
-       SDir snapshot_dir(info_dir, "snapshot");
-
-       return snapshot_dir;
-    }
-
-
-    string
-    Lvm::snapshotLvName(unsigned int num) const
-    {
-       return lv_name + "-snapshot" + decString(num);
-    }
-
-
-    void
-    Lvm::createSnapshot(unsigned int num) const
-    {
-       {
-           // TODO looks like a bug that this is needed (with ext4)
-#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 14)
-           SDir subvolume_dir = openSubvolumeDir();
-           syncfs(subvolume_dir.fd());
-#else
-           sync();
-#endif
-       }
-
-       SystemCmd cmd(LVCREATEBIN " --snapshot --name " + quote(snapshotLvName(num)) + " " +
-                     quote(vg_name + "/" + lv_name));
-       if (cmd.retcode() != 0)
-           throw CreateSnapshotFailedException();
-
-       SDir info_dir = openInfoDir(num);
-       int r1 = info_dir.mkdir("snapshot", 0755);
-       if (r1 != 0 && errno != EEXIST)
-       {
-           y2err("mkdir failed errno:" << errno << " (" << strerror(errno) << ")");
-           throw CreateSnapshotFailedException();
-       }
-    }
-
-
-    void
-    Lvm::deleteSnapshot(unsigned int num) const
-    {
-       SystemCmd cmd(LVREMOVEBIN " --force " + quote(vg_name + "/" + snapshotLvName(num)));
-       if (cmd.retcode() != 0)
-           throw DeleteSnapshotFailedException();
-
-       SDir info_dir = openInfoDir(num);
-       info_dir.unlink("snapshot", AT_REMOVEDIR);
-
-       SDir infos_dir = openInfosDir();
-       infos_dir.unlink(decString(num), AT_REMOVEDIR);
-    }
-
-
-    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;
-
-       SDir snapshot_dir = openSnapshotDir(num);
-
-       if (!mount(getDevice(num), snapshot_dir.fd(), mount_type, mount_options))
-           throw MountSnapshotFailedException();
-    }
-
-
-    void
-    Lvm::umountSnapshot(unsigned int num) const
-    {
-       if (!isSnapshotMounted(num))
-           return;
-
-       SDir info_dir = openInfoDir(num);
-
-       if (!umount(info_dir.fd(), "snapshot"))
-           throw UmountSnapshotFailedException();
-    }
-
-
-    bool
-    Lvm::checkSnapshot(unsigned int num) const
-    {
-       struct stat stat;
-       int r1 = ::stat(getDevice(num).c_str(), &stat);
-       return r1 == 0 && S_ISBLK(stat.st_mode);
-    }
-
-
-    bool
-    Lvm::detectThinVolumeNames(const MtabData& mtab_data)
-    {
-       Regex rx("^/dev/mapper/(.+[^-])-([^-].+)$");
-       if (!rx.match(mtab_data.device))
-       {
-           y2err("could not detect lvm names from '" << mtab_data.device << "'");
-           return false;
-       }
-
-       vg_name = boost::replace_all_copy(rx.cap(1), "--", "-");
-       lv_name = boost::replace_all_copy(rx.cap(2), "--", "-");
-
-       SystemCmd cmd(LVSBIN " -o segtype --noheadings " + quote(vg_name + "/" + lv_name));
-
-       if (cmd.retcode() != 0) {
-           y2err("could not detect segment type infromation from: " << vg_name << "/" << lv_name);
-           return false;
-       }
-
-       string segtype = boost::trim_copy(cmd.getLine(0));
-
-       if (segtype.compare("thin")) {
-           y2err(vg_name << "/" << lv_name << " is not a LVM thin volume");
-           return false;
-       }
-
-       return true;
-    }
-
-    string
-    Lvm::getDevice(unsigned int num) const
-    {
-       return "/dev/mapper/" + boost::replace_all_copy(vg_name, "-", "--") + "-" +
-           boost::replace_all_copy(snapshotLvName(num), "-", "--");
-    }
-    // ENABLE_LVM
-#endif
-
 }
index f4fe2ed24a3d40680c576fbcf8882e36e7007c0b..2f478324c271cb1f0e36b11f6c213779f18d7522 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) [2011-2012] Novell, Inc.
+ * Copyright (c) [2011-2013] Novell, Inc.
  *
  * All Rights Reserved.
  *
@@ -74,131 +74,13 @@ namespace snapper
 
        const string subvolume;
 
-    };
-
-
-#ifdef ENABLE_BTRFS
-    class Btrfs : public Filesystem
-    {
-    public:
-
-       static Filesystem* create(const string& fstype, const string& subvolume);
-
-       Btrfs(const string& subvolume);
-
-       virtual string fstype() const { return "btrfs"; }
-
-       virtual void createConfig() const;
-       virtual void deleteConfig() const;
-
-       virtual string snapshotDir(unsigned int num) const;
-
-       virtual SDir openSubvolumeDir() const;
-       virtual SDir openInfosDir() const;
-       virtual SDir openSnapshotDir(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;
+       static vector<string> filter_mount_options(const vector<string>& options);
 
-    private:
-
-       bool is_subvolume(const struct stat& stat) const;
-
-       bool create_subvolume(int fddst, const string& name) const;
-       bool create_snapshot(int fd, int fddst, const string& name) const;
-       bool delete_subvolume(int fd, const string& name) const;
+       static bool mount(const string& device, int fd, const string& mount_type,
+                         const vector<string>& options);
+       static bool umount(int fd, const string& mount_point);
 
     };
-#endif
-
-
-#ifdef ENABLE_EXT4
-    class Ext4 : public Filesystem
-    {
-    public:
-
-       static Filesystem* create(const string& fstype, const string& subvolume);
-
-       Ext4(const string& subvolume);
-
-       virtual string fstype() const { return "ext4"; }
-
-       virtual void createConfig() const;
-       virtual void deleteConfig() const;
-
-       virtual string snapshotDir(unsigned int num) const;
-       virtual string snapshotFile(unsigned int num) const;
-
-       virtual SDir openInfosDir() const;
-       virtual SDir openSnapshotDir(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:
-
-       vector<string> mount_options;
-
-    };
-#endif
-
-
-#ifdef ENABLE_LVM
-    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 snapshotDir(unsigned int num) const;
-       virtual string snapshotLvName(unsigned int num) const;
-
-       virtual SDir openInfosDir() const;
-       virtual SDir openSnapshotDir(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 detectThinVolumeNames(const MtabData& mtab_data);
-
-       string getDevice(unsigned int num) const;
-
-       string vg_name;
-       string lv_name;
-
-       vector<string> mount_options;
-
-    };
-#endif
 
 }
 
diff --git a/snapper/Lvm.cc b/snapper/Lvm.cc
new file mode 100644 (file)
index 0000000..8d5cfec
--- /dev/null
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) [2011-2013] 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 <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <asm/types.h>
+#include <boost/algorithm/string.hpp>
+
+#include "snapper/Log.h"
+#include "snapper/Filesystem.h"
+#include "snapper/Lvm.h"
+#include "snapper/Snapper.h"
+#include "snapper/SnapperTmpl.h"
+#include "snapper/SystemCmd.h"
+#include "snapper/SnapperDefines.h"
+#include "snapper/Regex.h"
+#include "config.h"
+
+
+namespace snapper
+{
+
+    Filesystem*
+    Lvm::create(const string& fstype, const string& subvolume)
+    {
+       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(LVCREATEBIN, X_OK) != 0)
+       {
+           throw ProgramNotInstalledException(LVCREATEBIN " not installed");
+       }
+
+       if (access(LVSBIN, X_OK) != 0)
+       {
+           throw ProgramNotInstalledException(LVSBIN " not installed");
+       }
+
+       bool found = false;
+       MtabData mtab_data;
+
+       if (!getMtabData(subvolume, found, mtab_data))
+           throw InvalidConfigException();
+
+       if (!found)
+       {
+           y2err("filesystem not mounted");
+           throw InvalidConfigException();
+       }
+
+       if (!detectThinVolumeNames(mtab_data))
+           throw InvalidConfigException();
+
+       mount_options = filter_mount_options(mtab_data.options);
+       if (mount_type == "xfs")
+           mount_options.push_back("nouuid");
+    }
+
+
+    void
+    Lvm::createConfig() const
+    {
+       SDir subvolume_dir = openSubvolumeDir();
+
+       int r1 = subvolume_dir.mkdir(".snapshots", 0750);
+       if (r1 != 0 && errno != EEXIST)
+       {
+           y2err("mkdir failed errno:" << errno << " (" << strerror(errno) << ")");
+           throw CreateConfigFailedException("mkdir failed");
+       }
+    }
+
+
+    void
+    Lvm::deleteConfig() const
+    {
+       SDir subvolume_dir = openSubvolumeDir();
+
+       int r1 = subvolume_dir.unlink(".snapshots", AT_REMOVEDIR);
+       if (r1 != 0)
+       {
+           y2err("rmdir failed errno:" << errno << " (" << strerror(errno) << ")");
+           throw DeleteConfigFailedException("rmdir failed");
+       }
+    }
+
+
+    string
+    Lvm::snapshotDir(unsigned int num) const
+    {
+       return (subvolume == "/" ? "" : subvolume) + "/.snapshots/" + decString(num) +
+           "/snapshot";
+    }
+
+
+    SDir
+    Lvm::openInfosDir() const
+    {
+       SDir subvolume_dir = openSubvolumeDir();
+       SDir infos_dir(subvolume_dir, ".snapshots");
+
+       struct stat stat;
+       if (infos_dir.stat(&stat) != 0)
+       {
+           throw IOErrorException();
+       }
+
+       if (stat.st_uid != 0)
+       {
+           y2err(".snapshots must have owner root");
+           throw IOErrorException();
+       }
+
+       if (stat.st_gid != 0 && stat.st_mode & S_IWGRP)
+       {
+           y2err(".snapshots must have group root or must not be group-writable");
+           throw IOErrorException();
+       }
+
+       if (stat.st_mode & S_IWOTH)
+       {
+           y2err(".snapshots must not be world-writable");
+           throw IOErrorException();
+       }
+
+       return infos_dir;
+    }
+
+
+    SDir
+    Lvm::openSnapshotDir(unsigned int num) const
+    {
+       SDir info_dir = openInfoDir(num);
+       SDir snapshot_dir(info_dir, "snapshot");
+
+       return snapshot_dir;
+    }
+
+
+    string
+    Lvm::snapshotLvName(unsigned int num) const
+    {
+       return lv_name + "-snapshot" + decString(num);
+    }
+
+
+    void
+    Lvm::createSnapshot(unsigned int num) const
+    {
+       {
+           // TODO looks like a bug that this is needed (with ext4)
+#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 14)
+           SDir subvolume_dir = openSubvolumeDir();
+           syncfs(subvolume_dir.fd());
+#else
+           sync();
+#endif
+       }
+
+       SystemCmd cmd(LVCREATEBIN " --snapshot --name " + quote(snapshotLvName(num)) + " " +
+                     quote(vg_name + "/" + lv_name));
+       if (cmd.retcode() != 0)
+           throw CreateSnapshotFailedException();
+
+       SDir info_dir = openInfoDir(num);
+       int r1 = info_dir.mkdir("snapshot", 0755);
+       if (r1 != 0 && errno != EEXIST)
+       {
+           y2err("mkdir failed errno:" << errno << " (" << strerror(errno) << ")");
+           throw CreateSnapshotFailedException();
+       }
+    }
+
+
+    void
+    Lvm::deleteSnapshot(unsigned int num) const
+    {
+       SystemCmd cmd(LVREMOVEBIN " --force " + quote(vg_name + "/" + snapshotLvName(num)));
+       if (cmd.retcode() != 0)
+           throw DeleteSnapshotFailedException();
+
+       SDir info_dir = openInfoDir(num);
+       info_dir.unlink("snapshot", AT_REMOVEDIR);
+
+       SDir infos_dir = openInfosDir();
+       infos_dir.unlink(decString(num), AT_REMOVEDIR);
+    }
+
+
+    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;
+
+       SDir snapshot_dir = openSnapshotDir(num);
+
+       if (!mount(getDevice(num), snapshot_dir.fd(), mount_type, mount_options))
+           throw MountSnapshotFailedException();
+    }
+
+
+    void
+    Lvm::umountSnapshot(unsigned int num) const
+    {
+       if (!isSnapshotMounted(num))
+           return;
+
+       SDir info_dir = openInfoDir(num);
+
+       if (!umount(info_dir.fd(), "snapshot"))
+           throw UmountSnapshotFailedException();
+    }
+
+
+    bool
+    Lvm::checkSnapshot(unsigned int num) const
+    {
+       struct stat stat;
+       int r1 = ::stat(getDevice(num).c_str(), &stat);
+       return r1 == 0 && S_ISBLK(stat.st_mode);
+    }
+
+
+    bool
+    Lvm::detectThinVolumeNames(const MtabData& mtab_data)
+    {
+       Regex rx("^/dev/mapper/(.+[^-])-([^-].+)$");
+       if (!rx.match(mtab_data.device))
+       {
+           y2err("could not detect lvm names from '" << mtab_data.device << "'");
+           return false;
+       }
+
+       vg_name = boost::replace_all_copy(rx.cap(1), "--", "-");
+       lv_name = boost::replace_all_copy(rx.cap(2), "--", "-");
+
+       SystemCmd cmd(LVSBIN " -o segtype --noheadings " + quote(vg_name + "/" + lv_name));
+
+       if (cmd.retcode() != 0) {
+           y2err("could not detect segment type infromation from: " << vg_name << "/" << lv_name);
+           return false;
+       }
+
+       string segtype = boost::trim_copy(cmd.getLine(0));
+
+       if (segtype.compare("thin")) {
+           y2err(vg_name << "/" << lv_name << " is not a LVM thin volume");
+           return false;
+       }
+
+       return true;
+    }
+
+    string
+    Lvm::getDevice(unsigned int num) const
+    {
+       return "/dev/mapper/" + boost::replace_all_copy(vg_name, "-", "--") + "-" +
+           boost::replace_all_copy(snapshotLvName(num), "-", "--");
+    }
+
+}
diff --git a/snapper/Lvm.h b/snapper/Lvm.h
new file mode 100644 (file)
index 0000000..8918335
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) [2011-2013] 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 SNAPPER_LVM_H
+#define SNAPPER_LVM_H
+
+
+#include "snapper/Filesystem.h"
+#include "config.h"
+
+
+namespace snapper
+{
+
+    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 snapshotDir(unsigned int num) const;
+       virtual string snapshotLvName(unsigned int num) const;
+
+       virtual SDir openInfosDir() const;
+       virtual SDir openSnapshotDir(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 detectThinVolumeNames(const MtabData& mtab_data);
+
+       string getDevice(unsigned int num) const;
+
+       string vg_name;
+       string lv_name;
+
+       vector<string> mount_options;
+
+    };
+
+}
+
+
+#endif
index 943b51bef8cd9db662a95cc02e5ad5cea80a8ad6..f0e8e6561c37c4f2204f767f27f9158a5ed46e88 100644 (file)
@@ -30,6 +30,22 @@ libsnapper_la_SOURCES =                                      \
        SnapperTypes.h                                  \
        SnapperDefines.h
 
+if ENABLE_BTRFS
+libsnapper_la_SOURCES +=                               \
+       Btrfs.cc                Btrfs.h
+endif
+
+if ENABLE_EXT4
+libsnapper_la_SOURCES +=                               \
+       Ext4.cc                 Ext4.h
+endif
+
+if ENABLE_LVM
+libsnapper_la_SOURCES +=                               \
+       Lvm.cc                  Lvm.h
+endif
+
+
 libsnapper_la_LDFLAGS = -version-info @LIBVERSION_INFO@
 libsnapper_la_LIBADD = -lboost_thread-mt -lxml2 -lz -lm
 
@@ -43,4 +59,3 @@ pkginclude_HEADERS =                                  \
        Comparison.h                                    \
        Exception.h                                     \
        Logger.h
-