| 731 | } |
| 732 | |
| 733 | void View::onPastePressed() { |
| 734 | auto clipboard = state->getClipboard(); |
| 735 | if (!clipboard.has_value()) return; |
| 736 | |
| 737 | std::string src = clipboard->first; |
| 738 | bool is_cut = clipboard->second; |
| 739 | std::string entry_name = file::getLastPathSegment(src); |
| 740 | std::string dst = file::getChildPath(state->getCurrentPath(), entry_name); |
| 741 | |
| 742 | // Note: getLock(src) guards the source path; the existence check below is |
| 743 | // against dst, so there is a TOCTOU gap — another writer could create dst |
| 744 | // between this check and the write inside doPaste. Acceptable on a |
| 745 | // single-user embedded device; locking dst instead would be more correct. |
| 746 | if (src == dst) { |
| 747 | LOGGER.info("Paste: source and destination are the same path, skipping"); |
| 748 | return; |
| 749 | } |
| 750 | auto lock = file::getLock(src); |
| 751 | lock->lock(); |
| 752 | |
| 753 | struct stat st; |
| 754 | bool dst_exists = (stat(dst.c_str(), &st) == 0); |
| 755 | lock->unlock(); |
| 756 | |
| 757 | if (dst_exists) { |
| 758 | state->setPendingPasteDst(dst); |
| 759 | state->setPendingAction(State::ActionPaste); |
| 760 | const std::vector<std::string> choices = {"Overwrite", "Cancel"}; |
| 761 | alertdialog::start("File exists", "Overwrite \"" + entry_name + "\"?", choices); |
| 762 | return; |
| 763 | } |
| 764 | |
| 765 | doPaste(src, is_cut, dst); |
| 766 | } |
| 767 | |
| 768 | void View::doPaste(const std::string& src, bool is_cut, const std::string& dst) { |
| 769 | bool success = false; |
no test coverage detected