github.com/richardwilkes/toolbox@v1.121.0/xio/fs/internal/temp.go (about)

     1  // Copyright (c) 2016-2024 by Richard A. Wilkes. All rights reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the Mozilla Public
     4  // License, version 2.0. If a copy of the MPL was not distributed with
     5  // this file, You can obtain one at http://mozilla.org/MPL/2.0/.
     6  //
     7  // This Source Code Form is "Incompatible With Secondary Licenses", as
     8  // defined by the Mozilla Public License, version 2.0.
     9  
    10  package internal
    11  
    12  import (
    13  	"errors"
    14  	"math"
    15  	"os"
    16  	"path/filepath"
    17  	"strconv"
    18  	"strings"
    19  
    20  	"github.com/richardwilkes/toolbox/xmath/rand"
    21  )
    22  
    23  // CreateTemp is essentially the same as os.CreateTemp, except it allows you to specify the file mode of the newly
    24  // created file. This is here solely because having it in the fs package would cause circular references.
    25  func CreateTemp(dir, pattern string, perm os.FileMode) (*os.File, error) {
    26  	if dir == "" {
    27  		dir = os.TempDir()
    28  	}
    29  	for i := 0; i < len(pattern); i++ {
    30  		if os.IsPathSeparator(pattern[i]) {
    31  			return nil, &os.PathError{Op: "createtemp", Path: pattern, Err: errors.New("pattern contains path separator")}
    32  		}
    33  	}
    34  	var prefix, suffix string
    35  	if pos := strings.LastIndexByte(pattern, '*'); pos != -1 {
    36  		prefix, suffix = pattern[:pos], pattern[pos+1:]
    37  	} else {
    38  		prefix = pattern
    39  	}
    40  	if dir != "" && os.IsPathSeparator(dir[len(dir)-1]) {
    41  		prefix = dir + prefix
    42  	} else {
    43  		prefix = filepath.Join(dir, prefix)
    44  	}
    45  	try := 0
    46  	for {
    47  		f, err := os.OpenFile(prefix+strconv.Itoa(rand.NewCryptoRand().Intn(math.MaxInt))+suffix,
    48  			os.O_RDWR|os.O_CREATE|os.O_EXCL, perm)
    49  		if os.IsExist(err) {
    50  			try++
    51  			if try < 1000 {
    52  				continue
    53  			}
    54  			return nil, &os.PathError{Op: "createtemp", Path: prefix + "*" + suffix, Err: os.ErrExist}
    55  		}
    56  		return f, err
    57  	}
    58  }