Repository navigation
File properties and directory listing: client.file(path).info(), exists(), list() (#145) - #179
Conversation
Second step of the remote file access family (#146, #145, #147, #148): client.file(path) gains info() (Optional<RemoteFileInfo>, empty for a missing path), exists(), and list(), which returns a RemoteDirectoryListing with glob, recursive, maxDepth, filesOnly/directoriesOnly, modifiedAfter/modifiedBefore, minSize/maxSize, onInaccessible and timeout, ending with execute() (RemoteFileList: entries + inaccessible) or stream(). The host walks the tree with a small PowerShell script built on the .NET DirectoryInfo API and an explicit stack. It uses EnumerateFileSystemInfos where available and GetFileSystemInfos on PowerShell 2.0 / .NET 2.0. Every filter is evaluated on the host. Reparse points are reported but never descended into, so a junction loop terminates. A subdirectory that cannot be read becomes a "!" record and the walk goes on. Each entry is one ASCII line: plain integers for attributes, size and FileTimes, and only the path base64-encoded (UTF-8), so non-ASCII names do not depend on the console code page. A malformed record fails with a clear exception. - Long paths: absolute paths get the \\?\ (or \\?\UNC\) prefix where .NET accepts it (4.6.2+). The prefix is stripped from the reported paths. - The per-entry record is inlined instead of calling helper functions. A PowerShell function call costs about 45 us, which made large listings about 10 times slower (System32 went from 9.5 s to 2.3 s on 2022). - The script is kept compact so it fits the command line with paths up to about 450 characters. Like reads, a script that does not fit is refused rather than uploaded. - RemoteFiles: shared start/finish/blocking helpers for reads and metadata. PowerShell CLIXML progress records are filtered out of failure messages. - Docs: files.md (properties, listing, shell vs WMI, limitations), plus index.md, preparing-the-host.md, timeouts-and-errors.md, migrating-from-winrm4j.md and file-transfers.md. Tests: record parser and FakeWsmanServer tests (RemoteFileListingTest). The scripts run in local PowerShell (RemoteFilesScriptTest): depth, glob, filters, junction loop, a path over 300 characters, an access-denied subdirectory, non-ASCII names. WinRMLiveTest passes on Windows Server 2022 and 2008 R2. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a24f8bc4e2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…t (Codex review) TickCount is a signed 32-bit millisecond counter, negative for about half of every 49.7-day cycle. With the clock starting at 0, "TickCount - $t" was negative on such hosts, so a selective walk that never filled the 32 KB buffer sent no keepalive and could trip stream()'s inactivity timeout. A wrap during a walk had the same effect, because PowerShell promotes the overflowing subtraction to a double instead of wrapping. The walker now times the silence since its last write with a System.Diagnostics.Stopwatch: monotonic, 64-bit, and available on .NET 2.0 (PowerShell 2.0). Verified locally: an 89-second walk of C:\Windows that matched nothing sent 86 keepalives. The live listing test passes on Windows Server 2008 R2. files.md: correct the command-line path limits (measured): about 1,150 characters for info(), 450 for list(); the docs had both at 450. Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
@codex please review again |
|
Codex Review: Didn't find any major issues. Keep them coming! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Closes #145. This is the second step of the remote file access family, after #146 (merged in #177). #147 (download) and #148 (CLI) come next.
What changed
client.file(path)gets metadata and directory listing:New public types (flat in
org.metricshub.winrm):RemoteDirectoryListing(the request),RemoteFileList(the result ofexecute()), andRemoteFileInfo(an immutable value: path, name, size, three timestamps, attribute flags, and the rawattributes()).How it works
DirectoryInfoAPI and an explicit stack. It never parsesdiroutput and doesn't useGet-ChildItem. It usesEnumerateFileSystemInfoswhere available andGetFileSystemInfoson PowerShell 2.0 / .NET 2.0.isReparsePoint()) but never descended into, so a junction loop terminates.!record and the walk goes on. The directory being listed is the exception: if it can't be read, the listing fails.\\?\prefix (\\?\UNC\for UNC paths) where .NET accepts it (4.6.2+). The prefix is stripped from the reported paths.Things a reviewer should know
info()/list(): the scripts must fit the ~8 KB-EncodedCommandline, and like reads (Remote file access (1/4): foundation + read remote file content — client.file(path), RemoteFiles primitive, whole file, byte ranges, text, streaming #146), a script that doesn't fit is refused rather than uploaded. The walker was compacted to leave room for that path length. ARemoteFileListingTesttest guards a 400-character path.RECORD. That made listings about 4× faster:C:\Windows\System32(16k entries) in 2.3 s instead of 9.5 s on Server 2022, 5.5 s instead of 12.7 s on 2008 R2. All ofC:\Windows(126k entries) streams in 15 s.stream()'s inactivity timeout.RemoteFilesScriptTest). The live test accepts either outcome. This is documented.RemoteFiles: sharedstart/finish/blockinghelpers for reads and metadata (moved out ofRemoteFile). PowerShell CLIXML progress records (#< CLIXML …) are now filtered out of failure messages, which also cleans up read errors.modifiedBeforeandmaxSizeround out the filters, andmaxDepth(n)impliesrecursive().onInaccessiblereceives only the path, as in the issue. The!record carries the error message, but it isn't exposed.Docs
files.md: file properties, listing (settings table, links and inaccessible directories, performance), shell vs WMI, paths and limitations (long paths, UNC second hop and Kerberos credential delegation: allowDelegation() and CLI --allow-delegate (winrs -allowdelegate) #141).index.md: overview and example.preparing-the-host.md: requirements row, no-SMB note, second hop.timeouts-and-errors.md: the list of streaming terminals.migrating-from-winrm4j.mdandfile-transfers.md.Tests
RemoteFileListingTest(new):!records, malformed/truncated lines;FakeWsmanServerruns: records split across Receive chunks, an empty directory, a partially inaccessible tree,stream(),info()on a missing path →Optional.empty(), access denied, filters reaching the script, a CLIXML-free error message, command-line fit with long paths.RemoteFilesScriptTestruns the real scripts in local PowerShell 5.1: all fields and timestamps at 100 ns precision,maxDepth, glob (case, brackets,?), type/size/time filters, a junction loop, a path over 300 characters, an access-denied subdirectory,info(), and the not-found / not-a-directory exit codes.WinRMLiveTest.remoteDirectoryListingpasses on Windows Server 2022 (PS 5.1, long path included) and Windows Server 2008 R2 (PS 2.0; the long-path part is skipped because .NET 2.0 rejects\\?\).mvn clean verify sitepasses locally, with 0 Checkstyle, PMD and SpotBugs findings.🤖 Generated with Claude Code