Skip to content

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

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

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

Conversation

@elibosley

Copy link
Copy Markdown
Member

Summary

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.

Root cause: validdir() runs the requested dir through realpath(), which follows the symlink that backs an exclusive share (/mnt/user/<share>/mnt/<pool|disk>/<share>). The "Browse" link on the Shares page correctly sends /mnt/user/<share>, but validdir() then rewrote it.

What changed

  • validdir() (in Browse.page and include/Browse.php) still validates with realpath() — the resolved path must live under /mnt or /boot, so path traversal is still blocked — but now re-anchors to a lexically-canonicalized /mnt/(user0?|rootshare) path when that's 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.
  • Icon swap: the open-in-File-Manager launcher changes from icon-u-tab (a square-with-arrow / open-in-tab glyph that doesn't communicate its action) to fa fa-folder-open across DiskList, ShareList, and nchan/device_list — matching the folder-open icon already used by the flash-backup links.

Notes

  • emhttp/webGui is a symlink to plugins/dynamix, so the single include/Browse.php change covers both paths.
  • The fix is correct whether an exclusive share is implemented as a symlink or a bind mount (for a bind mount, realpath() doesn't change the path and the re-anchor is a no-op).
  • Re-anchoring applies to all /mnt/user paths, not only exclusive ones — /mnt/user is the correct display path for the File Manager regardless, and it matches the link the user clicked.

Testing

  • php -l clean on all five changed files.
  • Standalone test of the new validdir() against a simulated exclusive-share symlink:
    • exclusive share → stays /mnt/user/media
    • normal user dir → /mnt/user/docs
    • raw /mnt/disk1/... disk browsing → unchanged ✅
    • /mnt/user root → unchanged ✅
    • /mnt/user/../../etc traversal → still rejected ✅
  • Not yet exercised on a live emhttp/Unraid server.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@elibosley, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 6 minutes and 23 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses rolling per-developer review limits. Reviews become available again as older review attempts age out of the rolling limit window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 66ec31aa-9ff1-47db-8514-cf113093a379

📥 Commits

Reviewing files that changed from the base of the PR and between bccf49e and 9bbd88d.

📒 Files selected for processing (6)
  • emhttp/plugins/dynamix/Browse.page
  • emhttp/plugins/dynamix/include/Browse.php
  • 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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch os-filemanager-exclusive-user-link

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.06.23.2320
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/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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant