Incorrect Trash behaviour for the whole file system if $HOME is on a bind mount
Submitted by Wouter Bolsterlee (uws)
Assigned to Allison (desrt)
Link to original bug (#637800)
Description
I'm having problems with trashing files outside my home. The issue I experience is that I cannot trash any files in /storage/Music/, even though I have a /storage/.Trash/ directory with permissions 1777 (world writable + sticky bit, just like /tmp), which is exactly what the XDG Trash Specification prescribes.
I investigated this a bit and seems that my /home being a bind mount is the culprit here. This seems strange at first, since the problem I have has to do with trashing files on a "regular" mount outside my home dir, i.e. not residing on the bind mount (and also outside $HOME).
My mount setup is like this:
/dev/mapper/vg-storage on /storage type ext3 (rw,user_xattr,acl) /storage/home on /home type none (rw,bind) /storage/srv on /srv type none (rw,bind)
In other words: I have a large logical volume named "storage" (which is my shiny big disk) mounted at /storage. Inside /storage I have directories like /storage/Music and /storage/Backups (both quite large). Since this is a desktop machine with a single big disk and multiple user accounts, I don't want to have the "file storage" and the /home directory (and also /srv, but that doesn't matter) on different file systems, since that means I should tell beforehand how big those would be. The solution is elegant and obvious: mount the big disk at /storage and add a bind mount for /home pointing to the directory /storage/home (on the big disk).
I hope you agree my setup is not "esoteric" or "totally unusual" - to the best of my knowledge this a clean and neat way to solve a standard "problem" for shared home machines.
Back to the trash problem. This is what happens:
$ cd /storage/Music /storage/Music $ touch empty /storage/Music $ gvfs-trash empty Error trashing file: Unable to move file to the wastebasket: Invalid cross-device link
Strace(1) tells me gvfs-trash tries to rename(2) the file, which doesn't succeed because it returns EXDEV. The rename(2) man page states:
EXDEV oldpath and newpath are not on the same mounted file system. (Linux permits a file system to be mounted at multiple points, but rename() does not work across different mount points, even if the same file system is mounted on both.)
This is quite strange, so let's look at the strace(1) output:
rename("/storage/Music/empty", "/home/uws/.local/share/Trash/files/empty.21") = -1 EXDEV (Invalid cross-device link) write(2, "Error trashing file: Unable to m"..., 87Error trashing file: Unable to move file to the wastebasket: Invalid cross-device link
This is strange. Why does gio try to move the file inside /storage/Music into the trash directory inside my home directory while /storage/.Trash/ exists as per the XDG trash spec? Strace(1) also shows that /storage/.Trash/ is not even tried: no stat or open calls to be found in the output.
I looked into the code for g_local_file_trash() in gio/glocalfile.c to discover this test that seems to cause the problem I'm seeing:
if (file_stat.st_dev == home_stat.st_dev)
{
...
trashdir = g_build_filename (g_get_user_data_dir (), "Trash", NULL);
...
}
else
{
... the toplevel/.Trash/
uid and toplevel/.Trash-
uid/ logic is here
}
The expected behaviour is that the file ends up in the /storage/.Trash/$uid/ directory. However, since my home dir is a bind mount inside /storage, and bind mounts and the "real" mount share the GStatBuf.st_dev value, GIO incorrectly concludes this file should be trashed inside the Trash in my home dir (consequently trying rename(2), which fails with EXDEV), even though a completely valid mount-local Trash directory exists!
I'm not sure why things are the way they are right now. Is this really the intended behaviour? Or is the st_dev check purely intended to find out whether the file is inside the user's home directory (as opposed to on a removable disk, for instance)? In that case, something like
if (g_file_has_prefix(file_to_trash, home_dir)) { ... }
would be more appropriate, imo. This would mean only files inside the user's home dir end up in the home dir Trash, which seems intended behaviour to me. In normal setups users cannot delete files from random directories outside their home dir (e.g. from other users' home dirs), checking for the same st_dev (for e.g. the /home file system) seems a bit overkill to me. The device-specific .Trash/ support handles all other cases (storage mounts, removable disks, and so on) just fine.
Oh, let me conclude with a final notion to be clear and complete. Nautilus shows the trash in /storage/.Trash/$uid just fine if I put some correct files in there by hand (data in files/ and metadata in the info/ directories), so this is purely a "trash this file" problem, not a "my trashed files are invisible" problem like as described in bug #604015.
(Side note: the "gboolean is_homedir_trash" seems superfluous in g_local_file_trash() since it's only assigned to, but never used.)
Version: git master