internal/filetx/filetx.go
Rama main · réplica de lectura del repositorio original.
package filetx
import (
"context"
"fmt"
"io"
"math"
"os"
"path/filepath"
"time"
"github.com/gofrs/flock"
)
const (
dirMode = 0o700
fileMode = 0o600
lockWait time.Duration = 5_000_000_000
retryWait time.Duration = 10_000_000
)
func Exclusive(ctx context.Context, path string, action func() error) (err error) {
if makeErr := os.MkdirAll(filepath.Dir(path), dirMode); makeErr != nil {
return makeErr
}
guard := flock.New(path+".lock", flock.SetPermissions(fileMode))
ctx, cancel := context.WithTimeout(ctx, lockWait)
defer cancel()
_, lockErr := guard.TryLockContext(ctx, retryWait)
if lockErr != nil {
return lockErr
}
defer func() {
unlockErr := guard.Unlock()
if err == nil {
err = unlockErr
}
}()
return action()
}
func Read(path string, limit int64) ([]byte, error) {
if limit < 1 {
return nil, fmt.Errorf("read %s: invalid byte limit %d", path, limit)
}
info, err := os.Stat(path)
if err != nil {
return nil, err
}
if info.Mode().IsRegular() && info.Size() > limit {
return nil, fmt.Errorf("%s is larger than %d bytes", path, limit)
}
file, err := os.Open(path)
if err != nil {
return nil, err
}
return read(path, file, limit)
}
func read(path string, file io.ReadCloser, limit int64) ([]byte, error) {
reader := io.Reader(file)
if limit < math.MaxInt64 {
reader = io.LimitReader(file, limit+1)
}
body, readErr := io.ReadAll(reader)
closeErr := file.Close()
if readErr != nil {
return nil, readErr
}
if closeErr != nil {
return nil, closeErr
}
if int64(len(body)) > limit {
return nil, fmt.Errorf("%s is larger than %d bytes", path, limit)
}
return body, nil
}