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