github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/engine/daemon/graphdriver/overlayutils/randomid.go (about) 1 //go:build linux 2 // +build linux 3 4 package overlayutils // import "github.com/docker/docker/daemon/graphdriver/overlayutils" 5 6 import ( 7 "crypto/rand" 8 "encoding/base32" 9 "fmt" 10 "io" 11 "os" 12 "syscall" 13 "time" 14 15 "github.com/sirupsen/logrus" 16 "golang.org/x/sys/unix" 17 ) 18 19 // GenerateID creates a new random string identifier with the given length 20 func GenerateID(l int, logger *logrus.Entry) string { 21 const ( 22 // ensures we backoff for less than 450ms total. Use the following to 23 // select new value, in units of 10ms: 24 // n*(n+1)/2 = d -> n^2 + n - 2d -> n = (sqrt(8d + 1) - 1)/2 25 maxretries = 9 26 backoff = time.Millisecond * 10 27 ) 28 29 var ( 30 totalBackoff time.Duration 31 count int 32 retries int 33 size = (l*5 + 7) / 8 34 u = make([]byte, size) 35 ) 36 // TODO: Include time component, counter component, random component 37 38 for { 39 // This should never block but the read may fail. Because of this, 40 // we just try to read the random number generator until we get 41 // something. This is a very rare condition but may happen. 42 b := time.Duration(retries) * backoff 43 time.Sleep(b) 44 totalBackoff += b 45 46 n, err := io.ReadFull(rand.Reader, u[count:]) 47 if err != nil { 48 if retryOnError(err) && retries < maxretries { 49 count += n 50 retries++ 51 logger.Errorf("error generating version 4 uuid, retrying: %v", err) 52 continue 53 } 54 55 // Any other errors represent a system problem. What did someone 56 // do to /dev/urandom? 57 panic(fmt.Errorf("error reading random number generator, retried for %v: %v", totalBackoff.String(), err)) 58 } 59 60 break 61 } 62 63 s := base32.StdEncoding.EncodeToString(u) 64 65 return s[:l] 66 } 67 68 // retryOnError tries to detect whether or not retrying would be fruitful. 69 func retryOnError(err error) bool { 70 switch err := err.(type) { 71 case *os.PathError: 72 return retryOnError(err.Err) // unpack the target error 73 case syscall.Errno: 74 if err == unix.EPERM { 75 // EPERM represents an entropy pool exhaustion, a condition under 76 // which we backoff and retry. 77 return true 78 } 79 } 80 81 return false 82 }