Repository navigation
Show a file's permissions, and make one's own files executable or not - #103
JaxonWright wants to merge 2 commits into
Conversation
The details pane gains a Permissions row, as ls writes them, read with the rest of the details off the UI thread; it keeps its place while they're read, and isn't shown in the trash. The menu on one's own files gains Make Executable, or Make Not Executable when they all run already: it lets whoever may read a file run it, so a private file stays private, and takes running away from everyone. Folders, whose x means something else, links, and other people's files don't get it. The pane reads the item again when its permissions change, since the folder looks the same. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UGcPV5q4WWVxgL57jZ2Vm7
[Medium impact] The changes affect Folio's file details and file-action workflows. Permission changes still need to move off the UI thread before merging. Findings
SummaryAdds a Permissions row and a menu action to make owned files executable or not.
Reviews (2) · Last reviewed commit: "Keep the details' reads off the listing ..." · Reviewed by Greptile |
| void FileOps::setExecutable(const QStringList &paths, bool on) { | ||
| QStringList changed, failures; | ||
| for (const QString &path : paths) { | ||
| struct stat st; | ||
| if (!ownFile(path, &st)) |
There was a problem hiding this comment.
Permission changes freeze the window
setExecutable() changes every selected file directly on the UI thread. On a slow mounted drive, each fchmodat() can wait on the server; a large selection adds those waits together. The window cannot repaint, respond, or cancel until the loop returns. Run the changes through the existing background-job path and report their results afterward.
Knowledge Base Used: File operation backend
There was a problem hiding this comment.
Leaving this one as it is, on purpose. Rename and New Folder also change the disk on the UI thread (FileOps::rename, FileOps::newFolder), and the menu stats the selection on the UI thread as it opens, as it does for Burn and Extract, so on a stalled mount the window has already waited before the change is picked. Moving only the chmod to the background job path would add the busy lane and progress reporting for something that's one syscall per file on a drive that answers. I've noted this in the description so the maintainer can decide.
There was a problem hiding this comment.
That’s fair. The existing UI-thread filesystem operations and menu-time stats make this consistent with the current interaction model, and the added selection size is bounded by the same selection the menu already inspects. I’ll treat synchronous permission changes as an intentional tradeoff, not a review finding.
Tip: You can customize Greptile's behavior for this repo with .greptile/rules.md and .greptile/config.json.
…looked at Moving across folders on a mount that stopped answering left a stuck stat per folder in the shared pool, and once those filled it no folder could be listed: the readings now have four threads of their own. The permissions are changed through the handle they were checked on, so a link put anywhere in the path since can't send the change to another file. And the row is hidden by the folder being the trash, known at once, rather than by the note read later, which showed it for a moment and kept it for what's inside a trashed folder. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UGcPV5q4WWVxgL57jZ2Vm7
You can't tell from Files whether a script will run, or make it run. The usual case is a downloaded
install.sh, or one just written in a project. Today that needs a terminal andchmod +x. This adds a Permissions row to the details pane and a Make Executable entry to the menu.What you'd notice
ls -lwrites them:rw-r--r--,rwxr-xr-x. It's there for any file or folder on a local or mounted drive, but not in the Trash (nor inside a trashed folder), where nothing changes them. The row keeps its place while the permissions are read, so moving through files with the arrows doesn't shift the rows below it.Folders, links and other people's files don't get the entry. A folder's
xmeans something else, and only a file's owner may change its mode.A folder's menu has no entry, and its row shows the folder's own permissions:
How it decides
rw-r--r--becomesrwxr-xr-x, the same aschmod +x. A privaterw-------becomesrwx------, wherechmod +xwould let everyone run it (rwx--x--x).O_PATH | O_NOFOLLOW), checked through that handle, and changed through it (its/proc/self/fdlink, sincefchmod()won't take anO_PATHhandle). A link put anywhere in the path after the menu opened can't send the change to another file, and the new mode comes from the same file that's changed.What changed
Backend::describe()reads the item's mode in its background read and addspermissionsto the details. Until now it skipped the read for anything but pictures, text and trashed items. Now every local item gets onestat, off the UI thread, with the same skip for selections the cursor has already moved past.These reads now run on a pool of four threads of their own, not the shared pool that folders are listed on. Moving across folders on a mount that stopped answering leaves one stuck
statper folder. In the shared pool, those stuck reads eventually stopped any folder from listing, even a local one. Greptile caught this, and I confirmed it on a real window: with a FUSE mount frozen by SIGSTOP, I moved across 20 of its folders and then opened a local folder. (mainand both builds of this PR here also carry Tell the trash from the path, without asking every drive #93's fix, so leaving the dead mount doesn't stall first.)Selecting a file there is a different, older problem: the Opens with row asks for its type on the UI thread (
mimeFor()), so the window waits until the mount answers. That's worth its own PR.FileOpsgainscanSetExecutable,executableandsetExecutable, and amodesChanged(paths)signal. The change itself runs on the UI thread, like Rename and New Folder do; the menu stats the selection there as it opens, as it does for Burn and Extract. The details pane listens for that signal, because a mode change leaves the folder listing as it was, so the pane would otherwise keep showing the old permissions.en.json.The mode isn't added to the folder listing's entries. #26 and #92 are reworking the listing and its memory, and only the selected item needs it.
Not in this PR: undo. The undo log records moves and renames. Picking the entry again puts the file back as it was.
Verification
I drove a real Files window on Hyprland: selected a
rw-r--r--script, opened the menu from the keyboard (Alt+⏎) and picked Make Executable. The file wasrwxr-xr-xon disk, the row saidrwxr-xr-x, and the reopened menu offered Make Not Executable. Picking that put the file back torw-r--r--, and the row followed. A folder's menu has no entry, and its row shows the folder's own permissions. I only drove a single file in the window. The selection rules (one ineligible item hides the entry, mixed states offer Make Executable) are covered by the unit test.Tests (suite 194 → 196, all pass):
fileOpsMakesFilesExecutablecovers 644 to 755 and back, 600 to 700 and back, and a selection that also holds a folder (the folder is left alone, andmodesChangednames only the files). It also refuses a folder, a link, an empty selection and a file you don't own.backendShowsWhoMayReadWriteAndRunreadsrw-r-----for a file andrwxr-xr-tfor a sticky folder, for a PDF, which the old early return would have skipped.🤖 Generated with Claude Code
https://claude.ai/code/session_01UGcPV5q4WWVxgL57jZ2Vm7