Remote file access (1/4): file properties and directory listing — client.file(path).info() and client.list(dir) with filters
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Idoneità per principianti
- 35/100
- Tipo di issue
- Funzionalità
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Tranquilla
- Stack tecnologico
- java, powershell
- Ambito
- api, backend, networking
Direzione di ricerca
Inizia leggendo ShellFileCopy per il framing dei comandi e il rafforzamento del trasporto, quindi WqlRequest.stream per il terminale di streaming; usa i test di FakeWsmanServer e WinRMLiveTest come punti di ingresso per i test. Il lavoro è completo quando i metadati dei file e gli elenchi filtrati eseguono il round-trip in modo sicuro, vengono segnalati i percorsi inaccessibili, vengono evitati i cicli dei reparse points e mvn verify site ha esito positivo, con files.md collegato da index.md e la README aggiornata.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
First issue of a remote file access family: metadata and listing here, content reading in #146,
downloadFilein #147, CLI subcommands in #148. This one introduces the shared foundation (theclient.file(...)entry point, theRemoteFileInfotype, and the base64-framed PowerShell helper) that the other three build on.
Context
WinRM has no file-access verb of its own — nothing like SFTP's OPENDIR/READ. Reading files on a remote host over WinRM therefore means reusing one of the two channels the client already speaks:
-
WMI/WQL —
CIM_DataFileandCIM_Directoryexpose real metadata (FileSize,LastModified,CreationDate,Readable,Hidden,System,Archive,Extension), and the client's WQL layer already supports the association form needed to list a directory:SELECT Name, FileSize, LastModified FROM ASSOCIATORS OF {Win32_Directory.Name="C:\\Windows\\Temp"} WHERE ResultClass = CIM_DataFileWorkable, but: the WMI file provider is notoriously slow (it walks the filesystem inside WMI, and an unindexed
CIM_DataFilequery on a large tree can take minutes), recursion means one association query per directory, timestamps come back as CIMDMTFdatetimes, and it cannot read content at all. -
The command shell — the channel
ShellFileCopyalready uses to upload files (chunked base64 throughcmd.exe, decoded withcertutil, digest-verified). Listing and reading are the same trick in reverse, and it is fast, recursive, and filters server-side.
This family takes the shell path and keeps WMI as a documented alternative for metadata-only cases. That keeps the zero-dependency promise, needs no extra port, and reuses the transport hardening (quota retries, envelope chunking, incremental decoding) already in the codebase.
Proposed API
A per-path entry point, sibling of wql(...) / command(...):
try (WinRMClient client = WinRMClient.builder("server01.acme.com")
.credentials("ACME\\admin", password)
.build()) {
// One path: existence and properties
if (client.file("C:\\Windows\\Temp\\collect.log").exists()) {
RemoteFileInfo info = client.file("C:\\Windows\\Temp\\collect.log").info().orElseThrow();
System.out.println(info.size() + " bytes, modified " + info.lastModified());
}
// A directory: listing with filters
List<RemoteFileInfo> logs = client.list("C:\\inetpub\\logs")
.glob("*.log") // server-side -Filter
.recursive() // off by default
.maxDepth(3)
.filesOnly() // or directoriesOnly()
.modifiedAfter(Instant.now().minus(Duration.ofDays(1)))
.minSize(1024L)
.execute();
// Large trees stream, like WQL rows
try (Stream<RemoteFileInfo> tree = client.list("D:\\data").recursive().stream()) {
tree.filter(RemoteFileInfo::isDirectory).forEach(System.out::println);
}
}
RemoteFileInfo — an immutable value type in the style of WqlRow (Java 11, so a final class with accessors, not a record):
| accessor | source |
|---|---|
String path() |
full path as reported by the host |
String name() |
last path element |
boolean isDirectory() |
Attributes |
long size() |
Length (0 for directories) |
Instant lastModified(), created(), lastAccessed() |
*TimeUtc |
boolean isHidden(), isSystem(), isReadOnly(), isArchive(), isReparsePoint() |
Attributes bit flags |
int attributes() |
raw FileAttributes value, for anything not exposed above |
Requirements
The remote helper — base64-framed PowerShell, not dir
- Never parse
diroutput: its column layout, date format and decimal separator are locale-dependent, and it has no timestamps beyond minute granularity. Use PowerShell (Get-ChildItem/[IO.FileInfo]). - Invoke it with
powershell -NoProfile -NonInteractive -EncodedCommand <base64 UTF-16LE>. This sidestepscmd.exequoting entirely (no^/"/%escaping games with user-supplied paths) and is immune to the console code page problems fixed in #142. Respect the existingMAX_COMMAND_LENGTH(8000) budget fromShellFileCopy: the scripts must stay terse; if one grows past the budget, upload it as a script file with the existingShellFileCopypath and invoke it by name. - The script emits pure ASCII: it builds its records as UTF-8 bytes and writes them back as base64, so non-ASCII file names survive whatever the remote console encoding is. The client decodes base64 → UTF-8 → records. No charset guessing anywhere in this path.
- Locale-independent field encoding inside the records: timestamps as
.Ticks(UTC), sizes and attributes as plain integers, one record per line, tab-delimited with the path last. Windows forbids control characters (0x00–0x1F) in file names, so tab and newline are safe delimiters — state that reasoning in the code comment. - PowerShell may be unavailable or constrained (AppLocker, Constrained Language Mode, PowerShell removed). Detect the failure and report it as a clear
WinRMClientExceptionnaming the cause, instead of surfacing a rawcmd.exeerror. Document the WMI/ASSOCIATORS OFalternative in that case.
Behavior
- Access-denied entries must not be silently dropped:
Get-ChildItemon a tree with protected subdirectories partially fails. Collect those paths and expose them (e.g.RemoteFileList.inaccessible()fromexecute(), and aonInaccessible(Consumer<String>)hook forstream()) so a caller can tell "no matches" from "could not look". - Do not follow reparse points/junctions/symlinks when recursing (cycle risk, and
C:\Documents and Settings-style legacy junctions loop straight back). Expose them as entries withisReparsePoint()true. - Filters are pushed server-side where PowerShell supports it cheaply (
-Filterfor the glob,-Recurse,-Depth,-File/-Directory); size and time predicates are applied by the script (Where-Object) so a big tree is not shipped over the wire just to be discarded locally. Document which filter is evaluated where. glob(String)is a Windows wildcard pattern (*,?), not a regex and not a full path — document that-Filteruses the legacy 8.3-aware matcher, so*.logalso matchesx.log1-style short names; offerregex(String)only if a follow-up needs it.stream()builds on the streaming command terminal of #111: records are parsed and yielded as the output chunks arrive, memory bounded by the parse buffer, not by the tree size. Must be closed (try-with-resources) likeWqlRequest.stream().info()returnsOptional.empty()for a missing path — a non-existent file is not an error. Genuine failures (access denied on the path itself, invalid path, unreachable) throw.- UNC paths (
\\server\share\...) work when the caller's credentials can reach them, i.e. only with credential delegation (#141) — document the second-hop limitation rather than pretending it works. - Long paths (>260 chars) must not silently truncate: use the
\\?\-aware .NET APIs and cover one such path in the live test.
Tests & docs
FakeWsmanServer-based tests: scripted base64 record payloads → parsedRemoteFileInfovalues, including a non-ASCII name, a >4 GB size, a hidden+system entry, a reparse point, an empty directory, and a partially-inaccessible tree.- Record-parser unit tests for malformed/truncated payloads (must fail with a clear exception, never with a half-populated object).
WinRMLiveTestcoverage against a real host (anaxagore): listC:\Windows\Temp, a recursive listing withmaxDepth, and a non-ASCII file name.- New
src/site/markdown/files.mdpage (remote file access: listing, properties, the shell-vs-WMI trade-off, limitations) linked fromindex.md; README gets the one-liner + snippet.
Acceptance criteria
client.file(path).exists()/.info()andclient.list(dir)with all documented filters work against a real host and againstFakeWsmanServer.- Non-ASCII file names round-trip correctly regardless of the remote machine's code page.
- A recursive listing of a tree containing an access-denied subdirectory returns the readable entries and reports the inaccessible paths.
- Recursion terminates on a junction loop.
mvn verify sitegreen: no checkstyle/PMD/SpotBugs findings, full Javadoc,files.mdpublished.
🤖 Generated with Claude Code
- Lingua principale
- Java
- Stelle
- 11
- Fork
- 4
- Merge medio
- 5g 5h
- PR unite (30g)
- 6
Guida per i contributori
Nessuna guida per i contributori indicizzata per questo repository
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di MetricsHub/winrm-java
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 35/100
MetricsHub/winrm-java#148 ·
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 35/100
MetricsHub/winrm-java#147 ·
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 38/100
MetricsHub/winrm-java#146 ·
-
Kerberos credential delegation: allowDelegation() and CLI --allow-delegate (winrs -allowdelegate) Aperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 48/100
MetricsHub/winrm-java#141 ·
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 68/100
MetricsHub/winrm-java#139 ·
Tutte le issue di MetricsHub/winrm-java
Issue simili
-
Bug Java Platform: Java
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
getsentry/sentry-java#6138 · 1 commento ·
-
bug needs triage p2
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
GoogleCloudPlatform/DataflowTemplates#4273 · 1 commento ·
-
[Studio][Bug] Bulk-deleting a full page of alert rules steps the page back while more rules remain Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
apache/rocketmq-dashboard#4654 · 1 commento ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100