From: Arvin Schnell Date: Wed, 16 Jan 2013 13:06:28 +0000 (+0100) Subject: - split Filesystem.{h,cc} X-Git-Tag: v0.1.3~59 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=608b9cb513bedc4d8756d35c4b4e3758e2643b19;p=thirdparty%2Fsnapper.git - split Filesystem.{h,cc} --- diff --git a/configure.in b/configure.in index 19fb3e4f..c4e7d9c6 100644 --- a/configure.in +++ b/configure.in @@ -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 index 00000000..0da2ad14 --- /dev/null +++ b/snapper/Btrfs.cc @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 00000000..583e8fa7 --- /dev/null +++ b/snapper/Btrfs.h @@ -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 index 00000000..a238b8bc --- /dev/null +++ b/snapper/Ext4.cc @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 00000000..aa4226b6 --- /dev/null +++ b/snapper/Ext4.h @@ -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 mount_options; + + }; + +} + + +#endif diff --git a/snapper/Filesystem.cc b/snapper/Filesystem.cc index baf05341..4eec7ad6 100644 --- a/snapper/Filesystem.cc +++ b/snapper/Filesystem.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) [2011-2012] Novell, Inc. + * Copyright (c) [2011-2013] Novell, Inc. * * All Rights Reserved. * @@ -28,51 +28,31 @@ #include #include #include -#include #include #include #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 - filter_mount_options(const vector& options) + Filesystem::filter_mount_options(const vector& 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& options) + Filesystem::mount(const string& device, int fd, const string& mount_type, + const vector& 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 - } diff --git a/snapper/Filesystem.h b/snapper/Filesystem.h index f4fe2ed2..2f478324 100644 --- a/snapper/Filesystem.h +++ b/snapper/Filesystem.h @@ -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 filter_mount_options(const vector& 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& 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 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 mount_options; - - }; -#endif } diff --git a/snapper/Lvm.cc b/snapper/Lvm.cc new file mode 100644 index 00000000..8d5cfec0 --- /dev/null +++ b/snapper/Lvm.cc @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 00000000..8918335e --- /dev/null +++ b/snapper/Lvm.h @@ -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 mount_options; + + }; + +} + + +#endif diff --git a/snapper/Makefile.am b/snapper/Makefile.am index 943b51be..f0e8e656 100644 --- a/snapper/Makefile.am +++ b/snapper/Makefile.am @@ -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 -