tempfile.mx raw

   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