Hello community, here is the log from the commit of package snapper for openSUSE:Factory checked in at 2015-05-06 11:18:16 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/snapper (Old) and /work/SRC/openSUSE:Factory/.snapper.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "snapper" Changes: -------- --- /work/SRC/openSUSE:Factory/snapper/snapper.changes 2015-03-09 09:59:41.000000000 +0100 +++ /work/SRC/openSUSE:Factory/.snapper.new/snapper.changes 2015-05-06 11:18:17.000000000 +0200 @@ -1,0 +2,11 @@ +Tue May 05 14:08:03 CEST 2015 - [email protected] + +- added option --sync to delete command (fate#317066) + +------------------------------------------------------------------- +Tue Apr 14 17:58:17 CEST 2015 - [email protected] + +- added option --input to diff command +- sort files according to locale + +------------------------------------------------------------------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ snapper-0.2.6.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/snapper-0.2.6/LIBVERSION new/snapper-0.2.6/LIBVERSION --- old/snapper-0.2.6/LIBVERSION 2015-02-18 18:54:03.000000000 +0100 +++ new/snapper-0.2.6/LIBVERSION 2015-04-15 14:16:56.000000000 +0200 @@ -1 +1 @@ -3.0.0 +3.1.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/snapper-0.2.6/client/commands.cc new/snapper-0.2.6/client/commands.cc --- old/snapper-0.2.6/client/commands.cc 2014-04-10 12:28:10.000000000 +0200 +++ new/snapper-0.2.6/client/commands.cc 2015-05-05 15:36:17.000000000 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) [2012-2014] Novell, Inc. + * Copyright (c) [2012-2015] Novell, Inc. * * All Rights Reserved. * @@ -351,6 +351,13 @@ } +int +operator<(const XFile& lhs, const XFile& rhs) +{ + return File::cmp_lt(lhs.name, rhs.name); +} + + list<XFile> command_get_xfiles(DBus::Connection& conn, const string& config_name, unsigned int number1, unsigned int number2) @@ -367,10 +374,25 @@ DBus::Hihi hihi(reply); hihi >> files; + files.sort(); // snapperd can have different locale than client + // so sorting is required here + return files; } +void +command_xsync(DBus::Connection& conn, const string& config_name) +{ + DBus::MessageMethodCall call(SERVICE, OBJECT, INTERFACE, "Sync"); + + DBus::Hoho hoho(call); + hoho << config_name; + + conn.send_with_reply_and_block(call); +} + + vector<string> command_xdebug(DBus::Connection& conn) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/snapper-0.2.6/client/commands.h new/snapper-0.2.6/client/commands.h --- old/snapper-0.2.6/client/commands.h 2014-04-10 12:28:10.000000000 +0200 +++ new/snapper-0.2.6/client/commands.h 2015-05-05 15:36:17.000000000 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) [2012-2014] Novell, Inc. + * Copyright (c) [2012-2015] Novell, Inc. * * All Rights Reserved. * @@ -115,5 +115,8 @@ command_get_xfiles(DBus::Connection& conn, const string& config_name, unsigned int number1, unsigned int number2); +void +command_xsync(DBus::Connection& conn, const string& config_name); + vector<string> command_xdebug(DBus::Connection& conn); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/snapper-0.2.6/client/snapper.cc new/snapper-0.2.6/client/snapper.cc --- old/snapper-0.2.6/client/snapper.cc 2015-02-18 18:54:03.000000000 +0100 +++ new/snapper-0.2.6/client/snapper.cc 2015-05-05 15:36:17.000000000 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) [2011-2014] Novell, Inc. + * Copyright (c) [2011-2015] Novell, Inc. * * All Rights Reserved. * @@ -95,9 +95,74 @@ MyFiles(const FilePaths* file_paths) : Files(file_paths) {} + + void bulk_process(FILE* file, std::function<void(File& file)> callback); + }; +void +MyFiles::bulk_process(FILE* file, std::function<void(File& file)> callback) +{ + if (file) + { + AsciiFileReader asciifile(file); + + string line; + while (asciifile.getline(line)) + { + if (line.empty()) + continue; + + string name = line; + + // strip optional status + if (name[0] != '/') + { + string::size_type pos = name.find(" "); + if (pos == string::npos) + continue; + + name.erase(0, pos + 1); + } + + Files::iterator it = findAbsolutePath(name); + if (it == end()) + { + cerr << sformat(_("File '%s' not found."), name.c_str()) << endl; + exit(EXIT_FAILURE); + } + + callback(*it); + } + } + else + { + if (getopts.numArgs() == 0) + { + for (Files::iterator it = begin(); it != end(); ++it) + callback(*it); + } + else + { + while (getopts.numArgs() > 0) + { + string name = getopts.popArg(); + + Files::iterator it = findAbsolutePath(name); + if (it == end()) + { + cerr << sformat(_("File '%s' not found."), name.c_str()) << endl; + exit(EXIT_FAILURE); + } + + callback(*it); + } + } + } +} + + struct MyComparison { MyComparison(DBus::Connection& conn, pair<unsigned int, unsigned int> nums, bool mount) @@ -779,7 +844,6 @@ }; GetOpts::parsed_opts opts = getopts.parse("modify", options); - if (!getopts.hasArgs()) { cerr << _("Command 'modify' needs at least one argument.") << endl; @@ -813,6 +877,9 @@ { cout << _(" Delete snapshot:") << endl << _("\tsnapper delete <number>") << endl + << endl + << _(" Options for 'delete' command:") << endl + << _("\t--sync, -s\t\t\tSync after deletion.") << endl << endl; } @@ -820,13 +887,25 @@ void command_delete(DBus::Connection* conn, Snapper* snapper) { - getopts.parse("delete", GetOpts::no_options); + const struct option options[] = { + { "sync", no_argument, 0, 's' }, + { 0, 0, 0, 0 } + }; + + GetOpts::parsed_opts opts = getopts.parse("delete", options); if (!getopts.hasArgs()) { cerr << _("Command 'delete' needs at least one argument.") << endl; exit(EXIT_FAILURE); } + bool sync = false; + + GetOpts::parsed_opts::const_iterator opt; + + if ((opt = opts.find("sync")) != opts.end()) + sync = true; + XSnapshots snapshots = command_list_xsnapshots(*conn, config_name); list<unsigned int> nums; @@ -859,6 +938,9 @@ } command_delete_xsnapshots(*conn, config_name, nums); + + if (sync) + command_xsync(*conn, config_name); } @@ -1005,6 +1087,7 @@ << _("\tsnapper diff <number1>..<number2> [files]") << endl << endl << _(" Options for 'diff' command:") << endl + << _("\t--input, -i <file>\t\tRead files to diff from file.") << endl << _("\t--diff-cmd <command>\t\tCommand used for comparing files.") << endl << _("\t--extensions, -x <options>\tExtra options passed to the diff command.") << endl << endl; @@ -1015,23 +1098,34 @@ command_diff(DBus::Connection* conn, Snapper* snapper) { const struct option options[] = { + { "input", required_argument, 0, 'i' }, { "diff-cmd", required_argument, 0, 0 }, { "extensions", required_argument, 0, 'x' }, { 0, 0, 0, 0 } }; GetOpts::parsed_opts opts = getopts.parse("diff", options); - if (getopts.numArgs() < 1) { cerr << _("Command 'diff' needs at least one argument.") << endl; exit(EXIT_FAILURE); } + FILE* file = NULL; Differ differ; GetOpts::parsed_opts::const_iterator opt; + if ((opt = opts.find("input")) != opts.end()) + { + file = fopen(opt->second.c_str(), "r"); + if (!file) + { + cerr << sformat(_("Opening file '%s' failed."), opt->second.c_str()) << endl; + exit(EXIT_FAILURE); + } + } + if ((opt = opts.find("diff-cmd")) != opts.end()) differ.command = opt->second; @@ -1043,22 +1137,9 @@ MyComparison comparison(*conn, nums, true); MyFiles& files = comparison.files; - if (getopts.numArgs() == 0) - { - for (Files::const_iterator it1 = files.begin(); it1 != files.end(); ++it1) - differ.run(it1->getAbsolutePath(LOC_PRE), it1->getAbsolutePath(LOC_POST)); - } - else - { - while (getopts.numArgs() > 0) - { - string name = getopts.popArg(); - - Files::const_iterator it1 = files.findAbsolutePath(name); - if (it1 != files.end()) - differ.run(it1->getAbsolutePath(LOC_PRE), it1->getAbsolutePath(LOC_POST)); - } - } + files.bulk_process(file, [differ](const File& file) { + differ.run(file.getAbsolutePath(LOC_PRE), file.getAbsolutePath(LOC_POST)); + }); } @@ -1114,57 +1195,9 @@ MyComparison comparison(*conn, nums, true); MyFiles& files = comparison.files; - if (file) - { - AsciiFileReader asciifile(file); - - string line; - while (asciifile.getline(line)) - { - if (line.empty()) - continue; - - string name = line; - - // strip optional status - if (name[0] != '/') - { - string::size_type pos = name.find(" "); - if (pos == string::npos) - continue; - - name.erase(0, pos + 1); - } - - Files::iterator it = files.findAbsolutePath(name); - if (it == files.end()) - { - cerr << sformat(_("File '%s' not found."), name.c_str()) << endl; - exit(EXIT_FAILURE); - } - - it->setUndo(true); - } - } - else - { - if (getopts.numArgs() == 0) - { - for (Files::iterator it = files.begin(); it != files.end(); ++it) - it->setUndo(true); - } - else - { - while (getopts.numArgs() > 0) - { - Files::iterator it = files.findAbsolutePath(getopts.popArg()); - if (it == files.end()) - continue; - - it->setUndo(true); - } - } - } + files.bulk_process(file, [](File& file) { + file.setUndo(true); + }); UndoStatistic undo_statistic = files.getUndoStatistic(); @@ -1455,7 +1488,6 @@ command_xa_diff(DBus::Connection* conn, Snapper* snapper) { GetOpts::parsed_opts opts = getopts.parse("xadiff", GetOpts::no_options); - if (getopts.numArgs() < 1) { cerr << _("Command 'xadiff' needs at least one argument.") << endl; @@ -1553,7 +1585,7 @@ int main(int argc, char** argv) { - setlocale(LC_ALL, ""); + locale::global(locale("")); setLogDo(&log_do); setLogQuery(&log_query); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/snapper-0.2.6/doc/dbus-protocol.txt new/snapper-0.2.6/doc/dbus-protocol.txt --- old/snapper-0.2.6/doc/dbus-protocol.txt 2015-01-19 16:56:41.000000000 +0100 +++ new/snapper-0.2.6/doc/dbus-protocol.txt 2015-05-05 15:36:17.000000000 +0200 @@ -42,6 +42,8 @@ Snapshots mounted with user-request set to false will be unmounted (delayed) after the client disconnects. +method Sync config-name + method CreateComparison config-name number1 number2 method DeleteComparison config-name number1 number2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/snapper-0.2.6/doc/snapper.xml.in new/snapper-0.2.6/doc/snapper.xml.in --- old/snapper-0.2.6/doc/snapper.xml.in 2014-12-11 18:21:17.000000000 +0100 +++ new/snapper-0.2.6/doc/snapper.xml.in 2015-05-05 15:36:17.000000000 +0200 @@ -409,6 +409,18 @@ <replaceable>number1-number2</replaceable></option></term> <listitem> <para>Delete a snapshot or a range of snapshots.</para> + <variablelist> + <varlistentry> + <term><option>-s, --sync</option></term> + <listitem> + <para>Sync the filesystem after deleting the snapshots. The + details depend on the filesystem type.</para> + <para>Btrfs normally asynchronously frees space after deleting + snapshots. With this option snapper will wait until the space once used by the + deleted snapshots is actually available again.</para> + </listitem> + </varlistentry> + </variablelist> </listitem> </varlistentry> @@ -485,6 +497,12 @@ deleted in the time between the two snapshots have been made.</para> <variablelist> <varlistentry> + <term><option>-i, --input</option> <replaceable>file</replaceable></term> + <listitem> + <para>Read files to diff from file <replaceable>file</replaceable>.</para> + </listitem> + </varlistentry> + <varlistentry> <term><option>--diff-cmd</option> <replaceable>command</replaceable></term> <listitem> <para>Command used for comparing files. The default is diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/snapper-0.2.6/server/Client.cc new/snapper-0.2.6/server/Client.cc --- old/snapper-0.2.6/server/Client.cc 2015-02-18 18:54:03.000000000 +0100 +++ new/snapper-0.2.6/server/Client.cc 2015-05-05 15:36:17.000000000 +0200 @@ -347,6 +347,10 @@ " <arg name='files' type='v' direction='out'/>\n" " </method>\n" + " <method name='Sync'>\n" + " <arg name='config-name' type='s' direction='in'/>\n" + " </method>\n" + " </interface>\n" "</node>\n"; @@ -1288,6 +1292,30 @@ void +Client::sync(DBus::Connection& conn, DBus::Message& msg) +{ + string config_name; + + DBus::Hihi hihi(msg); + hihi >> config_name; + + y2deb("Sync config_name:" << config_name); + + MetaSnappers::iterator it = meta_snappers.find(config_name); + + check_permission(conn, msg, *it); + + Snapper* snapper = it->getSnapper(); + + snapper->syncFilesystem(); + + DBus::MessageMethodReturn reply(msg); + + conn.send(reply); +} + + +void Client::debug(DBus::Connection& conn, DBus::Message& msg) const { y2deb("Debug"); @@ -1403,6 +1431,8 @@ delete_comparison(conn, msg); else if (msg.is_method_call(INTERFACE, "GetFiles")) get_files(conn, msg); + else if (msg.is_method_call(INTERFACE, "Sync")) + sync(conn, msg); else if (msg.is_method_call(INTERFACE, "Debug")) debug(conn, msg); else diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/snapper-0.2.6/server/Client.h new/snapper-0.2.6/server/Client.h --- old/snapper-0.2.6/server/Client.h 2014-04-10 12:28:10.000000000 +0200 +++ new/snapper-0.2.6/server/Client.h 2015-05-05 15:36:17.000000000 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) [2012-2014] Novell, Inc. + * Copyright (c) [2012-2015] Novell, Inc. * * All Rights Reserved. * @@ -104,6 +104,7 @@ void create_comparison(DBus::Connection& conn, DBus::Message& msg); void delete_comparison(DBus::Connection& conn, DBus::Message& msg); void get_files(DBus::Connection& conn, DBus::Message& msg); + void sync(DBus::Connection& conn, DBus::Message& msg); void debug(DBus::Connection& conn, DBus::Message& msg) const; void dispatch(DBus::Connection& conn, DBus::Message& msg); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/snapper-0.2.6/snapper/Btrfs.cc new/snapper-0.2.6/snapper/Btrfs.cc --- old/snapper-0.2.6/snapper/Btrfs.cc 2015-02-18 18:54:03.000000000 +0100 +++ new/snapper-0.2.6/snapper/Btrfs.cc 2015-05-05 15:36:17.000000000 +0200 @@ -305,7 +305,7 @@ Btrfs::createSnapshotOfDefault(unsigned int num, bool read_only) const { SDir subvolume_dir = openSubvolumeDir(); - unsigned long long id = get_default_id(subvolume_dir.fd()); + subvolid_t id = get_default_id(subvolume_dir.fd()); string name = get_subvolume(subvolume_dir.fd(), id); bool found = false; @@ -352,7 +352,11 @@ try { + subvolid_t subvolid = get_id(openSnapshotDir(num).fd()); + delete_subvolume(info_dir.fd(), "snapshot"); + + deleted_subvolids.push_back(subvolid); } catch (const runtime_error& e) { @@ -402,6 +406,8 @@ } catch (const IOErrorException& e) { + // TODO the openInfoDir above logs an error although when this + // function is used from nextNumber the failure is ok return false; } } @@ -1357,13 +1363,13 @@ if (num == 0) { SDir subvolume_dir = openSubvolumeDir(); - unsigned long long id = get_id(subvolume_dir.fd()); + subvolid_t id = get_id(subvolume_dir.fd()); set_default_id(subvolume_dir.fd(), id); } else { SDir snapshot_dir = openSnapshotDir(num); - unsigned long long id = get_id(snapshot_dir.fd()); + subvolid_t id = get_id(snapshot_dir.fd()); SDir subvolume_dir = openSubvolumeDir(); set_default_id(subvolume_dir.fd(), id); @@ -1387,6 +1393,28 @@ #endif + void + Btrfs::sync() const + { + SDir subvolume_dir = openSubvolumeDir(); + + BtrfsUtils::sync(subvolume_dir.fd()); + + if (!deleted_subvolids.empty()) + { + for (subvolid_t subvolid : deleted_subvolids) + { + while (!does_subvolume_exist(subvolume_dir.fd(), subvolid)) + sleep(1); + } + + deleted_subvolids.clear(); + + BtrfsUtils::sync(subvolume_dir.fd()); + } + } + + #ifdef ENABLE_ROLLBACK class MntTable diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/snapper-0.2.6/snapper/Btrfs.h new/snapper-0.2.6/snapper/Btrfs.h --- old/snapper-0.2.6/snapper/Btrfs.h 2015-02-18 18:54:03.000000000 +0100 +++ new/snapper-0.2.6/snapper/Btrfs.h 2015-05-05 15:36:17.000000000 +0200 @@ -31,6 +31,9 @@ namespace snapper { + using namespace BtrfsUtils; + + class Btrfs : public Filesystem { public: @@ -73,10 +76,14 @@ virtual void setDefault(unsigned int num) const; + virtual void sync() const; + private: qgroup_t qgroup; + mutable vector<subvolid_t> deleted_subvolids; + void addToFstabHelper(const string& default_subvolume_name) const; void removeFromFstabHelper() const; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/snapper-0.2.6/snapper/BtrfsUtils.cc new/snapper-0.2.6/snapper/BtrfsUtils.cc --- old/snapper-0.2.6/snapper/BtrfsUtils.cc 2014-06-30 15:03:26.000000000 +0200 +++ new/snapper-0.2.6/snapper/BtrfsUtils.cc 2015-05-05 15:36:17.000000000 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) [2011-2014] Novell, Inc. + * Copyright (c) [2011-2015] Novell, Inc. * * All Rights Reserved. * @@ -77,200 +77,237 @@ namespace snapper { - // See btrfsprogs source code for references. + namespace BtrfsUtils + { + // See btrfsprogs source code for references. - bool - is_subvolume(const struct stat& stat) - { - return stat.st_ino == 256 && S_ISDIR(stat.st_mode); - } + bool + is_subvolume(const struct stat& stat) + { + return stat.st_ino == 256 && S_ISDIR(stat.st_mode); + } - bool - is_subvolume_read_only(int fd) - { - __u64 flags; - if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) != 0) - throw runtime_error_with_errno("ioctl(BTRFS_IOC_SUBVOL_GETFLAGS) failed", errno); - return flags & BTRFS_SUBVOL_RDONLY; - } + bool + is_subvolume_read_only(int fd) + { + __u64 flags; + if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) != 0) + throw runtime_error_with_errno("ioctl(BTRFS_IOC_SUBVOL_GETFLAGS) failed", errno); + return flags & BTRFS_SUBVOL_RDONLY; + } - void - create_subvolume(int fddst, const string& name) - { - struct btrfs_ioctl_vol_args args; - memset(&args, 0, sizeof(args)); - strncpy(args.name, name.c_str(), sizeof(args.name) - 1); + void + create_subvolume(int fddst, const string& name) + { + struct btrfs_ioctl_vol_args args; + memset(&args, 0, sizeof(args)); - if (ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args) != 0) - throw runtime_error_with_errno("ioctl(BTRFS_IOC_SUBVOL_CREATE) failed", errno); - } + strncpy(args.name, name.c_str(), sizeof(args.name) - 1); + if (ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args) != 0) + throw runtime_error_with_errno("ioctl(BTRFS_IOC_SUBVOL_CREATE) failed", errno); + } - void - create_snapshot(int fd, int fddst, const string& name, bool read_only, qgroup_t qgroup) - { - struct btrfs_ioctl_vol_args_v2 args_v2; - memset(&args_v2, 0, sizeof(args_v2)); - args_v2.fd = fd; - args_v2.flags = read_only ? BTRFS_SUBVOL_RDONLY : 0; - strncpy(args_v2.name, name.c_str(), sizeof(args_v2.name) - 1); + void + create_snapshot(int fd, int fddst, const string& name, bool read_only, qgroup_t qgroup) + { + struct btrfs_ioctl_vol_args_v2 args_v2; + memset(&args_v2, 0, sizeof(args_v2)); + + args_v2.fd = fd; + args_v2.flags = read_only ? BTRFS_SUBVOL_RDONLY : 0; + strncpy(args_v2.name, name.c_str(), sizeof(args_v2.name) - 1); #ifdef ENABLE_BTRFS_QUOTA - if (qgroup != no_qgroup) - { - size_t size = sizeof(btrfs_qgroup_inherit) + sizeof(((btrfs_qgroup_inherit*) 0)->qgroups[0]); - vector<char> buffer(size, 0); - struct btrfs_qgroup_inherit* inherit = (btrfs_qgroup_inherit*) &buffer[0]; - - inherit->num_qgroups = 1; - inherit->num_ref_copies = 0; - inherit->num_excl_copies = 0; - inherit->qgroups[0] = qgroup; - - args_v2.flags |= BTRFS_SUBVOL_QGROUP_INHERIT; - args_v2.size = size; - args_v2.qgroup_inherit = inherit; - } + if (qgroup != no_qgroup) + { + size_t size = sizeof(btrfs_qgroup_inherit) + sizeof(((btrfs_qgroup_inherit*) 0)->qgroups[0]); + vector<char> buffer(size, 0); + struct btrfs_qgroup_inherit* inherit = (btrfs_qgroup_inherit*) &buffer[0]; + + inherit->num_qgroups = 1; + inherit->num_ref_copies = 0; + inherit->num_excl_copies = 0; + inherit->qgroups[0] = qgroup; + + args_v2.flags |= BTRFS_SUBVOL_QGROUP_INHERIT; + args_v2.size = size; + args_v2.qgroup_inherit = inherit; + } #endif - if (ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args_v2) == 0) - return; - else if (errno != ENOTTY && errno != EINVAL) - throw runtime_error_with_errno("ioctl(BTRFS_IOC_SNAP_CREATE_V2) failed", errno); + if (ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args_v2) == 0) + return; + else if (errno != ENOTTY && errno != EINVAL) + throw runtime_error_with_errno("ioctl(BTRFS_IOC_SNAP_CREATE_V2) failed", errno); - struct btrfs_ioctl_vol_args args; - memset(&args, 0, sizeof(args)); + struct btrfs_ioctl_vol_args args; + memset(&args, 0, sizeof(args)); - args.fd = fd; - strncpy(args.name, name.c_str(), sizeof(args.name) - 1); + args.fd = fd; + strncpy(args.name, name.c_str(), sizeof(args.name) - 1); - if (ioctl(fddst, BTRFS_IOC_SNAP_CREATE, &args) != 0) - throw runtime_error_with_errno("ioctl(BTRFS_IOC_SNAP_CREATE) failed", errno); - } + if (ioctl(fddst, BTRFS_IOC_SNAP_CREATE, &args) != 0) + throw runtime_error_with_errno("ioctl(BTRFS_IOC_SNAP_CREATE) failed", errno); + } - void - delete_subvolume(int fd, const string& name) - { - struct btrfs_ioctl_vol_args args; - memset(&args, 0, sizeof(args)); + void + delete_subvolume(int fd, const string& name) + { + struct btrfs_ioctl_vol_args args; + memset(&args, 0, sizeof(args)); - strncpy(args.name, name.c_str(), sizeof(args.name) - 1); + strncpy(args.name, name.c_str(), sizeof(args.name) - 1); - if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args) != 0) - throw runtime_error_with_errno("ioctl(BTRFS_IOC_SNAP_DESTROY) failed", errno); - } + if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args) != 0) + throw runtime_error_with_errno("ioctl(BTRFS_IOC_SNAP_DESTROY) failed", errno); + } #ifdef ENABLE_ROLLBACK - void - set_default_id(int fd, unsigned long long id) - { - if (ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &id) != 0) - throw runtime_error_with_errno("ioctl(BTRFS_IOC_DEFAULT_SUBVOL) failed", errno); - } + void + set_default_id(int fd, subvolid_t id) + { + if (ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &id) != 0) + throw runtime_error_with_errno("ioctl(BTRFS_IOC_DEFAULT_SUBVOL) failed", errno); + } - unsigned long long - get_default_id(int fd) - { - struct btrfs_ioctl_search_args args; - memset(&args, 0, sizeof(args)); + subvolid_t + get_default_id(int fd) + { + struct btrfs_ioctl_search_args args; + memset(&args, 0, sizeof(args)); - struct btrfs_ioctl_search_key* sk = &args.key; - sk->tree_id = BTRFS_ROOT_TREE_OBJECTID; - sk->nr_items = 1; - sk->max_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID; - sk->min_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID; - sk->max_type = BTRFS_DIR_ITEM_KEY; - sk->min_type = BTRFS_DIR_ITEM_KEY; - sk->max_offset = (__u64) -1; - sk->max_transid = (__u64) -1; - - if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) != 0) - throw runtime_error_with_errno("ioctl(BTRFS_IOC_TREE_SEARCH) failed", errno); - - if (sk->nr_items == 0) - throw std::runtime_error("sk->nr_items == 0"); - - struct btrfs_ioctl_search_header* sh = (struct btrfs_ioctl_search_header*) args.buf; - if (sh->type != BTRFS_DIR_ITEM_KEY) - throw std::runtime_error("sh->type != BTRFS_DIR_ITEM_KEY"); - - struct btrfs_dir_item* di = (struct btrfs_dir_item*)(sh + 1); - int name_len = btrfs_stack_dir_name_len(di); - const char* name = (const char*)(di + 1); - if (strncmp("default", name, name_len) != 0) - throw std::runtime_error("name != default"); + struct btrfs_ioctl_search_key* sk = &args.key; + sk->tree_id = BTRFS_ROOT_TREE_OBJECTID; + sk->nr_items = 1; + sk->max_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID; + sk->min_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID; + sk->max_type = BTRFS_DIR_ITEM_KEY; + sk->min_type = BTRFS_DIR_ITEM_KEY; + sk->max_offset = (__u64) -1; + sk->max_transid = (__u64) -1; + + if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) != 0) + throw runtime_error_with_errno("ioctl(BTRFS_IOC_TREE_SEARCH) failed", errno); + + if (sk->nr_items == 0) + throw std::runtime_error("sk->nr_items == 0"); + + struct btrfs_ioctl_search_header* sh = (struct btrfs_ioctl_search_header*) args.buf; + if (sh->type != BTRFS_DIR_ITEM_KEY) + throw std::runtime_error("sh->type != BTRFS_DIR_ITEM_KEY"); + + struct btrfs_dir_item* di = (struct btrfs_dir_item*)(sh + 1); + int name_len = btrfs_stack_dir_name_len(di); + const char* name = (const char*)(di + 1); + if (strncmp("default", name, name_len) != 0) + throw std::runtime_error("name != default"); - return btrfs_disk_key_objectid(&di->location); - } + return btrfs_disk_key_objectid(&di->location); + } - string - get_subvolume(int fd, unsigned long long id) - { - char path[BTRFS_PATH_NAME_MAX + 1]; + string + get_subvolume(int fd, subvolid_t id) + { + char path[BTRFS_PATH_NAME_MAX + 1]; - if (btrfs_subvolid_resolve(fd, path, sizeof(path), id) != 0) - throw std::runtime_error("btrfs_subvolid_resolve failed"); + if (btrfs_subvolid_resolve(fd, path, sizeof(path), id) != 0) + throw std::runtime_error("btrfs_subvolid_resolve failed"); - path[BTRFS_PATH_NAME_MAX] = '\0'; - return path; - } + path[BTRFS_PATH_NAME_MAX] = '\0'; + return path; + } +#endif - unsigned long long - get_id(int fd) - { - struct btrfs_ioctl_ino_lookup_args args; - memset(&args, 0, sizeof(args)); - args.treeid = 0; - args.objectid = BTRFS_FIRST_FREE_OBJECTID; - if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) != 0) - throw runtime_error_with_errno("ioctl(BTRFS_IOC_INO_LOOKUP) failed", errno); + subvolid_t + get_id(int fd) + { + struct btrfs_ioctl_ino_lookup_args args; + memset(&args, 0, sizeof(args)); + args.treeid = 0; + args.objectid = BTRFS_FIRST_FREE_OBJECTID; - return args.treeid; - } + if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) != 0) + throw runtime_error_with_errno("ioctl(BTRFS_IOC_INO_LOOKUP) failed", errno); -#endif + return args.treeid; + } - qgroup_t - make_qgroup(uint64_t level, uint64_t id) - { - return (level << 48) | id; - } + qgroup_t + make_qgroup(uint64_t level, subvolid_t id) + { + return (level << 48) | id; + } - qgroup_t - make_qgroup(const string& str) - { - string::size_type pos = str.find('/'); - if (pos == string::npos) - throw std::runtime_error("parsing qgroup failed"); - - std::istringstream a(str.substr(0, pos)); - uint64_t level = 0; - a >> level; - if (a.fail() || !a.eof()) - throw std::runtime_error("parsing qgroup failed"); - - std::istringstream b(str.substr(pos + 1)); - uint64_t id = 0; - b >> id; - if (b.fail() || !b.eof()) - throw std::runtime_error("parsing qgroup failed"); + qgroup_t + make_qgroup(const string& str) + { + string::size_type pos = str.find('/'); + if (pos == string::npos) + throw std::runtime_error("parsing qgroup failed"); + + std::istringstream a(str.substr(0, pos)); + uint64_t level = 0; + a >> level; + if (a.fail() || !a.eof()) + throw std::runtime_error("parsing qgroup failed"); + + std::istringstream b(str.substr(pos + 1)); + subvolid_t id = 0; + b >> id; + if (b.fail() || !b.eof()) + throw std::runtime_error("parsing qgroup failed"); + + return make_qgroup(level, id); + } + + + bool + does_subvolume_exist(int fd, subvolid_t subvolid) + { + struct btrfs_ioctl_search_args args; + struct btrfs_ioctl_search_key* sk = &args.key; + + sk->tree_id = BTRFS_ROOT_TREE_OBJECTID; + sk->min_objectid = subvolid; + sk->max_objectid = subvolid; + sk->min_type = BTRFS_ROOT_ITEM_KEY; + sk->max_type = BTRFS_ROOT_ITEM_KEY; + sk->min_offset = 0; + sk->max_offset = (u64) -1; + sk->min_transid = 0; + sk->max_transid = (u64) -1; + sk->nr_items = 1; + + if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) != 0) + throw runtime_error_with_errno("ioctl(BTRFS_IOC_TREE_SEARCH) failed", errno); + + return sk->nr_items == 0; + } + + + void + sync(int fd) + { + if (ioctl(fd, BTRFS_IOC_SYNC) != 0) + throw runtime_error_with_errno("ioctl(BTRFS_IOC_SYNC) failed", errno); + } - return make_qgroup(level, id); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/snapper-0.2.6/snapper/BtrfsUtils.h new/snapper-0.2.6/snapper/BtrfsUtils.h --- old/snapper-0.2.6/snapper/BtrfsUtils.h 2014-06-30 15:03:26.000000000 +0200 +++ new/snapper-0.2.6/snapper/BtrfsUtils.h 2015-05-05 15:36:17.000000000 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) [2011-2014] Novell, Inc. + * Copyright (c) [2011-2015] Novell, Inc. * * All Rights Reserved. * @@ -32,26 +32,37 @@ using std::string; - typedef uint64_t qgroup_t; - const qgroup_t no_qgroup = 0; + namespace BtrfsUtils + { - bool is_subvolume(const struct stat& stat); + typedef uint64_t subvolid_t; - bool is_subvolume_read_only(int fd); + typedef uint64_t qgroup_t; + const qgroup_t no_qgroup = 0; - void create_subvolume(int fddst, const string& name); - void create_snapshot(int fd, int fddst, const string& name, bool read_only, - qgroup_t qgroup); - void delete_subvolume(int fd, const string& name); + bool is_subvolume(const struct stat& stat); - void set_default_id(int fd, unsigned long long id); - unsigned long long get_default_id(int fd); + bool is_subvolume_read_only(int fd); - string get_subvolume(int fd, unsigned long long id); - unsigned long long get_id(int fd); + bool does_subvolume_exist(int fd, subvolid_t id); - qgroup_t make_qgroup(uint64_t level, uint64_t id); - qgroup_t make_qgroup(const string& str); + void create_subvolume(int fddst, const string& name); + void create_snapshot(int fd, int fddst, const string& name, bool read_only, + qgroup_t qgroup); + void delete_subvolume(int fd, const string& name); + + void set_default_id(int fd, subvolid_t id); + subvolid_t get_default_id(int fd); + + string get_subvolume(int fd, subvolid_t id); + subvolid_t get_id(int fd); + + qgroup_t make_qgroup(uint64_t level, subvolid_t id); + qgroup_t make_qgroup(const string& str); + + void sync(int fd); + + } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/snapper-0.2.6/snapper/File.cc new/snapper-0.2.6/snapper/File.cc --- old/snapper-0.2.6/snapper/File.cc 2014-02-28 13:36:50.000000000 +0100 +++ new/snapper-0.2.6/snapper/File.cc 2015-04-15 14:16:56.000000000 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) [2011-2014] Novell, Inc. + * Copyright (c) [2011-2015] Novell, Inc. * * All Rights Reserved. * @@ -29,6 +29,7 @@ #include <fnmatch.h> #include <errno.h> #include <fcntl.h> +#include <locale> #include <boost/algorithm/string.hpp> #include "snapper/File.h" @@ -72,33 +73,34 @@ } - struct FilterHelper + void + Files::filter(const vector<string>& ignore_patterns) { - FilterHelper(const vector<string>& patterns) - : patterns(patterns) {} - bool operator()(const File& file) - { - for (vector<string>::const_iterator it = patterns.begin(); it != patterns.end(); ++it) - if (fnmatch(it->c_str(), file.getName().c_str(), FNM_LEADING_DIR) == 0) - return true; - return false; - } - const vector<string>& patterns; - }; + std::function<bool(const File&)> pred = [&ignore_patterns](const File& file) { + for (const string& ignore_pattern : ignore_patterns) + if (fnmatch(ignore_pattern.c_str(), file.getName().c_str(), FNM_LEADING_DIR) == 0) + return true; + return false; + }; + entries.erase(remove_if(entries.begin(), entries.end(), pred), entries.end()); + } - void - Files::filter(const vector<string>& ignore_patterns) + + bool + File::cmp_lt(const string& lhs, const string& rhs) { - entries.erase(remove_if(entries.begin(), entries.end(), FilterHelper(ignore_patterns)), - entries.end()); + const std::collate<char>& c = std::use_facet<std::collate<char>>(std::locale()); + + return c.compare(lhs.c_str(), lhs.c_str() + lhs.length(), + rhs.c_str(), rhs.c_str() + rhs.length()) < 0; } - int - operator<(const File& a, const File& b) + bool + operator<(const File& lhs, const File& rhs) { - return a.getName() < b.getName(); + return File::cmp_lt(lhs.getName(), rhs.getName()); } @@ -110,16 +112,16 @@ bool - file_name_less(const File& file, const string& name) + operator<(const File& file, const string& name) { - return file.getName() < name; + return File::cmp_lt(file.getName(), name); } Files::iterator Files::find(const string& name) { - iterator ret = lower_bound(entries.begin(), entries.end(), name, file_name_less); + iterator ret = lower_bound(entries.begin(), entries.end(), name); return (ret != end() && ret->getName() == name) ? ret : end(); } @@ -127,7 +129,7 @@ Files::const_iterator Files::find(const string& name) const { - const_iterator ret = lower_bound(entries.begin(), entries.end(), name, file_name_less); + const_iterator ret = lower_bound(entries.begin(), entries.end(), name); return (ret != end() && ret->getName() == name) ? ret : end(); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/snapper-0.2.6/snapper/File.h new/snapper-0.2.6/snapper/File.h --- old/snapper-0.2.6/snapper/File.h 2014-02-26 18:11:56.000000000 +0100 +++ new/snapper-0.2.6/snapper/File.h 2015-04-15 14:16:56.000000000 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) [2011-2014] Novell, Inc. + * Copyright (c) [2011-2015] Novell, Inc. * * All Rights Reserved. * @@ -140,6 +140,9 @@ XAUndoStatistic getXAUndoStatistic() const; + // C++ locale aware less-than comparison + static bool cmp_lt(const string& lhs, const string& rhs); + private: bool createParentDirectories(const string& path) const; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/snapper-0.2.6/snapper/Filesystem.cc new/snapper-0.2.6/snapper/Filesystem.cc --- old/snapper-0.2.6/snapper/Filesystem.cc 2015-02-18 18:54:03.000000000 +0100 +++ new/snapper-0.2.6/snapper/Filesystem.cc 2015-05-05 15:36:17.000000000 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) [2011-2013] Novell, Inc. + * Copyright (c) [2011-2015] Novell, Inc. * * All Rights Reserved. * @@ -174,4 +174,10 @@ throw std::logic_error("not implemented"); } + + void + Filesystem::sync() const + { + } + } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/snapper-0.2.6/snapper/Filesystem.h new/snapper-0.2.6/snapper/Filesystem.h --- old/snapper-0.2.6/snapper/Filesystem.h 2015-02-18 18:54:03.000000000 +0100 +++ new/snapper-0.2.6/snapper/Filesystem.h 2015-05-05 15:36:17.000000000 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) [2011-2014] Novell, Inc. + * Copyright (c) [2011-2015] Novell, Inc. * * All Rights Reserved. * @@ -83,6 +83,8 @@ virtual void setDefault(unsigned int num) const; + virtual void sync() const; + protected: const string subvolume; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/snapper-0.2.6/snapper/Snapper.cc new/snapper-0.2.6/snapper/Snapper.cc --- old/snapper-0.2.6/snapper/Snapper.cc 2015-02-18 18:54:03.000000000 +0100 +++ new/snapper-0.2.6/snapper/Snapper.cc 2015-05-05 15:36:17.000000000 +0200 @@ -492,6 +492,13 @@ } + void + Snapper::syncFilesystem() const + { + filesystem->sync(); + } + + static void set_acl_permissions(acl_entry_t entry) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/snapper-0.2.6/snapper/Snapper.h new/snapper-0.2.6/snapper/Snapper.h --- old/snapper-0.2.6/snapper/Snapper.h 2015-02-18 18:54:03.000000000 +0100 +++ new/snapper-0.2.6/snapper/Snapper.h 2015-05-05 15:36:17.000000000 +0200 @@ -153,6 +153,8 @@ void syncAcl() const; + void syncFilesystem() const; + static const char* compileVersion(); static const char* compileFlags(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/snapper-0.2.6/snapper/Version.h new/snapper-0.2.6/snapper/Version.h --- old/snapper-0.2.6/snapper/Version.h 2015-03-03 11:07:56.000000000 +0100 +++ new/snapper-0.2.6/snapper/Version.h 2015-05-05 15:38:23.000000000 +0200 @@ -25,7 +25,7 @@ #define LIBSNAPPER_MAJOR="3" -#define LIBSNAPPER_MINOR="0" +#define LIBSNAPPER_MINOR="1" #define LIBSNAPPER_PATCHLEVEL="0" #define LIBSNAPPER_VERSION ( LIBSNAPPER_MAJOR * 10000 + \\ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/snapper-0.2.6/testsuite/Makefile.am new/snapper-0.2.6/testsuite/Makefile.am --- old/snapper-0.2.6/testsuite/Makefile.am 2015-01-19 16:56:41.000000000 +0100 +++ new/snapper-0.2.6/testsuite/Makefile.am 2015-04-15 14:16:56.000000000 +0200 @@ -7,7 +7,7 @@ LDADD = ../snapper/libsnapper.la ../dbus/libdbus.la -lboost_unit_test_framework check_PROGRAMS = sysconfig-get1.test dirname1.test basename1.test \ - equal-date.test dbus-escape.test + equal-date.test dbus-escape.test cmp-lt.test TESTS = $(check_PROGRAMS) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/snapper-0.2.6/testsuite/Makefile.in new/snapper-0.2.6/testsuite/Makefile.in --- old/snapper-0.2.6/testsuite/Makefile.in 2015-03-03 11:07:54.000000000 +0100 +++ new/snapper-0.2.6/testsuite/Makefile.in 2015-05-05 15:38:19.000000000 +0200 @@ -83,7 +83,7 @@ host_triplet = @host@ check_PROGRAMS = sysconfig-get1.test$(EXEEXT) dirname1.test$(EXEEXT) \ basename1.test$(EXEEXT) equal-date.test$(EXEEXT) \ - dbus-escape.test$(EXEEXT) + dbus-escape.test$(EXEEXT) cmp-lt.test$(EXEEXT) subdir = testsuite DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/depcomp $(top_srcdir)/test-driver @@ -104,6 +104,10 @@ am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = +cmp_lt_test_SOURCES = cmp-lt.cc +cmp_lt_test_OBJECTS = cmp-lt.$(OBJEXT) +cmp_lt_test_LDADD = $(LDADD) +cmp_lt_test_DEPENDENCIES = ../snapper/libsnapper.la ../dbus/libdbus.la dbus_escape_test_SOURCES = dbus-escape.cc dbus_escape_test_OBJECTS = dbus-escape.$(OBJEXT) dbus_escape_test_LDADD = $(LDADD) @@ -156,10 +160,10 @@ am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = -SOURCES = basename1.cc dbus-escape.cc dirname1.cc equal-date.cc \ - sysconfig-get1.cc -DIST_SOURCES = basename1.cc dbus-escape.cc dirname1.cc equal-date.cc \ - sysconfig-get1.cc +SOURCES = basename1.cc cmp-lt.cc dbus-escape.cc dirname1.cc \ + equal-date.cc sysconfig-get1.cc +DIST_SOURCES = basename1.cc cmp-lt.cc dbus-escape.cc dirname1.cc \ + equal-date.cc sysconfig-get1.cc am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ @@ -583,6 +587,10 @@ @rm -f basename1.test$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(basename1_test_OBJECTS) $(basename1_test_LDADD) $(LIBS) +cmp-lt.test$(EXEEXT): $(cmp_lt_test_OBJECTS) $(cmp_lt_test_DEPENDENCIES) $(EXTRA_cmp_lt_test_DEPENDENCIES) + @rm -f cmp-lt.test$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(cmp_lt_test_OBJECTS) $(cmp_lt_test_LDADD) $(LIBS) + dbus-escape.test$(EXEEXT): $(dbus_escape_test_OBJECTS) $(dbus_escape_test_DEPENDENCIES) $(EXTRA_dbus_escape_test_DEPENDENCIES) @rm -f dbus-escape.test$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(dbus_escape_test_OBJECTS) $(dbus_escape_test_LDADD) $(LIBS) @@ -606,6 +614,7 @@ -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/basename1.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmp-lt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-escape.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dirname1.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/equal-date.Po@am__quote@ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/snapper-0.2.6/testsuite/cmp-lt.cc new/snapper-0.2.6/testsuite/cmp-lt.cc --- old/snapper-0.2.6/testsuite/cmp-lt.cc 1970-01-01 01:00:00.000000000 +0100 +++ new/snapper-0.2.6/testsuite/cmp-lt.cc 2015-04-15 14:16:56.000000000 +0200 @@ -0,0 +1,71 @@ + +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MODULE snapper + +#include <locale> +#include <boost/test/unit_test.hpp> + +#include <snapper/File.h> + +using namespace snapper; + + +namespace std +{ + std::ostream& + operator<<(std::ostream& s, const vector<string>& v) + { + for(std::vector<string>::const_iterator it = v.begin(); it != v.end(); ++it) + { + if (it != v.begin()) + s << " "; + s << *it; + } + + return s; + } +} + + +BOOST_AUTO_TEST_CASE(test1) +{ + std::locale::global(std::locale("C")); + + vector<string> v = { "A", "B", "b", "a" }; + sort(v.begin(), v.end(), File::cmp_lt); + + BOOST_CHECK_EQUAL(v, vector<string>({ "A", "B", "a", "b" })); +} + + +BOOST_AUTO_TEST_CASE(test2) +{ + std::locale::global(std::locale("en_US.UTF-8")); + + vector<string> v = { "A", "B", "b", "a" }; + sort(v.begin(), v.end(), File::cmp_lt); + + BOOST_CHECK_EQUAL(v, vector<string>({ "a", "A", "b", "B" })); +} + + +BOOST_AUTO_TEST_CASE(test3) +{ + std::locale::global(std::locale("de_DE.UTF-8")); + + vector<string> v = { "a", "b", "ä" }; + sort(v.begin(), v.end(), File::cmp_lt); + + BOOST_CHECK_EQUAL(v, vector<string>({ "a", "ä", "b" })); +} + + +BOOST_AUTO_TEST_CASE(test4) +{ + std::locale::global(std::locale("en_US.UTF-8")); + + vector<string> v = { "a", "\344" }; // invalid UTF-8 + sort(v.begin(), v.end(), File::cmp_lt); + + BOOST_CHECK_EQUAL(v, vector<string>({ "\344", "a" })); +}
