github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/model/vfs/rand_suffix.go (about)

     1  package vfs
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"strconv"
     7  	"strings"
     8  	"sync"
     9  	"time"
    10  )
    11  
    12  // Code taken from io/ioutil go package
    13  
    14  // Random number state.
    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  var rand uint32
    19  var randmu sync.Mutex
    20  
    21  func nextSuffix() string {
    22  	randmu.Lock()
    23  	r := rand
    24  	if r == 0 {
    25  		r = reseed()
    26  	}
    27  	r = r*1664525 + 1013904223 // constants from Numerical Recipes
    28  	rand = r
    29  	randmu.Unlock()
    30  	return strconv.Itoa(int(1e9 + r%1e9))[1:]
    31  }
    32  
    33  func reseed() uint32 {
    34  	return uint32(time.Now().UnixNano() + int64(os.Getpid()))
    35  }
    36  
    37  // tryOrUseSuffix will try the given function until it succeed without
    38  // an os.ErrExist error. It is used for renaming safely a file without
    39  // collision.
    40  func tryOrUseSuffix(name, format string, do func(suffix string) error) error {
    41  	var err error
    42  	nconflict := 0
    43  	for i := 0; i < 1000; i++ {
    44  		var newname string
    45  		if i == 0 {
    46  			newname = name
    47  		} else {
    48  			newname = fmt.Sprintf(format, name, nextSuffix())
    49  		}
    50  		err = do(newname)
    51  		if !os.IsExist(err) {
    52  			break
    53  		}
    54  		if nconflict++; nconflict > 10 {
    55  			randmu.Lock()
    56  			rand = reseed()
    57  			randmu.Unlock()
    58  		}
    59  	}
    60  	return err
    61  }
    62  
    63  func stripConflictSuffix(name string) string {
    64  	loc := strings.LastIndex(name, "(")
    65  	if loc <= 0 {
    66  		return name
    67  	}
    68  	for i := loc; i < len(name); i++ {
    69  		switch name[i] {
    70  		case '(', ')', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
    71  			// OK
    72  		default:
    73  			return name
    74  		}
    75  	}
    76  	return name[:loc-1]
    77  }