Skip to content

fix(file-manager): keep exclusive shares in /mnt/user space (OS-475)#2679

Draft
elibosley wants to merge 7 commits into
masterfrom
os-filemanager-exclusive-user-link
Draft

fix(file-manager): keep exclusive shares in /mnt/user space (OS-475)#2679
elibosley wants to merge 7 commits into
masterfrom
os-filemanager-exclusive-user-link

Conversation

@elibosley

@elibosley elibosley commented Jun 23, 2026

Copy link
Copy Markdown
Member

Summary

Exclusive shares now stay in /mnt/user throughout both File Manager browsing and copy/move destination selection, preventing nested destinations from being rewritten to raw backing-storage paths and rejected as invalid targets.

Why This Exists

Opening an exclusive share originally ran the requested path through realpath(), which follows the exclusive-share symlink from /mnt/user/<share> to /mnt/<pool|disk>/<share>. The first fix preserved the path in the main File Manager listing, but QA found the destination picker had an independent realpath() boundary in FileTree.php. Selecting a folder below an exclusive share therefore still changed the target path and caused an Invalid target error.

Resolution

Preserve a lexically canonicalized /mnt/user, /mnt/user0, or /mnt/rootshare path after confirming the requested directory exists with realpath(). The same rule now applies in the main browser and the destination tree. Disk, pool, boot, and Unassigned Devices paths retain their existing resolved behavior.

The PR also improves the File Manager launcher affordance with a labeled folder icon, URL-encodes browse destinations containing reserved characters, and clarifies pool-status navigation.

Reviewer Considerations

  • emhttp/webGui is a hard-linked mirror of plugins/dynamix; the tracked files under plugins/dynamix cover both runtime paths.
  • Review include/FileTree.php together with the existing validdir() changes: both boundaries must preserve the requested user-share namespace to avoid reintroducing mixed /mnt/user and /mnt/<disk> paths.
  • The picker still requires realpath() and is_dir() success before preserving a user-share path, so nonexistent destinations are not accepted.
  • Browse-link paths use rawurlencode() because they are query parameter values; HTML escaping does not protect reserved URL characters such as & and #.

Behavior Changes

  • Browsing an exclusive share stays under /mnt/user/<share>.
  • Selecting the root or any nested folder of an exclusive share as a copy/move destination keeps /mnt/user/<share>/... in the target field.
  • Raw disk/pool browsing remains under /mnt/<disk|pool>/....
  • Share, disk, boot, pool, and generic device browse links support names containing reserved URL characters.
  • File Manager launch links use a closed folder icon that opens on hover and includes an Open in File Manager tooltip.
  • Pool health remains plain status text with a dedicated details affordance.

Implementation Summary

  • Preserve validated user-share paths in Browse.page and include/Browse.php.
  • Preserve validated user-share paths in the File Tree connector used by destination pickers.
  • URL-encode File Manager browse query parameters across share, disk, boot, pool, and generic device links.
  • Update browse-link and pool-status affordances across disk, share, and device views.

Verification

  • php -l on FileTree.php, ShareList.php, DiskList.php, and nchan/device_list — pass.
  • git diff --check — pass.
  • PHP 8.5 container fixture with /mnt/user/share -> /mnt/cache/share:
    • nested exclusive-share folder stays /mnt/user/share/folder — pass;
    • raw disk folder remains /mnt/disk1/data — pass;
    • missing folder remains invalid — pass;
    • fixture confirms unpatched realpath() resolves the nested user path to /mnt/cache/share/folder — pass.
  • Special-character query round-trip for My & Share#1 — pass.
  • PR plugin and GitHub checks are rebuilt on each push.

Risk

Low. The path-preservation change is confined to File Tree presentation for existing user-share directories; filesystem existence is still established from the symlink-resolved path, and non-user-share paths retain the prior behavior. Browse-link encoding uses the standard query-value encoding already used elsewhere in these views.

Linear

  • OS-475

Summary by CodeRabbit

  • New Features
    • Added clearer “Open in File Manager” links with folder icons across disk, share, device, and pool views.
    • Enhanced pool status display with separate controls for checking status vs viewing pool details.
  • Bug Fixes
    • Improved directory path validation and navigation for symlinked “exclusive shares,” strengthening allowlist behavior around /mnt and /boot.
  • Style
    • Updated link markup and visual styling (muted icons/text by default, brighter hover states) for a cleaner UI.

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

