github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/os/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 os 6 7 import ( 8 "errors" 9 "internal/itoa" 10 "sync" 11 "time" 12 ) 13 14 var minrandPreviousValue uint32 15 var minrandMutex sync.Mutex 16 17 func init() { 18 // Avoid getting same results on every run 19 now := time.Now() 20 seed := uint32(Getpid()) ^ uint32(now.Nanosecond()) ^ uint32(now.Unix()) 21 // initial state must be odd 22 minrandPreviousValue = seed | 1 23 } 24 25 // minrand() is a simple and rather poor placeholder for fastrand() 26 // TODO: provide fastrand in runtime, as go does. It is hard to implement properly elsewhere. 27 func minrand() uint32 { 28 // c++11's minstd_rand 29 // https://en.wikipedia.org/wiki/Linear_congruential_generator 30 // m=2^32, c=0, a=48271 31 minrandMutex.Lock() 32 minrandPreviousValue *= 48271 33 val := minrandPreviousValue 34 minrandMutex.Unlock() 35 return val 36 } 37 38 // We generate random temporary file names so that there's a good 39 // chance the file doesn't exist yet - keeps the number of tries in 40 // TempFile to a minimum. 41 func nextRandom() string { 42 // Discard lower four bits of minrand. 43 // They're not very random, and we don't need the full range here. 44 return itoa.Uitoa(uint(minrand() >> 4)) 45 } 46 47 // CreateTemp creates a new temporary file in the directory dir, 48 // opens the file for reading and writing, and returns the resulting file. 49 // The filename is generated by taking pattern and adding a random string to the end. 50 // If pattern includes a "*", the random string replaces the last "*". 51 // If dir is the empty string, CreateTemp uses the default directory for temporary files, as returned by TempDir. 52 // Multiple programs or goroutines calling CreateTemp simultaneously will not choose the same file. 53 // The caller can use the file's Name method to find the pathname of the file. 54 // It is the caller's responsibility to remove the file when it is no longer needed. 55 func CreateTemp(dir, pattern string) (*File, error) { 56 if dir == "" { 57 dir = TempDir() 58 } 59 60 prefix, suffix, err := prefixAndSuffix(pattern) 61 if err != nil { 62 return nil, &PathError{Op: "createtemp", Path: pattern, Err: err} 63 } 64 prefix = joinPath(dir, prefix) 65 66 try := 0 67 for { 68 name := prefix + nextRandom() + suffix 69 f, err := OpenFile(name, O_RDWR|O_CREATE|O_EXCL, 0600) 70 if IsExist(err) { 71 if try++; try < 10000 { 72 continue 73 } 74 return nil, &PathError{Op: "createtemp", Path: dir + string(PathSeparator) + prefix + "*" + suffix, Err: ErrExist} 75 } 76 return f, err 77 } 78 } 79 80 var errPatternHasSeparator = errors.New("pattern contains path separator") 81 82 // prefixAndSuffix splits pattern by the last wildcard "*", if applicable, 83 // returning prefix as the part before "*" and suffix as the part after "*". 84 func prefixAndSuffix(pattern string) (prefix, suffix string, err error) { 85 for i := 0; i < len(pattern); i++ { 86 if IsPathSeparator(pattern[i]) { 87 return "", "", errPatternHasSeparator 88 } 89 } 90 if pos := lastIndex(pattern, '*'); pos != -1 { 91 prefix, suffix = pattern[:pos], pattern[pos+1:] 92 } else { 93 prefix = pattern 94 } 95 return prefix, suffix, nil 96 } 97 98 // MkdirTemp creates a new temporary directory in the directory dir 99 // and returns the pathname of the new directory. 100 // The new directory's name is generated by adding a random string to the end of pattern. 101 // If pattern includes a "*", the random string replaces the last "*" instead. 102 // If dir is the empty string, MkdirTemp uses the default directory for temporary files, as returned by TempDir. 103 // Multiple programs or goroutines calling MkdirTemp simultaneously will not choose the same directory. 104 // It is the caller's responsibility to remove the directory when it is no longer needed. 105 func MkdirTemp(dir, pattern string) (string, error) { 106 if dir == "" { 107 dir = TempDir() 108 } 109 110 prefix, suffix, err := prefixAndSuffix(pattern) 111 if err != nil { 112 return "", &PathError{Op: "mkdirtemp", Path: pattern, Err: err} 113 } 114 prefix = joinPath(dir, prefix) 115 116 try := 0 117 for { 118 name := prefix + nextRandom() + suffix 119 err := Mkdir(name, 0700) 120 if err == nil { 121 return name, nil 122 } 123 if IsExist(err) { 124 if try++; try < 10000 { 125 continue 126 } 127 return "", &PathError{Op: "mkdirtemp", Path: dir + string(PathSeparator) + prefix + "*" + suffix, Err: ErrExist} 128 } 129 if IsNotExist(err) { 130 if _, err := Stat(dir); IsNotExist(err) { 131 return "", err 132 } 133 } 134 return "", err 135 } 136 } 137 138 func joinPath(dir, name string) string { 139 if len(dir) > 0 && IsPathSeparator(dir[len(dir)-1]) { 140 return dir + name 141 } 142 return dir + string(PathSeparator) + name 143 } 144 145 // LastIndexByte from the strings package. 146 func lastIndex(s string, sep byte) int { 147 for i := len(s) - 1; i >= 0; i-- { 148 if s[i] == sep { 149 return i 150 } 151 } 152 return -1 153 }