github.com/ssdev-go/moby@v17.12.1-ce-rc2+incompatible/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  	"golang.org/x/sys/unix"
    16  )
    17  
    18  // generateID creates a new random string identifier with the given length
    19  func generateID(l int) 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  				logrus.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  }