294 Commits

Author SHA1 Message Date
Warren
3d0d031677 Add SMB Previous Versions tests: GMT token generation and snapshot listing/open/restore verification
Some checks are pending
Test / build (push) Blocked by required conditions
Test / test (push) Waiting to run
2026-06-21 06:20:17 +08:00
Warren
d368a7a4c0 Implement SSH Multiplexing: Connection/Session/Channel management with expiration and cleanup
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
2026-06-21 05:31:06 +08:00
Warren
30c1e5fff9 Implement SSH Known Hosts Verification: Parse ~/.ssh/known_hosts + verify host keys + hashed host support
Some checks failed
Test / build (push) Has been cancelled
Test / test (push) Has been cancelled
2026-06-21 05:24:33 +08:00
Warren
5238a84972 Implement SMB Durable Handles (Phase 1): Persistent FileId + reconnect + expiration + cleanup
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
2026-06-21 05:11:39 +08:00
Warren
b014390d12 Implement SSH Connection Rate Limiting: IP rate limit + global rate limit + auth brute force prevention 2026-06-21 05:01:04 +08:00
Warren
56e73ad8a4 Implement SSH Host Key Management (Phase 1): Generate/Load/Rotate Ed25519 keys 2026-06-21 04:57:15 +08:00
Warren
bb886449d7 Implement SSH config file support Phase 1
Some checks failed
Test / build (push) Has been cancelled
Test / test (push) Has been cancelled
- ssh_config.rs module with SshConfigParser
- Parse ~/.ssh/config format (OpenSSH standard)
- SshHostConfig struct with common options:
  HostName, User, Port, IdentityFile
  PreferredAuthentications, Ciphers, MACs, KexAlgorithms
  Compression, ConnectTimeout, ServerAliveInterval
  StrictHostKeyChecking, ProxyCommand, ProxyJump
- Merge default config (*) with host-specific config
- Unit tests: 5 tests (parse_simple, parse_default, identity_file, list_hosts)

All 187 tests pass.
2026-06-21 02:36:32 +08:00
Warren
b24e4f727b Implement SSH X11 forwarding Phase 4: Save X11ForwardContext
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
- Save X11ForwardContext to Channel.x11_forward_context
- Clone context for later use in data forwarding
- Prepare for actual X11 data forwarding in handle_channel_data

All 182 tests pass.
2026-06-21 02:32:32 +08:00
Warren
df707bee7e Implement SSH X11 forwarding Phase 3: Channel structure
Some checks failed
Test / build (push) Has been cancelled
Test / test (push) Has been cancelled
- Add x11_forward_context field to Channel struct
- Initialize x11_forward_context: None in all Channel creations
- Prepare for actual X11 data forwarding

All 182 tests pass.
2026-06-21 02:29:56 +08:00
Warren
d3997acfcc Implement SSH X11 forwarding Phase 2
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
- Add 'x11' channel type in handle_channel_open()
- Add handle_x11_channel_open() method
- Add 'x11-req' request in handle_channel_request()
- Add handle_x11_request() method
- Parse x11-req parameters (single_connection, auth_protocol, auth_cookie, screen_number)
- Create X11ForwardContext from DISPLAY env

All 182 tests pass.
2026-06-21 02:20:46 +08:00
Warren
929ad150d8 Implement SSH X11 forwarding Phase 1
Some checks failed
Test / build (push) Has been cancelled
Test / test (push) Has been cancelled
- x11_forward.rs module with X11ForwardContext
- parse_display() to parse DISPLAY env variable
- read_xauthority_cookie() to read MIT-MAGIC-COOKIE-1
- X11Connection for socket forwarding
- Unit tests: parse_display/disabled/display_env

All tests pass.
2026-06-21 02:11:55 +08:00
Warren
913296fe96 Implement SSH Compression Phase 3: Actual packet compression
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
- EncryptedPacket::new(): compress payload before encryption
- EncryptedPacket::read(): decompress payload after decryption
- Apply to AES-GCM, ChaCha20-Poly1305, and AES-CTR modes
- Compression order: compress → encrypt (write)
- Decompression order: decrypt → decompress (read)

All 179 tests pass.
2026-06-21 02:07:35 +08:00
Warren
93e33b04a7 Implement SSH Compression Phase 2: Integration
Some checks failed
Test / build (push) Has been cancelled
Test / test (push) Has been cancelled
- Add compression_ctos/compression_stoc to EncryptionContext
- Default impl: CompressionContext::new(6)
- from_session_keys(): initialize compression fields
- enable_compression() method (based on KEX negotiation)
- server.rs: enable compression after NEWKEYS (if negotiated)

