mirror of
https://github.com/rustfs/rustfs.git
synced 2026-10-27 06:18:43 +00:00
b40f1e5fab
* fix(utils): NUL-terminate and extend paths passed to Win32 volume APIs `get_volume_name` returned the volume root truncated at its NUL and the result was then passed to `GetVolumeInformationW` as a PCWSTR, an out-of-bounds read. The disk-space and volume APIs also received the caller's raw path, so they failed beyond MAX_PATH, and a UNC share root lacked the trailing backslash these APIs require. - Wrap every Win32 path in a NUL-terminated `WidePath` that rejects interior NULs; volume roots returned by `GetVolumePathNameW` keep their terminator. - Convert inputs to the extended-length `\\?\` / `\\?\UNC\` form after `GetFullPathNameW` normalization, and give directory queries a trailing backslash. - Query `GetDiskFreeSpaceW` and `GetVolumeInformationW` on the volume root as documented; size buffers to the documented MAX_PATH + 1 or the input length. - Report Win32 error codes instead of raw HRESULTs so `ErrorKind` matches `std::fs`. - Keep volume ids in their conventional `C:\` / `\\server\share\` form. Add Windows-only `os::` tests for long directories, UNC share roots and non-empty fstype, and run them in the Windows filesystem workflow. * fix(utils): report Windows network volumes to the fs guard Windows names the server's filesystem (usually NTFS) for SMB shares, so the startup unsupported-filesystem guard, which matches fstype names such as cifs or smb2, never fired for UNC paths or mapped drives. - Add `DiskInfo::remote`. On Windows it is set when the directory handle answers `FileRemoteProtocolInfo` (only network redirectors do, and the handle follows symlinks and junctions to their target) or the volume root's `GetDriveTypeW` is `DRIVE_REMOTE`. Unix keeps it false because the client fstype already names network mounts. - Treat remote volumes as unsupported in the startup guard and label them "<fstype> on network share". - Cover UNC shares, paths beyond MAX_PATH below a share, a directory symlink to a share and a `net use` mapped drive with Windows-only `os::` tests that skip when `\\localhost\<drive>$` is unreachable; the Windows filesystem workflow maps the drive before running them. * chore(deps): replace yanked inout 0.2.3 with 0.2.4 cargo-deny's advisories check rejects the yanked inout 0.2.3 that main's lockfile pins, failing every open PR. Lockfile-only patch bump. * fix(utils): detect network targets from the final NT path On the Windows runner, a directory symlink to \\localhost\C$ was reported as local: FileRemoteProtocolInfo did not identify it, and the volume root of the link is a fixed drive. Also treat a handle whose final NT path (GetFinalPathNameByHandleW with VOLUME_NAME_NT) lies under \Device\Mup\ as remote; the Multiple UNC Provider fronts every UNC and mapped-drive target. The tests now assert the handle probe on its own for local and share paths and report both signals when it disagrees. --------- Co-authored-by: overtrue <anzhengchao@gmail.com>
221 lines
8.1 KiB
YAML
221 lines
8.1 KiB
YAML
# Copyright 2024 RustFS Team
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
name: Windows Filesystem Tests
|
|
|
|
on:
|
|
push:
|
|
branches: [ main ]
|
|
paths:
|
|
- "crates/ecstore/src/disk/**"
|
|
- "crates/ecstore/src/layout/**"
|
|
- "crates/ecstore/src/store/init_format.rs"
|
|
- "crates/ecstore/src/set_disk/ops/locking.rs"
|
|
- "crates/ecstore/src/object_api/write_commit_context.rs"
|
|
- "crates/ecstore/src/bucket/utils.rs"
|
|
- "crates/ecstore/Cargo.toml"
|
|
- "crates/utils/src/dunce.rs"
|
|
- "crates/utils/src/os/**"
|
|
- "crates/utils/Cargo.toml"
|
|
- "crates/utils/src/path.rs"
|
|
- "rustfs/build.rs"
|
|
- "rustfs/Cargo.toml"
|
|
- "rustfs/src/main.rs"
|
|
- "rustfs/src/server/runtime.rs"
|
|
- "rustfs/src/startup_*.rs"
|
|
- "Cargo.toml"
|
|
- "Cargo.lock"
|
|
- ".github/actions/setup/**"
|
|
- ".github/workflows/windows-filesystem.yml"
|
|
pull_request:
|
|
branches: [ main ]
|
|
paths:
|
|
- "crates/ecstore/src/disk/**"
|
|
- "crates/ecstore/src/layout/**"
|
|
- "crates/ecstore/src/store/init_format.rs"
|
|
- "crates/ecstore/src/set_disk/ops/locking.rs"
|
|
- "crates/ecstore/src/object_api/write_commit_context.rs"
|
|
- "crates/ecstore/src/bucket/utils.rs"
|
|
- "crates/ecstore/Cargo.toml"
|
|
- "crates/utils/src/dunce.rs"
|
|
- "crates/utils/src/os/**"
|
|
- "crates/utils/Cargo.toml"
|
|
- "crates/utils/src/path.rs"
|
|
- "rustfs/build.rs"
|
|
- "rustfs/Cargo.toml"
|
|
- "rustfs/src/main.rs"
|
|
- "rustfs/src/server/runtime.rs"
|
|
- "rustfs/src/startup_*.rs"
|
|
- "Cargo.toml"
|
|
- "Cargo.lock"
|
|
- ".github/actions/setup/**"
|
|
- ".github/workflows/windows-filesystem.yml"
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
CARGO_TERM_COLOR: always
|
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
|
RUST_BACKTRACE: 1
|
|
|
|
jobs:
|
|
rename-safety:
|
|
name: Rename Safety
|
|
runs-on: windows-latest
|
|
timeout-minutes: 60
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@f548e57e544e1ff5a4c46bf1e1b8685f8e4a348a # v7
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Setup Rust environment
|
|
uses: ./.github/actions/setup
|
|
with:
|
|
rust-version: stable
|
|
cache-shared-key: build-x86_64-pc-windows-msvc
|
|
cache-save-if: 'false'
|
|
install-build-packaging-tools: 'false'
|
|
install-test-tools: 'false'
|
|
|
|
- name: Check production Windows dependencies
|
|
shell: pwsh
|
|
run: cargo check -p rustfs-ecstore --lib
|
|
|
|
- name: Test guarded rename publication
|
|
shell: pwsh
|
|
run: cargo test -p rustfs-ecstore --lib rename_all_ -- --nocapture
|
|
|
|
- name: Test Windows path conversion
|
|
shell: pwsh
|
|
run: "cargo test -p rustfs-utils --no-default-features --features path --lib dunce:: -- --nocapture"
|
|
|
|
- name: Test Windows volume and disk-space probes
|
|
shell: pwsh
|
|
run: |
|
|
# Map a drive letter to the temp drive's administrative share so the
|
|
# remote-volume probe also covers mapped network drives.
|
|
$drive = (Split-Path -Qualifier ([System.IO.Path]::GetTempPath())).TrimEnd(':')
|
|
net use R: "\\localhost\$drive`$" /persistent:no
|
|
if ($LASTEXITCODE -eq 0) {
|
|
$env:RUSTFS_TEST_WINDOWS_MAPPED_DRIVE = 'R:'
|
|
} else {
|
|
Write-Output "::warning::could not map R: to \\localhost\$drive`$; mapped-drive test will skip"
|
|
}
|
|
cargo test -p rustfs-utils --features os --lib os:: -- --nocapture
|
|
$status = $LASTEXITCODE
|
|
if ($env:RUSTFS_TEST_WINDOWS_MAPPED_DRIVE) { net use R: /delete /y | Out-Null }
|
|
exit $status
|
|
|
|
- name: Test Windows handle guards
|
|
shell: pwsh
|
|
run: cargo test -p rustfs-ecstore --lib windows_ -- --nocapture
|
|
|
|
- name: Test endpoint path parsing
|
|
shell: pwsh
|
|
run: "cargo test -p rustfs-ecstore --lib layout::endpoint:: -- --nocapture"
|
|
|
|
- name: Test startup temporary-directory cleanup
|
|
shell: pwsh
|
|
run: cargo test -p rustfs-ecstore --lib cleanup_tmp_on_startup_ -- --nocapture
|
|
|
|
- name: Test object key Windows admission
|
|
shell: pwsh
|
|
run: "cargo test -p rustfs-ecstore --lib bucket::utils:: -- --nocapture"
|
|
|
|
- name: Test Windows file-name rules
|
|
shell: pwsh
|
|
run: "cargo test -p rustfs-utils --no-default-features --features path --lib dunce:: -- --nocapture"
|
|
|
|
- name: Test fresh format publication
|
|
shell: pwsh
|
|
run: cargo test -p rustfs-ecstore --lib fresh_format_load_initializes_all_disks -- --nocapture
|
|
|
|
# The native Windows main thread only gets the 1 MiB PE stack reserve, while
|
|
# Linux and macOS hand out 8 MiB. Startup runs the whole root future on the
|
|
# thread that calls `block_on`, so a debug server once overflowed before it
|
|
# listened on Windows only. Start a real server to keep that covered.
|
|
startup-smoke:
|
|
name: Startup Smoke
|
|
runs-on: windows-latest
|
|
timeout-minutes: 90
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@f548e57e544e1ff5a4c46bf1e1b8685f8e4a348a # v7
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Setup Rust environment
|
|
uses: ./.github/actions/setup
|
|
with:
|
|
rust-version: stable
|
|
cache-shared-key: build-x86_64-pc-windows-msvc
|
|
cache-save-if: 'false'
|
|
install-build-packaging-tools: 'false'
|
|
install-test-tools: 'false'
|
|
|
|
- name: Build debug server binary
|
|
shell: pwsh
|
|
run: cargo build --locked -p rustfs --bin rustfs
|
|
|
|
- name: Print version
|
|
shell: pwsh
|
|
run: |
|
|
& .\target\debug\rustfs.exe --version
|
|
if ($LASTEXITCODE -ne 0) { throw "rustfs --version exited with $LASTEXITCODE" }
|
|
|
|
- name: Start server and wait for readiness
|
|
shell: pwsh
|
|
env:
|
|
RUSTFS_ACCESS_KEY: rustfsadmin
|
|
RUSTFS_SECRET_KEY: rustfsadmin
|
|
run: |
|
|
$volume = Join-Path $env:RUNNER_TEMP 'rustfs-startup-smoke'
|
|
$stdout = Join-Path $env:RUNNER_TEMP 'rustfs-startup-smoke.out.log'
|
|
$stderr = Join-Path $env:RUNNER_TEMP 'rustfs-startup-smoke.err.log'
|
|
New-Item -ItemType Directory -Force -Path $volume | Out-Null
|
|
$server = Start-Process -FilePath .\target\debug\rustfs.exe `
|
|
-ArgumentList @('--address', '127.0.0.1:9000', "`"$volume`"") `
|
|
-RedirectStandardOutput $stdout -RedirectStandardError $stderr `
|
|
-NoNewWindow -PassThru
|
|
# Cache the process handle now; otherwise ExitCode reads back empty.
|
|
$null = $server.Handle
|
|
$ready = $false
|
|
try {
|
|
for ($i = 0; $i -lt 180; $i++) {
|
|
if ($server.HasExited) { break }
|
|
try {
|
|
$response = Invoke-WebRequest -Uri 'http://127.0.0.1:9000/health/ready' -UseBasicParsing -NoProxy -TimeoutSec 2
|
|
if ($response.StatusCode -eq 200) { $ready = $true; break }
|
|
} catch {}
|
|
Start-Sleep -Seconds 1
|
|
}
|
|
} finally {
|
|
if (-not $server.HasExited) { Stop-Process -Id $server.Id -Force }
|
|
$server.WaitForExit()
|
|
Write-Host '--- server stdout (tail) ---'
|
|
Get-Content $stdout -Tail 100 -ErrorAction SilentlyContinue
|
|
Write-Host '--- server stderr (tail) ---'
|
|
Get-Content $stderr -Tail 100 -ErrorAction SilentlyContinue
|
|
}
|
|
if (-not $ready) {
|
|
throw ("rustfs did not report ready on /health/ready (exit code: {0})" -f $server.ExitCode)
|
|
}
|