1// +build linux
2
3package overlay2 // import "github.com/docker/docker/daemon/graphdriver/overlay2"
4
5import (
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
19func 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.
68func 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}
82