All 179 tests pass.
2026-06-21 01:51:39 +08:00
Warren
a5375075b8 Implement SSH Compression support Phase 1
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
- compression.rs module with CompressionContext
- Compress/Decompress using flate2 (raw deflate, no zlib header)
- enable/disable/is_enabled methods
- compress/decompress with Sync flush
- Unit tests: disabled/enabled/roundtrip/supported

All tests pass.
2026-06-21 01:40:07 +08:00
Warren
a8e4e28533 Update AGENTS.md: SMB Oplocks + Lease complete (Phase 1-7 + ACK)
Some checks failed
Test / build (push) Has been cancelled
Test / test (push) Has been cancelled
2026-06-21 01:33:44 +08:00
Warren
c3e21560b6 Implement SMB 3.x Lease support Phase 5
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
- WRITE handler trigger lease break (READ leases conflict with WRITE)
- READ handler trigger lease break (HANDLE leases may conflict)
- Send LeaseBreakNotification via notification channel

All 229 tests pass.
2026-06-21 01:24:59 +08:00
Warren
4620475ba8 Implement SMB 3.x Lease support Phase 4
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
- CLOSE handler unregister lease_key from LeaseManager
- Extract lease_key from Open struct before close

All 229 tests pass.
2026-06-21 01:24:02 +08:00
Warren
344d13435e Implement SMB 3.x Lease support Phase 3
Some checks failed
Test / build (push) Has been cancelled
Test / test (push) Has been cancelled
- CREATE handler parse RqLs create context
- Extract LeaseKey (16 bytes) + LeaseState (4 bytes)
- Check can_grant() before registration
- Register with LeaseManager
- Set Open.lease_key/lease_state fields

All 229 tests pass.
2026-06-21 01:23:32 +08:00
Warren
21a9c3c6c4 Implement SMB 3.x Lease support Phase 1-2
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
Phase 1: Open struct lease fields
- lease_key: Option<[u8; 16]> - LeaseKey GUID
- lease_state: Option<u32> - READ/HANDLE/WRITE flags
- lease_flags: Option<u32> - BREAKING etc.

Phase 2: LeaseManager
- LeaseEntry with lease_key/state/flags
- register/unregister/can_grant methods
- break_lease returns LeaseBreakNotification
- LeaseBreakNotification struct (MS-SMB2 §2.2.26)

ServerState: lease_manager field added

All 229 tests pass.
2026-06-21 01:20:18 +08:00
Warren
3cf503d05f Implement Oplock Break Acknowledgement handler (MS-SMB2 §2.2.24)
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
- Parse client's OPLOCK_BREAK_ACK
- Update Open.oplock_level in Open struct
- Update OplockManager entry via update_oplock_level()
- Return confirmation response

All 229 tests pass.
2026-06-21 01:15:21 +08:00
Warren
063a697e83 Add READ handler oplock break (Phase 5.5)
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
- Trigger oplock break before read if conflicting opens exist
- Use granted_access from Open struct
- Send notifications via notification_tx channel
- Fix WRITE handler granted_access source (from Tree)

All 229 tests pass.
2026-06-21 01:13:35 +08:00
Warren
2dd50e4cb6 Implement SMB Oplocks Phase 3+5
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
Phase 3: NotificationQueue
- Add notification_tx to Connection struct
- Modify writer.rs to use tokio::select! for response + notification
- Add write_to_bytes() to OplockBreakNotification
- Support server→client async messages

Phase 5: WRITE Handler oplock break
- Get path/share_access before write
- Trigger OplockManager.break_oplock()
- Send OPLOCK_BREAK_NOTIFICATION to affected clients
- Encode and send via notification channel

All 229 tests pass.
2026-06-21 00:35:48 +08:00
Warren
be9fe72742 Update AGENTS.md: SMB Oplocks Phase 1-4-6-7 complete
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
2026-06-21 00:26:48 +08:00
Warren
276308af12 Implement SMB Byte-range Lock (Phase 7)
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
- Add LockManager to oplock.rs:
  - LockRange struct for tracking byte-range locks
  - acquire() - check conflicts before granting lock
  - release() - remove specific lock by offset/length
  - clear() - clear all locks when file closed
  - ranges_overlap() - helper for conflict detection

- Add LockManager to ServerState

