github.com/x04/go/src@v0.0.0-20200202162449-3d481ceb3525/io/ioutil/tempfile.go (about) 1 // Copyright 2010 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package ioutil 6 7 import ( 8 "github.com/x04/go/src/os" 9 "github.com/x04/go/src/path/filepath" 10 "github.com/x04/go/src/strconv" 11 "github.com/x04/go/src/strings" 12 "github.com/x04/go/src/sync" 13 "github.com/x04/go/src/time" 14 ) 15 16 // Random number state. 17 // We generate random temporary file names so that there's a good 18 // chance the file doesn't exist yet - keeps the number of tries in 19 // TempFile to a minimum. 20 var rand uint32 21 var randmu sync.Mutex 22 23 func reseed() uint32 { 24 return uint32(time.Now().UnixNano() + int64(os.Getpid())) 25 } 26 27 func nextRandom() string { 28 randmu.Lock() 29 r := rand 30 if r == 0 { 31 r = reseed() 32 } 33 r = r*1664525 + 1013904223 // constants from Numerical Recipes 34 rand = r 35 randmu.Unlock() 36 return strconv.Itoa(int(1e9 + r%1e9))[1:] 37 } 38 39 // TempFile creates a new temporary file in the directory dir, 40 // opens the file for reading and writing, and returns the resulting *os.File. 41 // The filename is generated by taking pattern and adding a random 42 // string to the end. If pattern includes a "*", the random string 43 // replaces the last "*". 44 // If dir is the empty string, TempFile uses the default directory 45 // for temporary files (see os.TempDir). 46 // Multiple programs calling TempFile simultaneously 47 // will not choose the same file. The caller can use f.Name() 48 // to find the pathname of the file. It is the caller's responsibility 49 // to remove the file when no longer needed. 50 func TempFile(dir, pattern string) (f *os.File, err error) { 51 if dir == "" { 52 dir = os.TempDir() 53 } 54 55 prefix, suffix := prefixAndSuffix(pattern) 56 57 nconflict := 0 58 for i := 0; i < 10000; i++ { 59 name := filepath.Join(dir, prefix+nextRandom()+suffix) 60 f, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) 61 if os.IsExist(err) { 62 if nconflict++; nconflict > 10 { 63 randmu.Lock() 64 rand = reseed() 65 randmu.Unlock() 66 } 67 continue 68 } 69 break 70 } 71 return 72 } 73 74 // prefixAndSuffix splits pattern by the last wildcard "*", if applicable, 75 // returning prefix as the part before "*" and suffix as the part after "*". 76 func prefixAndSuffix(pattern string) (prefix, suffix string) { 77 if pos := strings.LastIndex(pattern, "*"); pos != -1 { 78 prefix, suffix = pattern[:pos], pattern[pos+1:] 79 } else { 80 prefix = pattern 81 } 82 return 83 } 84 85 // TempDir creates a new temporary directory in the directory dir. 86 // The directory name is generated by taking pattern and applying a 87 // random string to the end. If pattern includes a "*", the random string 88 // replaces the last "*". TempDir returns the name of the new directory. 89 // If dir is the empty string, TempDir uses the 90 // default directory for temporary files (see os.TempDir). 91 // Multiple programs calling TempDir simultaneously 92 // will not choose the same directory. It is the caller's responsibility 93 // to remove the directory when no longer needed. 94 func TempDir(dir, pattern string) (name string, err error) { 95 if dir == "" { 96 dir = os.TempDir() 97 } 98 99 prefix, suffix := prefixAndSuffix(pattern) 100 101 nconflict := 0 102 for i := 0; i < 10000; i++ { 103 try := filepath.Join(dir, prefix+nextRandom()+suffix) 104 err = os.Mkdir(try, 0700) 105 if os.IsExist(err) { 106 if nconflict++; nconflict > 10 { 107 randmu.Lock() 108 rand = reseed() 109 randmu.Unlock() 110 } 111 continue 112 } 113 if os.IsNotExist(err) { 114 if _, err := os.Stat(dir); os.IsNotExist(err) { 115 return "", err 116 } 117 } 118 if err == nil { 119 name = try 120 } 121 break 122 } 123 return 124 }