Skip to content

Release Notes v0.7.0

Released: 2026-10-12

Highlights

A cancellable file lock, directory sync and owner-only permission checks in fs, so modules can stop keeping platform-specific files of their own, and a test that enforces that oscompat itself has no dependencies.

New Features

fs.Lock

Problem solved: mutual exclusion between processes on a file needs syscall.Flock on Unix and LockFileEx on Windows, which means a _unix.go, a _windows.go and a stub for everything else in every module that needs it.

Solution:

import "github.com/grokify/oscompat/fs"

unlock, err := fs.Lock(ctx, "/var/lib/myapp/state.lock")
if err != nil {
    return err // the context's error, or fs.ErrLockUnsupported
}
defer unlock()
Behavior Detail
Waiting Polls until the lock is free or the context ends, then returns the context's error
Release Calling the returned function releases it, and is safe to call twice; the operating system also releases it if the process exits, however it exits
Scope Two calls in one process exclude each other, as two processes do
Advisory It excludes only callers that also take it
Unsupported platform fs.ErrLockUnsupported, so a caller can fail closed rather than proceed unlocked

The lock file is created with owner-only permissions.

fs.SyncDir

Problem solved: after writing a file to a temporary name and renaming it over the target, the rename is only durable once the directory is flushed. Unix lets you open a directory and Sync it; Windows does not.

Solution:

if err := fs.SyncDir(filepath.Dir(path)); err != nil {
    return err
}

On Windows, and on platforms with no way to ask, it does nothing and returns nil, because there a rename is atomic and nothing stronger is available.

Permission checks

Problem solved: code that refuses a world-readable key file, and tests that assert 0600, are wrong on Windows, where modes are not enforced.

if fs.PermissionsEnforced() { /* false on Windows */ }

info, _ := os.Stat(path)
if err := fs.CheckPrivate(info); errors.Is(err, fs.ErrNotPrivate) {
    // group or others can access it
}

err := fs.ChmodPrivate(f) // 0600 on an open file
Function Behavior
PermissionsEnforced() Whether the platform enforces mode bits
IsPrivate(info) Owner-only; always true where modes are not enforced
CheckPrivate(info) An error matching ErrNotPrivate that names the mode; nil where modes are not enforced
ChmodPrivate(f) Restricts an open file to its owner; a failure is ignored where modes are not enforced

Policy test

The module imports the standard library and itself, and nothing else, so any module can import it. policy_test.go fails on a require, replace or tool directive, a go.sum, or an import from outside the standard library and the module. See the policy.

Documentation

  • A MkDocs site with a guide for each package.
  • The portable tests guide covers permission assertions with fs.PermissionsEnforced(), closing handles in simulated-crash tests, and the lock and directory sync.

Installation

go get github.com/grokify/oscompat@v0.7.0

Compatibility

No breaking changes. The module still has no dependencies outside the standard library, and fs.Lock and fs.SyncDir use only syscall.