- Update handlers/lock.rs:
  - Parse LockRequest and LockElement
  - Process each lock element (acquire/release)
  - Support FLAG_EXCLUSIVE_LOCK, FLAG_SHARED_LOCK, FLAG_UNLOCK
  - Return STATUS_LOCK_NOT_GRANTED on conflict

- Update handlers/close.rs:
  - Clear all locks when file closed

- Add STATUS_LOCK_NOT_GRANTED to ntstatus.rs

All 229 tests pass.
2026-06-21 00:25:55 +08:00
Warren
54ce0d6916 Implement SMB Oplocks Phase 4+6
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
Phase 4: CREATE Handler dynamic oplock granting
- Use OplockManager.can_grant() to determine oplock level
- Register OplockEntry if oplock granted
- Support ShareAccess compatibility checking
- Grant Level II if exclusive/batch oplock exists

Phase 6: CLOSE Handler oplock cleanup
- Unregister from OplockManager when file closed
- Only unregister if oplock_level > 0

All 229 tests pass.
2026-06-21 00:19:51 +08:00
Warren
27707bbe0e Implement SMB Oplocks Phase 1-2
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
Phase 1: Data structures
- Add oplock_level and share_access fields to Open struct
- Update Open::new() signature with new parameters
- Update handlers/create.rs to pass oplock params

Phase 2: OplockManager
- Create oplock.rs with OplockManager struct
- OplockEntry for tracking per-client oplock state
- can_grant() - check ShareAccess compatibility
- register() / unregister() - lifecycle management
- break_oplock() - generate OPLOCK_BREAK_NOTIFICATION
- Add OplockManager to ServerState
- Add Hash trait to SmbPath for HashMap key

All 229 tests pass.
2026-06-21 00:17:24 +08:00
Warren
487b4450f8 Implement SSH Banner/MOTD support
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
- Add banner and banner_file fields to SshSecurityConfig
- Enterprise default: 'MarkBaseSSH - Secure File Transfer Server'
- Support banner_file for reading from /etc/motd
- Send SSH_MSG_USERAUTH_BANNER before USERAUTH_SUCCESS
- Pass security_config to perform_ssh_auth function

All 229 tests pass.
2026-06-20 23:33:19 +08:00
Warren
783356852e Implement SSH Keep-alive support
Some checks failed
Test / build (push) Has been cancelled
Test / test (push) Has been cancelled
- Add keep_alive_interval and keep_alive_max_count to SshSecurityConfig
- Enterprise default: 15s interval, 3 max failures
- Development default: 30s interval, 5 max failures
- Track last_activity timestamp in service loop
- Send keepalive@openssh.com channel request when idle
- Disconnect after max keepalive failures
- Add build_keepalive_request() and get_first_session_channel()
- Prevents connection timeout on idle SSH sessions

All 229 tests pass.
2026-06-20 23:29:14 +08:00
Warren
82ff713b24 Implement SSH Agent forwarding support
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
- Add auth_agent_socket field to Channel struct
- Add handle_auth_agent_request() for auth-agent-req@openssh.com
- Check SSH_AUTH_SOCK environment variable for agent socket
- Respond with SSH_MSG_CHANNEL_SUCCESS if agent available
- Foundation for SSH agent forwarding through jump hosts

All 229 tests pass.
2026-06-20 23:25:38 +08:00
Warren
a48e253660 Update AGENTS.md: All VFS-layer SMB features complete (Dedup + RAID-Z)
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
2026-06-20 23:18:05 +08:00
Warren
4afd96c9ac Implement VFS RAID-Z (software RAID)
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
- Add VfsRaidLevel enum:
  - Single (no RAID)
  - RaidZ1 (single parity, similar to RAID 5)
  - RaidZ2 (double parity, similar to RAID 6)
  - RaidZ3 (triple parity)
- Add VfsRaidBackend with:
  - Stripe-based data distribution across disks
  - Galois Field arithmetic for parity (P/Q/R)
  - gf_exp, gf_mul for Reed-Solomon coding
  - rebuild_disk() for disk recovery
- Add VfsRaidConfig:
  - level (RAID level)
  - stripe_size (default 64KB)
  - disk_paths (storage devices)
- All VfsBackend methods propagate to all disks
- Foundation for ZFS-style software RAID

All 229 tests pass.
2026-06-20 23:17:00 +08:00
Warren
37f5da7d6c Implement VFS Deduplication (block-level)
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
- Add DedupStore with content-addressable storage:
  - SHA-256 hash-based block storage
  - Reference counting for block lifecycle
  - dedup_file() and restore_file() operations
  - DedupManifest for file reconstruction
  - DedupStats for storage statistics
