Bug#698640: gvfs-bin: gfvs-trash can not trash on fs that is bind mounted

Christian Pernegger pernegger at gmail.com
Tue Aug 8 00:09:45 BST 2023


Hi,

thanks for the detailed answer, I appreciate it!

I'm well aware this isn't a solution, but it's better than nothing,
and, more importantly, it restores the behaviour I am used to, warts
and all. I didn't mean to imply, apply this patch to the package, it's
just that while scratching my head and trying to find out why my trash
of all things wasn't working any more, I came across many confused
users. Maybe some will find this, and decide it's worth the trade-off.
It's not like Joe Newbie can patch and rebuild a package. ;)

I almost dare not admit it here, but I've been using Ubuntu 18.04 on
btrfs from about the day it released until last week. Nautilus, eog,
and friends would trash files just fine back in 2018, pretty much
wherever they were, and undo would work. I couldn't swear to the fact
that all the files in weird locations would show up in the trash GUI
now, maybe not, but, I mean, find the .Trash-1000 directory, find the
file. Ideal? No. Not standardised? Probably. But it's how it worked
for the longest time, for lots of users, and that makes it a de-facto
standard in my book.

Contrast with the new way, where trash simply won't work at all, for
files that are, from the user's perspective, seamlessly part of $HOME.
I mean, I know that this directory is actually the mount point for my
data drive, that that directory is really a subvolume--I set it up
that way, didn't I?--but it's not something I think about *using* the
box day-to-day. It shouldn't matter one bit.
Now every attempt to delete something throws up a warning dialogue.
Have you tried to go through a few hundred photos, weed out the bad
ones, like this? You know what this does? Train users to ignore
warning dialogues and/or delete everything with shift-del by default
...
That's way worse than not having a copy for "undelete" at all, even if
you have to hunt for it.

Never ever change how a piece of software behaves without a good
*practical* reason, never ever take functionality away without a
better replacement ready to go. Data loss bugs and/or critical
security vulnerabilities are a reason where it might be necessary to
yank functionality away as a last resort, the functionality being a
hack, the functionality not working perfectly in some circumstances,
the functionality not being in the spec, not so much.

Or look at it from the outside. A graphical desktop. Without working
trash. In 2023. It's absurd when you think about it.

As for deriving a trash path from any path deterministically, I only
see two options:
- Use one central trash directory (per user) for everything and live
with the fact that you'll have to do an expensive move sometimes
instead of a rename, deal with ENOSPC issues, etc.
- Prioritise that the trash should always be reachable via rename.

In the latter case I'd walk up the directory tree component by
component, starting from the directory the file to be deleted is in,
and check:
* whether rename would work here. If not, we've crossed (V)fs
boundaries; abort traversal and return the list of candidate dirs for
further consideration. In other words, if rename() can complain/fail
when trying to rename across subvolume boundaries or bind mounts,
there must be a way to test whether file-to-be-deleted and
prospective-trash-location are rename-compatible.
* whether .Trash-$UID exists. If yes, abort traversal. If it is a
directory and the user has the necessary permissions, designate it the
trash, otherwise disable trash (for the current operation).
* Add the current dir to the list of candidates and continue with the
parent dir.
---
* Now walk back down the list of candidate dirs, topmost to
bottommost. The first candidate dir that will let us create a
.Trash-$UID dir wins. If all candidates fail, disable trash (for the
current operation).

This should almost always give you a valid trash dir, considering the
file's directory (in which you can delete) will usually be a valid
candidate. No need to know about mount points or subvokumes at all. It
also allows some control over the behaviour. Don't want deleted files
from your project cluttering up the main trash, so you can delete it
separately? Make a .Trash-$UID dir in the project's root folder. Want
to disable trash somewhere? Make a trash dir and write-protect it.
Bonus: Will work on mounted network shares as-is.

Finding the trashes again ... Nothing to it but running something akin
to find / -type d -name .Trash-$UID whenever something or someone
wants to access the global trash. It's not that expensive, especially
if you cache the result.

