mirror of https://github.com/golang/go.git
io/ioutil: change TempFile prefix to a pattern
Users of TempFile need to be able to supply the suffix, especially when using operating systems that give semantic meaning to the filename extension such as Windows. Renaming the file to include an extension after the fact is insufficient as it could lead to race conditions. If the string given to TempFile includes a "*", the random string replaces the "*". For example "myname.*.bat" will result in a random filename such as "myname.123456.bat". If no "*' is included the old behavior is retained, and the random digits are appended to the end. If multiple "*" are included, the final one is replaced, thus permitting a pathological programmer to create filenames such as "foo*.123456.bat" but not "foo.123456.*.bat" Fixes #4896 Change-Id: Iae7f0980b4de6d7d31b87c8c3c3d40767b283c1f Reviewed-on: https://go-review.googlesource.com/105675 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
9be1921042
commit
191efbc419
|
|
@ -70,6 +70,24 @@ func ExampleTempFile() {
|
|||
}
|
||||
}
|
||||
|
||||
func ExampleTempFile_suffix() {
|
||||
content := []byte("temporary file's content")
|
||||
tmpfile, err := ioutil.TempFile("", "example.*.txt")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
defer os.Remove(tmpfile.Name()) // clean up
|
||||
|
||||
if _, err := tmpfile.Write(content); err != nil {
|
||||
tmpfile.Close()
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := tmpfile.Close(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleReadFile() {
|
||||
content, err := ioutil.ReadFile("testdata/hello")
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
|
@ -23,7 +24,7 @@ func reseed() uint32 {
|
|||
return uint32(time.Now().UnixNano() + int64(os.Getpid()))
|
||||
}
|
||||
|
||||
func nextSuffix() string {
|
||||
func nextRandom() string {
|
||||
randmu.Lock()
|
||||
r := rand
|
||||
if r == 0 {
|
||||
|
|
@ -35,23 +36,32 @@ func nextSuffix() string {
|
|||
return strconv.Itoa(int(1e9 + r%1e9))[1:]
|
||||
}
|
||||
|
||||
// TempFile creates a new temporary file in the directory dir
|
||||
// with a name beginning with prefix, opens the file for reading
|
||||
// and writing, and returns the resulting *os.File.
|
||||
// TempFile creates a new temporary file in the directory dir,
|
||||
// opens the file for reading and writing, and returns the resulting *os.File.
|
||||
// The filename is generated by taking pattern and adding a random
|
||||
// string to the end. If pattern includes a "*", the random string
|
||||
// replaces the last "*".
|
||||
// If dir is the empty string, TempFile uses the default directory
|
||||
// for temporary files (see os.TempDir).
|
||||
// Multiple programs calling TempFile simultaneously
|
||||
// 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 remove the file when no longer needed.
|
||||
func TempFile(dir, prefix string) (f *os.File, err error) {
|
||||
func TempFile(dir, pattern string) (f *os.File, err error) {
|
||||
if dir == "" {
|
||||
dir = os.TempDir()
|
||||
}
|
||||
|
||||
var prefix, suffix string
|
||||
if pos := strings.LastIndex(pattern, "*"); pos != -1 {
|
||||
prefix, suffix = pattern[:pos], pattern[pos+1:]
|
||||
} else {
|
||||
prefix = pattern
|
||||
}
|
||||
|
||||
nconflict := 0
|
||||
for i := 0; i < 10000; i++ {
|
||||
name := filepath.Join(dir, prefix+nextSuffix())
|
||||
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 {
|
||||
|
|
@ -80,7 +90,7 @@ func TempDir(dir, prefix string) (name string, err error) {
|
|||
|
||||
nconflict := 0
|
||||
for i := 0; i < 10000; i++ {
|
||||
try := filepath.Join(dir, prefix+nextSuffix())
|
||||
try := filepath.Join(dir, prefix+nextRandom())
|
||||
err = os.Mkdir(try, 0700)
|
||||
if os.IsExist(err) {
|
||||
if nconflict++; nconflict > 10 {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
|
@ -23,18 +24,26 @@ func TestTempFile(t *testing.T) {
|
|||
if f != nil || err == nil {
|
||||
t.Errorf("TempFile(%q, `foo`) = %v, %v", nonexistentDir, f, err)
|
||||
}
|
||||
}
|
||||
|
||||
dir = os.TempDir()
|
||||
f, err = TempFile(dir, "ioutil_test")
|
||||
if f == nil || err != nil {
|
||||
t.Errorf("TempFile(dir, `ioutil_test`) = %v, %v", f, err)
|
||||
func TestTempFile_pattern(t *testing.T) {
|
||||
tests := []struct{ pattern, prefix, suffix string }{
|
||||
{"ioutil_test", "ioutil_test", ""},
|
||||
{"ioutil_test*", "ioutil_test", ""},
|
||||
{"ioutil_test*xyz", "ioutil_test", "xyz"},
|
||||
}
|
||||
if f != nil {
|
||||
for _, test := range tests {
|
||||
f, err := TempFile("", test.pattern)
|
||||
if err != nil {
|
||||
t.Errorf("TempFile(..., %q) error: %v", test.pattern, err)
|
||||
continue
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
base := filepath.Base(f.Name())
|
||||
f.Close()
|
||||
os.Remove(f.Name())
|
||||
re := regexp.MustCompile("^" + regexp.QuoteMeta(filepath.Join(dir, "ioutil_test")) + "[0-9]+$")
|
||||
if !re.MatchString(f.Name()) {
|
||||
t.Errorf("TempFile(`"+dir+"`, `ioutil_test`) created bad name %s", f.Name())
|
||||
if !(strings.HasPrefix(base, test.prefix) && strings.HasSuffix(base, test.suffix)) {
|
||||
t.Errorf("TempFile pattern %q created bad name %q; want prefix %q & suffix %q",
|
||||
test.pattern, base, test.prefix, test.suffix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue