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 // lastIndex 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 }
154