- Add VfsDedupConfig:
  - block_size (default 4KB)
  - min_file_size threshold
  - store_path for dedup directory
- Add hex crate for hash encoding
- Block-level dedup foundation for SMB/ZFS

All 229 tests pass.
2026-06-20 22:39:25 +08:00
Warren
39a489d5c1 Update AGENTS.md: SMB ACLs complete (all VFS-layer features done)
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
2026-06-20 22:33:58 +08:00
Warren
1ca4913291 Implement SMB ACLs (NFSv4) at VFS layer
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
- Add ACL structures:
  - VfsAceType (Allow/Deny/Audit/Alarm)
  - VfsAceFlag (inheritance flags)
  - VfsAceMask (permission masks)
  - VfsAce (access control entry)
  - VfsAcl (ACL list with default_acl)
- Add VfsBackend methods:
  - get_acl() - retrieve ACL from .acl JSON
  - set_acl() - store ACL as .acl JSON
  - check_acl() - check permission for principal
  - add_ace() - add ACE to ACL
  - remove_ace() - remove ACE by index
- LocalFs implementation:
  - VfsAclMeta serialization struct
  - ACL stored as JSON metadata (similar to quota/snapshot)
  - Box<VfsAcl> for recursive default_acl
- Foundation for SMB/NFSv4 ACL support

All 229 tests pass.
2026-06-20 22:33:03 +08:00
Warren
de5f8d3cfb Update AGENTS.md: SMB Previous versions + Session summary
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
2026-06-20 22:27:58 +08:00
Warren
837ffa923d Implement SMB Previous versions (shadow copy) at VFS layer
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
- Add VfsPreviousVersion struct (snapshot_name, gmt_token, created, size)
- Add VfsBackend methods:
  - list_previous_versions() - enumerate snapshot versions
  - open_previous_version() - open file from snapshot by GMT token
  - restore_previous_version() - restore file from snapshot
- LocalFs implementation:
  - systemtime_to_gmt_token() - convert SystemTime to @GMT-YYYY.MM.DD-HH.MM.SS
  - scan .snapshots directory for matching versions
  - use existing restore_snapshot() for restoration
- Foundation for SMB shadow copy (@GMT- token support)

All 229 tests pass.
2026-06-20 22:26:58 +08:00
Warren
716eea788a Update AGENTS.md: SMB ZFS-style features (snapshots, quotas, compression)
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
2026-06-20 22:23:02 +08:00
Warren
70cc6d9921 Implement VFS compression support (ZSTD)
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
- Add VfsCompression and VfsCompressionConfig types
- Add compression module with Compressor:
  - compress/decompress methods
  - compress_file/decompress_file utilities
  - should_compress threshold check
  - extension detection (.zst, .lz4)
- Add zstd crate dependency
- LZ4 placeholder (future implementation)

Enables SMB transparent compression.

All 229 tests pass.
2026-06-20 22:21:50 +08:00
Warren
9c44bd5929 Implement VFS quota support
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
- Add VfsQuota and VfsQuotaUsage structs
- Add quota methods to VfsBackend trait:
  - set_quota: set space/file limits
  - get_quota: retrieve quota settings
  - get_quota_usage: current usage stats
  - check_quota: pre-write check
- Implement LocalFs quota support:
  - Uses .quota metadata file
  - JSON storage for quota limits
  - Recursive size/file counting
  - Hidden files excluded (.quota, .snapshots)

Enables SMB per-share/user quota enforcement.

All 229 tests pass.
2026-06-20 22:17:50 +08:00
Warren
f016525687 Implement VFS snapshot support (ZFS-style)
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
- Add VfsSnapshotInfo struct
- Add snapshot methods to VfsBackend trait:
  - create_snapshot: copy-on-write with metadata
  - list_snapshots: enumerate snapshots
  - delete_snapshot: remove snapshot and metadata
  - restore_snapshot: restore from snapshot
  - snapshot_info: get snapshot metadata
- Implement LocalFs snapshot support:
  - Uses .snapshots directory for storage
  - JSON metadata files (*.meta)
  - Recursive directory copy
  - Size calculation

This enables SMB 'Previous versions' feature foundation.

All 229 tests pass.
2026-06-20 22:13:17 +08:00
Warren
7b033e5276 Implement SMB streaming read using chunked READ requests
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
- Add file_id and read_chunk_size fields to SmbVfsFile
- Use Tree::open_file() to get file_id for reads
- Issue READ requests on each read() call (64KB chunks)
- Close file handle in Drop

