github.com/ph/moby@v1.13.1/daemon/graphdriver/overlay2/randomid.go (about)

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