// Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package os import ( "errors" "internal/itoa" ) // fastrand provided by runtime. // We generate random temporary file names so that there's a good // chance the file doesn't exist yet - keeps the number of tries in // TempFile to a minimum. func fastrand() uint32 func nextRandom() string { return itoa.Uitoa(uint(fastrand())) } // CreateTemp creates a new temporary file in the directory dir, // opens the file for reading and writing, and returns the resulting file. // The filename is generated by taking pattern and adding a random string to the end. // If pattern includes a "*", the random string replaces the last "*". // If dir is the empty string, CreateTemp uses the default directory for temporary files, as returned by TempDir. // Multiple programs or goroutines calling CreateTemp simultaneously will not choose the same file. // The caller can use the file's Name method to find the pathname of the file. // It is the caller's responsibility to remove the file when it is no longer needed. func CreateTemp(dir, pattern string) (*File, error) { if dir == "" { dir = TempDir() } prefix, suffix, err := prefixAndSuffix(pattern) if err != nil { return nil, &PathError{Op: "createtemp", Path: pattern, Err: err} } prefix = joinPath(dir, prefix) try := 0 for { name := prefix + nextRandom() + suffix f, err := OpenFile(name, O_RDWR|O_CREATE|O_EXCL, 0600) if IsExist(err) { if try++; try < 10000 { continue } return nil, &PathError{Op: "createtemp", Path: dir + string(PathSeparator) + prefix + "*" + suffix, Err: ErrExist} } return f, err } } var errPatternHasSeparator = errors.New("pattern contains path separator") // prefixAndSuffix splits pattern by the last wildcard "*", if applicable, // returning prefix as the part before "*" and suffix as the part after "*". func prefixAndSuffix(pattern string) (prefix, suffix string, err error) { for i := 0; i < len(pattern); i++ { if IsPathSeparator(pattern[i]) { return "", "", errPatternHasSeparator } } if pos := lastIndex(pattern, '*'); pos != -1 { prefix, suffix = pattern[:pos], pattern[pos+1:] } else { prefix = pattern } return prefix, suffix, nil } // MkdirTemp creates a new temporary directory in the directory dir // and returns the pathname of the new directory. // The new directory's name is generated by adding a random string to the end of pattern. // If pattern includes a "*", the random string replaces the last "*" instead. // If dir is the empty string, MkdirTemp uses the default directory for temporary files, as returned by TempDir. // Multiple programs or goroutines calling MkdirTemp simultaneously will not choose the same directory. // It is the caller's responsibility to remove the directory when it is no longer needed. func MkdirTemp(dir, pattern string) (string, error) { if dir == "" { dir = TempDir() } prefix, suffix, err := prefixAndSuffix(pattern) if err != nil { return "", &PathError{Op: "mkdirtemp", Path: pattern, Err: err} } prefix = joinPath(dir, prefix) try := 0 for { name := prefix + nextRandom() + suffix err := Mkdir(name, 0700) if err == nil { return name, nil } if IsExist(err) { if try++; try < 10000 { continue } return "", &PathError{Op: "mkdirtemp", Path: dir + string(PathSeparator) + prefix + "*" + suffix, Err: ErrExist} } if IsNotExist(err) { if _, err := Stat(dir); IsNotExist(err) { return "", err } } return "", err } } func joinPath(dir, name string) string { if len(dir) > 0 && IsPathSeparator(dir[len(dir)-1]) { return dir + name } return dir + string(PathSeparator) + name } // LastIndexByte from the strings package. func lastIndex(s string, sep byte) int { for i := len(s) - 1; i >= 0; i-- { if s[i] == sep { return i } } return -1 }