Benefits:
- No memory overhead for large files
- Read-ahead caching possible
- Compatible with SMB2 protocol

All 229 tests pass.
2026-06-20 21:24:55 +08:00
Warren
c91dbe2cc3 Fix SSH cipher key length: dynamically determine based on negotiated algorithm
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
- Add cipher_key_len() helper function
- Store encryption_ctos/stoc in KexExchangeHandler
- Use algorithm name to determine key_len (aes256 → 32, aes128 → 16)
- Remove hardcoded cipher_key_len=32 TODO

All 229 tests pass.
2026-06-20 21:16:25 +08:00
Warren
914eacb230 Suppress non_snake_case warning for RFC 4253 notation (K, H, X)
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
2026-06-20 21:10:28 +08:00
Warren
dbca6e6d35 Fix clippy warnings: unused imports, minor style fixes
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
2026-06-20 21:08:50 +08:00
Warren
24029501d9 Add placeholder smb-server integration test files
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
2026-06-20 21:07:27 +08:00
Warren
55b31a69c1 Update AGENTS.md: SMB VFS features complete (set_len, set_stat, streaming write, CLI)
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
2026-06-20 21:02:54 +08:00
Warren
3986fb28fb SMB CLI: Add S3 VFS backend support (--s3 flag)
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
Usage:
  smb-start --s3     --s3-endpoint https://s3.example.com     --s3-bucket mybucket     --s3-access-key AKIA...     --s3-secret-key secret...

All SMB operations now work over S3-compatible storage.

All 229 tests pass.
2026-06-20 20:49:22 +08:00
Warren
d1467f03bd SMB CLI: Add multi-user support (--user name:password)
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
- Add --user CLI argument (repeatable) format: name:password
- Default user 'demo:demo123' if no users specified
- All users get ReadWrite access to the share
- Note: SMB3 encryption not available (smb-server v1 out of scope)

Example:
  smb-start --user alice:pass1 --user bob:pass2 --share-name myshare

All 229 tests pass.
2026-06-20 20:44:23 +08:00
Warren
51ca0c4633 SMB VFS: Add set_len, set_stat, streaming write, auto_reconnect
Some checks failed
Test / build (push) Has been cancelled
Test / test (push) Has been cancelled
- set_len() via SMB SET_INFO compound (CREATE → SET_INFO → CLOSE)
  with FileEndOfFileInformation (class 14)
- set_stat() via SMB SET_INFO compound with FileBasicInformation (class 4)
  for timestamp updates (atime, mtime)
- Streaming write using Tree::create_file_writer + FileWriter::write_chunk
  + finish for pipelined uploads
- Add file_writer: Option<FileWriter> to SmbVfsFile for streaming state
- Enable auto_reconnect by default (new_with_options param)
- Add systemtime_to_filetime helper for timestamp conversion

All 229 tests pass.
2026-06-20 20:26:35 +08:00
Warren
8a85c2ef7c SMB comprehensive unit tests (229 passed, 0 failed)
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
smb_server_backend.rs tests (+135 lines):
- Full VfsHandle lifecycle: file create/write/read/flush/close, stat,
  truncate (zero + extend), set_times, list_dir error, write past end
- Directory: create/stat/list/close, contains-created-file, read/write/truncate
  error cases
- All OpenIntent variants: Create (new + existing fail), OpenOrCreate
  (new + existing), OverwriteOrCreate (new + truncate existing), Truncate
  (existing + nonexistent fail)
- Directory OpenIntent: Create (new + existing fail), Open (existing),
  OpenOrCreate (new + existing)
- non_directory flag on dir (IsDirectory), directory flag on file (NotADirectory)
- Unlink: file, directory, nonexistent (NotFound)
- Rename: success + content preserved, nonexistent source (NotFound),
  existing target (Exists)
- Error mapping: all 8 VfsError variants (adds Unsupported, UnexpectedEof)
- FILETIME: roundtrip, below-offset returns epoch, exactly-offset
- vfs_stat_to_file_info: custom name, dir name from path, alloc_size

smb_fs.rs tests (+40 lines):
- Error mapping: NotFound, AlreadyExists, AccessDenied, IsADirectory,
  NotADirectory, DiskFull, SharingViolation, ConnectionLost, TimedOut,
  SessionExpired, InvalidData, Auth, Io, Cancelled
- Filetime: conversion, below-epoch, exact epoch boundary
- Path: leading slash stripping, root, deep paths
- Rejects trailing backslash
2026-06-20 19:57:20 +08:00