Skip to content

utils: share one ls-permission-field parser across NetRocks and adb, and fix its set-id handling - #3533

Open
rctphone wants to merge 1 commit into
elfmz:masterfrom
rctphone:utils-lsmode
Open

rctphone wants to merge 1 commit into
elfmz:masterfrom
rctphone:utils-lsmode

Conversation

@rctphone

@rctphone rctphone commented Aug 9, 2026

Copy link
Copy Markdown

Depends on #3531 — this branch is stacked on it, so the first four commits shown here belong to that PR. Only the last commit, utils: share one ls-permission-field parser…, is new. Happy to rebase once #3531 is resolved, or to drop the adb/ hunk and land the utils/ + NetRocks/ part on its own if you prefer.

Recovering a mode_t from the textual permission field of an ls -l line was implemented three times — NetRocks' ShellParseUtils (SHELL and SCP), NetRocks' vendored FTP LIST parser, and the adb plugin — and all three mishandled the set-id and sticky spellings.

ShellParseUtils tested the wrong character:

if (c[2] == 'x' || c[1] == 's' || c[1] == 't') out |= 1;

c[1] is the write position, which is never s or t, so the execute bit was dropped for every set-id or sticky entry. And S_ISUID / S_ISGID / S_ISVTX were never extracted at all. far2l renders exactly those out of dwUnixMode (panelmix.cpp), so on a remote listing /system/bin/run-as showed up as -rwxr-xr-x and /tmp as drwxrwxrwx.

This moves one implementation into utils/include/UnixModeStr.h and deletes all three copies. The header is enough on its own: the top-level CMakeLists.txt already does include_directories(utils/include), so every target sees it without build changes.

FTPParseLIST was looser still — any character but - sets the bit — so -rwSr--r-- came out executable, and NetWare's d [RWCEAFMS] … form yielded 0777 from the bracketed flags instead of no permission bits:

field expected old FTP rule
-rwSr--r-- 4644 0744 — marked executable
-rwsr-xr-x 4755 0755 — setuid lost
drwxrwxrwt 1777 0777 — sticky lost
d [RWCEAFMS] 0000 0777

Net effect: 78 insertions against 124 deletions across 6 files.

Correctness

Table-tested against the previous NetRocks implementation over 17 field shapes — plain / dir / symlink / char / block / fifo / socket, s and S, t and T, truncated fields, and the d????????? form a shell emits for entries it may not stat. The new parser matches expectations on all 17; the old one was wrong on 6, every one of them set-id or sticky:

field expected old NetRocks
-rwsr-xr-x 0104755 0100655 setuid + exec
-rwxr-sr-x 0102755 0100745 setgid + exec
drwxrwxrwt 0041777 0040776 sticky + exec (/tmp)
-rwSr--r-- 0104644 0100644 setuid without exec
-rw-r-Sr-- 0102644 0100644 setgid without exec
drwxrwxrwT 0041776 0040776 sticky without exec

Behaviour changes worth naming

  • far2l renders these bits from dwUnixMode (mix/panelmix.cpp:487-493), so SHELL, SCP and FTP listings now show them, and FILE_ATTRIBUTE_EXECUTABLE is now set for entries like -rwsr-xr-x. WinPort derives its attribute flags from S_IFMT plus the r/w/x bits only (WinPort/src/APIFiles.cpp:24-49), so nothing else in that mapping moves. SFTP is unaffected — it takes real permissions from the sftp attributes.
  • Op/OpXfer.cpp propagates the source mode on copy (:469 SetMode, :513 FilePut, :626 DirectoryCreate). Those bits could not previously be present for a SHELL/SCP/FTP source, so copying a setuid binary from such a host now reproduces the setuid bit at the destination where before it silently did not. That matches what a local-to-local copy already does via real stat(2), but it is a privilege-bit change rather than a cosmetic one, so please weigh it.
  • OpChangeMode now pre-checks the suid/sgid/sticky boxes for these items.

Made with Orca 🐋

@rctphone

rctphone commented Aug 9, 2026

Copy link
Copy Markdown
Author

Note on ordering: this is stacked on #3531, so the four adb: commits shown here belong to that PR — only the final utils: commit is new. If you would rather keep them independent, say so and I will drop the adb/src/ADBDevice.cpp hunk from this one and move the adb repoint into #3531.

Recovering a mode_t from the textual permission field of an `ls -l` line
was implemented three times - NetRocks' ShellParseUtils (used by SHELL and
SCP), NetRocks' vendored FTP LIST parser, and the adb plugin - and all
three mishandled the set-id and sticky spellings.

ShellParseUtils tested the wrong character:

  if (c[2] == 'x' || c[1] == 's' || c[1] == 't') out |= 1;

c[1] is the write position, which is never 's' or 't', so the execute bit
was dropped for every set-id or sticky entry; S_ISUID/S_ISGID/S_ISVTX were
not extracted at all.

FTPParseLIST was looser still - "any character but '-' sets the bit" - so
"-rwSr--r--" came out executable (04644 is correct; it produced 0744), and
NetWare's "d [RWCEAFMS] ..." form yielded 0777 out of the bracketed flags
instead of no permission bits at all.

Move one implementation to utils/include/UnixModeStr.h (header-only, in
keeping with the dozen other header-only helpers there; the top-level
CMakeLists already puts utils/include on every target's include path, so no
build changes) and delete all three copies. Net effect is 47 lines of code
in the new header against 62 + 44 + 20 removed, so roughly 80 lines less.

Behaviour changes worth naming:

- far2l renders the set-id and sticky bits from dwUnixMode
  (mix/panelmix.cpp:487-493), so SHELL, SCP and FTP listings now show them,
  and FILE_ATTRIBUTE_EXECUTABLE is now set for entries like -rwsr-xr-x.
  WinPort derives the attribute flags from S_IFMT and the r/w/x bits only
  (WinPort/src/APIFiles.cpp:24-49), so nothing else in the mapping moves.
- Op/OpXfer.cpp propagates the source mode on copy (:469 SetMode, :513
  FilePut, :626 DirectoryCreate). Those bits could not previously be
  present for a SHELL/SCP/FTP source, so copying a setuid binary from such
  a host now reproduces the setuid bit at the destination, where before it
  silently did not. That matches what a local-to-local copy already does
  via real stat(2), but it is a privilege-bit change rather than a cosmetic
  one.
- OpChangeMode now pre-checks the suid/sgid/sticky boxes for these items.

Table-tested against all three previous implementations over 17 field
shapes - plain/dir/symlink/char/block/fifo/socket, s and S, t and T,
truncated and 11-character (ACL/SELinux-suffixed) fields, and the
"d?????????" form a shell emits for entries it may not stat. The new parser
matches expectations on all 17; ShellParseUtils was wrong on 6, all of them
set-id or sticky.
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