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