Skip to content

fix: nil-pointer guards in metadata, retry-ID and mod-time handling - #880

Open
terryrankine wants to merge 1 commit into
peak:masterfrom
terryrankine:fix/nil-guards
Open

terryrankine wants to merge 1 commit into
peak:masterfrom
terryrankine:fix/nil-guards

Conversation

@terryrankine

@terryrankine terryrankine commented Sep 17, 2026 •

Copy link
Copy Markdown

Context

A handful of small, independent nil-pointer and correctness fixes found while auditing the codebase. Each has a unit test that fails without it.

Fixes

  • storage/s3.go Copy(): input.Metadata was nil when --no-such-upload-retry-count added the retry ID → "assignment to entry in nil map" panic. Also, user-defined metadata (--metadata) replaced the map and dropped the retry ID; it is now merged.
  • storage/s3.go Stat() / retryOnNoSuchUpload(): guard *retryID against a nil map value.
  • command/cp.go shouldOverride(): --if-source-newer dereferenced ModTime without a nil check. Extracted isSourceNewer(); a missing mod time is treated as "copy".
  • command/validation.go: the "expected at most N arguments" message printed min instead of max.
  • parallel/global.go: Run() before Init() now panics with a clear message instead of a nil-pointer dereference.
  • e2e/util_test.go: setup() assigned opts.accessKeyID to the region.

Tests

TestCheckNumberOfArguments, TestRunPanicsWithoutInit, TestIsSourceNewer, TestS3CopyRetryIDWithUserMetadata, TestS3PutRetryIDWithUserMetadata, TestS3StatNilRetryID. Each was verified to fail with the corresponding fix reverted.

Linux, Go 1.24, go test -count=1 -race ./...: all packages ok. gofmt, go vet, staticcheck, unparam, semgrep: clean.

CI note: the test matrix fails before running anything — bitnami/minio:2023.7.18 no longer exists on Docker Hub (fixed by #840). All build and qa jobs pass. The full suite was run locally on Linux as above.

🤖 Generated with Claude Code

- storage/s3: Copy() wrote the retry ID into a nil Metadata map when
  --no-such-upload-retry-count > 0, panicking with "assignment to entry
  in nil map". Initialise the map first.
- storage/s3: user-defined metadata replaced the whole Metadata map in
  Copy() and Put(), silently dropping the retry ID set just above it.
  Merge into the existing map instead.
- storage/s3: Stat() and retryOnNoSuchUpload() dereferenced the retry-ID
  metadata value without a nil check.
- command/cp: --if-source-newer dereferenced ModTime without a nil check.
  Treat a missing modification time as "source is newer" so the copy
  proceeds. Comparison extracted to isSourceNewer() so it can be tested.
- parallel: Run() before Init() panicked with a bare nil-pointer
  dereference. Panic with an explicit message instead.
- command/validation: the "expected at most N arguments" error reported
  min instead of max.
- e2e: setup() assigned opts.accessKeyID to region instead of
  opts.region, so tests with a custom region silently used the access
  key ID as the region.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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