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 ioutil
6
7import (
8	"os"
9	"path/filepath"
10	"strconv"
11	"sync"
12	"time"
13)
14
15// Random number state.
16// We generate random temporary file names so that there's a good
17// chance the file doesn't exist yet - keeps the number of tries in
18// TempFile to a minimum.
19var rand uint32
20var randmu sync.Mutex
21
22func reseed() uint32 {
23	return uint32(time.Now().UnixNano() + int64(os.Getpid()))
24}
25
26func nextSuffix() string {
27	randmu.Lock()
28	r := rand
29	if r == 0 {
30		r = reseed()
31	}
32	r = r*1664525 + 1013904223 // constants from Numerical Recipes
33	rand = r
34	randmu.Unlock()
35	return strconv.Itoa(int(1e9 + r%1e9))[1:]
36}
37
38// TempFile creates a new temporary file in the directory dir
39// with a name beginning with prefix, opens the file for reading
40// and writing, and returns the resulting *os.File.
41// If dir is the empty string, TempFile uses the default directory
42// for temporary files (see os.TempDir).
43// Multiple programs calling TempFile simultaneously
44// will not choose the same file.  The caller can use f.Name()
45// to find the pathname of the file.  It is the caller's responsibility
46// to remove the file when no longer needed.
47func TempFile(dir, prefix string) (f *os.File, err error) {
48	if dir == "" {
49		dir = os.TempDir()
50	}
51
52	nconflict := 0
53	for i := 0; i < 10000; i++ {
54		name := filepath.Join(dir, prefix+nextSuffix())
55		f, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
56		if os.IsExist(err) {
57			if nconflict++; nconflict > 10 {
58				randmu.Lock()
59				rand = reseed()
60				randmu.Unlock()
61			}
62			continue
63		}
64		break
65	}
66	return
67}
68
69// TempDir creates a new temporary directory in the directory dir
70// with a name beginning with prefix and returns the path of the
71// new directory.  If dir is the empty string, TempDir uses the
72// default directory for temporary files (see os.TempDir).
73// Multiple programs calling TempDir simultaneously
74// will not choose the same directory.  It is the caller's responsibility
75// to remove the directory when no longer needed.
76func TempDir(dir, prefix string) (name string, err error) {
77	if dir == "" {
78		dir = os.TempDir()
79	}
80
81	nconflict := 0
82	for i := 0; i < 10000; i++ {
83		try := filepath.Join(dir, prefix+nextSuffix())
84		err = os.Mkdir(try, 0700)
85		if os.IsExist(err) {
86			if nconflict++; nconflict > 10 {
87				randmu.Lock()
88				rand = reseed()
89				randmu.Unlock()
90			}
91			continue
92		}
93		if err == nil {
94			name = try
95		}
96		break
97	}
98	return
99}
100