Source file src/os/tempfile.go

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

View as plain text