Make ordinary workspaces group shareable

This commit is contained in:
2026-08-10 17:27:00 +00:00
parent a7ec195587
commit 0b40cf8026
30 changed files with 310 additions and 95 deletions

View File

@@ -10,11 +10,6 @@ import (
"strings"
)
const (
promotedDirectoryMode = 0o755
promotedFileMode = 0o644
)
// ErrAtomicDirectoryPromotionUnsupported indicates that the current operating
// system lacks the atomic no-replace primitive required by PromoteDirectory.
var ErrAtomicDirectoryPromotionUnsupported = errors.New("atomic no-replace directory promotion is unsupported")
@@ -98,7 +93,7 @@ func promoteDirectoryWithHooks(
if err := copyRegularTree(sourceRoot, src, temporary, hooks); err != nil {
return err
}
if err := os.Chmod(temporary, promotedDirectoryMode); err != nil {
if err := os.Chmod(temporary, WorkspaceDirectoryMode); err != nil {
return fmt.Errorf("set temporary root permissions: %w", err)
}
if err := syncDirectory(temporary); err != nil {
@@ -223,13 +218,13 @@ func copyRegularDirectory(
return fmt.Errorf("source directory %q changed while being copied", sourcePath)
}
if err := os.Mkdir(dst, promotedDirectoryMode); err != nil {
if err := os.Mkdir(dst, WorkspaceDirectoryMode); err != nil {
return fmt.Errorf("create destination directory %q: %w", dst, err)
}
if err := copyRegularTree(child, sourcePath, dst, hooks); err != nil {
return err
}
if err := os.Chmod(dst, promotedDirectoryMode); err != nil {
if err := os.Chmod(dst, WorkspaceDirectoryMode); err != nil {
return fmt.Errorf("set destination directory permissions %q: %w", dst, err)
}
if err := syncDirectory(dst); err != nil {
@@ -264,7 +259,7 @@ func copyRegularFile(
return fmt.Errorf("source file %q changed while being copied", sourcePath)
}
out, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_EXCL, promotedFileMode)
out, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_EXCL, WorkspaceFileMode)
if err != nil {
return fmt.Errorf("create destination file %q: %w", dst, err)
}
@@ -278,7 +273,7 @@ func copyRegularFile(
if _, err := io.Copy(out, in); err != nil {
return fmt.Errorf("copy source file %q: %w", sourcePath, err)
}
if err := out.Chmod(promotedFileMode); err != nil {
if err := out.Chmod(WorkspaceFileMode); err != nil {
return fmt.Errorf("set destination file permissions %q: %w", dst, err)
}
if err := out.Sync(); err != nil {

View File

@@ -41,8 +41,8 @@ func TestPromoteDirectoryCopiesNestedRegularTree(t *testing.T) {
if err != nil {
t.Fatalf("Stat(%q) error = %v", path, err)
}
if got := info.Mode().Perm(); got != promotedDirectoryMode {
t.Fatalf("directory mode for %q = %o, want %o", path, got, promotedDirectoryMode)
if got := info.Mode().Perm(); got != WorkspaceDirectoryMode.Perm() {
t.Fatalf("directory mode for %q = %o, want %o", path, got, WorkspaceDirectoryMode.Perm())
}
}
for _, path := range []string{filepath.Join(dst, "a-first.txt"), filepath.Join(dst, "nested", "binary.dat"), filepath.Join(dst, "z-last.txt")} {
@@ -50,8 +50,8 @@ func TestPromoteDirectoryCopiesNestedRegularTree(t *testing.T) {
if err != nil {
t.Fatalf("Stat(%q) error = %v", path, err)
}
if got := info.Mode().Perm(); got != promotedFileMode {
t.Fatalf("file mode for %q = %o, want %o", path, got, promotedFileMode)
if got := info.Mode().Perm(); got != WorkspaceFileMode.Perm() {
t.Fatalf("file mode for %q = %o, want %o", path, got, WorkspaceFileMode.Perm())
}
}
}

View File

@@ -15,7 +15,7 @@ func WriteFileAtomic(dst string, data []byte, perm os.FileMode) error {
if strings.TrimSpace(dst) == "" {
return fmt.Errorf("destination path is required")
}
if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
if err := EnsureWorkspaceDirectory(filepath.Dir(dst)); err != nil {
return fmt.Errorf("create destination directory: %w", err)
}
@@ -71,7 +71,7 @@ func CopyFileAtomicWithChecksum(src, dst string, perm os.FileMode) (string, erro
}
defer func() { _ = in.Close() }()
if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
if err := EnsureWorkspaceDirectory(filepath.Dir(dst)); err != nil {
return "", fmt.Errorf("create destination directory: %w", err)
}
@@ -115,7 +115,7 @@ func InstallDownloadedTempFile(tmpPath, dst string, perm os.FileMode) error {
if strings.TrimSpace(tmpPath) == "" || strings.TrimSpace(dst) == "" {
return fmt.Errorf("temp and destination paths are required")
}
if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
if err := EnsureWorkspaceDirectory(filepath.Dir(dst)); err != nil {
return fmt.Errorf("create destination directory: %w", err)
}
if err := os.Chmod(tmpPath, perm); err != nil {

58
internal/fileops/modes.go Normal file
View File

@@ -0,0 +1,58 @@
package fileops
import (
"fmt"
"os"
"path/filepath"
"strings"
)
// WorkspaceDirectoryMode is the POSIX mode for ordinary, shareable workspace
// directories. The set-group-ID bit preserves the configured workspace group
// for nested files and directories.
const WorkspaceDirectoryMode os.FileMode = os.ModeSetgid | 0o775
// WorkspaceFileMode is the POSIX mode for ordinary, shareable workspace files.
const WorkspaceFileMode os.FileMode = 0o664
// EnsureWorkspaceDirectory creates directory and makes every directory created
// for it conform to the ordinary workspace sharing contract. Existing
// directories retain their owner and group; only their mode is updated.
func EnsureWorkspaceDirectory(directory string) error {
if strings.TrimSpace(directory) == "" {
return fmt.Errorf("workspace directory is required")
}
directory = filepath.Clean(directory)
missing := make([]string, 0)
for current := directory; ; current = filepath.Dir(current) {
info, err := os.Lstat(current)
if err == nil {
if !info.IsDir() {
return fmt.Errorf("workspace directory %q is not a directory", current)
}
break
}
if !os.IsNotExist(err) {
return fmt.Errorf("inspect workspace directory %q: %w", current, err)
}
missing = append(missing, current)
parent := filepath.Dir(current)
if parent == current {
break
}
}
if err := os.MkdirAll(directory, WorkspaceDirectoryMode); err != nil {
return fmt.Errorf("create workspace directory %q: %w", directory, err)
}
for index := len(missing) - 1; index >= 0; index-- {
if err := os.Chmod(missing[index], WorkspaceDirectoryMode); err != nil {
return fmt.Errorf("set workspace directory permissions %q: %w", missing[index], err)
}
}
if err := os.Chmod(directory, WorkspaceDirectoryMode); err != nil {
return fmt.Errorf("set workspace directory permissions %q: %w", directory, err)
}
return nil
}

View File

@@ -0,0 +1,87 @@
//go:build !windows
package fileops
import (
"os"
"path/filepath"
"syscall"
"testing"
)
func TestWorkspaceModesOverrideRestrictiveUmask(t *testing.T) {
restoreUmask := syscall.Umask(0o077)
t.Cleanup(func() { syscall.Umask(restoreUmask) })
root := t.TempDir()
nested := filepath.Join(root, "campaign", "session", "artifacts")
if err := EnsureWorkspaceDirectory(nested); err != nil {
t.Fatalf("EnsureWorkspaceDirectory() error = %v", err)
}
for _, path := range []string{
filepath.Join(root, "campaign"),
filepath.Join(root, "campaign", "session"),
nested,
} {
assertWorkspaceDirectoryMode(t, path)
}
file := filepath.Join(nested, "result.json")
if err := WriteFileAtomic(file, []byte("first"), WorkspaceFileMode); err != nil {
t.Fatalf("WriteFileAtomic(first) error = %v", err)
}
if err := WriteFileAtomic(file, []byte("replacement"), WorkspaceFileMode); err != nil {
t.Fatalf("WriteFileAtomic(replacement) error = %v", err)
}
assertWorkspaceFileMode(t, file)
}
func TestPromoteDirectoryUsesWorkspaceModesUnderRestrictiveUmask(t *testing.T) {
root := t.TempDir()
src := filepath.Join(root, "source")
if err := os.MkdirAll(filepath.Join(src, "nested"), 0o755); err != nil {
t.Fatalf("MkdirAll(source) error = %v", err)
}
if err := os.WriteFile(filepath.Join(src, "nested", "result.json"), []byte("result"), 0o600); err != nil {
t.Fatalf("WriteFile(source) error = %v", err)
}
destinationParent := filepath.Join(root, "workspace", "artifacts", "notarius")
if err := EnsureWorkspaceDirectory(destinationParent); err != nil {
t.Fatalf("EnsureWorkspaceDirectory(destination parent) error = %v", err)
}
restoreUmask := syscall.Umask(0o077)
t.Cleanup(func() { syscall.Umask(restoreUmask) })
destination := filepath.Join(destinationParent, "run-1")
if err := PromoteDirectory(src, destination); err != nil {
t.Fatalf("PromoteDirectory() error = %v", err)
}
assertWorkspaceDirectoryMode(t, destination)
assertWorkspaceDirectoryMode(t, filepath.Join(destination, "nested"))
assertWorkspaceFileMode(t, filepath.Join(destination, "nested", "result.json"))
}
func assertWorkspaceDirectoryMode(t *testing.T, path string) {
t.Helper()
info, err := os.Stat(path)
if err != nil {
t.Fatalf("Stat(%q) error = %v", path, err)
}
if got, want := info.Mode().Perm(), WorkspaceDirectoryMode.Perm(); got != want {
t.Fatalf("directory mode for %q = %o, want %o", path, got, want)
}
if info.Mode()&os.ModeSetgid == 0 {
t.Fatalf("directory mode for %q does not include setgid: %v", path, info.Mode())
}
}
func assertWorkspaceFileMode(t *testing.T, path string) {
t.Helper()
info, err := os.Stat(path)
if err != nil {
t.Fatalf("Stat(%q) error = %v", path, err)
}
if got, want := info.Mode().Perm(), WorkspaceFileMode.Perm(); got != want {
t.Fatalf("file mode for %q = %o, want %o", path, got, want)
}
}