Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 0 additions & 41 deletions resources/storage_capacity.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,16 +10,13 @@ package resources
import (
"context"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"math"
"os"
"path/filepath"
"strings"
"sync"
"syscall"
)

// StorageFilesystem is a path-free point-in-time capacity observation. The
Expand DownExpand Up@@ -54,44 +51,6 @@ var storageAuthorityScope struct {
err error
}

// InspectStorageFilesystem observes the filesystem containing root. root may
// name a not-yet-created application-state directory; in that case the nearest
// existing ancestor determines the target filesystem.
func InspectStorageFilesystem(root string) (StorageFilesystem, error) {
ancestor, err := existingStorageAncestor(root)
if err != nil {
return StorageFilesystem{}, err
}
var state syscall.Statfs_t
if err := syscall.Statfs(ancestor, &state); err != nil {
return StorageFilesystem{}, fmt.Errorf("inspect storage filesystem: %w", err)
}
if state.Bsize <= 0 {
return StorageFilesystem{}, fmt.Errorf("storage filesystem reported invalid block size %d", state.Bsize)
}
blockSize := uint64(state.Bsize)
totalBytes, ok := checkedStorageBytes(uint64(state.Blocks), blockSize)
if !ok {
return StorageFilesystem{}, errors.New("storage filesystem total byte count overflows uint64")
}
availableBytes, ok := checkedStorageBytes(uint64(state.Bavail), blockSize)
if !ok {
return StorageFilesystem{}, errors.New("storage filesystem available byte count overflows uint64")
}
scope, err := storageAuthorityScopeBytes()
if err != nil {
return StorageFilesystem{}, err
}
identity := fmt.Sprintf("%x:%v:%d", scope, state.Fsid, totalBytes)
digest := sha256.Sum256([]byte(identity))
return StorageFilesystem{
AuthorityID: "storage/sha256:" + hex.EncodeToString(digest[:16]),
TotalBytes: totalBytes,
AvailableBytes: availableBytes,
AllocationUnitBytes: blockSize,
}, nil
}

// InspectStorageTree measures an existing tree through filesystem metadata.
// It never reads file contents and never follows symlinks. Callers must invoke
// it only at the execution/storage boundary that owns the inspected tree.
Expand Down
16 changes: 16 additions & 0 deletions resources/storage_capacity_other.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
//go:build !unix

package resources

import (
"fmt"
"runtime"
)

// InspectStorageFilesystem is a Unix-only observer: it depends on the statfs
// syscall, which platforms outside the `unix` build constraint (e.g. Windows)
// do not provide. Cross-compiled builds must still link, so the capability
// reports itself unavailable at run time instead of failing to compile.
func InspectStorageFilesystem(root string) (StorageFilesystem, error) {
return StorageFilesystem{}, fmt.Errorf("inspect storage filesystem: unsupported on %s", runtime.GOOS)
}
19 changes: 19 additions & 0 deletions resources/storage_capacity_other_test.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
//go:build !unix

package resources

import (
"context"
"testing"
)

// On platforms without statfs the observer must still link and fail cleanly at
// run time rather than break the cross-compiled release build.
func TestInspectStorageFilesystemUnsupportedOffUnix(t *testing.T) {
if _, err := InspectStorageFilesystem(t.TempDir()); err == nil {
t.Fatal("expected an unsupported-platform error off unix")
}
if _, err := InspectStorageTree(context.Background(), t.TempDir()); err == nil {
t.Fatal("InspectStorageTree must surface the unsupported-platform error")
}
}
2 changes: 2 additions & 0 deletions resources/storage_capacity_test.go
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
//go:build unix

package resources

import (
Expand Down
49 changes: 49 additions & 0 deletions resources/storage_capacity_unix.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
//go:build unix

package resources

import (
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"syscall"
)

// InspectStorageFilesystem observes the filesystem containing root. root may
// name a not-yet-created application-state directory; in that case the nearest
// existing ancestor determines the target filesystem.
func InspectStorageFilesystem(root string) (StorageFilesystem, error) {
ancestor, err := existingStorageAncestor(root)
if err != nil {
return StorageFilesystem{}, err
}
var state syscall.Statfs_t
if err = syscall.Statfs(ancestor, &state); err != nil {
return StorageFilesystem{}, fmt.Errorf("inspect storage filesystem: %w", err)
}
if state.Bsize <= 0 {
return StorageFilesystem{}, fmt.Errorf("storage filesystem reported invalid block size %d", state.Bsize)
}
blockSize := uint64(state.Bsize)
totalBytes, ok := checkedStorageBytes(uint64(state.Blocks), blockSize)
if !ok {
return StorageFilesystem{}, errors.New("storage filesystem total byte count overflows uint64")
}
availableBytes, ok := checkedStorageBytes(uint64(state.Bavail), blockSize)
if !ok {
return StorageFilesystem{}, errors.New("storage filesystem available byte count overflows uint64")
}
scope, err := storageAuthorityScopeBytes()
if err != nil {
return StorageFilesystem{}, err
}
identity := fmt.Sprintf("%x:%v:%d", scope, state.Fsid, totalBytes)
digest := sha256.Sum256([]byte(identity))
return StorageFilesystem{
AuthorityID: "storage/sha256:" + hex.EncodeToString(digest[:16]),
TotalBytes: totalBytes,
AvailableBytes: availableBytes,
AllocationUnitBytes: blockSize,
}, nil
}
Loading