-
Notifications
You must be signed in to change notification settings - Fork 18.3k
Open
Labels
BugReportIssues describing a possible bug in the Go implementation.Issues describing a possible bug in the Go implementation.
Description
Go version
go version go1.24.5 linux/amd64
Output of go env
in your module/workspace:
AR='ar'
CC='gcc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='g++'
GCCGO='gccgo'
GO111MODULE=''
GOAMD64='v1'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN='/home/jono/.local/bin'
GOCACHE='/home/jono/.cache/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/home/jono/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build3907116877=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/home/jono/code/example/go.mod'
GOMODCACHE='/home/jono/.cache/go/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/home/jono/.local/share/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/snap/go/10927'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/home/jono/.config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/snap/go/10927/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.24.5'
GOWORK=''
PKG_CONFIG='pkg-config'
What did you do?
I was just looking at the os.MkdirTemp
code and noticed one of the error messages looked off. It's virtually impossible to trigger the error normally, since it requires the RNG to collide with existing directories 10000 times.
Instead I copied the code into this standalone file, and modified it to build properly (mostly just adding os.
prefixes). Then I swapped out os.Mkdir
with a stub which always returns os.ErrExist
:
package main
import (
"errors"
"math/rand/v2"
"os"
"strconv"
"strings"
)
func Mkdir(name string, perm os.FileMode) error {
return os.ErrExist
}
func nextRandom() string {
return strconv.FormatUint(uint64(rand.Uint32()), 10)
}
func MkdirTemp(dir, pattern string) (string, error) {
if dir == "" {
dir = os.TempDir()
}
prefix, suffix, err := prefixAndSuffix(pattern)
if err != nil {
return "", &os.PathError{Op: "mkdirtemp", Path: pattern, Err: err}
}
prefix = joinPath(dir, prefix)
try := 0
for {
name := prefix + nextRandom() + suffix
err := Mkdir(name, 0700)
if err == nil {
return name, nil
}
if os.IsExist(err) {
if try++; try < 10000 {
continue
}
return "", &os.PathError{Op: "mkdirtemp", Path: dir + string(os.PathSeparator) + prefix + "*" + suffix, Err: os.ErrExist}
}
if os.IsNotExist(err) {
if _, err := os.Stat(dir); os.IsNotExist(err) {
return "", err
}
}
return "", err
}
}
var errPatternHasSeparator = errors.New("pattern contains path separator")
// prefixAndSuffix splits pattern by the last wildcard "*", if applicable,
// returning prefix as the part before "*" and suffix as the part after "*".
func prefixAndSuffix(pattern string) (prefix, suffix string, err error) {
for i := 0; i < len(pattern); i++ {
if os.IsPathSeparator(pattern[i]) {
return "", "", errPatternHasSeparator
}
}
if pos := strings.LastIndexByte(pattern, '*'); pos != -1 {
prefix, suffix = pattern[:pos], pattern[pos+1:]
} else {
prefix = pattern
}
return prefix, suffix, nil
}
func joinPath(dir, name string) string {
if len(dir) > 0 && os.IsPathSeparator(dir[len(dir)-1]) {
return dir + name
}
return dir + string(os.PathSeparator) + name
}
func main() {
if _, err := MkdirTemp("", "foo"); err != nil {
println(err.Error())
}
}
What did you see happen?
$ go run .
mkdirtemp /tmp//tmp/foo*: file already exists
What did you expect to see?
$ go run .
mkdirtemp /tmp/foo*: file already exists
Metadata
Metadata
Assignees
Labels
BugReportIssues describing a possible bug in the Go implementation.Issues describing a possible bug in the Go implementation.