Skip to content

Commit

Permalink
Fix deleting of directories on Linux
Browse files Browse the repository at this point in the history
Trailing slash of directories was mishandled, and incorrect derived paths
were formed. Stripping the slash fixes this.
  • Loading branch information
maiself committed Feb 20, 2022
1 parent 499eec1 commit dfdc1c1
Showing 1 changed file with 20 additions and 19 deletions.
39 changes: 20 additions & 19 deletions platform/linuxbsd/os_linuxbsd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -398,9 +398,11 @@ static String get_mountpoint(const String &p_path) {
}

Error OS_LinuxBSD::move_to_trash(const String &p_path) {
String path = p_path.rstrip("/"); // Strip trailing slash when path points to a directory

int err_code;
List<String> args;
args.push_back(p_path);
args.push_back(path);
args.push_front("trash"); // The command is `gio trash <file_name>` so we need to add it to args.
Error result = execute("gio", args, nullptr, &err_code); // For GNOME based machines.
if (result == OK && !err_code) {
Expand Down Expand Up @@ -430,14 +432,14 @@ Error OS_LinuxBSD::move_to_trash(const String &p_path) {

// If the commands `kioclient5`, `gio` or `gvfs-trash` don't exist on the system we do it manually.
String trash_path = "";
String mnt = get_mountpoint(p_path);
String mnt = get_mountpoint(path);

// If there is a directory "[Mountpoint]/.Trash-[UID], use it as the trash can.
if (!mnt.is_empty()) {
String path(mnt + "/.Trash-" + itos(getuid()));
String mountpoint_trash_path(mnt + "/.Trash-" + itos(getuid()));
struct stat s;
if (!stat(path.utf8().get_data(), &s)) {
trash_path = path;
if (!stat(mountpoint_trash_path.utf8().get_data(), &s)) {
trash_path = mountpoint_trash_path;
}
}

Expand Down Expand Up @@ -468,18 +470,15 @@ Error OS_LinuxBSD::move_to_trash(const String &p_path) {
// Issue an error if trash can is not created properly.
ERR_FAIL_COND_V_MSG(err != OK, err, "Could not create the trash path \"" + trash_path + "\"");
err = dir_access->make_dir_recursive(trash_path + "/files");
ERR_FAIL_COND_V_MSG(err != OK, err, "Could not create the trash path \"" + trash_path + "\"/files");
ERR_FAIL_COND_V_MSG(err != OK, err, "Could not create the trash path \"" + trash_path + "/files\"");
err = dir_access->make_dir_recursive(trash_path + "/info");
ERR_FAIL_COND_V_MSG(err != OK, err, "Could not create the trash path \"" + trash_path + "\"/info");
ERR_FAIL_COND_V_MSG(err != OK, err, "Could not create the trash path \"" + trash_path + "/info\"");
}

// The trash can is successfully created, now we check that we don't exceed our file name length limit.
// If the file name is too long trim it so we can add the identifying number and ".trashinfo".
// Assumes that the file name length limit is 255 characters.
String file_name = p_path.get_file();
if (file_name.length() == 0) {
file_name = p_path.get_base_dir().get_file();
}
String file_name = path.get_file();
if (file_name.length() > 240) {
file_name = file_name.substr(0, file_name.length() - 15);
}
Expand All @@ -502,41 +501,43 @@ Error OS_LinuxBSD::move_to_trash(const String &p_path) {
}
file_name = fn;

String renamed_path = path.get_base_dir() + "/" + file_name;

// Generates the .trashinfo file
OS::Date date = OS::get_singleton()->get_date(false);
OS::Time time = OS::get_singleton()->get_time(false);
String timestamp = vformat("%04d-%02d-%02dT%02d:%02d:", date.year, (int)date.month, date.day, time.hour, time.minute);
timestamp = vformat("%s%02d", timestamp, time.second); // vformat only supports up to 6 arguments.
String trash_info = "[Trash Info]\nPath=" + p_path.uri_encode() + "\nDeletionDate=" + timestamp + "\n";
String trash_info = "[Trash Info]\nPath=" + path.uri_encode() + "\nDeletionDate=" + timestamp + "\n";
{
Error err;
FileAccessRef file = FileAccess::open(trash_path + "/info/" + file_name + ".trashinfo", FileAccess::WRITE, &err);
ERR_FAIL_COND_V_MSG(err != OK, err, "Can't create trashinfo file:" + trash_path + "/info/" + file_name + ".trashinfo");
ERR_FAIL_COND_V_MSG(err != OK, err, "Can't create trashinfo file: \"" + trash_path + "/info/" + file_name + ".trashinfo\"");
file->store_string(trash_info);
file->close();

// Rename our resource before moving it to the trash can.
DirAccessRef dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
err = dir_access->rename(p_path, p_path.get_base_dir() + "/" + file_name);
ERR_FAIL_COND_V_MSG(err != OK, err, "Can't rename file \"" + p_path + "\"");
err = dir_access->rename(path, renamed_path);
ERR_FAIL_COND_V_MSG(err != OK, err, "Can't rename file \"" + path + "\" to \"" + renamed_path + "\"");
}

// Move the given resource to the trash can.
// Do not use DirAccess:rename() because it can't move files across multiple mountpoints.
List<String> mv_args;
mv_args.push_back(p_path.get_base_dir() + "/" + file_name);
mv_args.push_back(renamed_path);
mv_args.push_back(trash_path + "/files");
{
int retval;
Error err = execute("mv", mv_args, nullptr, &retval);

// Issue an error if "mv" failed to move the given resource to the trash can.
if (err != OK || retval != 0) {
ERR_PRINT("move_to_trash: Could not move the resource \"" + p_path + "\" to the trash can \"" + trash_path + "/files\"");
ERR_PRINT("move_to_trash: Could not move the resource \"" + path + "\" to the trash can \"" + trash_path + "/files\"");
DirAccess *dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
err = dir_access->rename(p_path.get_base_dir() + "/" + file_name, p_path);
err = dir_access->rename(renamed_path, path);
memdelete(dir_access);
ERR_FAIL_COND_V_MSG(err != OK, err, "Could not rename " + p_path.get_base_dir() + "/" + file_name + " back to its original name:" + p_path);
ERR_FAIL_COND_V_MSG(err != OK, err, "Could not rename \"" + renamed_path + "\" back to its original name: \"" + path + "\"");
return FAILED;
}
}
Expand Down

0 comments on commit dfdc1c1

Please sign in to comment.