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