1
0
mirror of https://github.com/golang/go synced 2024-11-26 07:38:00 -07:00

io/ioutil: forward TempFile and TempDir to os package

For #42026
Fixes #44311

Change-Id: I3dabcf902d155f95800b4adf1d7578906a194ce6
Reviewed-on: https://go-review.googlesource.com/c/go/+/285378
Trust: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Emmanuel Odeke <emmanuel@orijtech.com>
This commit is contained in:
Ian Lance Taylor 2021-01-21 16:59:29 -08:00
parent 04edf418d2
commit 07c658316b
3 changed files with 13 additions and 115 deletions

View File

@ -1,7 +0,0 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ioutil
var ErrPatternHasSeparator = errPatternHasSeparator

View File

@ -5,38 +5,9 @@
package ioutil package ioutil
import ( import (
"errors"
"os" "os"
"path/filepath"
"strconv"
"strings"
"sync"
"time"
) )
// Random number state.
// We generate random temporary file names so that there's a good
// chance the file doesn't exist yet - keeps the number of tries in
// TempFile to a minimum.
var rand uint32
var randmu sync.Mutex
func reseed() uint32 {
return uint32(time.Now().UnixNano() + int64(os.Getpid()))
}
func nextRandom() string {
randmu.Lock()
r := rand
if r == 0 {
r = reseed()
}
r = r*1664525 + 1013904223 // constants from Numerical Recipes
rand = r
randmu.Unlock()
return strconv.Itoa(int(1e9 + r%1e9))[1:]
}
// TempFile creates a new temporary file in the directory dir, // TempFile creates a new temporary file in the directory dir,
// opens the file for reading and writing, and returns the resulting *os.File. // opens the file for reading and writing, and returns the resulting *os.File.
// The filename is generated by taking pattern and adding a random // The filename is generated by taking pattern and adding a random
@ -48,48 +19,10 @@ func nextRandom() string {
// will not choose the same file. The caller can use f.Name() // will not choose the same file. The caller can use f.Name()
// to find the pathname of the file. It is the caller's responsibility // to find the pathname of the file. It is the caller's responsibility
// to remove the file when no longer needed. // to remove the file when no longer needed.
//
// As of Go 1.16, this function simply calls os.CreateTemp.
func TempFile(dir, pattern string) (f *os.File, err error) { func TempFile(dir, pattern string) (f *os.File, err error) {
if dir == "" { return os.CreateTemp(dir, pattern)
dir = os.TempDir()
}
prefix, suffix, err := prefixAndSuffix(pattern)
if err != nil {
return
}
nconflict := 0
for i := 0; i < 10000; i++ {
name := filepath.Join(dir, prefix+nextRandom()+suffix)
f, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
if os.IsExist(err) {
if nconflict++; nconflict > 10 {
randmu.Lock()
rand = reseed()
randmu.Unlock()
}
continue
}
break
}
return
}
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) {
if strings.ContainsRune(pattern, os.PathSeparator) {
err = errPatternHasSeparator
return
}
if pos := strings.LastIndex(pattern, "*"); pos != -1 {
prefix, suffix = pattern[:pos], pattern[pos+1:]
} else {
prefix = pattern
}
return
} }
// TempDir creates a new temporary directory in the directory dir. // TempDir creates a new temporary directory in the directory dir.
@ -101,37 +34,8 @@ func prefixAndSuffix(pattern string) (prefix, suffix string, err error) {
// Multiple programs calling TempDir simultaneously // Multiple programs calling TempDir simultaneously
// will not choose the same directory. It is the caller's responsibility // will not choose the same directory. It is the caller's responsibility
// to remove the directory when no longer needed. // to remove the directory when no longer needed.
//
// As of Go 1.16, this function simply calls os.MkdirTemp.
func TempDir(dir, pattern string) (name string, err error) { func TempDir(dir, pattern string) (name string, err error) {
if dir == "" { return os.MkdirTemp(dir, pattern)
dir = os.TempDir()
}
prefix, suffix, err := prefixAndSuffix(pattern)
if err != nil {
return
}
nconflict := 0
for i := 0; i < 10000; i++ {
try := filepath.Join(dir, prefix+nextRandom()+suffix)
err = os.Mkdir(try, 0700)
if os.IsExist(err) {
if nconflict++; nconflict > 10 {
randmu.Lock()
rand = reseed()
randmu.Unlock()
}
continue
}
if os.IsNotExist(err) {
if _, err := os.Stat(dir); os.IsNotExist(err) {
return "", err
}
}
if err == nil {
name = try
}
break
}
return
} }

View File

@ -50,6 +50,9 @@ func TestTempFile_pattern(t *testing.T) {
} }
} }
// This string is from os.errPatternHasSeparator.
const patternHasSeparator = "pattern contains path separator"
func TestTempFile_BadPattern(t *testing.T) { func TestTempFile_BadPattern(t *testing.T) {
tmpDir, err := TempDir("", t.Name()) tmpDir, err := TempDir("", t.Name())
if err != nil { if err != nil {
@ -81,9 +84,8 @@ func TestTempFile_BadPattern(t *testing.T) {
if tt.wantErr { if tt.wantErr {
if err == nil { if err == nil {
t.Errorf("Expected an error for pattern %q", tt.pattern) t.Errorf("Expected an error for pattern %q", tt.pattern)
} } else if !strings.Contains(err.Error(), patternHasSeparator) {
if g, w := err, ErrPatternHasSeparator; g != w { t.Errorf("Error mismatch: got %#v, want %q for pattern %q", err, patternHasSeparator, tt.pattern)
t.Errorf("Error mismatch: got %#v, want %#v for pattern %q", g, w, tt.pattern)
} }
} else if err != nil { } else if err != nil {
t.Errorf("Unexpected error %v for pattern %q", err, tt.pattern) t.Errorf("Unexpected error %v for pattern %q", err, tt.pattern)
@ -183,9 +185,8 @@ func TestTempDir_BadPattern(t *testing.T) {
if tt.wantErr { if tt.wantErr {
if err == nil { if err == nil {
t.Errorf("Expected an error for pattern %q", tt.pattern) t.Errorf("Expected an error for pattern %q", tt.pattern)
} } else if !strings.Contains(err.Error(), patternHasSeparator) {
if g, w := err, ErrPatternHasSeparator; g != w { t.Errorf("Error mismatch: got %#v, want %q for pattern %q", err, patternHasSeparator, tt.pattern)
t.Errorf("Error mismatch: got %#v, want %#v for pattern %q", g, w, tt.pattern)
} }
} else if err != nil { } else if err != nil {
t.Errorf("Unexpected error %v for pattern %q", err, tt.pattern) t.Errorf("Unexpected error %v for pattern %q", err, tt.pattern)