github.com/x04/go/src@v0.0.0-20200202162449-3d481ceb3525/io/ioutil/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 ioutil
     6  
     7  import (
     8  	"github.com/x04/go/src/os"
     9  	"github.com/x04/go/src/path/filepath"
    10  	"github.com/x04/go/src/strconv"
    11  	"github.com/x04/go/src/strings"
    12  	"github.com/x04/go/src/sync"
    13  	"github.com/x04/go/src/time"
    14  )
    15  
    16  // Random number state.
    17  // We generate random temporary file names so that there's a good
    18  // chance the file doesn't exist yet - keeps the number of tries in
    19  // TempFile to a minimum.
    20  var rand uint32
    21  var randmu sync.Mutex
    22  
    23  func reseed() uint32 {
    24  	return uint32(time.Now().UnixNano() + int64(os.Getpid()))
    25  }
    26  
    27  func nextRandom() string {
    28  	randmu.Lock()
    29  	r := rand
    30  	if r == 0 {
    31  		r = reseed()
    32  	}
    33  	r = r*1664525 + 1013904223	// constants from Numerical Recipes
    34  	rand = r
    35  	randmu.Unlock()
    36  	return strconv.Itoa(int(1e9 + r%1e9))[1:]
    37  }
    38  
    39  // TempFile creates a new temporary file in the directory dir,
    40  // opens the file for reading and writing, and returns the resulting *os.File.
    41  // The filename is generated by taking pattern and adding a random
    42  // string to the end. If pattern includes a "*", the random string
    43  // replaces the last "*".
    44  // If dir is the empty string, TempFile uses the default directory
    45  // for temporary files (see os.TempDir).
    46  // Multiple programs calling TempFile simultaneously
    47  // will not choose the same file. The caller can use f.Name()
    48  // to find the pathname of the file. It is the caller's responsibility
    49  // to remove the file when no longer needed.
    50  func TempFile(dir, pattern string) (f *os.File, err error) {
    51  	if dir == "" {
    52  		dir = os.TempDir()
    53  	}
    54  
    55  	prefix, suffix := prefixAndSuffix(pattern)
    56  
    57  	nconflict := 0
    58  	for i := 0; i < 10000; i++ {
    59  		name := filepath.Join(dir, prefix+nextRandom()+suffix)
    60  		f, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
    61  		if os.IsExist(err) {
    62  			if nconflict++; nconflict > 10 {
    63  				randmu.Lock()
    64  				rand = reseed()
    65  				randmu.Unlock()
    66  			}
    67  			continue
    68  		}
    69  		break
    70  	}
    71  	return
    72  }
    73  
    74  // prefixAndSuffix splits pattern by the last wildcard "*", if applicable,
    75  // returning prefix as the part before "*" and suffix as the part after "*".
    76  func prefixAndSuffix(pattern string) (prefix, suffix string) {
    77  	if pos := strings.LastIndex(pattern, "*"); pos != -1 {
    78  		prefix, suffix = pattern[:pos], pattern[pos+1:]
    79  	} else {
    80  		prefix = pattern
    81  	}
    82  	return
    83  }
    84  
    85  // TempDir creates a new temporary directory in the directory dir.
    86  // The directory name is generated by taking pattern and applying a
    87  // random string to the end. If pattern includes a "*", the random string
    88  // replaces the last "*". TempDir returns the name of the new directory.
    89  // If dir is the empty string, TempDir uses the
    90  // default directory for temporary files (see os.TempDir).
    91  // Multiple programs calling TempDir simultaneously
    92  // will not choose the same directory. It is the caller's responsibility
    93  // to remove the directory when no longer needed.
    94  func TempDir(dir, pattern string) (name string, err error) {
    95  	if dir == "" {
    96  		dir = os.TempDir()
    97  	}
    98  
    99  	prefix, suffix := prefixAndSuffix(pattern)
   100  
   101  	nconflict := 0
   102  	for i := 0; i < 10000; i++ {
   103  		try := filepath.Join(dir, prefix+nextRandom()+suffix)
   104  		err = os.Mkdir(try, 0700)
   105  		if os.IsExist(err) {
   106  			if nconflict++; nconflict > 10 {
   107  				randmu.Lock()
   108  				rand = reseed()
   109  				randmu.Unlock()
   110  			}
   111  			continue
   112  		}
   113  		if os.IsNotExist(err) {
   114  			if _, err := os.Stat(dir); os.IsNotExist(err) {
   115  				return "", err
   116  			}
   117  		}
   118  		if err == nil {
   119  			name = try
   120  		}
   121  		break
   122  	}
   123  	return
   124  }