validdir() and FileTree root resolution now preserve canonical exclusive-share paths while enforcing mount allowlists. File Manager links use encoded paths and labeled folder controls, and pool status output separates status, warning, and details navigation with matching styles.

Changes

File Manager path and navigation

Layer / File(s) Summary
Exclusive-share path resolution
emhttp/plugins/dynamix/Browse.page, emhttp/plugins/dynamix/include/Browse.php, emhttp/plugins/dynamix/include/FileTree.php
Path validation and tree-root initialization now canonicalize supported /mnt/user-style paths, reject invalid roots, and retain resolved paths otherwise.
File Manager link presentation
emhttp/plugins/dynamix/include/DiskList.php, emhttp/plugins/dynamix/include/ShareList.php, emhttp/plugins/dynamix/nchan/device_list, emhttp/plugins/dynamix/styles/default-base.css
Disk, share, device, boot, and pool controls use URL-encoded paths, folder icons, visible “Open in File Manager” labels, and updated hover styling.
Pool status navigation
emhttp/plugins/dynamix/nchan/device_list
Pool status is rendered separately from warning and pool-details links, with distinct labels and navigation behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Request
  participant PathHandlers
  participant Filesystem
  Request->>PathHandlers: provide requested directory
  PathHandlers->>Filesystem: resolve with realpath()
  Filesystem-->>PathHandlers: resolved filesystem path
  PathHandlers->>PathHandlers: canonicalize /mnt/user lexical path
  PathHandlers-->>Request: permitted canonical or resolved path
Loading

Poem

A rabbit hops through paths so neat,
Where user shares and mounts now meet.
Folder links bloom, icons shine,
Pool warnings pause beside the line.
“Open the files!” the bunny sings,
While tidy states grow helpful wings.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main fix: keeping exclusive shares in /mnt/user space for File Manager browsing.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch os-filemanager-exclusive-user-link

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown

🔧 PR Test Plugin Available

A test plugin has been generated for this PR that includes the modified files.

Version: 2026.07.14.2035
Build: View Workflow Run

📥 Installation Instructions:

Install via Unraid Web UI:

  1. Go to Plugins → Install Plugin
  2. Copy and paste this URL:
https://preview.dl.unraid.net/pr-plugins/pr-2679/webgui-pr-2679.plg
  1. Click Install

Alternative: Direct Download

⚠️ Important Notes:

  • Testing only: This plugin is for testing PR changes
  • Backup included: Original files are automatically backed up
  • Easy removal: Files are restored when plugin is removed
  • Conflicts: Remove this plugin before installing production updates
  • Post-merge behavior: This preview stays available after merge until preview storage expires or it is manually cleaned up

📝 Modified Files:

Click to expand file list
emhttp/plugins/dynamix/Browse.page
emhttp/plugins/dynamix/include/Browse.php
emhttp/plugins/dynamix/include/DiskList.php
emhttp/plugins/dynamix/include/FileTree.php
emhttp/plugins/dynamix/include/ShareList.php
emhttp/plugins/dynamix/nchan/device_list
emhttp/plugins/dynamix/styles/default-base.css

🔄 To Remove:

Navigate to Plugins → Installed Plugins and remove webgui-pr-2679, or run:

plugin remove webgui-pr-2679

🤖 This comment is automatically generated and will be updated with each new push to this PR.

Opening an exclusive share in the File Manager silently switched the
whole view - and every folder/row link - from /mnt/user/<share> to the
raw /mnt/<disk>/<share> path. Cause: validdir() ran the requested dir
through realpath(), which follows the symlink that backs an exclusive
share (/mnt/user/<share> -> /mnt/<pool|disk>/<share>).

validdir() now still validates with realpath() (the resolved path must
live under /mnt or /boot, so traversal is still blocked), but re-anchors
to a lexically-canonicalized /mnt/(user0?|rootshare) path when that is
what was requested. FUSE is bypassed for exclusive shares, so this is
the same data at native speed - just shown under the path the user
clicked. Applied to both Browse.page and include/Browse.php.

