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

View as plain text