Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions .github/workflows/package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -233,12 +233,14 @@ jobs:
context menu entries work out of the box.

### Latest fix
- **Orphaned column staying on screen after deleting its backing folder in Column View.**
Each column now watches its own backing directory's `NemoFile` "changed" signal; when
that directory is deleted or moved (e.g. to Trash) the column and every column to its
right are torn down. Previously the removal relied on the parent column's directory
monitor or the `NemoView` `remove_file` virtual observing the deletion, which left the
child column visible after its backing folder was removed.
- **Crashes on file delete and rename in Column View.** When a column was closed, its
backing directory's `NemoFile` "changed" signal handler was never disconnected, so a
later directory change (triggered by deleting or renaming a file) invoked the callback
with a freed column struct and crashed (SEGV). The handler is now disconnected in
column teardown.
- **Use-after-free in `column_view_purge_dead_descendant_columns`.** It iterated the
live column list while tearing columns down, so a deleted folder with open descendant
columns could dereference freed memory. It now walks a snapshot and rebuilds once.

### Highlights
- **Column View**: a new view mode (toolbar button "Columns", view menu, and the
Expand Down
13 changes: 13 additions & 0 deletions debian/changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
nemo (1.0.35) unstable; urgency=medium

[ Column View ]
* Fix crashes on file delete and rename in Column View. When a column was
closed, its backing directory's NemoFile "changed" signal handler was
never disconnected, so a later directory change (triggered by deleting
or renaming a file) invoked the callback with a freed column struct and
crashed (SEGV). The handler is now disconnected in column teardown.
* Fix use-after-free in column_view_purge_dead_descendant_columns: it
iterated the live column list while tearing columns down, so a deleted
folder with open descendant columns could dereference freed memory. It
now walks a snapshot and rebuilds once.

nemo (1.0.33) unstable; urgency=medium

[ Column View ]
Expand Down
2 changes: 1 addition & 1 deletion meson.build
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Meson build file

# https://github.com/simpletools-nl/mint-nemo
project('nemo', 'c', version : '1.0.33', meson_version : '>=0.64.0')
project('nemo', 'c', version : '1.0.35', meson_version : '>=0.64.0')

# 1. If the library code has changed at all since last release, then increment revision.
# 2. If any interfaces have been added, then increment current and set revision to 0.
Expand Down
86 changes: 53 additions & 33 deletions src/nemo-column-view.c
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,10 @@ column_view_column_free (NemoColumnViewColumn *col)
col->directory = NULL;
}

if (col->directory_file_changed_id > 0 && col->directory_file != NULL) {
g_signal_handler_disconnect (col->directory_file, col->directory_file_changed_id);
col->directory_file_changed_id = 0;
}
if (col->directory_file != NULL) {
nemo_file_unref (col->directory_file);
col->directory_file = NULL;
Expand Down Expand Up @@ -1135,48 +1139,64 @@ column_view_purge_dead_descendant_columns (NemoColumnView *view,
return;
}

for (l = view->priv->columns; l != NULL; l = l->next) {
NemoColumnViewColumn *col = l->data;
GFile *col_loc;
GFile *file_loc;

if (col->directory_file == NULL ||
!NEMO_IS_FILE (col->directory_file)) {
continue;
}

/* Compare by location, not by NemoFile pointer identity: the
* directory may hand out a different NemoFile instance for a
* file that was just deleted. */
col_loc = nemo_file_get_location (col->directory_file);
file_loc = nemo_file_get_location (file);
/* Iterate over a snapshot: column_view_rebuild_after_column() removes
* and frees columns from view->priv->columns while we run, so walking
* the live list would be a use-after-free. A single rebuild already
* tears down every column to the right of source_index, so once we
* find one match we rebuild and return. */
{
GList *columns_copy = g_list_copy (view->priv->columns);

if (col_loc != NULL && file_loc != NULL &&
g_file_equal (col_loc, file_loc)) {
gint index = g_list_index (view->priv->columns, l);
for (l = columns_copy; l != NULL; l = l->next) {
NemoColumnViewColumn *col = l->data;
GFile *col_loc = NULL;
GFile *file_loc = NULL;
gint index;

/* The removed file is the directory backing this column
* (and, since columns form a parent->child chain, every
* column to its right as well). Tear them all down,
* keeping the source column (which still legitimately
* exists). */
if (index > source_index) {
column_view_rebuild_after_column (view, source_index);
if (col->directory_file == NULL ||
!NEMO_IS_FILE (col->directory_file)) {
continue;
}

{
NemoColumnViewColumn *last;
/* Compare by location, not by NemoFile pointer identity: the
* directory may hand out a different NemoFile instance for a
* file that was just deleted. */
col_loc = nemo_file_get_location (col->directory_file);
file_loc = nemo_file_get_location (file);

last = g_list_last (view->priv->columns)->data;
if (last != NULL && last->location != NULL) {
column_view_update_address_bar (view,
last->location);
if (col_loc != NULL && file_loc != NULL &&
g_file_equal (col_loc, file_loc)) {
index = g_list_index (view->priv->columns, col);

/* The removed file is the directory backing this
* column (and, since columns form a parent->child
* chain, every column to its right as well). Tear
* them all down, keeping the source column (which
* still legitimately exists). */
if (index > source_index) {
g_list_free (columns_copy);
g_clear_object (&col_loc);
g_clear_object (&file_loc);
column_view_rebuild_after_column (view, source_index);

{
NemoColumnViewColumn *last;

last = g_list_last (view->priv->columns)->data;
if (last != NULL && last->location != NULL) {
column_view_update_address_bar (view,
last->location);
}
}
return;
}
}

g_clear_object (&col_loc);
g_clear_object (&file_loc);
}

g_clear_object (&col_loc);
g_clear_object (&file_loc);
g_list_free (columns_copy);
}
}

Expand Down
Loading