diff --git a/debian/changelog b/debian/changelog index f302454..b39ede5 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,46 @@ +timeshift (22.11.1) vera; urgency=medium + + [ Matin Lotfaliei ] + * Update README.md to include build dependencies and instructions (#45) + + [ Isaac Carter ] + * Changes man page to GROFF format (#73) + + [ Michael Webster ] + * Add github workflow. + * Clean up debian/control, appdata, build file. + * debian/control: Restore newline. + + [ Tony George ] + * #939, #937: Fix crash on ArchLinux and Ubuntu 22.10 Kinetic + * Don't show message when unmounting temporary mount points + * Remove some unused code and functions + * Use -Os for compilation instead of -O3 + + [ JungHee Lee ] + * Update timeshift-gtk.desktop + + [ Tony George ] + * Fix typo in timeshift-gtk.desktop + + [ Michael Webster ] + * Fix some file permissions. + + [ Tony George ] + * Delete the empty 'timeshift-wiki' folder + * Remove mention of installer in README; Remove config file for installer + * Remove config file for old GitHub pages site + * Remove empty NOTES file + * Remove BUILD_CONFIG + + [ Thomas Praxl ] + * Restore btrfs qgroups size & unshared size columns + + [ Michael Webster ] + * Main.vala: Don't continue to try to get btrfs quota info if it fails the first time. + + -- Clement Lefebvre Tue, 22 Nov 2022 10:07:06 +0000 + timeshift (22.06.5) vanessa; urgency=medium * l10n: Update translations diff --git a/src/Console/AppConsole.vala b/src/Console/AppConsole.vala index 3524f6c..d70c8eb 100644 --- a/src/Console/AppConsole.vala +++ b/src/Console/AppConsole.vala @@ -38,7 +38,7 @@ using TeeJee.Misc; public Main App; public const string AppName = "Timeshift"; public const string AppShortName = "timeshift"; -public const string AppVersion = "22.06.5"; +public const string AppVersion = "22.11.1"; public const string AppAuthor = "Tony George"; public const string AppAuthorEmail = "teejeetech@gmail.com"; diff --git a/src/Core/Main.vala b/src/Core/Main.vala index 16b5c7f..9ee386b 100644 --- a/src/Core/Main.vala +++ b/src/Core/Main.vala @@ -170,7 +170,19 @@ public class Main : GLib.Object{ public string encrypted_private_dirs = ""; public bool encrypted_private_warning_shown = false; - + + protected enum QGroupStatus { + UNKNOWN = -1, + DISABLED = 0, + ENABLED = 1 + } + + private QGroupStatus _btrfs_qgroups_enabled_internal = QGroupStatus.UNKNOWN; + public bool btrfs_qgroups_enabled + { + get { return _btrfs_qgroups_enabled_internal == QGroupStatus.ENABLED; } + } + public Main(string[] args, bool gui_mode){ this.mount_point_app = "/run/timeshift/%lld".printf(Posix.getpid()); @@ -3980,6 +3992,14 @@ public class Main : GLib.Object{ return; } + if (_btrfs_qgroups_enabled_internal != QGroupStatus.DISABLED) { + bool success = query_subvolume_quotas(); + + if (_btrfs_qgroups_enabled_internal == QGroupStatus.UNKNOWN) { + _btrfs_qgroups_enabled_internal = success ? QGroupStatus.ENABLED : QGroupStatus.DISABLED; + } + } + thread_subvol_info_success = true; thread_subvol_info_running = false; return; @@ -4054,6 +4074,118 @@ public class Main : GLib.Object{ return true; } + public bool query_subvolume_quotas(){ + bool ok = query_subvolume_quota("@"); + if (repo.device.uuid != repo.device_home.uuid){ + ok = ok && query_subvolume_quota("@home"); + } + return ok; + } + + public bool query_subvolume_quota(string subvol_name){ + log_debug("query_subvolume_quota():%s".printf(subvol_name)); + + string cmd = ""; + string std_out; + string std_err; + int ret_val; + + string options = use_option_raw ? "--raw" : ""; + + cmd = "btrfs qgroup show %s '%s'".printf(options, repo.mount_paths[subvol_name]); + log_debug(cmd); + ret_val = exec_sync(cmd, out std_out, out std_err); + + if (ret_val != 0){ + if (use_option_raw){ + use_option_raw = false; + + // try again without --raw option + cmd = "btrfs qgroup show '%s'".printf(repo.mount_paths[subvol_name]); + log_debug(cmd); + ret_val = exec_sync(cmd, out std_out, out std_err); + } + + if (ret_val != 0){ + if (std_err.contains("not enabled")) { + log_msg("btrfs: Quotas are not enabled"); + return false; + } + log_error (std_err); + log_error(_("btrfs returned an error") + ": %d".printf(ret_val)); + log_error(_("Failed to query subvolume quota")); + return false; + } + } + + /* Sample Output: + * + qgroupid rfer excl + -------- ---- ---- + 0/5 106496 106496 + 0/257 3825262592 557056 + 0/258 12689408 49152 + * */ + + foreach(string line in std_out.split("\n")){ + if (line == null) { continue; } + + string[] parts = line.split(" "); + if (parts.length < 3) { continue; } + if (parts[0].split("/").length < 2) { continue; } + + int subvol_id = int.parse(parts[0].split("/")[1]); + + Subvolume subvol = null; + + if ((sys_subvolumes.size > 0) && (sys_subvolumes["@"].id == subvol_id)){ + + subvol = sys_subvolumes["@"]; + } + else if ((sys_subvolumes.size > 0) + && sys_subvolumes.has_key("@home") + && (sys_subvolumes["@home"].id == subvol_id)){ + + subvol = sys_subvolumes["@home"]; + } + else { + foreach(var bak in repo.snapshots){ + foreach(var sub in bak.subvolumes.values){ + if (sub.id == subvol_id){ + subvol = sub; + } + } + } + } + + if (subvol != null){ + int part_num = -1; + foreach(string part in parts){ + if (part.strip().length > 0){ + part_num ++; + switch (part_num){ + case 1: + subvol.total_bytes = int64.parse(part); + break; + case 2: + subvol.unshared_bytes = int64.parse(part); + break; + default: + //ignore + break; + } + } + } + } + } + + foreach(var bak in repo.snapshots){ + bak.update_control_file(); + } + + return true; + } + // cron jobs public void cron_job_update(){ diff --git a/src/Core/Subvolume.vala b/src/Core/Subvolume.vala index 3331c50..86ca531 100644 --- a/src/Core/Subvolume.vala +++ b/src/Core/Subvolume.vala @@ -166,6 +166,22 @@ public class Subvolume : GLib.Object{ log_msg("%s: %s (Id:%ld)\n".printf(_("Deleted subvolume"), name, id)); + if (App.btrfs_qgroups_enabled) { + if ((id > 0) && (repo != null)){ + log_msg("%s: 0/%ld".printf(_("Destroying qgroup"), id)); + + cmd = "btrfs qgroup destroy 0/%ld '%s'".printf(id, repo.mount_paths[name]); + log_debug(cmd); + ret_val = exec_sync(cmd, out std_out, out std_err); + if (ret_val != 0){ + log_error(_("Failed to destroy qgroup") + ": '0/%ld'".printf(id)); + return false; + } + + log_msg("%s: 0/%ld\n".printf(_("Destroyed qgroup"), id)); + } + } + return true; } diff --git a/src/Gtk/AppGtk.vala b/src/Gtk/AppGtk.vala index e7f9b5d..a0c380f 100644 --- a/src/Gtk/AppGtk.vala +++ b/src/Gtk/AppGtk.vala @@ -37,7 +37,7 @@ using TeeJee.Misc; public Main App; public const string AppName = "Timeshift"; public const string AppShortName = "timeshift"; -public const string AppVersion = "22.06.5"; +public const string AppVersion = "22.11.1"; public const string AppAuthor = "Tony George"; public const string AppAuthorEmail = "teejeetech@gmail.com"; diff --git a/src/Gtk/SnapshotListBox.vala b/src/Gtk/SnapshotListBox.vala index 8bf985b..25956c2 100644 --- a/src/Gtk/SnapshotListBox.vala +++ b/src/Gtk/SnapshotListBox.vala @@ -37,6 +37,8 @@ class SnapshotListBox : Gtk.Box{ public Gtk.TreeView treeview; private Gtk.TreeViewColumn col_date; private Gtk.TreeViewColumn col_tags; + private Gtk.TreeViewColumn col_size; + private Gtk.TreeViewColumn col_unshared; private Gtk.TreeViewColumn col_system; private Gtk.TreeViewColumn col_desc; private int treeview_sort_column_index = 0; @@ -167,6 +169,56 @@ class SnapshotListBox : Gtk.Box{ refresh(); }); + //col_size + var col = new TreeViewColumn(); + col.title = _("Size"); + col.resizable = true; + col.min_width = 80; + col.clickable = true; + var cell_size = new CellRendererText (); + cell_size.ellipsize = Pango.EllipsizeMode.END; + cell_size.xalign = (float) 1.0; + col.pack_start (cell_size, false); + col.set_cell_data_func (cell_size, cell_size_render); + col_size = col; + treeview.append_column(col_size); + + col_size.clicked.connect(() => { + if(treeview_sort_column_index == 2){ + treeview_sort_column_desc = !treeview_sort_column_desc; + } + else{ + treeview_sort_column_index = 2; + treeview_sort_column_desc = false; + } + refresh(); + }); + + //col_unshared + col = new TreeViewColumn(); + col.title = _("Unshared"); + col.resizable = true; + col.min_width = 80; + col.clickable = true; + var cell_unshared = new CellRendererText (); + cell_unshared.ellipsize = Pango.EllipsizeMode.END; + cell_unshared.xalign = (float) 1.0; + col.pack_start (cell_unshared, false); + col.set_cell_data_func (cell_unshared, cell_unshared_render); + col_unshared = col; + treeview.append_column(col_unshared); + + col_unshared.clicked.connect(() => { + if(treeview_sort_column_index == 2){ + treeview_sort_column_desc = !treeview_sort_column_desc; + } + else{ + treeview_sort_column_index = 2; + treeview_sort_column_desc = false; + } + refresh(); + }); + //cell_desc col_desc = new TreeViewColumn(); col_desc.title = _("Comments (click to edit)"); @@ -568,6 +620,9 @@ class SnapshotListBox : Gtk.Box{ model.set (iter, 0, bak); } + col_size.visible = App.btrfs_qgroups_enabled; + col_unshared.visible = App.btrfs_qgroups_enabled; + treeview.set_model (model); treeview.columns_autosize (); }