/*
* Copyright (c) [2011-2015] Novell, Inc.
+ * Copyright (c) 2016 SUSE LLC
*
* All Rights Reserved.
*
void
- Btrfs::createSnapshot(unsigned int num, unsigned int num_parent, bool read_only) const
+ Btrfs::createSnapshot(unsigned int num, unsigned int num_parent, bool read_only,
+ bool quota) const
{
if (num_parent == 0)
{
try
{
- create_snapshot(subvolume_dir.fd(), info_dir.fd(), "snapshot", read_only, qgroup);
+ create_snapshot(subvolume_dir.fd(), info_dir.fd(), "snapshot", read_only,
+ quota ? qgroup : no_qgroup);
}
catch (const runtime_error& e)
{
try
{
- create_snapshot(snapshot_dir.fd(), info_dir.fd(), "snapshot", read_only, qgroup);
+ create_snapshot(snapshot_dir.fd(), info_dir.fd(), "snapshot", read_only,
+ quota ? qgroup : no_qgroup);
}
catch (const runtime_error& e)
{
#ifdef ENABLE_ROLLBACK
void
- Btrfs::createSnapshotOfDefault(unsigned int num, bool read_only) const
+ Btrfs::createSnapshotOfDefault(unsigned int num, bool read_only, bool quota) const
{
SDir subvolume_dir = openSubvolumeDir();
subvolid_t id = get_default_id(subvolume_dir.fd());
try
{
- create_snapshot(tmp_mount_dir.fd(), info_dir.fd(), "snapshot", read_only, qgroup);
+ create_snapshot(tmp_mount_dir.fd(), info_dir.fd(), "snapshot", read_only,
+ quota ? qgroup : no_qgroup);
}
catch (const runtime_error& e)
{
#else
void
- Btrfs::createSnapshotOfDefault(unsigned int num, bool read_only) const
+ Btrfs::createSnapshotOfDefault(unsigned int num, bool read_only, bool quota) const
{
throw std::logic_error("not implemented");
}
/*
* Copyright (c) [2011-2015] Novell, Inc.
+ * Copyright (c) 2016 SUSE LLC
*
* All Rights Reserved.
*
virtual SDir openInfosDir() const;
virtual SDir openSnapshotDir(unsigned int num) const;
- virtual void createSnapshot(unsigned int num, unsigned int num_parent,
- bool read_only) const;
- virtual void createSnapshotOfDefault(unsigned int num, bool read_only) const;
+ virtual void createSnapshot(unsigned int num, unsigned int num_parent, bool read_only,
+ bool quota) const;
+ virtual void createSnapshotOfDefault(unsigned int num, bool read_only, bool quota) const;
virtual void deleteSnapshot(unsigned int num) const;
virtual bool isSnapshotMounted(unsigned int num) const;
virtual void sync() const;
+ virtual qgroup_t getQGroup() const { return qgroup; }
+
private:
qgroup_t qgroup;
/*
* Copyright (c) [2011-2015] Novell, Inc.
+ * Copyright (c) 2016 SUSE LLC
*
* All Rights Reserved.
*
}
}
+#endif
+
qgroup_t
calc_qgroup(uint64_t level, subvolid_t id)
}
+#ifdef ENABLE_BTRFS_QUOTA
+
void
qgroup_create(int fd, qgroup_t qgroup)
{
/*
* Copyright (c) [2011-2015] Novell, Inc.
+ * Copyright (c) 2016 SUSE LLC
*
* All Rights Reserved.
*
void
- Ext4::createSnapshot(unsigned int num, unsigned int num_parent, bool read_only) const
+ Ext4::createSnapshot(unsigned int num, unsigned int num_parent, bool read_only, bool quota) const
{
if (num_parent != 0 || !read_only)
throw std::logic_error("not implemented");
virtual SDir openSnapshotDir(unsigned int num) const;
virtual void createSnapshot(unsigned int num, unsigned int num_parent,
- bool read_only) const;
+ bool read_only, bool quota) const;
virtual void deleteSnapshot(unsigned int num) const;
virtual bool isSnapshotMounted(unsigned int num) const;
/*
* Copyright (c) [2011-2015] Novell, Inc.
+ * Copyright (c) 2016 SUSE LLC
*
* All Rights Reserved.
*
void
- Filesystem::createSnapshotOfDefault(unsigned int num, bool read_only) const
+ Filesystem::createSnapshotOfDefault(unsigned int num, bool read_only, bool quota) const
{
throw std::logic_error("not implemented");
}
/*
* Copyright (c) [2011-2015] Novell, Inc.
+ * Copyright (c) 2016 SUSE LLC
*
* All Rights Reserved.
*
virtual SDir openInfoDir(unsigned int num) const;
virtual SDir openSnapshotDir(unsigned int num) const = 0;
- virtual void createSnapshot(unsigned int num, unsigned int num_parent,
- bool read_only) const = 0;
- virtual void createSnapshotOfDefault(unsigned int num, bool read_only) const;
+ virtual void createSnapshot(unsigned int num, unsigned int num_parent, bool read_only,
+ bool quota) const = 0;
+ virtual void createSnapshotOfDefault(unsigned int num, bool read_only, bool quota) const;
virtual void deleteSnapshot(unsigned int num) const = 0;
virtual bool isSnapshotMounted(unsigned int num) const = 0;
void
- Lvm::createSnapshot(unsigned int num, unsigned int num_parent, bool read_only) const
+ Lvm::createSnapshot(unsigned int num, unsigned int num_parent, bool read_only, bool quota) const
{
if (num_parent != 0 || !read_only)
throw std::logic_error("not implemented");
virtual SDir openSnapshotDir(unsigned int num) const;
virtual void createSnapshot(unsigned int num, unsigned int num_parent,
- bool read_only) const;
+ bool read_only, bool quota) const;
virtual void deleteSnapshot(unsigned int num) const;
virtual bool isSnapshotMounted(unsigned int num) const;
/*
* Copyright (c) [2011-2015] Novell, Inc.
+ * Copyright (c) 2016 SUSE LLC
*
* All Rights Reserved.
*
#include <sys/stat.h>
#include <sys/types.h>
+#include <sys/statvfs.h>
#include <glob.h>
#include <string.h>
#include <mntent.h>
#include "snapper/AsciiFile.h"
#include "snapper/Exception.h"
#include "snapper/Hooks.h"
+#include "snapper/Btrfs.h"
+#include "snapper/BtrfsUtils.h"
namespace snapper
}
+ void
+ Snapper::prepareQuota() const
+ {
+#ifdef ENABLE_BTRFS_QUOTA
+
+ const Btrfs* btrfs = dynamic_cast<const Btrfs*>(getFilesystem());
+ if (!btrfs)
+ SN_THROW(QuotaException("quota only supported with btrfs"));
+
+ if (btrfs->getQGroup() == no_qgroup)
+ SN_THROW(QuotaException("qgroup not set"));
+
+ SDir subvolume_dir = openSubvolumeDir();
+
+ vector<qgroup_t> children = BtrfsUtils::qgroup_query_children(subvolume_dir.fd(),
+ btrfs->getQGroup());
+ sort(children.begin(), children.end());
+
+ // Iterate all snapshot and ensure that those and only those with a
+ // cleanup algorithm are included in the high level qgroup.
+
+ for (const Snapshot& snapshot : snapshots)
+ {
+ if (snapshot.isCurrent())
+ continue;
+
+ BtrfsUtils::subvolid_t subvolid = get_id(snapshot.openSnapshotDir().fd());
+ BtrfsUtils::qgroup_t qgroup = calc_qgroup(0, subvolid);
+
+ bool included = binary_search(children.begin(), children.end(), qgroup);
+
+ if (!snapshot.getCleanup().empty() && !included)
+ {
+ BtrfsUtils::qgroup_assign(subvolume_dir.fd(), qgroup, btrfs->getQGroup());
+ }
+ else if (snapshot.getCleanup().empty() && included)
+ {
+ BtrfsUtils::qgroup_remove(subvolume_dir.fd(), qgroup, btrfs->getQGroup());
+ }
+ }
+
+ // Strictly speaking the rescan is not needed if we did not assign or
+ // remove qgroups. On the other hand the consistency of qgroup data
+ // before our modifications is not guaranteed. The status flag is
+ // unfortunately not reliable, see
+ // https://bugzilla.suse.com/show_bug.cgi?id=972508#c5.
+
+ quota_rescan(subvolume_dir.fd());
+
+#else
+
+ SN_THROW(QuotaException("not implemented"));
+ __builtin_unreachable();
+
+#endif
+ }
+
+
+ QuotaData
+ Snapper::queryQuotaData() const
+ {
+#ifdef ENABLE_BTRFS_QUOTA
+
+ const Btrfs* btrfs = dynamic_cast<const Btrfs*>(getFilesystem());
+ if (!btrfs)
+ SN_THROW(QuotaException("quota only supported with btrfs"));
+
+ if (btrfs->getQGroup() == no_qgroup)
+ SN_THROW(QuotaException("qgroup not set"));
+
+ QuotaData quota_data;
+
+ SDir subvolume_dir = openSubvolumeDir();
+
+ BtrfsUtils::sync(subvolume_dir.fd());
+
+ struct statvfs64 fsbuf;
+ if (fstatvfs64(subvolume_dir.fd(), &fsbuf) != 0)
+ SN_THROW(QuotaException("statvfs64 failed"));
+ quota_data.size = fsbuf.f_blocks * fsbuf.f_bsize;
+
+ BtrfsUtils::QGroupUsage qgroup_usage = BtrfsUtils::qgroup_query_usage(subvolume_dir.fd(),
+ btrfs->getQGroup());
+ quota_data.used = qgroup_usage.exclusive;
+
+ y2mil("size:" << quota_data.size << " used:" << quota_data.used);
+
+ return quota_data;
+
+#else
+
+ SN_THROW(QuotaException("not implemented"));
+ __builtin_unreachable();
+
+#endif
+ }
+
+
static bool
is_subpath(const string& a, const string& b)
{
/*
* Copyright (c) [2011-2015] Novell, Inc.
+ * Copyright (c) 2016 SUSE LLC
*
* All Rights Reserved.
*
explicit DeleteConfigFailedException(const char* msg) : Exception(msg) {}
};
+ struct QuotaException : public Exception
+ {
+ explicit QuotaException(const char* msg) : Exception(msg) {}
+ };
+
+
+ struct QuotaData
+ {
+ uint64_t size;
+ uint64_t used;
+ };
+
class Snapper : private boost::noncopyable
{
void syncFilesystem() const;
+ void prepareQuota() const;
+
+ QuotaData queryQuotaData() const;
+
static const char* compileVersion();
static const char* compileFlags();
/*
* Copyright (c) [2011-2015] Novell, Inc.
+ * Copyright (c) 2016 SUSE LLC
*
* All Rights Reserved.
*
if (isCurrent())
SN_THROW(IllegalSnapshotException());
- snapper->getFilesystem()->createSnapshot(num, num_parent, read_only);
+ snapper->getFilesystem()->createSnapshot(num, num_parent, read_only, !cleanup.empty());
}
if (isCurrent())
SN_THROW(IllegalSnapshotException());
- snapper->getFilesystem()->createSnapshotOfDefault(num, read_only);
+ snapper->getFilesystem()->createSnapshotOfDefault(num, read_only, !cleanup.empty());
}