]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/idtools/idtools.go
a1301ee976b9f34fa61b741c6f7f6415e2a2b474
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / fsouza / go-dockerclient / external / github.com / docker / docker / pkg / idtools / idtools.go
1 package idtools
2
3 import (
4 "bufio"
5 "fmt"
6 "os"
7 "sort"
8 "strconv"
9 "strings"
10 )
11
12 // IDMap contains a single entry for user namespace range remapping. An array
13 // of IDMap entries represents the structure that will be provided to the Linux
14 // kernel for creating a user namespace.
15 type IDMap struct {
16 ContainerID int `json:"container_id"`
17 HostID int `json:"host_id"`
18 Size int `json:"size"`
19 }
20
21 type subIDRange struct {
22 Start int
23 Length int
24 }
25
26 type ranges []subIDRange
27
28 func (e ranges) Len() int { return len(e) }
29 func (e ranges) Swap(i, j int) { e[i], e[j] = e[j], e[i] }
30 func (e ranges) Less(i, j int) bool { return e[i].Start < e[j].Start }
31
32 const (
33 subuidFileName string = "/etc/subuid"
34 subgidFileName string = "/etc/subgid"
35 )
36
37 // MkdirAllAs creates a directory (include any along the path) and then modifies
38 // ownership to the requested uid/gid. If the directory already exists, this
39 // function will still change ownership to the requested uid/gid pair.
40 func MkdirAllAs(path string, mode os.FileMode, ownerUID, ownerGID int) error {
41 return mkdirAs(path, mode, ownerUID, ownerGID, true, true)
42 }
43
44 // MkdirAllNewAs creates a directory (include any along the path) and then modifies
45 // ownership ONLY of newly created directories to the requested uid/gid. If the
46 // directories along the path exist, no change of ownership will be performed
47 func MkdirAllNewAs(path string, mode os.FileMode, ownerUID, ownerGID int) error {
48 return mkdirAs(path, mode, ownerUID, ownerGID, true, false)
49 }
50
51 // MkdirAs creates a directory and then modifies ownership to the requested uid/gid.
52 // If the directory already exists, this function still changes ownership
53 func MkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int) error {
54 return mkdirAs(path, mode, ownerUID, ownerGID, false, true)
55 }
56
57 // GetRootUIDGID retrieves the remapped root uid/gid pair from the set of maps.
58 // If the maps are empty, then the root uid/gid will default to "real" 0/0
59 func GetRootUIDGID(uidMap, gidMap []IDMap) (int, int, error) {
60 var uid, gid int
61
62 if uidMap != nil {
63 xUID, err := ToHost(0, uidMap)
64 if err != nil {
65 return -1, -1, err
66 }
67 uid = xUID
68 }
69 if gidMap != nil {
70 xGID, err := ToHost(0, gidMap)
71 if err != nil {
72 return -1, -1, err
73 }
74 gid = xGID
75 }
76 return uid, gid, nil
77 }
78
79 // ToContainer takes an id mapping, and uses it to translate a
80 // host ID to the remapped ID. If no map is provided, then the translation
81 // assumes a 1-to-1 mapping and returns the passed in id
82 func ToContainer(hostID int, idMap []IDMap) (int, error) {
83 if idMap == nil {
84 return hostID, nil
85 }
86 for _, m := range idMap {
87 if (hostID >= m.HostID) && (hostID <= (m.HostID + m.Size - 1)) {
88 contID := m.ContainerID + (hostID - m.HostID)
89 return contID, nil
90 }
91 }
92 return -1, fmt.Errorf("Host ID %d cannot be mapped to a container ID", hostID)
93 }
94
95 // ToHost takes an id mapping and a remapped ID, and translates the
96 // ID to the mapped host ID. If no map is provided, then the translation
97 // assumes a 1-to-1 mapping and returns the passed in id #
98 func ToHost(contID int, idMap []IDMap) (int, error) {
99 if idMap == nil {
100 return contID, nil
101 }
102 for _, m := range idMap {
103 if (contID >= m.ContainerID) && (contID <= (m.ContainerID + m.Size - 1)) {
104 hostID := m.HostID + (contID - m.ContainerID)
105 return hostID, nil
106 }
107 }
108 return -1, fmt.Errorf("Container ID %d cannot be mapped to a host ID", contID)
109 }
110
111 // CreateIDMappings takes a requested user and group name and
112 // using the data from /etc/sub{uid,gid} ranges, creates the
113 // proper uid and gid remapping ranges for that user/group pair
114 func CreateIDMappings(username, groupname string) ([]IDMap, []IDMap, error) {
115 subuidRanges, err := parseSubuid(username)
116 if err != nil {
117 return nil, nil, err
118 }
119 subgidRanges, err := parseSubgid(groupname)
120 if err != nil {
121 return nil, nil, err
122 }
123 if len(subuidRanges) == 0 {
124 return nil, nil, fmt.Errorf("No subuid ranges found for user %q", username)
125 }
126 if len(subgidRanges) == 0 {
127 return nil, nil, fmt.Errorf("No subgid ranges found for group %q", groupname)
128 }
129
130 return createIDMap(subuidRanges), createIDMap(subgidRanges), nil
131 }
132
133 func createIDMap(subidRanges ranges) []IDMap {
134 idMap := []IDMap{}
135
136 // sort the ranges by lowest ID first
137 sort.Sort(subidRanges)
138 containerID := 0
139 for _, idrange := range subidRanges {
140 idMap = append(idMap, IDMap{
141 ContainerID: containerID,
142 HostID: idrange.Start,
143 Size: idrange.Length,
144 })
145 containerID = containerID + idrange.Length
146 }
147 return idMap
148 }
149
150 func parseSubuid(username string) (ranges, error) {
151 return parseSubidFile(subuidFileName, username)
152 }
153
154 func parseSubgid(username string) (ranges, error) {
155 return parseSubidFile(subgidFileName, username)
156 }
157
158 func parseSubidFile(path, username string) (ranges, error) {
159 var rangeList ranges
160
161 subidFile, err := os.Open(path)
162 if err != nil {
163 return rangeList, err
164 }
165 defer subidFile.Close()
166
167 s := bufio.NewScanner(subidFile)
168 for s.Scan() {
169 if err := s.Err(); err != nil {
170 return rangeList, err
171 }
172
173 text := strings.TrimSpace(s.Text())
174 if text == "" {
175 continue
176 }
177 parts := strings.Split(text, ":")
178 if len(parts) != 3 {
179 return rangeList, fmt.Errorf("Cannot parse subuid/gid information: Format not correct for %s file", path)
180 }
181 if parts[0] == username {
182 // return the first entry for a user; ignores potential for multiple ranges per user
183 startid, err := strconv.Atoi(parts[1])
184 if err != nil {
185 return rangeList, fmt.Errorf("String to int conversion failed during subuid/gid parsing of %s: %v", path, err)
186 }
187 length, err := strconv.Atoi(parts[2])
188 if err != nil {
189 return rangeList, fmt.Errorf("String to int conversion failed during subuid/gid parsing of %s: %v", path, err)
190 }
191 rangeList = append(rangeList, subIDRange{startid, length})
192 }
193 }
194 return rangeList, nil
195 }