Share bounded regular file read mechanics
This commit is contained in:
75
source_file.go
Normal file
75
source_file.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package configkit
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
)
|
||||
|
||||
type sourceReadFailureKind uint8
|
||||
|
||||
const (
|
||||
sourceReadIO sourceReadFailureKind = iota
|
||||
sourceReadNotRegular
|
||||
sourceReadLimit
|
||||
)
|
||||
|
||||
// sourceReadFailure never escapes the private source boundary. missingTarget is
|
||||
// set only for preflight/open failures; a later missing-file error is not optional.
|
||||
type sourceReadFailure struct {
|
||||
kind sourceReadFailureKind
|
||||
cause error
|
||||
missingTarget bool
|
||||
}
|
||||
|
||||
// readRegularFile borrows stat/open operations so root-bound callers retain their
|
||||
// containment policy. Preflight rejects FIFOs before open can block. The opened
|
||||
// handle is checked again and always closed; read-only close errors are ignored.
|
||||
func readRegularFile(name string, stat func(string) (fs.FileInfo, error), open func(string) (*os.File, error), limit int64) ([]byte, *sourceReadFailure) {
|
||||
info, err := stat(name)
|
||||
if err != nil {
|
||||
return nil, &sourceReadFailure{cause: err, missingTarget: errors.Is(err, fs.ErrNotExist)}
|
||||
}
|
||||
if !info.Mode().IsRegular() {
|
||||
return nil, &sourceReadFailure{kind: sourceReadNotRegular}
|
||||
}
|
||||
file, err := open(name)
|
||||
if err != nil {
|
||||
return nil, &sourceReadFailure{cause: err, missingTarget: errors.Is(err, fs.ErrNotExist)}
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
info, err = file.Stat()
|
||||
if err != nil {
|
||||
return nil, &sourceReadFailure{cause: err}
|
||||
}
|
||||
if !info.Mode().IsRegular() {
|
||||
return nil, &sourceReadFailure{kind: sourceReadNotRegular}
|
||||
}
|
||||
if info.Size() > limit {
|
||||
return nil, &sourceReadFailure{kind: sourceReadLimit}
|
||||
}
|
||||
return readBoundedSource(file, limit)
|
||||
}
|
||||
|
||||
func readBoundedSource(input io.Reader, limit int64) ([]byte, *sourceReadFailure) {
|
||||
data, err := io.ReadAll(io.LimitReader(input, limit+1))
|
||||
if err != nil {
|
||||
return nil, &sourceReadFailure{cause: err}
|
||||
}
|
||||
if int64(len(data)) > limit {
|
||||
return nil, &sourceReadFailure{kind: sourceReadLimit}
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func sourceReadDiagnostic(failure *sourceReadFailure, sourceID string, limitCode Code, limitMessage string) Diagnostic {
|
||||
switch failure.kind {
|
||||
case sourceReadNotRegular:
|
||||
return sourceDiagnostic(CodeSourceNotRegular, sourceID, "source is not a regular file")
|
||||
case sourceReadLimit:
|
||||
return sourceDiagnostic(limitCode, sourceID, limitMessage)
|
||||
default:
|
||||
return classifySourceError(failure.cause, sourceID)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user