Also swap the open-in-File-Manager launcher icon from icon-u-tab (a
square-with-arrow / open-in-tab glyph that doesn't say what it does) to
fa fa-folder-open across DiskList, ShareList and device_list, matching
the folder-open icon already used elsewhere.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@elibosley
elibosley force-pushed the os-filemanager-exclusive-user-link branch from 09b0b6f to f86397e Compare June 23, 2026 22:52
elibosley and others added 4 commits June 23, 2026 18:59
…e link

The folder-open browse icon sits immediately left of the device/share
name link and inherited the same link blue, so the two read as a single
element - you couldn't tell the icon was a separate clickable action.

Mute the browse icon to --alt-text-color (brightening to the link color
on hover to keep the clickable affordance) and add a right-margin gap
before the name. Applied once on the shared a.view rule, so DiskList,
ShareList and the device list all get it; the empty .view placeholders
take the same slot, so column alignment is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Use a closed folder (fa-folder-o) at rest for the browse links and swap
to the open variant on hover, so the icon visibly previews the "open this
location" action. Done via a single a.view:hover content override in CSS;
the icon class changes to fa-folder-o in DiskList, ShareList and the
device list.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The ZFS/btrfs pool health text linked to the pool's details section but
looked like a stray blue label - same color as the device-name link next
to it - so it wasn't clear it was clickable or what it did. Give it a
dedicated .pool-status style (muted text, brightens + underlines on
hover) and a trailing chevron to mark it navigable, keeping the existing
"View pool details" tooltip. Applies to the healthy and problem states.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Address the affordance gaps with comprehension cues, not just styling:

- Browse folder links now carry Unraid's standard a.info tooltip ("Open
  in File Manager") instead of the slow native title=, so hovering says
  what the icon does. Combined with the closed->open swap and hover
  color, the action is now self-explanatory. Applied on DiskList,
  ShareList and the device list.

- Pool health (ONLINE) is no longer a hyperlink - a clickable status word
  is an odd pattern. It's now plain status text, with a small info-circle
  beside it as the only clickable element (a.info tooltip "View pool
  details") linking to the pool summary. The existing warning icon for
  DEGRADED/ERRORS states gets the same styled tooltip.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@elibosley elibosley changed the title fix(file-manager): keep exclusive shares in /mnt/user space fix(file-manager): keep exclusive shares in /mnt/user space (OS-475) Jul 8, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@emhttp/plugins/dynamix/include/ShareList.php`:
- Line 269: Replace htmlspecialchars with rawurlencode for URL path values in
ShareList.php lines 269-269 ($name), device_list lines 510-514 ($dir),
device_list lines 1399-1407 ($bootBrowseUrl and $poolBrowseUrl), and
DiskList.php lines 113-113 ($name), preserving the surrounding link generation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 36295cec-49f1-41fa-b630-cbe787f810fb

📥 Commits

Reviewing files that changed from the base of the PR and between bccf49e and 5c20006.

📒 Files selected for processing (7)
  • emhttp/plugins/dynamix/Browse.page
  • emhttp/plugins/dynamix/include/Browse.php
  • emhttp/plugins/dynamix/include/DiskList.php
  • emhttp/plugins/dynamix/include/FileTree.php
  • emhttp/plugins/dynamix/include/ShareList.php
  • emhttp/plugins/dynamix/nchan/device_list
  • emhttp/plugins/dynamix/styles/default-base.css

Comment thread emhttp/plugins/dynamix/include/ShareList.php Outdated
@elibosley

Copy link
Copy Markdown
Member Author

QA follow-up addressed in 5c20006bf.

The destination tree had its own realpath() call, so expanding a nested folder under an exclusive share still emitted /mnt/<pool|disk>/... even though the main File Manager view had been fixed. FileTree.php now preserves the validated /mnt/user/... namespace for both the tree root and expanded directories.

Verification:

  • simulated /mnt/user/share -> /mnt/cache/share fixture keeps a nested selection at /mnt/user/share/folder;
  • raw disk paths remain unchanged;
  • missing folders remain invalid;
  • PHP syntax, CodeQL, CodeRabbit, and PR plugin build all pass.

The current retest plugin is 2026.07.14.2035 from the existing PR plugin URL. Please rerun the reported copy/file and copy/folder cases against an exclusive share root and a nested folder.

@unraid-bot unraid-bot added the 7.4.0 Approved for release 7.4.0 (auto-managed by notification-worker) label Jul 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

7.4.0 Approved for release 7.4.0 (auto-managed by notification-worker)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants