github.com/geraldss/go/src@v0.0.0-20210511222824-ac7d0ebfc235/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 "errors" 8 9 // fastrand provided by runtime. 10 // We generate random temporary file names so that there's a good 11 // chance the file doesn't exist yet - keeps the number of tries in 12 // TempFile to a minimum. 13 func fastrand() uint32 14 15 func nextRandom() string { 16 return uitoa(uint(fastrand())) 17 } 18 19 // CreateTemp creates a new temporary file in the directory dir, 20 // opens the file for reading and writing, and returns the resulting file. 21 // The filename is generated by taking pattern and adding a random string to the end. 22 // If pattern includes a "*", the random string replaces the last "*". 23 // If dir is the empty string, CreateTemp uses the default directory for temporary files, as returned by TempDir. 24 // Multiple programs or goroutines calling CreateTemp simultaneously will not choose the same file. 25 // The caller can use the file's Name method to find the pathname of the file. 26 // It is the caller's responsibility to remove the file when it is no longer needed. 27 func CreateTemp(dir, pattern string) (*File, error) { 28 if dir == "" { 29 dir = TempDir() 30 } 31 32 prefix, suffix, err := prefixAndSuffix(pattern) 33 if err != nil { 34 return nil, &PathError{Op: "createtemp", Path: pattern, Err: err} 35 } 36 prefix = joinPath(dir, prefix) 37 38 try := 0 39 for { 40 name := prefix + nextRandom() + suffix 41 f, err := OpenFile(name, O_RDWR|O_CREATE|O_EXCL, 0600) 42 if IsExist(err) { 43 if try++; try < 10000 { 44 continue 45 } 46 return nil, &PathError{Op: "createtemp", Path: dir + string(PathSeparator) + prefix + "*" + suffix, Err: ErrExist} 47 } 48 return f, err 49 } 50 } 51 52 var errPatternHasSeparator = errors.New("pattern contains path separator") 53 54 // prefixAndSuffix splits pattern by the last wildcard "*", if applicable, 55 // returning prefix as the part before "*" and suffix as the part after "*". 56 func prefixAndSuffix(pattern string) (prefix, suffix string, err error) { 57 for i := 0; i < len(pattern); i++ { 58 if IsPathSeparator(pattern[i]) { 59 return "", "", errPatternHasSeparator 60 } 61 } 62 if pos := lastIndex(pattern, '*'); pos != -1 { 63 prefix, suffix = pattern[:pos], pattern[pos+1:] 64 } else { 65 prefix = pattern 66 } 67 return prefix, suffix, nil 68 } 69 70 // MkdirTemp creates a new temporary directory in the directory dir 71 // and returns the pathname of the new directory. 72 // The new directory's name is generated by adding a random string to the end of pattern. 73 // If pattern includes a "*", the random string replaces the last "*" instead. 74 // If dir is the empty string, MkdirTemp uses the default directory for temporary files, as returned by TempDir. 75 // Multiple programs or goroutines calling MkdirTemp simultaneously will not choose the same directory. 76 // It is the caller's responsibility to remove the directory when it is no longer needed. 77 func MkdirTemp(dir, pattern string) (string, error) { 78 if dir == "" { 79 dir = TempDir() 80 } 81 82 prefix, suffix, err := prefixAndSuffix(pattern) 83 if err != nil { 84 return "", &PathError{Op: "mkdirtemp", Path: pattern, Err: err} 85 } 86 prefix = joinPath(dir, prefix) 87 88 try := 0 89 for { 90 name := prefix + nextRandom() + suffix 91 err := Mkdir(name, 0700) 92 if err == nil { 93 return name, nil 94 } 95 if IsExist(err) { 96 if try++; try < 10000 { 97 continue 98 } 99 return "", &PathError{Op: "mkdirtemp", Path: dir + string(PathSeparator) + prefix + "*" + suffix, Err: ErrExist} 100 } 101 if IsNotExist(err) { 102 if _, err := Stat(dir); IsNotExist(err) { 103 return "", err 104 } 105 } 106 return "", err 107 } 108 } 109 110 func joinPath(dir, name string) string { 111 if len(dir) > 0 && IsPathSeparator(dir[len(dir)-1]) { 112 return dir + name 113 } 114 return dir + string(PathSeparator) + name 115 } 116 117 // LastIndexByte from the strings package. 118 func lastIndex(s string, sep byte) int { 119 for i := len(s) - 1; i >= 0; i-- { 120 if s[i] == sep { 121 return i 122 } 123 } 124 return -1 125 }