string
command_mount_xsnapshots(DBus::Connection& conn, const string& config_name,
- unsigned int num)
+ unsigned int num, bool user_request)
{
DBus::MessageMethodCall call(SERVICE, OBJECT, INTERFACE, "MountSnapshot");
DBus::Hoho hoho(call);
- hoho << config_name << num;
+ hoho << config_name << num << user_request;
DBus::Message reply = conn.send_with_reply_and_block(call);
void
command_umount_xsnapshots(DBus::Connection& conn, const string& config_name,
- unsigned int num)
+ unsigned int num, bool user_request)
{
DBus::MessageMethodCall call(SERVICE, OBJECT, INTERFACE, "UmountSnapshot");
DBus::Hoho hoho(call);
- hoho << config_name << num;
+ hoho << config_name << num << user_request;
conn.send_with_reply_and_block(call);
}
string
command_mount_xsnapshots(DBus::Connection& conn, const string& config_name,
- unsigned int num);
+ unsigned int num, bool user_request);
void
command_umount_xsnapshots(DBus::Connection& conn, const string& config_name,
- unsigned int num);
+ unsigned int num, bool user_request);
string
command_get_xmount_point(DBus::Connection& conn, const string& config_name,
if (mount)
{
if (nums.first != 0)
- file_paths.pre_path = command_mount_xsnapshots(conn, config_name, nums.first);
+ file_paths.pre_path = command_mount_xsnapshots(conn, config_name, nums.first, false);
else
file_paths.pre_path = file_paths.system_path;
if (nums.second != 0)
- file_paths.post_path = command_mount_xsnapshots(conn, config_name, nums.second);
+ file_paths.post_path = command_mount_xsnapshots(conn, config_name, nums.second, false);
else
file_paths.post_path = file_paths.system_path;
}
{
unsigned int num = read_num(getopts.popArg());
- command_mount_xsnapshots(conn, config_name, num);
+ command_mount_xsnapshots(conn, config_name, num, true);
}
}
{
unsigned int num = read_num(getopts.popArg());
- command_umount_xsnapshots(conn, config_name, num);
+ command_umount_xsnapshots(conn, config_name, num, true);
}
}
cerr << _("Config is locked.") << endl;
else if (name == "error.config_in_use")
cerr << _("Config is in use.") << endl;
+ else if (name == "error.snapshot_in_use")
+ cerr << _("Snapshot is in use.") << endl;
else if (name == "error.unknown_file_use")
cerr << _("Unknown file.") << endl;
else if (name == "error.io_error")
method GetConfig config-name
-method CreateConfig config-name subvolume fstype template_name
+method CreateConfig config-name subvolume fstype template-name
method DeleteConfig config-name
signal ConfigCreated config-name
signal SnapshotModified config-name number
signal SnapshotsDeleted config-name list(numbers)
-method MountSnapshot config-name number
-method UmountSnapshot config-name number
+method MountSnapshot config-name number user-request
+method UmountSnapshot config-name number user-request
method GetMountPoint config-name number
+Snapshots mounted with user-request set to false will be unmounted (delayed)
+after the client disconnects.
+
method CreateComparison config-name number1 number2
method DeleteComparison config-name number1 number2
+-------------------------------------------------------------------
+Mon Nov 26 12:40:54 CET 2012 - aschnell@suse.de
+
+- implemented use-counter for mounts of snapshots
+
-------------------------------------------------------------------
Wed Nov 21 11:55:28 CET 2012 - aschnell@suse.de
{
delete_comparison(it);
}
+
+ for (map<pair<string, unsigned int>, unsigned int>::iterator it1 = mounts.begin();
+ it1 != mounts.end(); ++it1)
+ {
+ string config_name = it1->first.first;
+ unsigned int number = it1->first.second;
+ unsigned int use_count = it1->second;
+
+ MetaSnappers::iterator it2 = meta_snappers.find(config_name);
+
+ Snapper* snapper = it2->getSnapper();
+ Snapshots& snapshots = snapper->getSnapshots();
+
+ Snapshots::iterator snap = snapshots.find(number);
+ if (snap == snapshots.end())
+ throw IllegalSnapshotException();
+
+ for (unsigned int i = 0; i < use_count; ++i)
+ snap->umountFilesystemSnapshot(false);
+ }
}
}
+void
+Client::add_mount(const string& config_name, unsigned int number)
+{
+ mounts[make_pair(config_name, number)]++;
+}
+
+
+void
+Client::remove_mount(const string& config_name, unsigned int number)
+{
+ map<pair<string, unsigned int>, unsigned int>::iterator it =
+ mounts.find(make_pair(config_name, number));
+ if (it != mounts.end())
+ {
+ if (it->second > 1)
+ it->second--;
+ else
+ mounts.erase(it);
+ }
+}
+
+
void
Client::introspect(DBus::Connection& conn, DBus::Message& msg)
{
" <method name='MountSnapshot'>\n"
" <arg name='config-name' type='s' direction='in'/>\n"
" <arg name='number' type='u' direction='in'/>\n"
+ " <arg name='user-request' type='b' direction='in'/>\n"
" </method>\n"
" <method name='UmountSnapshot'>\n"
" <arg name='config-name' type='s' direction='in'/>\n"
" <arg name='number' type='u' direction='in'/>\n"
+ " <arg name='user-request' type='b' direction='in'/>\n"
" </method>\n"
" <method name='GetMountPoint'>\n"
}
-struct InUse : public std::exception
+struct ConfigInUse : public std::exception
{
- explicit InUse() throw() {}
- virtual const char* what() const throw() { return "in use"; }
+ explicit ConfigInUse() throw() {}
+ virtual const char* what() const throw() { return "config in use"; }
+};
+
+
+struct SnapshotInUse : public std::exception
+{
+ explicit SnapshotInUse() throw() {}
+ virtual const char* what() const throw() { return "snapshot in use"; }
};
void
-Client::check_in_use(const MetaSnapper& meta_snapper) const
+Client::check_config_in_use(const MetaSnapper& meta_snapper) const
{
if (meta_snapper.use_count() != 0)
- throw InUse();
+ throw ConfigInUse();
+}
+
+
+void
+Client::check_snapshot_in_use(const MetaSnapper& meta_snapper, unsigned int number) const
+{
+ for (Clients::const_iterator it1 = clients.begin(); it1 != clients.end(); ++it1)
+ {
+ map<pair<string, unsigned int>, unsigned int>::const_iterator it2 =
+ it1->mounts.find(make_pair(meta_snapper.configName(), number));
+
+ if (it2 != it1->mounts.end())
+ throw SnapshotInUse();
+ }
}
check_permission(conn, msg);
check_lock(conn, msg, config_name);
- check_in_use(*it);
+ check_config_in_use(*it);
meta_snappers.deleteConfig(it);
boost::unique_lock<boost::shared_mutex> lock(big_mutex);
- MetaSnappers::iterator it = meta_snappers.find(config_name);
+ MetaSnappers::iterator it1 = meta_snappers.find(config_name);
- check_permission(conn, msg, *it);
+ check_permission(conn, msg, *it1);
check_lock(conn, msg, config_name);
- check_in_use(*it);
+ check_config_in_use(*it1);
- Snapper* snapper = it->getSnapper();
+ Snapper* snapper = it1->getSnapper();
Snapshots& snapshots = snapper->getSnapshots();
- for (list<unsigned int>::const_iterator it = nums.begin(); it != nums.end(); ++it)
+ for (list<unsigned int>::const_iterator it2 = nums.begin(); it2 != nums.end(); ++it2)
{
- Snapshots::iterator snap = snapshots.find(*it);
+ check_snapshot_in_use(*it1, *it2);
+
+ Snapshots::iterator snap = snapshots.find(*it2);
snapper->deleteSnapshot(snap);
}
{
string config_name;
dbus_uint32_t num;
+ bool user_request;
DBus::Hihi hihi(msg);
- hihi >> config_name >> num;
+ hihi >> config_name >> num >> user_request;
- y2deb("MountSnapshot config_name:" << config_name << " num:" << num);
+ y2deb("MountSnapshot config_name:" << config_name << " num:" << num <<
+ " user_request:" << user_request);
boost::unique_lock<boost::shared_mutex> lock(big_mutex);
if (snap == snapshots.end())
throw IllegalSnapshotException();
- snap->mountFilesystemSnapshot();
+ snap->mountFilesystemSnapshot(user_request);
+
+ if (!user_request)
+ add_mount(config_name, num);
string mount_point = snap->snapshotDir();
{
string config_name;
dbus_uint32_t num;
+ bool user_request;
DBus::Hihi hihi(msg);
- hihi >> config_name >> num;
+ hihi >> config_name >> num >> user_request;
- y2deb("UmountSnapshot config_name:" << config_name << " num:" << num);
+ y2deb("UmountSnapshot config_name:" << config_name << " num:" << num <<
+ " user_request:" << user_request);
boost::unique_lock<boost::shared_mutex> lock(big_mutex);
if (snap == snapshots.end())
throw IllegalSnapshotException();
- snap->umountFilesystemSnapshot();
+ snap->umountFilesystemSnapshot(user_request);
+
+ if (!user_request)
+ remove_mount(config_name, num);
DBus::MessageMethodReturn reply(msg);
DBus::MessageError reply(msg, "error.config_locked", DBUS_ERROR_FAILED);
conn.send(reply);
}
- catch (const InUse& e)
+ catch (const ConfigInUse& e)
{
DBus::MessageError reply(msg, "error.config_in_use", DBUS_ERROR_FAILED);
conn.send(reply);
}
+ catch (const SnapshotInUse& e)
+ {
+ DBus::MessageError reply(msg, "error.snapshot_in_use", DBUS_ERROR_FAILED);
+ conn.send(reply);
+ }
catch (const NoComparison& e)
{
DBus::MessageError reply(msg, "error.no_comparisons", DBUS_ERROR_FAILED);
void check_permission(DBus::Connection& conn, DBus::Message& msg,
const MetaSnapper& meta_snapper) const;
void check_lock(DBus::Connection& conn, DBus::Message& msg, const string& config_name) const;
- void check_in_use(const MetaSnapper& meta_snapper) const;
+ void check_config_in_use(const MetaSnapper& meta_snapper) const;
+ void check_snapshot_in_use(const MetaSnapper& meta_snapper, unsigned int number) const;
void signal_config_created(DBus::Connection& conn, const string& config_name);
void signal_config_deleted(DBus::Connection& conn, const string& config_name);
void remove_lock(const string& config_name);
bool has_lock(const string& config_name) const;
+ void add_mount(const string& config_name, unsigned int number);
+ void remove_mount(const string& config_name, unsigned int number);
+
const string name;
list<Comparison*> comparisons;
set<string> locks;
+ map<pair<string, unsigned int>, unsigned int> mounts;
+
struct Task
{
Task(DBus::Connection& conn, DBus::Message& msg) : conn(conn), msg(msg) {}
MetaSnapper::unload()
{
delete snapper;
- snapper = 0;
+ snapper = NULL;
}
void
- Comparison::mount()
+ Comparison::mount() const
{
if (!getSnapshot1()->isCurrent())
- getSnapshot1()->mountFilesystemSnapshot();
+ getSnapshot1()->mountFilesystemSnapshot(false);
if (!getSnapshot2()->isCurrent())
- getSnapshot2()->mountFilesystemSnapshot();
+ getSnapshot2()->mountFilesystemSnapshot(false);
+ }
+
+
+ void
+ Comparison::umount() const
+ {
+ if (!getSnapshot1()->isCurrent())
+ getSnapshot1()->umountFilesystemSnapshot(false);
+ if (!getSnapshot2()->isCurrent())
+ getSnapshot2()->umountFilesystemSnapshot(false);
}
{
y2mil("num1:" << getSnapshot1()->getNum() << " num2:" << getSnapshot2()->getNum());
- mount();
-
#if 1
cmpdirs_cb_t cb = AppendHelper(&file_paths, files);
#else
};
#endif
- SDir dir1 = getSnapshot1()->openSnapshotDir();
- SDir dir2 = getSnapshot2()->openSnapshotDir();
- cmpDirs(dir1, dir2, cb);
+ mount();
+
+ {
+ SDir dir1 = getSnapshot1()->openSnapshotDir();
+ SDir dir2 = getSnapshot2()->openSnapshotDir();
+ cmpDirs(dir1, dir2, cb);
+ }
+
+ umount();
files.sort();
Files& getFiles() { return files; }
const Files& getFiles() const { return files; }
- void mount();
+ void mount() const;
+ void umount() const;
UndoStatistic getUndoStatistic() const;
y2mil("Snapper destructor");
for (Snapshots::iterator it = snapshots.begin(); it != snapshots.end(); ++it)
+ {
it->flushInfo();
+ try
+ {
+ it->handleUmountFilesystemSnapshot();
+ }
+ catch (const UmountSnapshotFailedException& e)
+ {
+ }
+ }
+
delete filesystem;
delete config;
}
}
+ Snapshot::Snapshot(const Snapper* snapper, SnapshotType type, unsigned int num, time_t date)
+ : snapper(snapper), type(type), num(num), date(date), uid(0), pre_num(0),
+ info_modified(false), mount_checked(false), mount_user_request(false),
+ mount_use_count(0)
+ {
+ }
+
+
+ Snapshot::~Snapshot()
+ {
+ }
+
+
// Directory containing the actual content of the snapshot.
// For btrfs e.g. "/" or "/home" for current and "/.snapshots/1/snapshot"
// or "/home/.snapshots/1/snapshot" otherwise.
void
- Snapshot::mountFilesystemSnapshot() const
+ Snapshot::mountFilesystemSnapshot(bool user_request) const
{
if (isCurrent())
throw IllegalSnapshotException();
+ if (!mount_checked)
+ {
+ mount_user_request = snapper->getFilesystem()->isSnapshotMounted(num);
+ mount_checked = true;
+ }
+
+ if (user_request)
+ mount_user_request = true;
+ else
+ mount_use_count++;
+
snapper->getFilesystem()->mountSnapshot(num);
}
void
- Snapshot::umountFilesystemSnapshot() const
+ Snapshot::umountFilesystemSnapshot(bool user_request) const
{
if (isCurrent())
throw IllegalSnapshotException();
- snapper->getFilesystem()->umountSnapshot(num);
+ if (!mount_checked)
+ {
+ mount_user_request = snapper->getFilesystem()->isSnapshotMounted(num);
+ mount_checked = true;
+ }
+
+ if (user_request)
+ mount_user_request = false;
+ else
+ mount_use_count--;
+
+ if (user_request && mount_use_count == 0)
+ snapper->getFilesystem()->umountSnapshot(num);
+ }
+
+
+ void
+ Snapshot::handleUmountFilesystemSnapshot() const
+ {
+ if (!mount_checked)
+ return;
+
+ if (!mount_user_request && mount_use_count == 0)
+ snapper->getFilesystem()->umountSnapshot(num);
}
friend class Snapshots;
- Snapshot(const Snapper* snapper, SnapshotType type, unsigned int num, time_t date)
- : snapper(snapper), type(type), num(num), date(date), uid(0), pre_num(0),
- info_modified(false) {}
+ Snapshot(const Snapper* snapper, SnapshotType type, unsigned int num, time_t date);
+ ~Snapshot();
SnapshotType getType() const { return type; }
SDir openSnapshotDir() const;
#endif
- void mountFilesystemSnapshot() const;
- void umountFilesystemSnapshot() const;
+ void mountFilesystemSnapshot(bool user_request) const;
+ void umountFilesystemSnapshot(bool user_request) const;
+ void handleUmountFilesystemSnapshot() const;
friend std::ostream& operator<<(std::ostream& s, const Snapshot& snapshot);
bool info_modified;
+ mutable bool mount_checked;
+ mutable bool mount_user_request;
+ mutable unsigned int mount_use_count;
+
void writeInfo() const;
void createFilesystemSnapshot() const;