Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

S3 without an SDK

rcp copy /data s3://backups/site-a -s3.endpoint http://minio.lab:9000 speaks the S3 REST API with AWS Signature Version 4, over a small HTTP/1.1 client in the standard library: ListObjectsV2, HeadObject, GetObject (with ranges), PutObject, multipart uploads, CopyObject and DeleteObject. Credentials and region resolve the way the AWS tools resolve them: AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_SESSION_TOKEN / AWS_REGION first, then the profile named by -s3.profile or AWS_PROFILE in ~/.aws/credentials and ~/.aws/config (including its endpoint_url and region). -s3.endpoint URL (or AWS_ENDPOINT_URL) names a MinIO, ONTAP S3 or other service, addressed path-style; with no endpoint the bucket is on AWS, addressed virtual-hosted in its region. A -s3.endpoint carrying user:password@ is refused by name, as the WebDAV and http endpoints are and for the same reason, and the refusal does not repeat the credential – an endpoint reaches the job log, a report and ps, so a message that quotes it back writes the password into the log on the one path that exists to keep it out. -s3.insecure picks plain HTTP when the endpoint names no scheme, -s3.noverify skips certificate verification (and says so), AWS_CA_BUNDLE names a private CA, and HTTPS_PROXY / HTTP_PROXY / NO_PROXY are honored, CONNECT for TLS and absolute URIs for plain HTTP.

A bucket has no inodes, so the tree is spelled out the way s3fs spells it: dir/ zero-byte objects are directories (a directory that only exists as a key prefix is listed too), a symlink is an object whose body is its target, and modification time, mode, uid and gid ride in user metadata (x-amz-meta-mtime as seconds.nanoseconds, mode as the full st_mode in decimal, uid, gid). A tree copied into a bucket and back comes home with its times, modes and owners; verify between a directory and a bucket compares all of them. Hard links have no representation: the second name becomes a server-side copy (no data moves), verify reports the lost link unless -nolinks, and extended attributes are reported as refused.

Uploads stream. The executor declares each file’s size up front, so an object up to 64 MiB is one PUT with a known Content-Length and a larger one is a multipart upload whose parts open as the bytes arrive, sized so that any object fits in the service’s 10,000 parts; a sparse source’s holes are written as zeros because objects have none. Nothing is buffered beyond the socket, whatever the object size. Reads are one streaming GET per file, reissued with a Range on a seek. Listing is ListObjectsV2 in pages of 1,000 followed by one HEAD per object in parallel for the metadata, which is the honest cost of carrying POSIX metadata in a bucket; scan -q on a million objects is a million small requests.

TLS is the one thing not in the standard library. It is behind the rustls cargo feature (rustls with the ring provider), so the default build carries no TLS stack and speaks plain HTTP, which is what a MinIO on a kit or a gateway on localhost offers, and refuses an https:// endpoint with a message naming the feature. cargo build --release --features rustls needs the registry once (the lockfile pins the versions) and produces a binary that speaks to AWS and to a MinIO behind a private CA.

The contract suite runs the client against an in-process stand-in service (src/s3/testserver.rs) that verifies every request’s signature and payload hash, so a signing mistake fails a test here; the signer is also checked against the worked examples in the S3 developer guide. A real service (AWS, MinIO, ONTAP) is a separate check to make before trusting a migration to it.