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

View as plain text