This is the first thing that came to mind, so it's probably asinine,
but you did ask.

The point is, as a regular user, doing regular user thing with files
via the GUI, I expect trash to work everywhere I have write access to
in the first place. There's simply no need to exclude system fs paths
(except /proc and /sys and so on), because users can't write there,
and if they can, there's a reason. I'd disable it outright for root,
though. And there's certainly no reason to disallow subvolumes, bind
mounts, etc.

Oh, and before I forget, mounted directories do NOT work by default,
either. The first change would allow me to trash in $HOME/mounted-disk
(but not in $HOME/@subvol), both of them together enabled the latter
as well.

Cheers,
Christian


 Mo., 7. Aug. 2023 um 11:37 Uhr schrieb Simon McVittie <smcv at debian.org>:
>
> On Sat, 05 Aug 2023 at 06:30:01 +0200, Christian Pernegger wrote:
> > Funnily enough this used to work, in fact it still does work if you
> > remove the checks. It'll find a location to trash to (in the root of
> > the mount/subvolume) just fine; and if it doesn't it errors out
> > elsewhere. The rationale for introducing them in the first place
> > escapes me, to put it politely.
>
> If you find yourself asking "why was this more obvious thing not done?"
> then the answer is often "because there are cases where it doesn't work,
> which might not apply to you, but do affect others".
>
> The major reason not to allow trashing on a particular filesystem is
> that it should not be possible to trash files unless it will also be
> possible to undo the trash operation or empty the trash (possibly from
> another process, or after a reboot or other state-losing operation).
> Trashing the file is the easy part, being able to find it in the trash
> directory afterwards is harder.
>
> Being able to untrash or empty the trash requires file managers to be
> able to enumerate and monitor all the possible trash directories on
> the system. Otherwise, you can trash a file (moving it into a trash
> directory), and then not have the ability to either untrash it again,
> or delete it permanently: it'll continue to take up space, but you can't
> get it back without knowing how to find it via a shell.
>
> In GLib, sending files to trash is part of GLib itself, while retrieving
> files from trash (either to untrash or to delete them permanently) is
> part of gvfs, and both sides need to work together in a coordinated
> way. If GLib can trash a file to a directory that gvfs won't find,
> then we have a problem.
>
> (It is entirely possible that some of the measures taken to avoid trashing
> files into un-listable locations are broader than they strictly need to
> be, because that's a less serious bug than the other way around.)
>
> The freedesktop.org trash specification defines how to find the trash
> directory in $HOME, and in a mounted drive (for example removable media),
> so those are fine: it's straightforward to list mounted drives, and gvfs
> can look for a possible trash directory inside each one.
>
> However, the trash specification *doesn't* define how to find a trash
> directory in a bind-mounted directory or in a btrfs subvolume. At first
> glance a reasonable interpretation would be to treat them like mounted
> drives, but that has design issues:
>
> * It isn't possible for an unprivileged process to enumerate all of the
>   btrfs subvolumes on the system (if they just exist inside a larger
>   mounted directory, rather than being a separate mount point), which
>   means that if each subvolume had its own trash directory, gvfs wouldn't
>   be able to show you a merged list of trash for untrashing or permanent
>   deletion: it can't know all the subvolumes that exist, and each one
>   *might* have its own trash directory.
>
> * Bind-mounts have the opposite problem: if a file is below a bind mount
>   within the same filesystem (same device number), the usual Unix way
>   to locate the root of a drive (go upwards through the hierarchy until
>   you find a different device number) will not stop at the root of the
>   bind-mount, because the device number remains the same across the
>   mount point.
>
> If you have a design in mind for how to do better, and you have checked
> that design to make sure it will never result in files being trashed
> to a directory that trash implementations like the one in gvfs cannot
> subsequently list, then please contribute merge requests upstream. It is
> likely that any change to how GLib's trash implementation works would
> need to go into gvfs *before* it goes into GLib, because gvfs needs to
> be able to find a trash directory before it becomes safe for GLib to
> move files into it.
>
>     smcv



More information about the pkg-gnome-maintainers mailing list